From 0c9c1c013e9060ccafca9ac4c22031e687b6c984 Mon Sep 17 00:00:00 2001 From: cooldome Date: Sun, 29 Apr 2018 22:57:05 +0100 Subject: Add a test --- tests/float/tfloatrange.nim | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 tests/float/tfloatrange.nim (limited to 'tests') diff --git a/tests/float/tfloatrange.nim b/tests/float/tfloatrange.nim new file mode 100644 index 000000000..56aaf7450 --- /dev/null +++ b/tests/float/tfloatrange.nim @@ -0,0 +1,37 @@ +discard """ + cmd: "nim c -d:release --rangeChecks:on $file" + output: '''StrictPositiveRange +float +range fail expected +''' +""" +import math, fenv + +type + Positive = range[0.0..Inf] + StrictPositive = range[minimumPositiveValue(float)..Inf] + Negative32 = range[-maximumPositiveValue(float32) .. -1.0'f32] + +proc myoverload(x: float) = + echo "float" + +proc myoverload(x: Positive) = + echo "PositiveRange" + +proc myoverload(x: StrictPositive) = + echo "StrictPositiveRange" + + +let x = 9.0.StrictPositive +myoverload(x) +myoverload(9.0) + +doAssert(sqrt(x) == 3.0) + +var z = -10.0 +try: + myoverload(StrictPositive(z)) +except: + echo "range fail expected" + + -- cgit 1.4.1-2-gfad0 From 0ef93bdea4dba268e7e30e31aab7922ee55168f7 Mon Sep 17 00:00:00 2001 From: cooldome Date: Sun, 29 Apr 2018 23:26:21 +0100 Subject: update test --- tests/float/tfloatrange.nim | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/float/tfloatrange.nim b/tests/float/tfloatrange.nim index 56aaf7450..e8ea1912e 100644 --- a/tests/float/tfloatrange.nim +++ b/tests/float/tfloatrange.nim @@ -3,6 +3,7 @@ discard """ output: '''StrictPositiveRange float range fail expected +range fail expected ''' """ import math, fenv @@ -21,7 +22,6 @@ proc myoverload(x: Positive) = proc myoverload(x: StrictPositive) = echo "StrictPositiveRange" - let x = 9.0.StrictPositive myoverload(x) myoverload(9.0) @@ -35,3 +35,15 @@ except: echo "range fail expected" +proc strictOnlyProc(x: StrictPositive): bool = + if x > 1.0: true else: false + +let x2 = 5.0.Positive +doAssert(strictOnlyProc(x2)) + +try: + let x4 = 0.0.Positive + discard strictOnlyProc(x4) +except: + echo "range fail expected" + \ No newline at end of file -- cgit 1.4.1-2-gfad0 From 06122ff7116e699a50c968bb0192e6faf00fa6ef Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 29 May 2018 01:18:50 +0200 Subject: rewrote nimeval.nim; added tcompilerapi example to show how the compiler can be used as an API --- changelog.md | 5 ++ compiler/nimeval.nim | 127 ++++++++++++++++++++++++++++++------- tests/compilerapi/exposed.nim | 3 + tests/compilerapi/myscript.nim | 7 ++ tests/compilerapi/tcompilerapi.nim | 36 +++++++++++ 5 files changed, 155 insertions(+), 23 deletions(-) create mode 100644 tests/compilerapi/exposed.nim create mode 100644 tests/compilerapi/myscript.nim create mode 100644 tests/compilerapi/tcompilerapi.nim (limited to 'tests') diff --git a/changelog.md b/changelog.md index 813c4caf8..46a221700 100644 --- a/changelog.md +++ b/changelog.md @@ -78,6 +78,11 @@ - Added the parameter ``val`` for the ``CritBitTree[int].inc`` proc. - An exception raised from ``test`` block of ``unittest`` now show its type in error message +- The ``compiler/nimeval`` API was rewritten to simplify the "compiler as an + API". Using the Nim compiler and its VM as a scripting engine has never been + easier. See ``tests/compilerapi/tcompilerapi.nim`` for an example of how to + use the Nim VM in a native Nim application. + ### Language additions diff --git a/compiler/nimeval.nim b/compiler/nimeval.nim index 714efcdda..d5df6a9df 100644 --- a/compiler/nimeval.nim +++ b/compiler/nimeval.nim @@ -1,7 +1,7 @@ # # # The Nim Compiler -# (c) Copyright 2013 Andreas Rumpf +# (c) Copyright 2018 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -9,27 +9,108 @@ ## exposes the Nim VM to clients. import - ast, modules, passes, passaux, condsyms, - options, nimconf, sem, semdata, llstream, vm, modulegraphs, idents - -proc execute*(program: string) = - passes.gIncludeFile = includeModule - passes.gImportModule = importModule - initDefines() - loadConfigs(DefaultConfig) - - initDefines() - defineSymbol("nimvm") - defineSymbol("nimscript") - when hasFFI: defineSymbol("nimffi") - registerPass(verbosePass) - registerPass(semPass) - registerPass(evalPass) - - searchPaths.add options.libpath - var graph = newModuleGraph() + ast, astalgo, modules, passes, condsyms, + options, sem, semdata, llstream, vm, vmdef, + modulegraphs, idents, os + +type + Interpreter* = ref object ## Use Nim as an interpreter with this object + mainModule: PSym + graph: ModuleGraph + scriptName: string + +iterator exportedSymbols*(i: Interpreter): PSym = + assert i != nil + assert i.mainModule != nil, "no main module selected" + var it: TTabIter + var s = initTabIter(it, i.mainModule.tab) + while s != nil: + yield s + s = nextIter(it, i.mainModule.tab) + +proc selectUniqueSymbol*(i: Interpreter; name: string; + symKinds: set[TSymKind]): PSym = + ## Can be used to access a unique symbol of ``name`` and + ## the given ``symKinds`` filter. + assert i != nil + assert i.mainModule != nil, "no main module selected" + let n = getIdent(i.graph.cache, name) + var it: TIdentIter + var s = initIdentIter(it, i.mainModule.tab, n) + result = nil + while s != nil: + if s.kind in symKinds: + if result == nil: result = s + else: return nil # ambiguous + s = nextIdentIter(it, i.mainModule.tab) + +proc selectRoutine*(i: Interpreter; name: string): PSym = + ## Selects a declared rountine (proc/func/etc) from the main module. + ## The routine needs to have the export marker ``*``. The only matching + ## routine is returned and ``nil`` if it is overloaded. + result = selectUniqueSymbol(i, name, {skTemplate, skMacro, skFunc, + skMethod, skProc, skConverter}) + +proc callRoutine*(i: Interpreter; routine: PSym; args: openArray[PNode]): PNode = + assert i != nil + result = vm.execProc(PCtx i.graph.vm, routine, args) + +proc declareRoutine*(i: Interpreter; pkg, module, name: string; + impl: proc (a: VmArgs) {.closure, gcsafe.}) = + assert i != nil + let vm = PCtx(i.graph.vm) + vm.registerCallback(pkg & "." & module & "." & name, impl) + +proc evalScript*(i: Interpreter; scriptStream: PLLStream = nil) = + ## This can also be used to *reload* the script. + assert i != nil + assert i.mainModule != nil, "no main module selected" + initStrTable(i.mainModule.tab) + i.mainModule.ast = nil + + let s = if scriptStream != nil: scriptStream + else: llStreamOpen(findFile(i.graph.config, i.scriptName), fmRead) + processModule(i.graph, i.mainModule, s, nil, i.graph.cache) + +proc findNimStdLib*(): string = + ## Tries to find a path to a valid "system.nim" file. + ## Returns "" on failure. + try: + let nimexe = os.findExe("nim") + if nimexe.len == 0: return "" + result = nimexe.splitPath()[0] /../ "lib" + if not fileExists(result / "system.nim"): + when defined(unix): + result = nimexe.expandSymlink.splitPath()[0] /../ "lib" + if not fileExists(result / "system.nim"): return "" + except OSError, ValueError: + return "" + +proc createInterpreter*(scriptName: string; + searchPaths: openArray[string]; + flags: TSandboxFlags = {}): Interpreter = + var conf = newConfigRef() var cache = newIdentCache() - var m = makeStdinModule(graph) + var graph = newModuleGraph(cache, conf) + initDefines(conf.symbols) + defineSymbol(conf.symbols, "nimscript") + defineSymbol(conf.symbols, "nimconfig") + registerPass(graph, semPass) + registerPass(graph, evalPass) + + for p in searchPaths: + conf.searchPaths.add(p) + if conf.libpath.len == 0: conf.libpath = p + + var m = graph.makeModule(scriptName) incl(m.flags, sfMainModule) - compileSystemModule(graph,cache) - processModule(graph,m, llStreamOpen(program), nil, cache) + var vm = newCtx(m, cache, graph) + vm.mode = emRepl + vm.features = flags + graph.vm = vm + graph.compileSystemModule(cache) + result = Interpreter(mainModule: m, graph: graph, scriptName: scriptName) + +proc destroyInterpreter*(i: Interpreter) = + ## destructor. + discard "currently nothing to do." diff --git a/tests/compilerapi/exposed.nim b/tests/compilerapi/exposed.nim new file mode 100644 index 000000000..73becd93d --- /dev/null +++ b/tests/compilerapi/exposed.nim @@ -0,0 +1,3 @@ + +proc addFloats*(x, y, z: float): float = + discard "implementation overriden by tcompilerapi.nim" diff --git a/tests/compilerapi/myscript.nim b/tests/compilerapi/myscript.nim new file mode 100644 index 000000000..083385b6f --- /dev/null +++ b/tests/compilerapi/myscript.nim @@ -0,0 +1,7 @@ + +import exposed + +echo "top level statements are executed!" + +proc hostProgramRunsThis*(a, b: float): float = + result = addFloats(a, b, 1.0) diff --git a/tests/compilerapi/tcompilerapi.nim b/tests/compilerapi/tcompilerapi.nim new file mode 100644 index 000000000..00c9bc523 --- /dev/null +++ b/tests/compilerapi/tcompilerapi.nim @@ -0,0 +1,36 @@ +discard """ + output: '''top level statements are executed! +2.0 +''' +""" + +## Example program that demonstrates how to use the +## compiler as an API to embed into your own projects. + +import "../../compiler" / [ast, vmdef, vm, nimeval] +import std / [os] + +proc main() = + let std = findNimStdLib() + if std.len == 0: + quit "cannot find Nim's standard library" + + var intr = createInterpreter("myscript.nim", [std, getAppDir()]) + intr.declareRoutine("*", "exposed", "addFloats", proc (a: VmArgs) = + setResult(a, getFloat(a, 0) + getFloat(a, 1) + getFloat(a, 2)) + ) + + intr.evalScript() + + let foreignProc = selectRoutine(intr, "hostProgramRunsThis") + if foreignProc == nil: + quit "script does not export a proc of the name: 'hostProgramRunsThis'" + let res = intr.callRoutine(foreignProc, [newFloatNode(nkFloatLit, 0.9), + newFloatNode(nkFloatLit, 0.1)]) + if res.kind == nkFloatLit: + echo res.floatVal + else: + echo "bug!" + destroyInterpreter(intr) + +main() \ No newline at end of file -- cgit 1.4.1-2-gfad0 From 688c54d8f158ff977161a234374b1cd321ba95f3 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 29 May 2018 09:07:24 +0200 Subject: compiler API: final cleanups; improve security by diabling 'gorge' and friends --- compiler/main.nim | 3 +++ compiler/nim.cfg | 2 +- compiler/nimeval.nim | 9 ++++++--- compiler/vm.nim | 21 ++++++++++++--------- compiler/vmops.nim | 23 ++++++++++++----------- nimsuggest/nimsuggest.nim | 9 ++++++--- nimsuggest/nimsuggest.nim.cfg | 3 ++- tests/compilerapi/myscript.nim | 2 ++ tests/compilerapi/tcompilerapi.nim | 13 ++++++++++++- 9 files changed, 56 insertions(+), 29 deletions(-) (limited to 'tests') diff --git a/compiler/main.nim b/compiler/main.nim index c5b2ddca5..ba2537ef8 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -9,6 +9,9 @@ # implements the command dispatcher and several commands +when not defined(nimcore): + {.error: "nimcore MUST be defined for Nim's core tooling".} + import llstream, strutils, ast, astalgo, lexer, syntaxes, renderer, options, msgs, os, condsyms, rodread, rodwrite, times, diff --git a/compiler/nim.cfg b/compiler/nim.cfg index 853ae7e00..f4211fae5 100644 --- a/compiler/nim.cfg +++ b/compiler/nim.cfg @@ -5,6 +5,7 @@ path:"llvm" path:"$projectPath/.." define:booting +define:nimcore #import:"$projectpath/testability" @if windows: @@ -13,6 +14,5 @@ define:booting define:useStdoutAsStdmsg -cs:partial #define:useNodeIds #gc:markAndSweep diff --git a/compiler/nimeval.nim b/compiler/nimeval.nim index d5df6a9df..dde6039ba 100644 --- a/compiler/nimeval.nim +++ b/compiler/nimeval.nim @@ -29,7 +29,7 @@ iterator exportedSymbols*(i: Interpreter): PSym = s = nextIter(it, i.mainModule.tab) proc selectUniqueSymbol*(i: Interpreter; name: string; - symKinds: set[TSymKind]): PSym = + symKinds: set[TSymKind] = {skLet, skVar}): PSym = ## Can be used to access a unique symbol of ``name`` and ## the given ``symKinds`` filter. assert i != nil @@ -55,8 +55,11 @@ proc callRoutine*(i: Interpreter; routine: PSym; args: openArray[PNode]): PNode assert i != nil result = vm.execProc(PCtx i.graph.vm, routine, args) -proc declareRoutine*(i: Interpreter; pkg, module, name: string; - impl: proc (a: VmArgs) {.closure, gcsafe.}) = +proc getGlobalValue*(i: Interpreter; letOrVar: PSym): PNode = + result = vm.getGlobalValue(PCtx i.graph.vm, letOrVar) + +proc implementRoutine*(i: Interpreter; pkg, module, name: string; + impl: proc (a: VmArgs) {.closure, gcsafe.}) = assert i != nil let vm = PCtx(i.graph.vm) vm.registerCallback(pkg & "." & module & "." & name, impl) diff --git a/compiler/vm.nim b/compiler/vm.nim index c7b68a24c..b1b8132e2 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -972,7 +972,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = when hasFFI: let prcValue = c.globals.sons[prc.position-1] if prcValue.kind == nkEmpty: - globalError(c.config, c.debug[pc], "canot run " & prc.name.s) + globalError(c.config, c.debug[pc], "cannot run " & prc.name.s) let newValue = callForeignFunction(prcValue, prc.typ, tos.slots, rb+1, rc-1, c.debug[pc]) if newValue.kind != nkEmpty: @@ -1336,14 +1336,17 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = regs[ra].node.strVal = opSlurp(regs[rb].node.strVal, c.debug[pc], c.module, c.config) of opcGorge: - decodeBC(rkNode) - inc pc - let rd = c.code[pc].regA - - createStr regs[ra] - regs[ra].node.strVal = opGorge(regs[rb].node.strVal, - regs[rc].node.strVal, regs[rd].node.strVal, - c.debug[pc], c.config)[0] + when defined(nimcore): + decodeBC(rkNode) + inc pc + let rd = c.code[pc].regA + + createStr regs[ra] + regs[ra].node.strVal = opGorge(regs[rb].node.strVal, + regs[rc].node.strVal, regs[rd].node.strVal, + c.debug[pc], c.config)[0] + else: + globalError(c.config, c.debug[pc], "VM is not built with 'gorge' support") of opcNError: decodeB(rkNode) let a = regs[ra].node diff --git a/compiler/vmops.nim b/compiler/vmops.nim index 617295b0d..a7d47d7a3 100644 --- a/compiler/vmops.nim +++ b/compiler/vmops.nim @@ -107,15 +107,16 @@ proc registerAdditionalOps*(c: PCtx) = wrap1f_math(ceil) wrap2f_math(fmod) - wrap2s(getEnv, ospathsop) - wrap1s(existsEnv, ospathsop) - wrap2svoid(putEnv, ospathsop) - wrap1s(dirExists, osop) - wrap1s(fileExists, osop) - wrap2svoid(writeFile, systemop) - wrap1s(readFile, systemop) - systemop getCurrentExceptionMsg - registerCallback c, "stdlib.*.staticWalkDir", proc (a: VmArgs) {.nimcall.} = - setResult(a, staticWalkDirImpl(getString(a, 0), getBool(a, 1))) - systemop gorgeEx + when defined(nimcore): + wrap2s(getEnv, ospathsop) + wrap1s(existsEnv, ospathsop) + wrap2svoid(putEnv, ospathsop) + wrap1s(dirExists, osop) + wrap1s(fileExists, osop) + wrap2svoid(writeFile, systemop) + wrap1s(readFile, systemop) + systemop getCurrentExceptionMsg + registerCallback c, "stdlib.*.staticWalkDir", proc (a: VmArgs) {.nimcall.} = + setResult(a, staticWalkDirImpl(getString(a, 0), getBool(a, 1))) + systemop gorgeEx macrosop getProjectPath diff --git a/nimsuggest/nimsuggest.nim b/nimsuggest/nimsuggest.nim index 4956f4bf2..dd52e0383 100644 --- a/nimsuggest/nimsuggest.nim +++ b/nimsuggest/nimsuggest.nim @@ -9,6 +9,9 @@ ## Nimsuggest is a tool that helps to give editors IDE like capabilities. +when not defined(nimcore): + {.error: "nimcore MUST be defined for Nim's core tooling".} + import strutils, os, parseopt, parseutils, sequtils, net, rdstdin, sexp # Do NOT import suggest. It will lead to wierd bugs with # suggestionResultHook, because suggest.nim is included by sigmatch. @@ -486,9 +489,9 @@ var proc mainCommand(graph: ModuleGraph; cache: IdentCache) = let conf = graph.config - clearPasses() - registerPass verbosePass - registerPass semPass + clearPasses(graph) + registerPass graph, verbosePass + registerPass graph, semPass conf.cmd = cmdIdeTools incl conf.globalOptions, optCaasEnabled wantMainModule(conf) diff --git a/nimsuggest/nimsuggest.nim.cfg b/nimsuggest/nimsuggest.nim.cfg index 38e74b3c7..820db0dba 100644 --- a/nimsuggest/nimsuggest.nim.cfg +++ b/nimsuggest/nimsuggest.nim.cfg @@ -8,6 +8,8 @@ path:"$lib/packages/docutils" define:useStdoutAsStdmsg define:nimsuggest +define:nimcore + # die when nimsuggest uses more than 4GB: @if cpu32: define:"nimMaxHeap=2000" @@ -15,7 +17,6 @@ define:nimsuggest define:"nimMaxHeap=4000" @end -#cs:partial #define:useNodeIds #define:booting #define:noDocgen diff --git a/tests/compilerapi/myscript.nim b/tests/compilerapi/myscript.nim index 083385b6f..539b07de1 100644 --- a/tests/compilerapi/myscript.nim +++ b/tests/compilerapi/myscript.nim @@ -5,3 +5,5 @@ echo "top level statements are executed!" proc hostProgramRunsThis*(a, b: float): float = result = addFloats(a, b, 1.0) + +let hostProgramWantsThis* = "my secret" diff --git a/tests/compilerapi/tcompilerapi.nim b/tests/compilerapi/tcompilerapi.nim index 00c9bc523..90d343264 100644 --- a/tests/compilerapi/tcompilerapi.nim +++ b/tests/compilerapi/tcompilerapi.nim @@ -1,6 +1,7 @@ discard """ output: '''top level statements are executed! 2.0 +my secret ''' """ @@ -16,7 +17,7 @@ proc main() = quit "cannot find Nim's standard library" var intr = createInterpreter("myscript.nim", [std, getAppDir()]) - intr.declareRoutine("*", "exposed", "addFloats", proc (a: VmArgs) = + intr.implementRoutine("*", "exposed", "addFloats", proc (a: VmArgs) = setResult(a, getFloat(a, 0) + getFloat(a, 1) + getFloat(a, 2)) ) @@ -31,6 +32,16 @@ proc main() = echo res.floatVal else: echo "bug!" + + let foreignValue = selectUniqueSymbol(intr, "hostProgramWantsThis") + if foreignValue == nil: + quit "script does not export a global of the name: hostProgramWantsThis" + let val = intr.getGlobalValue(foreignValue) + if val.kind in {nkStrLit..nkTripleStrLit}: + echo val.strVal + else: + echo "bug!" + destroyInterpreter(intr) main() \ No newline at end of file -- cgit 1.4.1-2-gfad0 From e03b3bdde75f5e82f9c5512fcd00033704de4a34 Mon Sep 17 00:00:00 2001 From: cooldome Date: Wed, 6 Jun 2018 23:41:19 +0100 Subject: fixes 7980 --- compiler/hlo.nim | 50 +++++++++++++++++------------- tests/template/tpattern_with_converter.nim | 27 ++++++++++++++++ 2 files changed, 56 insertions(+), 21 deletions(-) create mode 100644 tests/template/tpattern_with_converter.nim (limited to 'tests') diff --git a/compiler/hlo.nim b/compiler/hlo.nim index 8251e3179..511712b54 100644 --- a/compiler/hlo.nim +++ b/compiler/hlo.nim @@ -36,27 +36,35 @@ proc applyPatterns(c: PContext, n: PNode): PNode = # we apply the last pattern first, so that pattern overriding is possible; # however the resulting AST would better not trigger the old rule then # anymore ;-) - for i in countdown(c.patterns.len-1, 0): - let pattern = c.patterns[i] - if not isNil(pattern): - let x = applyRule(c, pattern, result) - if not isNil(x): - assert x.kind in {nkStmtList, nkCall} - # better be safe than sorry, so check evalTemplateCounter too: - inc(evalTemplateCounter) - if evalTemplateCounter > evalTemplateLimit: - globalError(c.config, n.info, "template instantiation too nested") - # deactivate this pattern: - c.patterns[i] = nil - if x.kind == nkStmtList: - assert x.len == 3 - x.sons[1] = evalPattern(c, x.sons[1], result) - result = flattenStmts(x) - else: - result = evalPattern(c, x, result) - dec(evalTemplateCounter) - # activate this pattern again: - c.patterns[i] = pattern + if c.patterns.len > 0: + + # temporary disable converters + var ctx_converters: TSymSeq + shallowCopy(ctx_converters, c.converters) + c.converters = @[] + defer: shallowCopy(c.converters, ctx_converters) + + for i in countdown(c.patterns.len-1, 0): + let pattern = c.patterns[i] + if not isNil(pattern): + let x = applyRule(c, pattern, result) + if not isNil(x): + assert x.kind in {nkStmtList, nkCall} + # better be safe than sorry, so check evalTemplateCounter too: + inc(evalTemplateCounter) + if evalTemplateCounter > evalTemplateLimit: + globalError(c.config, n.info, "template instantiation too nested") + # deactivate this pattern: + c.patterns[i] = nil + if x.kind == nkStmtList: + assert x.len == 3 + x.sons[1] = evalPattern(c, x.sons[1], result) + result = flattenStmts(x) + else: + result = evalPattern(c, x, result) + dec(evalTemplateCounter) + # activate this pattern again: + c.patterns[i] = pattern proc hlo(c: PContext, n: PNode): PNode = inc(c.hloLoopDetector) diff --git a/tests/template/tpattern_with_converter.nim b/tests/template/tpattern_with_converter.nim new file mode 100644 index 000000000..e0632552b --- /dev/null +++ b/tests/template/tpattern_with_converter.nim @@ -0,0 +1,27 @@ +discard """ + output: 10.0 +""" + +type + MyFloat = object + val: float + +converter to_myfloat*(x: float): MyFloat {.inline.} = + MyFloat(val: x) + +proc `+`(x1, x2: MyFloat): MyFloat = + MyFloat(val: x1.val + x2.val) + +proc `*`(x1, x2: MyFloat): MyFloat = + MyFloat(val: x1.val * x2.val) + +template optMul{`*`(a, 2.0)}(a: MyFloat): MyFloat = + a + a + +func floatMyFloat(x: MyFloat): MyFloat = + result = x * 2.0 + +func floatDouble(x: float): float = + result = x * 2.0 + +echo floatDouble(5) \ No newline at end of file -- cgit 1.4.1-2-gfad0 From 9c5f38850d5ebc3fba8ea2e6b0a561c75d70675b Mon Sep 17 00:00:00 2001 From: Vindaar Date: Sun, 10 Jun 2018 17:09:26 +0200 Subject: add test case for fix to #7997 --- tests/template/tdefined_overload.nim | 46 ++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 tests/template/tdefined_overload.nim (limited to 'tests') diff --git a/tests/template/tdefined_overload.nim b/tests/template/tdefined_overload.nim new file mode 100644 index 000000000..bcc83e414 --- /dev/null +++ b/tests/template/tdefined_overload.nim @@ -0,0 +1,46 @@ +discard """ + output: "Valid and not defined" +""" +# test for issue #7997 +# checking for `when not defined` in a template for some compile time symbol +# results in a compilation error of: +# Error: obsolete usage of 'defined', use 'declared' instead +# if the symbol is 'overloaded' by some variable or procedure, because in +# that case the argument of `defined` is of kind `nkSym` instead of `nkIdent` +# (for which was checked in `semexprs.semDefined`). + +block: + # check whether a proc with the same name as the argument to `defined` + # compiles + proc overloaded() = + discard + + template definedCheck(): untyped = + when not defined(overloaded): true + else: false + doAssert definedCheck == true + +block: + # check whether a variable with the same name as the argument to `defined` + # compiles + var overloaded: int + + template definedCheck(): untyped = + when not defined(overloaded): true + else: false + doAssert definedCheck == true + +block: + # check whether a non overloaded when check still works properly + when not defined(validIdentifier): + echo "Valid and not defined" + +block: + # now check that invalid identifiers cause a compilation error + # by using reject template. + template reject(b) = + static: doAssert(not compiles(b)) + + reject: + when defined(123): + echo "Invalid identifier! Will not be echoed" -- cgit 1.4.1-2-gfad0 From 5f2cdcd4fa0f3d5dd0156026c0685aa59d9f7f92 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Sun, 10 Jun 2018 16:44:49 +0300 Subject: fix #7653 --- compiler/ccgtypes.nim | 2 -- compiler/sighashes.nim | 25 +++++++++++++++---------- tests/cpp/tempty_generic_obj.nim | 16 ++++++++++++++++ tests/generics/t3977.nim | 4 +++- 4 files changed, 34 insertions(+), 13 deletions(-) (limited to 'tests') diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 7b44cddad..4b0fd49aa 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -791,8 +791,6 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope = else: addAbiCheck(m, t, result) of tyObject, tyTuple: if isImportedCppType(t) and origTyp.kind == tyGenericInst: - # for instantiated templates we do not go through the type cache as the - # the type cache is not aware of 'tyGenericInst'. let cppName = getTypeName(m, t, sig) var i = 0 var chunkStart = 0 diff --git a/compiler/sighashes.nim b/compiler/sighashes.nim index 46b83c386..0b95387cd 100644 --- a/compiler/sighashes.nim +++ b/compiler/sighashes.nim @@ -12,7 +12,7 @@ import ast, md5, tables, ropes from hashes import Hash from astalgo import debug -from types import typeToString, preferDesc +import types from strutils import startsWith, contains when false: @@ -148,19 +148,23 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]) = of tyGenericInvocation: for i in countup(0, sonsLen(t) - 1): c.hashType t.sons[i], flags - return of tyDistinct: if CoType in flags: c.hashType t.lastSon, flags else: c.hashSym(t.sym) - return - of tyAlias, tySink, tyGenericInst, tyUserTypeClasses: + of tyGenericInst: + if sfInfixCall in t.base.sym.flags: + # This is an imported C++ generic type. + # We cannot trust the `lastSon` to hold a properly populated and unique + # value for each instantiation, so we hash the generic parameters here: + let normalizedType = t.skipGenericAlias + for i in 0 .. normalizedType.len - 2: + c.hashType t.sons[i], flags + else: + c.hashType t.lastSon, flags + of tyAlias, tySink, tyUserTypeClasses: c.hashType t.lastSon, flags - return - else: - discard - case t.kind of tyBool, tyChar, tyInt..tyUInt64: # no canonicalization for integral types, so that e.g. ``pid_t`` is # produced instead of ``NI``: @@ -168,11 +172,12 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]) = if t.sym != nil and {sfImportc, sfExportc} * t.sym.flags != {}: c.hashSym(t.sym) of tyObject, tyEnum: - c &= char(t.kind) if t.typeInst != nil: assert t.typeInst.kind == tyGenericInst - for i in countup(1, sonsLen(t.typeInst) - 2): + for i in countup(0, sonsLen(t.typeInst) - 2): c.hashType t.typeInst.sons[i], flags + return + c &= char(t.kind) # Every cyclic type in Nim need to be constructed via some 't.sym', so this # is actually safe without an infinite recursion check: if t.sym != nil: diff --git a/tests/cpp/tempty_generic_obj.nim b/tests/cpp/tempty_generic_obj.nim index b4c746a30..b61c699f6 100644 --- a/tests/cpp/tempty_generic_obj.nim +++ b/tests/cpp/tempty_generic_obj.nim @@ -20,3 +20,19 @@ v.doSomething() var vf = initVector[float]() vf.doSomething() # Nim uses doSomething[int] here in C++ + +# Alternative definition: +# https://github.com/nim-lang/Nim/issues/7653 + +type VectorAlt* {.importcpp: "std::vector", header: "", nodecl.} [T] = object +proc mkVector*[T]: VectorAlt[T] {.importcpp: "std::vector<'*0>()", header: "", constructor, nodecl.} + +proc foo(): VectorAlt[cint] = + mkVector[cint]() + +proc bar(): VectorAlt[cstring] = + mkVector[cstring]() + +var x = foo() +var y = bar() + diff --git a/tests/generics/t3977.nim b/tests/generics/t3977.nim index 314017744..eed1a7d63 100644 --- a/tests/generics/t3977.nim +++ b/tests/generics/t3977.nim @@ -1,12 +1,14 @@ discard """ - output: '''42''' + output: "42\n42" """ type Foo[N: static[int]] = object proc foo[N](x: Foo[N]) = + let n = N echo N + echo n var f1: Foo[42] f1.foo -- cgit 1.4.1-2-gfad0 From 5c449c8cd112139f430d3be67effffb304aeb521 Mon Sep 17 00:00:00 2001 From: Yuriy Glukhov Date: Sun, 10 Jun 2018 23:42:53 +0300 Subject: Removed oldIterTranf feature --- compiler/lambdalifting.nim | 88 ++-------------------------------------------- compiler/options.nim | 3 +- compiler/semexprs.nim | 2 -- compiler/transf.nim | 2 +- tests/async/tasynctry2.nim | 18 ---------- 5 files changed, 4 insertions(+), 109 deletions(-) delete mode 100644 tests/async/tasynctry2.nim (limited to 'tests') diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index 40e5bb6b0..b7240f7e9 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -607,81 +607,6 @@ proc getStateField*(g: ModuleGraph; owner: PSym): PSym = proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass; c: var LiftingPass): PNode -proc transformYield(n: PNode; owner: PSym; d: DetectionPass; - c: var LiftingPass): PNode = - if c.inContainer > 0: - localError(d.graph.config, n.info, "invalid control flow: 'yield' within a constructor") - let state = getStateField(d.graph, owner) - assert state != nil - assert state.typ != nil - assert state.typ.n != nil - inc state.typ.n.sons[1].intVal - let stateNo = state.typ.n.sons[1].intVal - - var stateAsgnStmt = newNodeI(nkAsgn, n.info) - stateAsgnStmt.add(rawIndirectAccess(newSymNode(getEnvParam(owner)), - state, n.info)) - stateAsgnStmt.add(newIntTypeNode(nkIntLit, stateNo, - getSysType(d.graph, n.info, tyInt))) - - var retStmt = newNodeI(nkReturnStmt, n.info) - if n.sons[0].kind != nkEmpty: - var a = newNodeI(nkAsgn, n.sons[0].info) - var retVal = liftCapturedVars(n.sons[0], owner, d, c) - addSon(a, newSymNode(getClosureIterResult(owner))) - addSon(a, retVal) - retStmt.add(a) - else: - retStmt.add(emptyNode) - - var stateLabelStmt = newNodeI(nkState, n.info) - stateLabelStmt.add(newIntTypeNode(nkIntLit, stateNo, - getSysType(d.graph, n.info, tyInt))) - - result = newNodeI(nkStmtList, n.info) - result.add(stateAsgnStmt) - result.add(retStmt) - result.add(stateLabelStmt) - -proc transformReturn(n: PNode; owner: PSym; d: DetectionPass; - c: var LiftingPass): PNode = - let state = getStateField(d.graph, owner) - result = newNodeI(nkStmtList, n.info) - var stateAsgnStmt = newNodeI(nkAsgn, n.info) - stateAsgnStmt.add(rawIndirectAccess(newSymNode(getEnvParam(owner)), - state, n.info)) - stateAsgnStmt.add(newIntTypeNode(nkIntLit, -1, getSysType(d.graph, n.info, tyInt))) - result.add(stateAsgnStmt) - result.add(n) - -proc wrapIterBody(g: ModuleGraph; n: PNode; owner: PSym): PNode = - if not owner.isIterator: return n - when false: - # unfortunately control flow is still convoluted and we can end up - # multiple times here for the very same iterator. We shield against this - # with some rather primitive check for now: - if n.kind == nkStmtList and n.len > 0: - if n.sons[0].kind == nkGotoState: return n - if n.len > 1 and n[1].kind == nkStmtList and n[1].len > 0 and - n[1][0].kind == nkGotoState: - return n - let info = n.info - result = newNodeI(nkStmtList, info) - var gs = newNodeI(nkGotoState, info) - gs.add(rawIndirectAccess(newSymNode(getHiddenParam(g, owner)), getStateField(g, owner), info)) - result.add(gs) - var state0 = newNodeI(nkState, info) - state0.add(newIntNode(nkIntLit, 0)) - result.add(state0) - - result.add(n) - - var stateAsgnStmt = newNodeI(nkAsgn, info) - stateAsgnStmt.add(rawIndirectAccess(newSymNode(getHiddenParam(g, owner)), - getStateField(g, owner), info)) - stateAsgnStmt.add(newIntTypeNode(nkIntLit, -1, getSysType(g, info, tyInt))) - result.add(stateAsgnStmt) - proc symToClosure(n: PNode; owner: PSym; d: DetectionPass; c: var LiftingPass): PNode = let s = n.sym @@ -722,8 +647,6 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass; let oldInContainer = c.inContainer c.inContainer = 0 var body = liftCapturedVars(s.getBody, s, d, c) - if oldIterTransf in d.graph.config.features: - body = wrapIterBody(d.graph, body, s) if c.envvars.getOrDefault(s.id).isNil: s.ast.sons[bodyPos] = body else: @@ -766,11 +689,7 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: DetectionPass; if n[1].kind == nkClosure: result = n[1] else: if owner.isIterator: - if oldIterTransf in d.graph.config.features and n.kind == nkYieldStmt: - return transformYield(n, owner, d, c) - elif oldIterTransf in d.graph.config.features and n.kind == nkReturnStmt: - return transformReturn(n, owner, d, c) - elif nfLL in n.flags: + if nfLL in n.flags: # special case 'when nimVm' due to bug #3636: n.sons[1] = liftCapturedVars(n[1], owner, d, c) return @@ -811,7 +730,7 @@ proc liftIterToProc*(g: ModuleGraph; fn: PSym; body: PNode; ptrType: PType): PNo fn.typ.callConv = ccClosure d.ownerToType[fn.id] = ptrType detectCapturedVars(body, fn, d) - result = wrapIterBody(g, liftCapturedVars(body, fn, d, c), fn) + result = liftCapturedVars(body, fn, d, c) fn.kind = oldKind fn.typ.callConv = oldCC @@ -840,9 +759,6 @@ proc liftLambdas*(g: ModuleGraph; fn: PSym, body: PNode; tooEarly: var bool): PN result = liftCapturedVars(body, fn, d, c) if c.envvars.getOrDefault(fn.id) != nil: result = newTree(nkStmtList, rawClosureCreation(fn, d, c), result) - - if oldIterTransf in g.config.features: - result = wrapIterBody(g, result, fn) else: result = body #if fn.name.s == "get2": diff --git a/compiler/options.nim b/compiler/options.nim index 150e67a18..9779f2020 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -116,8 +116,7 @@ type callOperator, parallel, destructor, - notnil, - oldIterTransf + notnil SymbolFilesOption* = enum disabledSf, enabledSf, writeOnlySf, readOnlySf, v2Sf diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 92b9c365a..3722989b2 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1556,8 +1556,6 @@ proc semYield(c: PContext, n: PNode): PNode = checkSonsLen(n, 1, c.config) if c.p.owner == nil or c.p.owner.kind != skIterator: localError(c.config, n.info, errYieldNotAllowedHere) - elif oldIterTransf in c.features and c.p.inTryStmt > 0 and c.p.owner.typ.callConv != ccInline: - localError(c.config, n.info, errYieldNotAllowedInTryStmt) elif n.sons[0].kind != nkEmpty: n.sons[0] = semExprWithType(c, n.sons[0]) # check for type compatibility: var iterType = c.p.owner.typ diff --git a/compiler/transf.nim b/compiler/transf.nim index c2add13ff..c37462e12 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -985,7 +985,7 @@ proc transformBody*(g: ModuleGraph; module: PSym, n: PNode, prc: PSym): PNode = if c.needsDestroyPass: #and newDestructors: result = injectDestructorCalls(g, prc, result) - if prc.isIterator and oldIterTransf notin g.config.features: + if prc.isIterator: result = g.transformClosureIterator(prc, result) incl(result.flags, nfTransf) diff --git a/tests/async/tasynctry2.nim b/tests/async/tasynctry2.nim deleted file mode 100644 index 4b3f17cc5..000000000 --- a/tests/async/tasynctry2.nim +++ /dev/null @@ -1,18 +0,0 @@ -discard """ - file: "tasynctry2.nim" - errormsg: "\'yield\' cannot be used within \'try\' in a non-inlined iterator" - line: 14 -""" -import asyncdispatch - -{.experimental: "oldIterTransf".} - -proc foo(): Future[bool] {.async.} = discard - -proc test5(): Future[int] {.async.} = - try: - discard await foo() - raise newException(ValueError, "Test5") - except: - discard await foo() - result = 0 -- cgit 1.4.1-2-gfad0 From 61e189792220b7eb3b6a6b2ac543c6de84862aae Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 11 Jun 2018 01:23:14 +0200 Subject: make tests green again --- compiler/nimeval.nim | 4 ++-- compiler/semstmts.nim | 14 ++++++++------ tests/macros/tmacros1.nim | 6 +++--- 3 files changed, 13 insertions(+), 11 deletions(-) (limited to 'tests') diff --git a/compiler/nimeval.nim b/compiler/nimeval.nim index 308560010..f20b5642c 100644 --- a/compiler/nimeval.nim +++ b/compiler/nimeval.nim @@ -73,7 +73,7 @@ proc evalScript*(i: Interpreter; scriptStream: PLLStream = nil) = let s = if scriptStream != nil: scriptStream else: llStreamOpen(findFile(i.graph.config, i.scriptName), fmRead) - processModule(i.graph, i.mainModule, s, nil, i.graph.cache) + processModule(i.graph, i.mainModule, s) proc findNimStdLib*(): string = ## Tries to find a path to a valid "system.nim" file. @@ -112,7 +112,7 @@ proc createInterpreter*(scriptName: string; vm.mode = emRepl vm.features = flags graph.vm = vm - graph.compileSystemModule(cache) + graph.compileSystemModule() result = Interpreter(mainModule: m, graph: graph, scriptName: scriptName) proc destroyInterpreter*(i: Interpreter) = diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 0f8e77fc9..0e143e7c1 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1771,12 +1771,14 @@ proc semStaticStmt(c: PContext, n: PNode): PNode = inc c.inStaticContext let a = semStmt(c, n.sons[0]) dec c.inStaticContext - n.sons[0] = a - evalStaticStmt(c.module, c.graph, a, c.p.owner) - # for incremental replays, keep the AST as required for replays: - result = n - #result = newNodeI(nkDiscardStmt, n.info, 1) - #result.sons[0] = c.graph.emptyNode + when false: + n.sons[0] = a + evalStaticStmt(c.module, c.graph, a, c.p.owner) + # for incremental replays, keep the AST as required for replays: + result = n + else: + result = newNodeI(nkDiscardStmt, n.info, 1) + result.sons[0] = c.graph.emptyNode proc usesResult(n: PNode): bool = # nkStmtList(expr) properly propagates the void context, diff --git a/tests/macros/tmacros1.nim b/tests/macros/tmacros1.nim index 9e3ab028b..80afb6641 100644 --- a/tests/macros/tmacros1.nim +++ b/tests/macros/tmacros1.nim @@ -12,12 +12,12 @@ macro outterMacro*(n, blck: untyped): untyped = echo "Using arg ! " & n.repr result = "Got: '" & $n.kind & "' " & $j var callNode = n[0] - expectKind(n, TNimrodNodeKind.nnkCall) - if n.len != 3 or n[1].kind != TNimrodNodeKind.nnkIdent: + expectKind(n, NimNodeKind.nnkCall) + if n.len != 3 or n[1].kind != NimNodeKind.nnkIdent: error("Macro " & callNode.repr & " requires the ident passed as parameter (eg: " & callNode.repr & "(the_name_you_want)): statements.") - result = newNimNode(TNimrodNodeKind.nnkStmtList) + result = newNimNode(NimNodeKind.nnkStmtList) var ass : NimNode = newNimNode(nnkAsgn) ass.add(newIdentNode(n[1].ident)) ass.add(newStrLitNode(innerProc(4))) -- cgit 1.4.1-2-gfad0 From 8f067634919eeb232ee04f759c4c1786ddd26ea6 Mon Sep 17 00:00:00 2001 From: Yuriy Glukhov Date: Mon, 11 Jun 2018 17:57:19 +0300 Subject: Fixes #6803 --- compiler/cgen.nim | 21 +++--- compiler/semexprs.nim | 3 + tests/errmsgs/tproper_stacktrace.nim | 124 +++++++++++++++++++++++++++++++++-- 3 files changed, 129 insertions(+), 19 deletions(-) (limited to 'tests') diff --git a/compiler/cgen.nim b/compiler/cgen.nim index ee60e62d2..cd344f096 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -204,27 +204,22 @@ proc freshLineInfo(p: BProc; info: TLineInfo): bool = result = true proc genLineDir(p: BProc, t: PNode) = - var tt = t - #while tt.kind in {nkStmtListExpr}+nkCallKinds: - # tt = tt.lastSon - if tt.kind in nkCallKinds and tt.len > 1: - tt = tt.sons[1] - let line = tt.info.safeLineNm + let line = t.info.safeLineNm if optEmbedOrigSrc in p.config.globalOptions: - add(p.s(cpsStmts), ~"//" & sourceLine(p.config, tt.info) & "\L") - genCLineDir(p.s(cpsStmts), toFullPath(p.config, tt.info), line, p.config) + add(p.s(cpsStmts), ~"//" & sourceLine(p.config, t.info) & "\L") + genCLineDir(p.s(cpsStmts), toFullPath(p.config, t.info), line, p.config) if ({optStackTrace, optEndb} * p.options == {optStackTrace, optEndb}) and (p.prc == nil or sfPure notin p.prc.flags): - if freshLineInfo(p, tt.info): + if freshLineInfo(p, t.info): linefmt(p, cpsStmts, "#endb($1, $2);$N", - line.rope, makeCString(toFilename(p.config, tt.info))) + line.rope, makeCString(toFilename(p.config, t.info))) elif ({optLineTrace, optStackTrace} * p.options == {optLineTrace, optStackTrace}) and - (p.prc == nil or sfPure notin p.prc.flags) and tt.info.fileIndex != InvalidFileIDX: - if freshLineInfo(p, tt.info): + (p.prc == nil or sfPure notin p.prc.flags) and t.info.fileIndex != InvalidFileIDX: + if freshLineInfo(p, t.info): linefmt(p, cpsStmts, "nimln_($1, $2);$n", - line.rope, quotedFilename(p.config, tt.info)) + line.rope, quotedFilename(p.config, t.info)) proc postStmtActions(p: BProc) {.inline.} = add(p.s(cpsStmts), p.module.injectStmt) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 64c145e51..135c1b32c 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -31,6 +31,9 @@ proc semTemplateExpr(c: PContext, n: PNode, s: PSym, if efNoSemCheck notin flags: result = semAfterMacroCall(c, n, result, s, flags) popInfoContext(c.config) + # XXX: A more elaborate line info rewrite might be needed + result.info = n.info + proc semFieldAccess(c: PContext, n: PNode, flags: TExprFlags = {}): PNode proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = diff --git a/tests/errmsgs/tproper_stacktrace.nim b/tests/errmsgs/tproper_stacktrace.nim index 57e65fa6f..4e5c5fbf8 100644 --- a/tests/errmsgs/tproper_stacktrace.nim +++ b/tests/errmsgs/tproper_stacktrace.nim @@ -1,11 +1,123 @@ discard """ - outputsub: '''tproper_stacktrace.nim(7) tproper_stacktrace''' - exitcode: 1 + output: '''ok''' """ +import strscans, strutils -template fuzzy(x) = - echo x[] != 9 +proc raiseTestException*() = + raise newException(Exception, "test") -var p: ptr int -fuzzy p +proc matchStackTrace(actualEntries: openarray[StackTraceEntry], expected: string) = + var expectedEntries = newSeq[StackTraceEntry]() + var i = 0 + template checkEqual(actual, expected: typed, subject: string) = + if actual != expected: + echo "Unexpected ", subject, " on line ", i + echo "Actual: ", actual + echo "Expected: ", expected + doAssert(false) + + for l in splitLines(expected.strip): + var procname, filename: string + var line: int + if not scanf(l, "$s$w.nim($i) $w", filename, line, procname): + doAssert(false, "Wrong expected stack trace") + checkEqual($actualEntries[i].filename, filename & ".nim", "file name") + if line != 0: + checkEqual(actualEntries[i].line, line, "line number") + checkEqual($actualEntries[i].procname, procname, "proc name") + inc i + + doAssert(i == actualEntries.len, "Unexpected number of lines in stack trace") + +template verifyStackTrace*(expectedStackTrace: string, body: untyped) = + var verified = false + try: + body + except Exception as e: + verified = true + # echo "Stack trace:" + # echo e.getStackTrace + matchStackTrace(e.getStackTraceEntries(), expectedStackTrace) + + doAssert(verified, "No exception was raised") + + + + + + + + + + + + + + + + + + + + + + + + + +when isMainModule: +# <-- Align with line 70 in the text editor + block: + proc bar() = + raiseTestException() + + proc foo() = + bar() + + const expectedStackTrace = """ + tproper_stacktrace.nim(86) tproper_stacktrace + tproper_stacktrace.nim(76) foo + tproper_stacktrace.nim(73) bar + tproper_stacktrace.nim(7) raiseTestException + """ + + verifyStackTrace expectedStackTrace: + foo() + + block: + proc bar(x: int) = + raiseTestException() + + template foo(x: int) = + bar(x) + + const expectedStackTrace = """ + tproper_stacktrace.nim(103) tproper_stacktrace + tproper_stacktrace.nim(90) bar + tproper_stacktrace.nim(7) raiseTestException + """ + + verifyStackTrace expectedStackTrace: + var x: int + foo(x) + + block: #6803 + proc bar(x = 500) = + raiseTestException() + + proc foo() = + bar() + + const expectedStackTrace = """ + tproper_stacktrace.nim(120) tproper_stacktrace + tproper_stacktrace.nim(110) foo + tproper_stacktrace.nim(107) bar + tproper_stacktrace.nim(7) raiseTestException + """ + + verifyStackTrace expectedStackTrace: + foo() + + + echo "ok" -- cgit 1.4.1-2-gfad0 From ac0f9860081ce20cd1ce1f823db7361ad284b1be Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Mon, 11 Jun 2018 23:19:00 +0200 Subject: Correct field lookup in concept types Fixes #6770 --- compiler/ccgexprs.nim | 2 +- tests/concepts/t6770.nim | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 tests/concepts/t6770.nim (limited to 'tests') diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index bcfee0aab..b5dcc76e5 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -828,7 +828,7 @@ proc genCheckedRecordField(p: BProc, e: PNode, d: var TLoc) = if optFieldCheck in p.options: var a: TLoc genRecordFieldAux(p, e.sons[0], d, a) - let ty = skipTypes(a.t, abstractInst) + let ty = skipTypes(a.t, abstractInst + tyUserTypeClasses) var r = rdLoc(a) let f = e.sons[0].sons[1].sym let field = lookupFieldAgain(p, ty, f, r) diff --git a/tests/concepts/t6770.nim b/tests/concepts/t6770.nim new file mode 100644 index 000000000..1787ee670 --- /dev/null +++ b/tests/concepts/t6770.nim @@ -0,0 +1,27 @@ +discard """ +output: ''' +10 +10 +''' +""" + +type GA = concept c + c.a is int + +type A = object + a: int + +type AA = object + case exists: bool + of true: + a: int + else: + discard + +proc print(inp: GA) = + echo inp.a + +let failing = AA(exists: true, a: 10) +let working = A(a:10) +print(working) +print(failing) -- cgit 1.4.1-2-gfad0 From 7e7b85afc7b915781c37fcf43487a99a30144dcd Mon Sep 17 00:00:00 2001 From: Yuriy Glukhov Date: Tue, 12 Jun 2018 12:17:03 +0300 Subject: Allow stacktrace and linetrace pragmas on procs --- compiler/pragmas.nim | 4 ++-- compiler/semstmts.nim | 3 +++ tests/errmsgs/tproper_stacktrace.nim | 18 ++++++++++++++++++ 3 files changed, 23 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index bfb06e00a..bfb8e78eb 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -24,8 +24,8 @@ const wCompilerProc, wCore, wProcVar, wDeprecated, wVarargs, wCompileTime, wMerge, wBorrow, wExtern, wImportCompilerProc, wThread, wImportCpp, wImportObjC, wAsmNoStackFrame, wError, wDiscardable, wNoInit, wCodegenDecl, - wGensym, wInject, wRaises, wTags, wLocks, wDelegator, wGcSafe, - wOverride, wConstructor, wExportNims, wUsed, wLiftLocals} + wGensym, wInject, wRaises, wTags, wLocks, wDelegator, wGcSafe, wOverride, + wConstructor, wExportNims, wUsed, wLiftLocals, wStacktrace, wLinetrace} converterPragmas* = procPragmas methodPragmas* = procPragmas+{wBase}-{wImportCpp} templatePragmas* = {wImmediate, wDeprecated, wError, wGensym, wInject, wDirty, diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 455e06e82..b4c327362 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1489,6 +1489,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, # before compiling the proc body, set as current the scope # where the proc was declared let oldScope = c.currentScope + let oldOptions = c.config.options #c.currentScope = s.scope pushOwner(c, s) openScope(c) @@ -1569,6 +1570,8 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, popOwner(c) pushOwner(c, s) s.options = c.config.options + c.config.options = oldOptions + if sfOverriden in s.flags or s.name.s[0] == '=': semOverride(c, s, n) if s.name.s[0] in {'.', '('}: if s.name.s in [".", ".()", ".="] and {destructor, dotOperators} * c.features == {}: diff --git a/tests/errmsgs/tproper_stacktrace.nim b/tests/errmsgs/tproper_stacktrace.nim index 4e5c5fbf8..134946651 100644 --- a/tests/errmsgs/tproper_stacktrace.nim +++ b/tests/errmsgs/tproper_stacktrace.nim @@ -119,5 +119,23 @@ when isMainModule: verifyStackTrace expectedStackTrace: foo() + block: + proc bar() {.stackTrace: off.} = + proc baz() = # Stack trace should be enabled + raiseTestException() + baz() + + proc foo() = + bar() + + const expectedStackTrace = """ + tproper_stacktrace.nim(139) tproper_stacktrace + tproper_stacktrace.nim(129) foo + tproper_stacktrace.nim(125) baz + tproper_stacktrace.nim(7) raiseTestException + """ + + verifyStackTrace expectedStackTrace: + foo() echo "ok" -- cgit 1.4.1-2-gfad0 From 51fdb071cb274530ce865cae2b1e011788ce5c8d Mon Sep 17 00:00:00 2001 From: Arne Döring Date: Wed, 13 Jun 2018 12:18:21 +0200 Subject: fix #5930 --- tests/macros/tmacrostmt.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/tests/macros/tmacrostmt.nim b/tests/macros/tmacrostmt.nim index 6f648958f..849a32ea3 100644 --- a/tests/macros/tmacrostmt.nim +++ b/tests/macros/tmacrostmt.nim @@ -1,5 +1,5 @@ import macros -macro case_token(n: untyped): untyped {.immediate.} = +macro case_token(n: varargs[untyped]): untyped = # creates a lexical analyzer from regular expressions # ... (implementation is an exercise for the reader :-) nil @@ -21,6 +21,6 @@ case_token: inc i macro foo: typed = var exp = newCall("whatwhat", newIntLitNode(1)) if compiles(getAst(exp)): return exp - else: echo "Does not compute!" + else: echo "Does not compute! (test OK)" foo() -- cgit 1.4.1-2-gfad0 From e80be6173d854ecb50e19ba1459529432fc5efbe Mon Sep 17 00:00:00 2001 From: Vindaar Date: Wed, 13 Jun 2018 19:32:12 +0200 Subject: Add parse bin int, fixes #8018 (#8020) * clarify `parseHexInt`, `parseOctInt` docstring and exception msgs * add `parseBinInt` based on `parseutil.parseBin` implementation Adds a `parseBinInt`, which parses a binary integer string and returns it as an integer. This is based on the implementation of `parseutil.parseBin`, removing the unnecessary parts. * add tests for all `parse(Hex|Oct|Bin)Int` procs * replace `parse*Int` proc impls by call to parseutil procs Replaces the `parse(Hex|Oct|Bin)Int` procedure implementation by calls to the `parseutil` procs, which receive a mutable argument. Has the main advantage that the empty string as well as a "prefix only" string, e.g. "0x" counts as an invalid integer. Also moves the `parseOctInt` proc further up in the file so that all `parse` procs are below one another. * replace `var L` by `let L` in `parse` procs There's no reason for the usage of `var` here. * add `maxLen` optional arg for `parseutil.parse(Oct|Bin)` Plus small change to test cases. * update changelog about `parse*Int` procs * fix `rejectParse` template in `tstrutils` * make sure only `s.len` chars are parsed, if `maxLen+start` > s.len Fixes a previous bug in `parseHex` (and now affected `parseOct` and `parseBin`), which allowed to set `start + maxLen` to be larger than the strings length. This resulted in an out of bounds access. * move `parse*Int` proc change to breaking changes, add double ` --- changelog.md | 7 ++++- lib/pure/parseutils.nim | 38 +++++++++++++++++-------- lib/pure/strutils.nim | 70 +++++++++++++++++++++-------------------------- tests/stdlib/tstrutil.nim | 49 ++++++++++++++++++++++++++++++++- 4 files changed, 111 insertions(+), 53 deletions(-) (limited to 'tests') diff --git a/changelog.md b/changelog.md index 959990900..8919cf702 100644 --- a/changelog.md +++ b/changelog.md @@ -48,6 +48,10 @@ - For string inputs, ``strutils.isUpperAscii`` and ``strutils.isLowerAscii`` now require a second mandatory parameter ``skipNonAlpha``. +- The procs ``parseHexInt`` and ``parseOctInt`` now fail on empty strings + and strings containing only valid prefixes, e.g. "0x" for hex integers. + + #### Breaking changes in the compiler - The undocumented ``#? braces`` parsing mode was removed. @@ -72,6 +76,8 @@ - Added the procs ``math.floorMod`` and ``math.floorDiv`` for floor based integer division. - Added the procs ``rationals.`div```, ``rationals.`mod```, ``rationals.floorDiv`` and ``rationals.floorMod`` for rationals. - Added the proc ``math.prod`` for product of elements in openArray. +- Added the proc ``parseBinInt`` to parse a binary integer from a string, which returns the value. +- ``parseOct`` and ``parseBin`` in parseutils now also support the ``maxLen`` argument similar to ``parseHexInt`` ### Library changes @@ -100,7 +106,6 @@ - Added the parameter ``val`` for the ``CritBitTree[T].incl`` proc. - The proc ``tgamma`` was renamed to ``gamma``. ``tgamma`` is deprecated. - ### Language additions - Dot calls combined with explicit generic instantiations can now be written diff --git a/lib/pure/parseutils.nim b/lib/pure/parseutils.nim index d54f1454b..e633d8cf7 100644 --- a/lib/pure/parseutils.nim +++ b/lib/pure/parseutils.nim @@ -47,12 +47,14 @@ proc parseHex*(s: string, number: var int, start = 0; maxLen = 0): int {. ## discard parseHex("0x38", value) ## assert value == -200 ## - ## If 'maxLen==0' the length of the hexadecimal number has no - ## upper bound. Not more than ```maxLen`` characters are parsed. + ## If ``maxLen == 0`` the length of the hexadecimal number has no upper bound. + ## Else no more than ``start + maxLen`` characters are parsed, up to the + ## length of the string. var i = start var foundDigit = false - let last = if maxLen == 0: s.len else: i+maxLen - if i+1 < last and s[i] == '0' and (s[i+1] == 'x' or s[i+1] == 'X'): inc(i, 2) + # get last index based on minimum `start + maxLen` or `s.len` + let last = min(s.len, if maxLen == 0: s.len else: i+maxLen) + if i+1 < last and s[i] == '0' and (s[i+1] in {'x', 'X'}): inc(i, 2) elif i < last and s[i] == '#': inc(i) while i < last: case s[i] @@ -70,14 +72,20 @@ proc parseHex*(s: string, number: var int, start = 0; maxLen = 0): int {. inc(i) if foundDigit: result = i-start -proc parseOct*(s: string, number: var int, start = 0): int {. +proc parseOct*(s: string, number: var int, start = 0, maxLen = 0): int {. rtl, extern: "npuParseOct", noSideEffect.} = - ## parses an octal number and stores its value in ``number``. Returns + ## Parses an octal number and stores its value in ``number``. Returns ## the number of the parsed characters or 0 in case of an error. + ## + ## If ``maxLen == 0`` the length of the octal number has no upper bound. + ## Else no more than ``start + maxLen`` characters are parsed, up to the + ## length of the string. var i = start var foundDigit = false - if i+1 < s.len and s[i] == '0' and (s[i+1] == 'o' or s[i+1] == 'O'): inc(i, 2) - while i < s.len: + # get last index based on minimum `start + maxLen` or `s.len` + let last = min(s.len, if maxLen == 0: s.len else: i+maxLen) + if i+1 < last and s[i] == '0' and (s[i+1] in {'o', 'O'}): inc(i, 2) + while i < last: case s[i] of '_': discard of '0'..'7': @@ -87,14 +95,20 @@ proc parseOct*(s: string, number: var int, start = 0): int {. inc(i) if foundDigit: result = i-start -proc parseBin*(s: string, number: var int, start = 0): int {. +proc parseBin*(s: string, number: var int, start = 0, maxLen = 0): int {. rtl, extern: "npuParseBin", noSideEffect.} = - ## parses an binary number and stores its value in ``number``. Returns + ## Parses an binary number and stores its value in ``number``. Returns ## the number of the parsed characters or 0 in case of an error. + ## + ## If ``maxLen == 0`` the length of the binary number has no upper bound. + ## Else no more than ``start + maxLen`` characters are parsed, up to the + ## length of the string. var i = start var foundDigit = false - if i+1 < s.len and s[i] == '0' and (s[i+1] == 'b' or s[i+1] == 'B'): inc(i, 2) - while i < s.len: + # get last index based on minimum `start + maxLen` or `s.len` + let last = min(s.len, if maxLen == 0: s.len else: i+maxLen) + if i+1 < last and s[i] == '0' and (s[i+1] in {'b', 'B'}): inc(i, 2) + while i < last: case s[i] of '_': discard of '0'..'1': diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index bea0a0243..5de013c26 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -844,7 +844,7 @@ proc parseInt*(s: string): int {.noSideEffect, procvar, ## Parses a decimal integer value contained in `s`. ## ## If `s` is not a valid integer, `ValueError` is raised. - var L = parseutils.parseInt(s, result, 0) + let L = parseutils.parseInt(s, result, 0) if L != s.len or L == 0: raise newException(ValueError, "invalid integer: " & s) @@ -853,7 +853,7 @@ proc parseBiggestInt*(s: string): BiggestInt {.noSideEffect, procvar, ## Parses a decimal integer value contained in `s`. ## ## If `s` is not a valid integer, `ValueError` is raised. - var L = parseutils.parseBiggestInt(s, result, 0) + let L = parseutils.parseBiggestInt(s, result, 0) if L != s.len or L == 0: raise newException(ValueError, "invalid integer: " & s) @@ -862,7 +862,7 @@ proc parseUInt*(s: string): uint {.noSideEffect, procvar, ## Parses a decimal unsigned integer value contained in `s`. ## ## If `s` is not a valid integer, `ValueError` is raised. - var L = parseutils.parseUInt(s, result, 0) + let L = parseutils.parseUInt(s, result, 0) if L != s.len or L == 0: raise newException(ValueError, "invalid unsigned integer: " & s) @@ -871,7 +871,7 @@ proc parseBiggestUInt*(s: string): BiggestUInt {.noSideEffect, procvar, ## Parses a decimal unsigned integer value contained in `s`. ## ## If `s` is not a valid integer, `ValueError` is raised. - var L = parseutils.parseBiggestUInt(s, result, 0) + let L = parseutils.parseBiggestUInt(s, result, 0) if L != s.len or L == 0: raise newException(ValueError, "invalid unsigned integer: " & s) @@ -880,33 +880,42 @@ proc parseFloat*(s: string): float {.noSideEffect, procvar, ## Parses a decimal floating point value contained in `s`. If `s` is not ## a valid floating point number, `ValueError` is raised. ``NAN``, ## ``INF``, ``-INF`` are also supported (case insensitive comparison). - var L = parseutils.parseFloat(s, result, 0) + let L = parseutils.parseFloat(s, result, 0) if L != s.len or L == 0: raise newException(ValueError, "invalid float: " & s) +proc parseBinInt*(s: string): int {.noSideEffect, procvar, + rtl, extern: "nsuParseBinInt".} = + ## Parses a binary integer value contained in `s`. + ## + ## If `s` is not a valid binary integer, `ValueError` is raised. `s` can have + ## one of the following optional prefixes: ``0b``, ``0B``. Underscores within + ## `s` are ignored. + let L = parseutils.parseBin(s, result, 0) + if L != s.len or L == 0: + raise newException(ValueError, "invalid binary integer: " & s) + +proc parseOctInt*(s: string): int {.noSideEffect, + rtl, extern: "nsuParseOctInt".} = + ## Parses an octal integer value contained in `s`. + ## + ## If `s` is not a valid oct integer, `ValueError` is raised. `s` can have one + ## of the following optional prefixes: ``0o``, ``0O``. Underscores within + ## `s` are ignored. + let L = parseutils.parseOct(s, result, 0) + if L != s.len or L == 0: + raise newException(ValueError, "invalid oct integer: " & s) + proc parseHexInt*(s: string): int {.noSideEffect, procvar, rtl, extern: "nsuParseHexInt".} = ## Parses a hexadecimal integer value contained in `s`. ## - ## If `s` is not a valid integer, `ValueError` is raised. `s` can have one + ## If `s` is not a valid hex integer, `ValueError` is raised. `s` can have one ## of the following optional prefixes: ``0x``, ``0X``, ``#``. Underscores ## within `s` are ignored. - var i = 0 - if i+1 < s.len and s[i] == '0' and (s[i+1] == 'x' or s[i+1] == 'X'): inc(i, 2) - elif i < s.len and s[i] == '#': inc(i) - while i < s.len: - case s[i] - of '_': inc(i) - of '0'..'9': - result = result shl 4 or (ord(s[i]) - ord('0')) - inc(i) - of 'a'..'f': - result = result shl 4 or (ord(s[i]) - ord('a') + 10) - inc(i) - of 'A'..'F': - result = result shl 4 or (ord(s[i]) - ord('A') + 10) - inc(i) - else: raise newException(ValueError, "invalid integer: " & s) + let L = parseutils.parseHex(s, result, 0) + if L != s.len or L == 0: + raise newException(ValueError, "invalid hex integer: " & s) proc generateHexCharToValueMap(): string = ## Generate a string to map a hex digit to uint value @@ -1616,23 +1625,6 @@ proc delete*(s: var string, first, last: int) {.noSideEffect, inc(j) setLen(s, newLen) -proc parseOctInt*(s: string): int {.noSideEffect, - rtl, extern: "nsuParseOctInt".} = - ## Parses an octal integer value contained in `s`. - ## - ## If `s` is not a valid integer, `ValueError` is raised. `s` can have one - ## of the following optional prefixes: ``0o``, ``0O``. Underscores within - ## `s` are ignored. - var i = 0 - if i+1 < s.len and s[i] == '0' and (s[i+1] == 'o' or s[i+1] == 'O'): inc(i, 2) - while i < s.len: - case s[i] - of '_': inc(i) - of '0'..'7': - result = result shl 3 or (ord(s[i]) - ord('0')) - inc(i) - else: raise newException(ValueError, "invalid integer: " & s) - proc toOct*(x: BiggestInt, len: Positive): string {.noSideEffect, rtl, extern: "nsuToOct".} = ## Converts `x` into its octal representation. diff --git a/tests/stdlib/tstrutil.nim b/tests/stdlib/tstrutil.nim index 6f78a91ac..4d4081d39 100644 --- a/tests/stdlib/tstrutil.nim +++ b/tests/stdlib/tstrutil.nim @@ -7,6 +7,14 @@ discard """ import strutils +import macros + +template rejectParse(e) = + try: + discard e + raise newException(AssertionError, "This was supposed to fail: $#!" % astToStr(e)) + except ValueError: discard + proc testStrip() = write(stdout, strip(" ha ")) @@ -148,7 +156,6 @@ proc testDelete = delete(s, 0, 0) assert s == "1236789ABCDEFG" - proc testIsAlphaNumeric = assert isAlphaNumeric("abcdABC1234") == true assert isAlphaNumeric("a") == true @@ -203,10 +210,50 @@ proc testCountLines = assertCountLines("\nabc\n123") assertCountLines("\nabc\n123\n") +proc testParseInts = + # binary + assert "0b1111".parseBinInt == 15 + assert "0B1111".parseBinInt == 15 + assert "1111".parseBinInt == 15 + assert "1110".parseBinInt == 14 + assert "1_1_1_1".parseBinInt == 15 + assert "0b1_1_1_1".parseBinInt == 15 + rejectParse "".parseBinInt + rejectParse "_".parseBinInt + rejectParse "0b".parseBinInt + rejectParse "0b1234".parseBinInt + # hex + assert "0x72".parseHexInt == 114 + assert "0X72".parseHexInt == 114 + assert "#72".parseHexInt == 114 + assert "72".parseHexInt == 114 + assert "FF".parseHexInt == 255 + assert "ff".parseHexInt == 255 + assert "fF".parseHexInt == 255 + assert "0x7_2".parseHexInt == 114 + rejectParse "".parseHexInt + rejectParse "_".parseHexInt + rejectParse "0x".parseHexInt + rejectParse "0xFFG".parseHexInt + rejectParse "reject".parseHexInt + # octal + assert "0o17".parseOctInt == 15 + assert "0O17".parseOctInt == 15 + assert "17".parseOctInt == 15 + assert "10".parseOctInt == 8 + assert "0o1_0_0".parseOctInt == 64 + rejectParse "".parseOctInt + rejectParse "_".parseOctInt + rejectParse "0o".parseOctInt + rejectParse "9".parseOctInt + rejectParse "0o9".parseOctInt + rejectParse "reject".parseOctInt + testDelete() testFind() testRFind() testCountLines() +testParseInts() assert(insertSep($1000_000) == "1_000_000") assert(insertSep($232) == "232") -- cgit 1.4.1-2-gfad0 From bf5d619a52da04c857a6f7fb3d68afc12182bc22 Mon Sep 17 00:00:00 2001 From: Dmitry Atamanov Date: Thu, 14 Jun 2018 19:34:26 +0300 Subject: Add MemMapFileStream. Fixes in memFiles. (#7944) * Add MemMapFileStream * Added tests * Fixed bug in memfiles (zero index for string) * Added flush to changelog * Attempt to fix Win's nuances * Fix attempt to fix * Continue... * And again... * Reworked tests (all for win on Win) * Fixes in flush (Win) * Replace fn vars to consts * Added the attempts parameter to the flush * Replace while to for * Move to memfiles * Use Natural instead of uint * Better error messages for append mode. Handle specific cases. --- changelog.md | 2 + lib/pure/memfiles.nim | 118 +++++++++++++++++++++++++++++++++++++--- lib/windows/winlean.nim | 42 +++++++++----- tests/stdlib/tmemfiles2.nim | 3 +- tests/stdlib/tmemmapstreams.nim | 53 ++++++++++++++++++ 5 files changed, 195 insertions(+), 23 deletions(-) create mode 100644 tests/stdlib/tmemmapstreams.nim (limited to 'tests') diff --git a/changelog.md b/changelog.md index 8919cf702..4067cb693 100644 --- a/changelog.md +++ b/changelog.md @@ -78,6 +78,8 @@ - Added the proc ``math.prod`` for product of elements in openArray. - Added the proc ``parseBinInt`` to parse a binary integer from a string, which returns the value. - ``parseOct`` and ``parseBin`` in parseutils now also support the ``maxLen`` argument similar to ``parseHexInt`` +- Added the proc ``flush`` for memory mapped files. +- Added the ``MemMapFileStream``. ### Library changes diff --git a/lib/pure/memfiles.nim b/lib/pure/memfiles.nim index bf6795b0c..c7b8ebbd8 100644 --- a/lib/pure/memfiles.nim +++ b/lib/pure/memfiles.nim @@ -22,7 +22,11 @@ elif defined(posix): else: {.error: "the memfiles module is not supported on your operating system!".} -import os +import os, streams + +proc newEIO(msg: string): ref IOError = + new(result) + result.msg = msg type MemFile* = object ## represents a memory mapped file @@ -44,11 +48,14 @@ proc mapMem*(m: var MemFile, mode: FileMode = fmRead, ## ## ``mappedSize`` of ``-1`` maps to the whole file, and ## ``offset`` must be multiples of the PAGE SIZE of your OS + if mode == fmAppend: + raise newEIO("The append mode is not supported.") + var readonly = mode == fmRead when defined(windows): result = mapViewOfFileEx( m.mapHandle, - if readonly: FILE_MAP_READ else: FILE_MAP_WRITE, + if readonly: FILE_MAP_READ else: FILE_MAP_READ or FILE_MAP_WRITE, int32(offset shr 32), int32(offset and 0xffffffff), if mappedSize == -1: 0 else: mappedSize, @@ -113,6 +120,9 @@ proc open*(filename: string, mode: FileMode = fmRead, ## mm_half = memfiles.open("/tmp/test.mmap", mode = fmReadWrite, mappedSize = 512) # The file can be resized only when write mode is used: + if mode == fmAppend: + raise newEIO("The append mode is not supported.") + assert newFileSize == -1 or mode != fmRead var readonly = mode == fmRead @@ -121,6 +131,10 @@ proc open*(filename: string, mode: FileMode = fmRead, result.size = 0 when defined(windows): + let desiredAccess = GENERIC_READ + let shareMode = FILE_SHARE_READ + let flags = FILE_FLAG_RANDOM_ACCESS + template fail(errCode: OSErrorCode, msg: untyped) = rollback() if result.fHandle != 0: discard closeHandle(result.fHandle) @@ -133,11 +147,11 @@ proc open*(filename: string, mode: FileMode = fmRead, winApiProc( filename, # GENERIC_ALL != (GENERIC_READ or GENERIC_WRITE) - if readonly: GENERIC_READ else: GENERIC_READ or GENERIC_WRITE, - FILE_SHARE_READ, + if readonly: desiredAccess else: desiredAccess or GENERIC_WRITE, + if readonly: shareMode else: shareMode or FILE_SHARE_WRITE, nil, if newFileSize != -1: CREATE_ALWAYS else: OPEN_EXISTING, - if readonly: FILE_ATTRIBUTE_READONLY else: FILE_ATTRIBUTE_TEMPORARY, + if readonly: FILE_ATTRIBUTE_READONLY or flags else: FILE_ATTRIBUTE_NORMAL or flags, 0) when useWinUnicode: @@ -172,7 +186,7 @@ proc open*(filename: string, mode: FileMode = fmRead, result.mem = mapViewOfFileEx( result.mapHandle, - if readonly: FILE_MAP_READ else: FILE_MAP_WRITE, + if readonly: FILE_MAP_READ else: FILE_MAP_READ or FILE_MAP_WRITE, int32(offset shr 32), int32(offset and 0xffffffff), if mappedSize == -1: 0 else: mappedSize, @@ -245,6 +259,28 @@ proc open*(filename: string, mode: FileMode = fmRead, if close(result.handle) == 0: result.handle = -1 +proc flush*(f: var MemFile; attempts: Natural = 3) = + ## Flushes `f`'s buffer for the number of attempts equal to `attempts`. + ## If were errors an exception `OSError` will be raised. + var res = false + var lastErr: OSErrorCode + when defined(windows): + for i in 1..attempts: + res = flushViewOfFile(f.mem, 0) != 0 + if res: + break + lastErr = osLastError() + if lastErr != ERROR_LOCK_VIOLATION.OSErrorCode: + raiseOSError(lastErr) + else: + for i in 1..attempts: + res = msync(f.mem, f.size, MS_SYNC or MS_INVALIDATE) == 0 + if res: + break + lastErr = osLastError() + if lastErr != EBUSY.OSErrorCode: + raiseOSError(lastErr, "error flushing mapping") + proc close*(f: var MemFile) = ## closes the memory mapped file `f`. All changes are written back to the ## file system, if `f` was opened with write access. @@ -362,9 +398,8 @@ iterator lines*(mfile: MemFile, buf: var TaintedString, delim='\l', eat='\r'): T ## echo line for ms in memSlices(mfile, delim, eat): - buf.setLen(ms.size) - copyMem(addr(buf[0]), ms.data, ms.size) - buf[ms.size] = '\0' + setLen(buf.string, ms.size) + copyMem(buf.cstring, ms.data, ms.size) yield buf iterator lines*(mfile: MemFile, delim='\l', eat='\r'): TaintedString {.inline.} = @@ -382,3 +417,68 @@ iterator lines*(mfile: MemFile, delim='\l', eat='\r'): TaintedString {.inline.} var buf = TaintedString(newStringOfCap(80)) for line in lines(mfile, buf, delim, eat): yield buf + +type + MemMapFileStream* = ref MemMapFileStreamObj ## a stream that encapsulates a `MemFile` + MemMapFileStreamObj* = object of Stream + mf: MemFile + mode: FileMode + pos: ByteAddress + +proc mmsClose(s: Stream) = + MemMapFileStream(s).pos = -1 + close(MemMapFileStream(s).mf) + +proc mmsFlush(s: Stream) = flush(MemMapFileStream(s).mf) + +proc mmsAtEnd(s: Stream): bool = (MemMapFileStream(s).pos >= MemMapFileStream(s).mf.size) or + (MemMapFileStream(s).pos < 0) + +proc mmsSetPosition(s: Stream, pos: int) = + if pos > MemMapFileStream(s).mf.size or pos < 0: + raise newEIO("cannot set pos in stream") + MemMapFileStream(s).pos = pos + +proc mmsGetPosition(s: Stream): int = MemMapFileStream(s).pos + +proc mmsPeekData(s: Stream, buffer: pointer, bufLen: int): int = + let startAddress = cast[ByteAddress](MemMapFileStream(s).mf.mem) + let p = cast[ByteAddress](MemMapFileStream(s).pos) + let l = min(bufLen, MemMapFileStream(s).mf.size - p) + moveMem(buffer, cast[pointer](startAddress + p), l) + result = l + +proc mmsReadData(s: Stream, buffer: pointer, bufLen: int): int = + result = mmsPeekData(s, buffer, bufLen) + inc(MemMapFileStream(s).pos, result) + +proc mmsWriteData(s: Stream, buffer: pointer, bufLen: int) = + if MemMapFileStream(s).mode == fmRead: + raise newEIO("cannot write to read-only stream") + let size = MemMapFileStream(s).mf.size + if MemMapFileStream(s).pos + bufLen > size: + raise newEIO("cannot write to stream") + let p = cast[ByteAddress](MemMapFileStream(s).mf.mem) + + cast[ByteAddress](MemMapFileStream(s).pos) + moveMem(cast[pointer](p), buffer, bufLen) + inc(MemMapFileStream(s).pos, bufLen) + +proc newMemMapFileStream*(filename: string, mode: FileMode = fmRead, fileSize: int = -1): + MemMapFileStream = + ## creates a new stream from the file named `filename` with the mode `mode`. + ## Raises ## `EOS` if the file cannot be opened. See the `system + ## `_ module for a list of available FileMode enums. + ## ``fileSize`` can only be set if the file does not exist and is opened + ## with write access (e.g., with fmReadWrite). + var mf: MemFile = open(filename, mode, newFileSize = fileSize) + new(result) + result.mode = mode + result.mf = mf + result.closeImpl = mmsClose + result.atEndImpl = mmsAtEnd + result.setPositionImpl = mmsSetPosition + result.getPositionImpl = mmsGetPosition + result.readDataImpl = mmsReadData + result.peekDataImpl = mmsPeekData + result.writeDataImpl = mmsWriteData + result.flushImpl = mmsFlush diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim index 7e22f98c7..3263f1d37 100644 --- a/lib/windows/winlean.nim +++ b/lib/windows/winlean.nim @@ -129,7 +129,6 @@ const PIPE_ACCESS_OUTBOUND* = 2'i32 PIPE_NOWAIT* = 0x00000001'i32 SYNCHRONIZE* = 0x00100000'i32 - FILE_FLAG_WRITE_THROUGH* = 0x80000000'i32 CREATE_NO_WINDOW* = 0x08000000'i32 @@ -281,15 +280,31 @@ else: importc:"CreateHardLinkA", dynlib: "kernel32", stdcall.} const - FILE_ATTRIBUTE_ARCHIVE* = 32'i32 - FILE_ATTRIBUTE_COMPRESSED* = 2048'i32 - FILE_ATTRIBUTE_NORMAL* = 128'i32 - FILE_ATTRIBUTE_DIRECTORY* = 16'i32 - FILE_ATTRIBUTE_HIDDEN* = 2'i32 - FILE_ATTRIBUTE_READONLY* = 1'i32 - FILE_ATTRIBUTE_REPARSE_POINT* = 1024'i32 - FILE_ATTRIBUTE_SYSTEM* = 4'i32 - FILE_ATTRIBUTE_TEMPORARY* = 256'i32 + FILE_ATTRIBUTE_READONLY* = 0x00000001'i32 + FILE_ATTRIBUTE_HIDDEN* = 0x00000002'i32 + FILE_ATTRIBUTE_SYSTEM* = 0x00000004'i32 + FILE_ATTRIBUTE_DIRECTORY* = 0x00000010'i32 + FILE_ATTRIBUTE_ARCHIVE* = 0x00000020'i32 + FILE_ATTRIBUTE_DEVICE* = 0x00000040'i32 + FILE_ATTRIBUTE_NORMAL* = 0x00000080'i32 + FILE_ATTRIBUTE_TEMPORARY* = 0x00000100'i32 + FILE_ATTRIBUTE_SPARSE_FILE* = 0x00000200'i32 + FILE_ATTRIBUTE_REPARSE_POINT* = 0x00000400'i32 + FILE_ATTRIBUTE_COMPRESSED* = 0x00000800'i32 + FILE_ATTRIBUTE_OFFLINE* = 0x00001000'i32 + FILE_ATTRIBUTE_NOT_CONTENT_INDEXED* = 0x00002000'i32 + + FILE_FLAG_FIRST_PIPE_INSTANCE* = 0x00080000'i32 + FILE_FLAG_OPEN_NO_RECALL* = 0x00100000'i32 + FILE_FLAG_OPEN_REPARSE_POINT* = 0x00200000'i32 + FILE_FLAG_POSIX_SEMANTICS* = 0x01000000'i32 + FILE_FLAG_BACKUP_SEMANTICS* = 0x02000000'i32 + FILE_FLAG_DELETE_ON_CLOSE* = 0x04000000'i32 + FILE_FLAG_SEQUENTIAL_SCAN* = 0x08000000'i32 + FILE_FLAG_RANDOM_ACCESS* = 0x10000000'i32 + FILE_FLAG_NO_BUFFERING* = 0x20000000'i32 + FILE_FLAG_OVERLAPPED* = 0x40000000'i32 + FILE_FLAG_WRITE_THROUGH* = 0x80000000'i32 MAX_PATH* = 260 @@ -683,8 +698,6 @@ const FILE_MAP_WRITE* = 2'i32 INVALID_FILE_SIZE* = -1'i32 - FILE_FLAG_BACKUP_SEMANTICS* = 33554432'i32 - FILE_FLAG_OPEN_REPARSE_POINT* = 0x00200000'i32 DUPLICATE_SAME_ACCESS* = 2 FILE_READ_DATA* = 0x00000001 # file & pipe FILE_WRITE_DATA* = 0x00000002 # file & pipe @@ -695,6 +708,7 @@ const ERROR_PATH_NOT_FOUND* = 3 ERROR_ACCESS_DENIED* = 5 ERROR_NO_MORE_FILES* = 18 + ERROR_LOCK_VIOLATION* = 33 ERROR_HANDLE_EOF* = 38 ERROR_BAD_ARGUMENTS* = 165 @@ -763,6 +777,9 @@ when not useWinUnicode: proc unmapViewOfFile*(lpBaseAddress: pointer): WINBOOL {.stdcall, dynlib: "kernel32", importc: "UnmapViewOfFile".} +proc flushViewOfFile*(lpBaseAddress: pointer, dwNumberOfBytesToFlush: DWORD): WINBOOL {. + stdcall, dynlib: "kernel32", importc: "FlushViewOfFile".} + type OVERLAPPED* {.pure, inheritable.} = object internal*: PULONG @@ -785,7 +802,6 @@ type const ERROR_IO_PENDING* = 997 # a.k.a WSA_IO_PENDING - FILE_FLAG_OVERLAPPED* = 1073741824 WSAECONNABORTED* = 10053 WSAEADDRINUSE* = 10048 WSAECONNRESET* = 10054 diff --git a/tests/stdlib/tmemfiles2.nim b/tests/stdlib/tmemfiles2.nim index 7ea94cffc..d6cfa533a 100644 --- a/tests/stdlib/tmemfiles2.nim +++ b/tests/stdlib/tmemfiles2.nim @@ -4,9 +4,10 @@ discard """ Half read size: 10 Data: Hello''' """ import memfiles, os +const + fn = "test.mmap" var mm, mm_full, mm_half: MemFile - fn = "test.mmap" p: pointer if fileExists(fn): removeFile(fn) diff --git a/tests/stdlib/tmemmapstreams.nim b/tests/stdlib/tmemmapstreams.nim new file mode 100644 index 000000000..243574f1a --- /dev/null +++ b/tests/stdlib/tmemmapstreams.nim @@ -0,0 +1,53 @@ +discard """ + file: "tmemmapstreams.nim" + output: '''Created size: 10 +Position after writing: 5 +Position after writing one char: 6 +Peeked data: Hello +Position after peeking: 0 +Readed data: Hello! +Position after reading line: 7 +Position after setting position: 6 +Readed line: Hello! +Position after reading line: 7''' +""" +import os, streams, memfiles +const + fn = "test.mmapstream" +var + mms: MemMapFileStream + +if fileExists(fn): removeFile(fn) + +# Create a new memory mapped file, data all zeros +mms = newMemMapFileStream(fn, mode = fmReadWrite, fileSize = 10) +mms.close() +if fileExists(fn): echo "Created size: ", getFileSize(fn) + +# write, flush, peek, read +mms = newMemMapFileStream(fn, mode = fmReadWrite) +let s = "Hello" + +mms.write(s) +mms.flush +echo "Position after writing: ", mms.getPosition() +mms.write('!') +mms.flush +echo "Position after writing one char: ", mms.getPosition() +mms.close() + +mms = newMemMapFileStream(fn, mode = fmRead) +echo "Peeked data: ", mms.peekStr(s.len) +echo "Position after peeking: ", mms.getPosition() +echo "Readed data: ", mms.readLine +echo "Position after reading line: ", mms.getPosition() +mms.setPosition(mms.getPosition() - 1) +echo "Position after setting position: ", mms.getPosition() + +mms.setPosition(0) +echo "Readed line: ", mms.readLine +echo "Position after reading line: ", mms.getPosition() + +mms.close() + +if fileExists(fn): removeFile(fn) -- cgit 1.4.1-2-gfad0 From 78cbf6734a2bb0514703232e623fac344f4b7b74 Mon Sep 17 00:00:00 2001 From: Yuriy Glukhov Date: Fri, 15 Jun 2018 19:46:17 +0300 Subject: Added more tests to toverflw --- tests/overflw/toverflw.nim | 75 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 69 insertions(+), 6 deletions(-) (limited to 'tests') diff --git a/tests/overflw/toverflw.nim b/tests/overflw/toverflw.nim index 771a43303..20bc56a53 100644 --- a/tests/overflw/toverflw.nim +++ b/tests/overflw/toverflw.nim @@ -1,21 +1,84 @@ discard """ file: "toverflw.nim" - output: "the computation overflowed" + output: "ok" + cmd: "nim $target -d:release $options $file" + """ # Tests nim's ability to detect overflows {.push overflowChecks: on.} var - a, b: int -a = high(int) -b = -2 + a = high(int) + b = -2 + overflowDetected = false + try: writeLine(stdout, b - a) except OverflowError: - writeLine(stdout, "the computation overflowed") + overflowDetected = true {.pop.} # overflow check -#OUT the computation overflowed + +doAssert(overflowDetected) + +block: # Overflow checks in a proc + var + a = high(int) + b = -2 + overflowDetected = false + + {.push overflowChecks: on.} + proc foo() = + let c = b - a + {.pop.} + + try: + foo() + except OverflowError: + overflowDetected = true + + doAssert(overflowDetected) + +block: # Overflow checks in a forward declared proc + var + a = high(int) + b = -2 + overflowDetected = false + + proc foo() + + {.push overflowChecks: on.} + proc foo() = + let c = b - a + {.pop.} + + try: + foo() + except OverflowError: + overflowDetected = true + + doAssert(overflowDetected) + +block: # Overflow checks doesn't affect fwd declaration + var + a = high(int) + b = -2 + overflowDetected = false + + {.push overflowChecks: on.} + proc foo() + {.pop.} + + proc foo() = + let c = b - a + + try: + foo() + except OverflowError: + overflowDetected = true + + doAssert(not overflowDetected) +echo "ok" -- cgit 1.4.1-2-gfad0 From 8633b1b3097e1627f16a52fc648f0e14c647a688 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Fri, 20 Apr 2018 12:54:06 +0300 Subject: Starting test recording the current state of the parser In the next commit, I'll introduce changes to the parser bringing consistent handling of all type modifiers (ref, ptr, var, static and type). The goal of this commit is to record precisely what is going to be changed (i.e. by allowing you to look at the diff). To preserve the diff, please don't squash upon merging. --- tests/parser/ttypemodifiers.nim | 481 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 481 insertions(+) create mode 100644 tests/parser/ttypemodifiers.nim (limited to 'tests') diff --git a/tests/parser/ttypemodifiers.nim b/tests/parser/ttypemodifiers.nim new file mode 100644 index 000000000..db7ab903f --- /dev/null +++ b/tests/parser/ttypemodifiers.nim @@ -0,0 +1,481 @@ +discard """ +nimout: ''' +StmtList + TypeSection + TypeDef + Ident "BarePtr" + Empty + PtrTy + TypeDef + Ident "GenericPtr" + Empty + PtrTy + Bracket + Ident "int" + TypeDef + Ident "PrefixPtr" + Empty + PtrTy + Ident "int" + TypeDef + Ident "PtrTuple" + Empty + PtrTy + Par + Ident "int" + Ident "string" + TypeDef + Ident "BareRef" + Empty + RefTy + TypeDef + Ident "GenericRef" + Empty + RefTy + Bracket + Ident "int" + TypeDef + Ident "RefTupleCl" + Empty + RefTy + TupleTy + TypeDef + Ident "RefTupleType" + Empty + RefTy + Par + Ident "int" + Ident "string" + TypeDef + Ident "RefTupleVars" + Empty + RefTy + Par + Ident "a" + Ident "b" + TypeDef + Ident "GenericStatic" + Empty + StaticTy + Ident "int" + TypeDef + Ident "PrefixStatic" + Empty + StaticExpr + Ident "int" + TypeDef + Ident "StaticTupleCl" + Empty + StaticExpr + TupleClassTy + TypeDef + Ident "StaticTuple" + Empty + StaticExpr + Par + Ident "int" + Ident "string" + TypeDef + Ident "BareType" + Empty + Ident "type" + TypeDef + Ident "GenericType" + Empty + BracketExpr + Ident "type" + Ident "float" + TypeDef + Ident "TypeTupleGen" + Empty + BracketExpr + Ident "type" + TupleClassTy + TypeDef + Ident "TypeInstance" + Empty + Command + Ident "type" + BracketExpr + Ident "Foo" + RefTy + TypeDef + Ident "bareTypeDesc" + Empty + Ident "typedesc" + TypeDef + Ident "TypeOfVar" + Empty + Call + Ident "type" + Ident "a" + TypeDef + Ident "TypeOfTuple1" + Empty + Call + Ident "type" + Ident "a" + TypeDef + Ident "TypeOfTuple2" + Empty + Call + Ident "type" + Ident "a" + Ident "b" + TypeDef + Ident "GenericTypedesc" + Empty + BracketExpr + Ident "typedesc" + Ident "int" + TypeDef + Ident "T" + Empty + Ident "type" + ProcDef + Ident "foo" + Empty + Empty + FormalParams + Ident "type" + IdentDefs + Ident "bareType" + Ident "type" + Empty + IdentDefs + Ident "genType" + BracketExpr + Ident "type" + Ident "int" + Empty + IdentDefs + Ident "typeInt" + Command + Ident "type" + Ident "int" + Empty + IdentDefs + Ident "typeIntAlt" + Call + Ident "type" + Ident "int" + Empty + IdentDefs + Ident "typeOfVar" + Call + Ident "type" + Ident "a" + Empty + IdentDefs + Ident "typeDotType" + DotExpr + Ident "foo" + Ident "type" + Empty + IdentDefs + Ident "genStatic" + StaticTy + Ident "int" + Empty + IdentDefs + Ident "staticInt" + StaticExpr + Ident "int" + Empty + IdentDefs + Ident "staticVal1" + StaticExpr + IntLit 10 + Empty + IdentDefs + Ident "staticVal2" + StaticExpr + Par + StrLit "str" + Empty + IdentDefs + Ident "staticVal3" + StaticExpr + StrLit "str" + Empty + IdentDefs + Ident "staticDotVal" + DotExpr + IntLit 10 + Ident "static" + Empty + IdentDefs + Ident "bareRef" + RefTy + Empty + IdentDefs + Ident "refTuple1" + RefTy + Par + Ident "int" + Empty + IdentDefs + Ident "refTuple1A" + RefTy + TupleConstr + Ident "int" + Empty + IdentDefs + Ident "refTuple2" + RefTy + Par + Ident "int" + Ident "string" + Empty + IdentDefs + Ident "genRef" + RefTy + Bracket + Ident "int" + Empty + IdentDefs + Ident "refInt" + RefTy + Ident "int" + Empty + IdentDefs + Ident "refCall" + RefTy + Par + Ident "a" + Empty + IdentDefs + Ident "macroCall1" + Command + Ident "foo" + Ident "bar" + Empty + IdentDefs + Ident "macroCall2" + Call + Ident "foo" + Ident "bar" + Empty + IdentDefs + Ident "macroCall3" + Call + DotExpr + Ident "foo" + Ident "bar" + Ident "baz" + Empty + IdentDefs + Ident "macroCall4" + Call + BracketExpr + Ident "foo" + Ident "bar" + Ident "baz" + Empty + IdentDefs + Ident "macroCall5" + Command + Ident "foo" + Command + Ident "bar" + Ident "baz" + IntLit 10 + Empty + Empty + StmtList + Asgn + Ident "staticTen" + StaticExpr + IntLit 10 + Asgn + Ident "staticA" + StaticExpr + Par + Ident "a" + Asgn + Ident "staticAspace" + StaticExpr + Par + Ident "a" + Asgn + Ident "staticAtuple" + StaticExpr + TupleConstr + Ident "a" + Asgn + Ident "staticTuple" + StaticExpr + Par + Ident "a" + Ident "b" + Asgn + Ident "staticTypeTuple" + StaticExpr + Par + Ident "int" + Ident "string" + Asgn + Ident "staticCall" + StaticExpr + Call + Ident "foo" + IntLit 1 + Asgn + Ident "staticStrCall" + StaticExpr + CallStrLit + Ident "foo" + RStrLit "x" + Asgn + Ident "staticChainCall" + StaticExpr + Command + Ident "foo" + Ident "bar" + Asgn + Ident "typeTen" + Command + Ident "type" + IntLit 10 + Asgn + Ident "typeA" + Call + Ident "type" + Ident "a" + Asgn + Ident "typeCall" + Command + Ident "type" + Call + Ident "foo" + IntLit 1 + Asgn + Ident "typeStrCall" + Command + Ident "type" + CallStrLit + Ident "foo" + RStrLit "x" + Asgn + Ident "typeChainCall" + Command + Ident "type" + Command + Ident "foo" + Ident "bar" + Asgn + Ident "normalChainCall" + Command + Ident "foo" + Command + Ident "bar" + Ident "baz" + Asgn + Ident "normalTupleCall2" + Call + Ident "foo" + Ident "a" + Ident "b" + StaticStmt + StmtList + Ident "singleStaticStmt" + StaticStmt + StmtList + Ident "staticStmtList1" + Ident "staticStmtList2" +''' +""" + +import macros + +dumpTree: + type + BarePtr = ptr + GenericPtr = ptr[int] + PrefixPtr = ptr int + PtrTuple = ptr (int, string) + BareRef = ref + GenericRef = ref[int] + RefTupleCl = ref tuple + RefTupleType = ref (int, string) + RefTupleVars = ref (a, b) + # BareStatic = static # Error: invalid indentation + GenericStatic = static[int] + PrefixStatic = static int + StaticTupleCl = static tuple + StaticTuple = static (int, string) + BareType = type + GenericType = type[float] + TypeTupleGen = type[tuple] + # TypeTupleCl = type tuple # Error: invalid indentation + TypeInstance = type Foo[ref] + bareTypeDesc = typedesc + TypeOfVar = type(a) + # TypeOfVarAlt= type (a) # Error: invalid indentation + TypeOfTuple1 = type(a,) + TypeOfTuple2 = type(a,b) + # TypeOfTuple1A = type (a,) # Error: invalid indentation + # TypeOfTuple2A = type (a,b) # Error: invalid indentation + # TypeTuple = type (int, string) # Error: invalid indentation + GenericTypedesc = typedesc[int] + T = type + + proc foo( + bareType : type, + genType : type[int], + typeInt : type int, + typeIntAlt : type(int), + typeOfVar : type(a), + typeDotType : foo.type, + # typeTupleCl : type tuple, # Error: ')' expected + # bareStatic : static, # Error: expression expected, but found ',' + genStatic : static[int], + staticInt : static int, + staticVal1 : static 10, + staticVal2 : static("str"), + staticVal3 : static "str", + # staticVal4 : static"str", # Error: expression expected, but found 'str' + staticDotVal : 10.static, + bareRef : ref, + refTuple1 : ref (int), + refTuple1A : ref (int,), + refTuple2 : ref (int,string), + genRef : ref[int], + refInt : ref int, + refCall : ref(a), + macroCall1 : foo bar, + macroCall2 : foo(bar), + macroCall3 : foo.bar(baz), + macroCall4 : foo[bar](baz), + macroCall5 : foo bar baz = 10 + ): type = + staticTen = static 10 + staticA = static(a) + staticAspace = static (a) + staticAtuple = static (a,) + staticTuple = static (a,b) + staticTypeTuple = static (int,string) + staticCall = static foo(1) + staticStrCall = static foo"x" + staticChainCall = static foo bar + + typeTen = type 10 + typeA = type(a) + # typeAspace = type (a) # Error: invalid indentation + # typeAtuple = type (a,) # Error: invalid indentation + # typeTuple = type (a,b) # Error: invalid indentation + # typeTypeTuple = type (int,string) # Error: invalid indentation + typeCall = type foo(1) + typeStrCall = type foo"x" + typeChainCall = type foo bar + + normalChainCall = foo bar baz + # normalTupleCall1 = foo(a,) # Error: invalid indentation + normalTupleCall2 = foo(a,b) + # normalTupleCall3 = foo (a,b) # Error: invalid indentation + + static: singleStaticStmt + static: + staticStmtList1 + staticStmtList2 + -- cgit 1.4.1-2-gfad0 From fb27357b6217f53dc882e83fc128da578ad51764 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Fri, 20 Apr 2018 11:44:13 +0300 Subject: A minimal patch enabling the new typedesc and static types syntax --- compiler/ast.nim | 4 +- compiler/condsyms.nim | 1 + compiler/parser.nim | 63 ++++++++++------- compiler/sem.nim | 3 + compiler/semexprs.nim | 39 +++++++++-- compiler/semtypes.nim | 38 ++++++---- compiler/sigmatch.nim | 14 ++-- lib/system.nim | 22 ++++-- tests/lexer/tmissingnl.nim | 2 +- tests/parser/ttypemodifiers.nim | 152 ++++++++++++++++++++++++++-------------- 10 files changed, 228 insertions(+), 110 deletions(-) (limited to 'tests') diff --git a/compiler/ast.nim b/compiler/ast.nim index 40c1b064d..d266e20b0 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -571,8 +571,8 @@ type TMagic* = enum # symbols that require compiler magic: mNone, mDefined, mDefinedInScope, mCompiles, mArrGet, mArrPut, mAsgn, - mLow, mHigh, mSizeOf, mTypeTrait, mIs, mOf, mAddr, mTypeOf, mRoof, mPlugin, - mEcho, mShallowCopy, mSlurp, mStaticExec, + mLow, mHigh, mSizeOf, mTypeTrait, mIs, mOf, mAddr, mType, mTypeOf, + mRoof, mPlugin, mEcho, mShallowCopy, mSlurp, mStaticExec, mStatic, mParseExprToAst, mParseStmtToAst, mExpandToAst, mQuoteAst, mUnaryLt, mInc, mDec, mOrd, mNew, mNewFinalize, mNewSeq, mNewSeqOfCap, diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index 56587a4fa..8b2d36722 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -44,6 +44,7 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimcomputedgoto") defineSymbol("nimunion") defineSymbol("nimnewshared") + defineSymbol("nimNewTypedesc") defineSymbol("nimrequiresnimframe") defineSymbol("nimparsebiggestfloatmagic") defineSymbol("nimalias") diff --git a/compiler/parser.nim b/compiler/parser.nim index c0465b05e..33ee8c9e6 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -50,6 +50,9 @@ type SymbolMode = enum smNormal, smAllowNil, smAfterDot + TPrimaryMode = enum + pmNormal, pmTypeDesc, pmTypeDef, pmSkipSuffix + proc parseAll*(p: var TParser): PNode proc closeParser*(p: var TParser) proc parseTopLevelStmt*(p: var TParser): PNode @@ -81,6 +84,9 @@ proc parsePragma(p: var TParser): PNode proc postExprBlocks(p: var TParser, x: PNode): PNode proc parseExprStmt(p: var TParser): PNode proc parseBlock(p: var TParser): PNode +proc primary(p: var TParser, mode: TPrimaryMode): PNode +proc simpleExprAux(p: var TParser, limit: int, mode: TPrimaryMode): PNode + # implementation proc getTok(p: var TParser) = @@ -332,6 +338,8 @@ proc colcom(p: var TParser, n: PNode) = eat(p, tkColon) skipComment(p, n) +const tkBuiltInMagics = {tkType, tkStatic, tkAddr} + proc parseSymbol(p: var TParser, mode = smNormal): PNode = #| symbol = '`' (KEYW|IDENT|literal|(operator|'('|')'|'['|']'|'{'|'}'|'=')+)+ '`' #| | IDENT | KEYW @@ -340,7 +348,7 @@ proc parseSymbol(p: var TParser, mode = smNormal): PNode = result = newIdentNodeP(p.tok.ident, p) getTok(p) of tokKeywordLow..tokKeywordHigh: - if p.tok.tokType == tkAddr or p.tok.tokType == tkType or mode == smAfterDot: + if p.tok.tokType in tkBuiltInMagics or mode == smAfterDot: # for backwards compatibility these 2 are always valid: result = newIdentNodeP(p.tok.ident, p) getTok(p) @@ -525,9 +533,6 @@ proc parseGStrLit(p: var TParser, a: PNode): PNode = else: result = a -type - TPrimaryMode = enum pmNormal, pmTypeDesc, pmTypeDef, pmSkipSuffix - proc complexOrSimpleStmt(p: var TParser): PNode proc simpleExpr(p: var TParser, mode = pmNormal): PNode @@ -625,7 +630,7 @@ proc identOrLiteral(p: var TParser, mode: TPrimaryMode): PNode = #| tupleConstr = '(' optInd (exprColonEqExpr comma?)* optPar ')' #| arrayConstr = '[' optInd (exprColonEqExpr comma?)* optPar ']' case p.tok.tokType - of tkSymbol, tkType, tkAddr: + of tkSymbol, tkBuiltInMagics: result = newIdentNodeP(p.tok.ident, p) getTok(p) result = parseGStrLit(p, result) @@ -741,7 +746,12 @@ proc commandParam(p: var TParser, isFirstParam: var bool): PNode = addSon(result, parseExpr(p)) isFirstParam = false -proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode = +const + tkTypeClasses = {tkRef, tkPtr, tkVar, tkStatic, tkType, + tkEnum, tkTuple, tkObject, tkProc} + +proc primarySuffix(p: var TParser, r: PNode, + baseIndent: int, mode: TPrimaryMode): PNode = #| primarySuffix = '(' (exprColonEqExpr comma?)* ')' doBlocks? #| | doBlocks #| | '.' optInd symbol generalizedLit? @@ -758,7 +768,14 @@ proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode = case p.tok.tokType of tkParLe: # progress guaranteed - somePar() + if p.tok.strongSpaceA > 0: + # inside type sections, expressions such as `ref (int, bar)` + # are parsed as a nkCommand with a single tuple argument (nkPar) + if mode == pmTypeDef: + result = newNodeP(nkCommand, p) + result.addSon r + result.addSon primary(p, pmNormal) + break result = namedParams(p, result, nkCall, tkParRi) if result.len > 1 and result.sons[1].kind == nkExprColonExpr: result.kind = nkObjConstr @@ -774,9 +791,15 @@ proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode = # progress guaranteed somePar() result = namedParams(p, result, nkCurlyExpr, tkCurlyRi) - of tkSymbol, tkAccent, tkIntLit..tkCharLit, tkNil, tkCast, tkAddr, tkType, - tkOpr, tkDotDot: - if p.inPragma == 0 and (isUnary(p) or p.tok.tokType notin {tkOpr, tkDotDot}): + of tkSymbol, tkAccent, tkIntLit..tkCharLit, tkNil, tkCast, + tkOpr, tkDotDot, tkTypeClasses - {tkRef, tkPtr}: + # XXX: In type sections we allow the free application of the + # command syntax, with the exception of expressions such as + # `foo ref` or `foo ptr`. Unfortunately, these two are also + # used as infix operators for the memory regions feature and + # the current parsing rules don't play well here. + if mode == pmTypeDef or + (p.inPragma == 0 and (isUnary(p) or p.tok.tokType notin {tkOpr, tkDotDot})): # actually parsing {.push hints:off.} as {.push(hints:off).} is a sweet # solution, but pragmas.nim can't handle that let a = result @@ -800,9 +823,6 @@ proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode = else: break -proc primary(p: var TParser, mode: TPrimaryMode): PNode -proc simpleExprAux(p: var TParser, limit: int, mode: TPrimaryMode): PNode - proc parseOperators(p: var TParser, headNode: PNode, limit: int, mode: TPrimaryMode): PNode = result = headNode @@ -1107,9 +1127,9 @@ proc parseProcExpr(p: var TParser; isExpr: bool; kind: TNodeKind): PNode = proc isExprStart(p: TParser): bool = case p.tok.tokType of tkSymbol, tkAccent, tkOpr, tkNot, tkNil, tkCast, tkIf, - tkProc, tkFunc, tkIterator, tkBind, tkAddr, + tkProc, tkFunc, tkIterator, tkBind, tkBuiltInMagics, tkParLe, tkBracketLe, tkCurlyLe, tkIntLit..tkCharLit, tkVar, tkRef, tkPtr, - tkTuple, tkObject, tkType, tkWhen, tkCase, tkOut: + tkTuple, tkObject, tkWhen, tkCase, tkOut: result = true else: result = false @@ -1170,7 +1190,6 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode = #| | 'proc' | 'iterator' | 'distinct' | 'object' | 'enum' #| primary = typeKeyw typeDescK #| / prefixOperator* identOrLiteral primarySuffix* - #| / 'static' primary #| / 'bind' primary if isOperator(p.tok): let isSigil = isSigilLike(p.tok) @@ -1183,7 +1202,7 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode = #XXX prefix operators let baseInd = p.lex.currLineIndent addSon(result, primary(p, pmSkipSuffix)) - result = primarySuffix(p, result, baseInd) + result = primarySuffix(p, result, baseInd, mode) else: addSon(result, primary(p, pmNormal)) return @@ -1213,14 +1232,6 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode = result = parseTypeClass(p) else: parMessage(p, "the 'concept' keyword is only valid in 'type' sections") - of tkStatic: - let info = parLineInfo(p) - getTokNoInd(p) - let next = primary(p, pmNormal) - if next.kind == nkBracket and next.sonsLen == 1: - result = newNode(nkStaticTy, info, @[next.sons[0]]) - else: - result = newNode(nkStaticExpr, info, @[next]) of tkBind: result = newNodeP(nkBind, p) getTok(p) @@ -1235,7 +1246,7 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode = let baseInd = p.lex.currLineIndent result = identOrLiteral(p, mode) if mode != pmSkipSuffix: - result = primarySuffix(p, result, baseInd) + result = primarySuffix(p, result, baseInd, mode) proc parseTypeDesc(p: var TParser): PNode = #| typeDesc = simpleExpr diff --git a/compiler/sem.nim b/compiler/sem.nim index 3b16e0938..3c3ffa194 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -48,6 +48,9 @@ proc semQuoteAst(c: PContext, n: PNode): PNode proc finishMethod(c: PContext, s: PSym) proc evalAtCompileTime(c: PContext, n: PNode): PNode proc indexTypesMatch(c: PContext, f, a: PType, arg: PNode): PNode +proc semStaticExpr(c: PContext, n: PNode): PNode +proc semStaticType(c: PContext, childNode: PNode, prev: PType): PType +proc semTypeOf(c: PContext; n: PNode): PNode proc isArrayConstr(n: PNode): bool {.inline.} = result = n.kind == nkBracket and diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 9d7c493a7..4b45ccf87 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -191,7 +191,25 @@ proc semConv(c: PContext, n: PNode): PNode = return n result = newNodeI(nkConv, n.info) - var targetType = semTypeNode(c, n.sons[0], nil).skipTypes({tyTypeDesc}) + + var targetType = semTypeNode(c, n.sons[0], nil) + if targetType.kind == tyTypeDesc: + internalAssert targetType.len > 0 + if targetType.base.kind == tyNone: + return semTypeOf(c, n[1]) + else: + targetType = targetType.base + elif targetType.kind == tyStatic: + var evaluated = semStaticExpr(c, n[1]) + if evaluated.kind == nkType or evaluated.typ.kind == tyTypeDesc: + result = n + result.typ = c.makeTypeDesc semStaticType(c, evaluated, nil) + return + elif targetType.base.kind == tyNone: + return evaluated + else: + targetType = targetType.base + maybeLiftType(targetType, c, n[0].info) if targetType.kind in {tySink, tyLent}: @@ -632,7 +650,7 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode = # echo "SUCCESS evaluated at compile time: ", call.renderTree proc semStaticExpr(c: PContext, n: PNode): PNode = - let a = semExpr(c, n.sons[0]) + let a = semExpr(c, n) if a.findUnresolvedStatic != nil: return a result = evalStaticExpr(c.module, c.graph, a, c.p.owner) if result.isNil: @@ -1034,7 +1052,7 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode = of skType: markUsed(c.config, n.info, s, c.graph.usageSym) styleCheckUse(n.info, s) - if s.typ.kind == tyStatic and s.typ.n != nil: + if s.typ.kind == tyStatic and s.typ.base.kind != tyNone and s.typ.n != nil: return s.typ.n result = newSymNode(s, n.info) result.typ = makeTypeDesc(c, s.typ) @@ -1261,8 +1279,18 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode = # make sure we don't evaluate generic macros/templates n.sons[0] = semExprWithType(c, n.sons[0], {efNoEvaluateGeneric}) - let arr = skipTypes(n.sons[0].typ, {tyGenericInst, + var arr = skipTypes(n.sons[0].typ, {tyGenericInst, tyVar, tyLent, tyPtr, tyRef, tyAlias, tySink}) + if arr.kind == tyStatic: + if arr.base.kind == tyNone: + result = n + result.typ = semStaticType(c, n[1], nil) + return + elif arr.n != nil: + return semSubscript(c, arr.n, flags) + else: + arr = arr.base + case arr.kind of tyArray, tyOpenArray, tyVarargs, tySequence, tyString, tyCString: @@ -2426,8 +2454,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = # handling of sym choices is context dependent # the node is left intact for now discard - of nkStaticExpr: - result = semStaticExpr(c, n) + of nkStaticExpr: result = semStaticExpr(c, n[0]) of nkAsgn: result = semAsgn(c, n) of nkBlockStmt, nkBlockExpr: result = semBlock(c, n) of nkStmtList, nkStmtListExpr: result = semStmtList(c, n, flags) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 3e62652a7..597522f6f 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -37,6 +37,12 @@ const errNoGenericParamsAllowedForX = "no generic parameters allowed for $1" errInOutFlagNotExtern = "the '$1' modifier can be used only with imported types" +const + mStaticTy = {mStatic} + mTypeTy = {mType, mTypeOf} + # XXX: This should be needed only temporarily until the C + # sources are rebuilt + proc newOrPrevType(kind: TTypeKind, prev: PType, c: PContext): PType = if prev == nil: result = newTypeS(kind, c) @@ -363,6 +369,7 @@ proc semTypeIdent(c: PContext, n: PNode): PSym = if result != nil: markUsed(c.config, n.info, result, c.graph.usageSym) styleCheckUse(n.info, result) + if result.kind == skParam and result.typ.kind == tyTypeDesc: # This is a typedesc param. is it already bound? # it's not bound when it's used multiple times in the @@ -388,8 +395,7 @@ proc semTypeIdent(c: PContext, n: PNode): PSym = else: localError(c.config, n.info, errTypeExpected) return errorSym(c, n) - - if result.kind != skType: + if result.kind != skType and result.magic notin (mStaticTy + mTypeTy): # this implements the wanted ``var v: V, x: V`` feature ... var ov: TOverloadIter var amb = initOverloadIter(ov, c, n) @@ -856,9 +862,9 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, result = addImplicitGenericImpl(c, newTypeS(tyGenericParam, c), nil) of tyStatic: - # proc(a: expr{string}, b: expr{nkLambda}) - # overload on compile time values and AST trees - if paramType.n != nil: return # this is a concrete type + if paramType.base.kind != tyNone and paramType.n != nil: + # this is a concrete type + return if tfUnresolved in paramType.flags: return # already lifted let base = paramType.base.maybeLift if base.isMetaType and procKind == skMacro: @@ -879,7 +885,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, of tyDistinct: if paramType.sonsLen == 1: # disable the bindOnce behavior for the type class - result = liftingWalk(paramType.sons[0], true) + result = liftingWalk(paramType.base, true) of tySequence, tySet, tyArray, tyOpenArray, tyVar, tyLent, tyPtr, tyRef, tyProc: @@ -1349,6 +1355,12 @@ proc symFromExpectedTypeNode(c: PContext, n: PNode): PSym = localError(c.config, n.info, errTypeExpected) result = errorSym(c, n) +proc semStaticType(c: PContext, childNode: PNode, prev: PType): PType = + result = newOrPrevType(tyStatic, prev, c) + var base = semTypeNode(c, childNode, nil).skipTypes({tyTypeDesc, tyAlias}) + result.rawAddSon(base) + result.flags.incl tfHasStatic + proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = result = nil inc c.inTypeContext @@ -1456,7 +1468,8 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = of mSeq: result = semContainer(c, n, tySequence, "seq", prev) of mOpt: result = semContainer(c, n, tyOpt, "opt", prev) of mVarargs: result = semVarargs(c, n, prev) - of mTypeDesc: result = makeTypeDesc(c, semTypeNode(c, n[1], nil)) + of mTypeDesc, mTypeTy: result = makeTypeDesc(c, semTypeNode(c, n[1], nil)) + of mStaticTy: result = semStaticType(c, n[1], prev) of mExpr: result = semTypeNode(c, n.sons[0], nil) if result != nil: @@ -1545,11 +1558,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = of nkPtrTy: result = semAnyRef(c, n, tyPtr, prev) of nkVarTy: result = semVarType(c, n, prev) of nkDistinctTy: result = semDistinct(c, n, prev) - of nkStaticTy: - result = newOrPrevType(tyStatic, prev, c) - var base = semTypeNode(c, n.sons[0], nil).skipTypes({tyTypeDesc}) - result.rawAddSon(base) - result.flags.incl tfHasStatic + of nkStaticTy: result = semStaticType(c, n[0], prev) of nkIteratorTy: if n.sonsLen == 0: result = newTypeS(tyBuiltInTypeClass, c) @@ -1645,9 +1654,12 @@ proc processMagicType(c: PContext, m: PSym) = of mStmt: setMagicType(c.config, m, tyStmt, 0) if m.name.s == "stmt": m.typ.flags.incl tfOldSchoolExprStmt - of mTypeDesc: + of mTypeDesc, mType: setMagicType(c.config, m, tyTypeDesc, 0) rawAddSon(m.typ, newTypeS(tyNone, c)) + of mStatic: + setMagicType(m, tyStatic, 0) + rawAddSon(m.typ, newTypeS(tyNone, c)) of mVoidType: setMagicType(c.config, m, tyVoid, 0) of mArray: diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 9a3c75261..5a556732b 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -1666,13 +1666,17 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType, let prev = PType(idTableGet(c.bindings, f)) if prev == nil: if aOrig.kind == tyStatic: - result = typeRel(c, f.lastSon, a) - if result != isNone and f.n != nil: - if not exprStructuralEquivalent(f.n, aOrig.n): - result = isNone + if f.base.kind != tyNone: + result = typeRel(c, f.base, a) + if result != isNone and f.n != nil: + if not exprStructuralEquivalent(f.n, aOrig.n): + result = isNone + else: + result = isGeneric if result != isNone: put(c, f, aOrig) elif aOrig.n != nil and aOrig.n.typ != nil: - result = typeRel(c, f.lastSon, aOrig.n.typ) + result = if f.base.kind != tyNone: typeRel(c, f.lastSon, aOrig.n.typ) + else: isGeneric if result != isNone: var boundType = newTypeWithSons(c.c, tyStatic, @[aOrig.n.typ]) boundType.n = aOrig.n diff --git a/lib/system.nim b/lib/system.nim index fb02fde23..15f952feb 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -179,10 +179,24 @@ proc unsafeAddr*[T](x: T): ptr T {.magic: "Addr", noSideEffect.} = ## Cannot be overloaded. discard -proc `type`*(x: untyped): typeDesc {.magic: "TypeOf", noSideEffect, compileTime.} = - ## Builtin 'type' operator for accessing the type of an expression. - ## Cannot be overloaded. - discard +when defined(nimNewTypedesc): + type + `static`* {.magic: "Static".}[T] + ## meta type representing all values that can be evaluated at compile-time. + ## + ## The type coercion ``static(x)`` can be used to force the compile-time + ## evaluation of the given expression ``x``. + + `type`* {.magic: "Type".}[T] + ## meta type representing the type of all type values. + ## + ## The coercion ``type(x)`` can be used to obtain the type of the given + ## expression ``x``. +else: + proc `type`*(x: untyped): typeDesc {.magic: "TypeOf", noSideEffect, compileTime.} = + ## Builtin 'type' operator for accessing the type of an expression. + ## Cannot be overloaded. + discard proc `not` *(x: bool): bool {.magic: "Not", noSideEffect.} ## Boolean not; returns true iff ``x == false``. diff --git a/tests/lexer/tmissingnl.nim b/tests/lexer/tmissingnl.nim index 33b7debf1..095d9570e 100644 --- a/tests/lexer/tmissingnl.nim +++ b/tests/lexer/tmissingnl.nim @@ -4,7 +4,7 @@ discard """ errormsg: "invalid indentation" """ -import strutils var s: seq[int] = @[0, 1, 2, 3, 4, 5, 6] +import strutils let s: seq[int] = @[0, 1, 2, 3, 4, 5, 6] #s[1..3] = @[] diff --git a/tests/parser/ttypemodifiers.nim b/tests/parser/ttypemodifiers.nim index db7ab903f..2c322b44b 100644 --- a/tests/parser/ttypemodifiers.nim +++ b/tests/parser/ttypemodifiers.nim @@ -53,25 +53,33 @@ StmtList Par Ident "a" Ident "b" + TypeDef + Ident "BareStatic" + Empty + Ident "static" TypeDef Ident "GenericStatic" Empty - StaticTy + BracketExpr + Ident "static" Ident "int" TypeDef Ident "PrefixStatic" Empty - StaticExpr + Command + Ident "static" Ident "int" TypeDef Ident "StaticTupleCl" Empty - StaticExpr + Command + Ident "static" TupleClassTy TypeDef Ident "StaticTuple" Empty - StaticExpr + Command + Ident "static" Par Ident "int" Ident "string" @@ -91,6 +99,12 @@ StmtList BracketExpr Ident "type" TupleClassTy + TypeDef + Ident "TypeTupleCl" + Empty + Command + Ident "type" + TupleClassTy TypeDef Ident "TypeInstance" Empty @@ -109,6 +123,13 @@ StmtList Call Ident "type" Ident "a" + TypeDef + Ident "TypeOfVarAlt" + Empty + Command + Ident "type" + Par + Ident "a" TypeDef Ident "TypeOfTuple1" Empty @@ -122,6 +143,29 @@ StmtList Ident "type" Ident "a" Ident "b" + TypeDef + Ident "TypeOfTuple1A" + Empty + Command + Ident "type" + TupleConstr + Ident "a" + TypeDef + Ident "TypeOfTuple2A" + Empty + Command + Ident "type" + Par + Ident "a" + Ident "b" + TypeDef + Ident "TypeTuple" + Empty + Command + Ident "type" + Par + Ident "int" + Ident "string" TypeDef Ident "GenericTypedesc" Empty @@ -172,32 +216,52 @@ StmtList Ident "foo" Ident "type" Empty + IdentDefs + Ident "typeTupleCl" + Command + Ident "type" + TupleClassTy + Empty + IdentDefs + Ident "bareStatic" + Ident "static" + Empty IdentDefs Ident "genStatic" - StaticTy + BracketExpr + Ident "static" Ident "int" Empty IdentDefs Ident "staticInt" - StaticExpr + Command + Ident "static" Ident "int" Empty IdentDefs Ident "staticVal1" - StaticExpr + Command + Ident "static" IntLit 10 Empty IdentDefs Ident "staticVal2" - StaticExpr - Par - StrLit "str" + Call + Ident "static" + StrLit "str" Empty IdentDefs Ident "staticVal3" - StaticExpr + Command + Ident "static" StrLit "str" Empty + IdentDefs + Ident "staticVal4" + CallStrLit + Ident "static" + RStrLit "str" + Empty IdentDefs Ident "staticDotVal" DotExpr @@ -285,50 +349,32 @@ StmtList StmtList Asgn Ident "staticTen" - StaticExpr + Command + Ident "static" IntLit 10 Asgn Ident "staticA" - StaticExpr - Par - Ident "a" - Asgn - Ident "staticAspace" - StaticExpr - Par - Ident "a" - Asgn - Ident "staticAtuple" - StaticExpr - TupleConstr - Ident "a" - Asgn - Ident "staticTuple" - StaticExpr - Par - Ident "a" - Ident "b" - Asgn - Ident "staticTypeTuple" - StaticExpr - Par - Ident "int" - Ident "string" + Call + Ident "static" + Ident "a" Asgn Ident "staticCall" - StaticExpr + Command + Ident "static" Call Ident "foo" IntLit 1 Asgn Ident "staticStrCall" - StaticExpr + Command + Ident "static" CallStrLit Ident "foo" RStrLit "x" Asgn Ident "staticChainCall" - StaticExpr + Command + Ident "static" Command Ident "foo" Ident "bar" @@ -399,7 +445,7 @@ dumpTree: RefTupleCl = ref tuple RefTupleType = ref (int, string) RefTupleVars = ref (a, b) - # BareStatic = static # Error: invalid indentation + BareStatic = static # Used to be Error: invalid indentation GenericStatic = static[int] PrefixStatic = static int StaticTupleCl = static tuple @@ -407,16 +453,16 @@ dumpTree: BareType = type GenericType = type[float] TypeTupleGen = type[tuple] - # TypeTupleCl = type tuple # Error: invalid indentation + TypeTupleCl = type tuple # Used to be Error: invalid indentation TypeInstance = type Foo[ref] bareTypeDesc = typedesc TypeOfVar = type(a) - # TypeOfVarAlt= type (a) # Error: invalid indentation + TypeOfVarAlt = type (a) # Used to be Error: invalid indentation TypeOfTuple1 = type(a,) TypeOfTuple2 = type(a,b) - # TypeOfTuple1A = type (a,) # Error: invalid indentation - # TypeOfTuple2A = type (a,b) # Error: invalid indentation - # TypeTuple = type (int, string) # Error: invalid indentation + TypeOfTuple1A = type (a,) # Used to be Error: invalid indentation + TypeOfTuple2A = type (a,b) # Used to be Error: invalid indentation + TypeTuple = type (int, string) # Used to be Error: invalid indentation GenericTypedesc = typedesc[int] T = type @@ -427,14 +473,14 @@ dumpTree: typeIntAlt : type(int), typeOfVar : type(a), typeDotType : foo.type, - # typeTupleCl : type tuple, # Error: ')' expected - # bareStatic : static, # Error: expression expected, but found ',' + typeTupleCl : type tuple, # Used to be Error: ')' expected + bareStatic : static, # Used to be Error: expression expected, but found ',' genStatic : static[int], staticInt : static int, staticVal1 : static 10, staticVal2 : static("str"), staticVal3 : static "str", - # staticVal4 : static"str", # Error: expression expected, but found 'str' + staticVal4 : static"str", # Used to be Error: expression expected, but found 'str' staticDotVal : 10.static, bareRef : ref, refTuple1 : ref (int), @@ -451,10 +497,10 @@ dumpTree: ): type = staticTen = static 10 staticA = static(a) - staticAspace = static (a) - staticAtuple = static (a,) - staticTuple = static (a,b) - staticTypeTuple = static (int,string) + # staticAspace = static (a) # With newTypedesc: Error: invalid indentation + # staticAtuple = static (a,) # With newTypedesc: Error: invalid indentation + # staticTuple = static (a,b) # With newTypedesc: Error: invalid indentation + # staticTypeTuple = static (int,string) # With newTypedesc: Error: invalid indentation staticCall = static foo(1) staticStrCall = static foo"x" staticChainCall = static foo bar -- cgit 1.4.1-2-gfad0 From 509d6e923284f1f02c5dbc64e43aee9df1a012d3 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Sun, 22 Apr 2018 17:26:10 +0300 Subject: Bugfix: aliases to generic types were not considered implicit generic parameters --- compiler/semtypes.nim | 3 +++ tests/statictypes/tstatictypes.nim | 17 +++++++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 tests/statictypes/tstatictypes.nim (limited to 'tests') diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 597522f6f..19bb53209 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -887,6 +887,9 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, # disable the bindOnce behavior for the type class result = liftingWalk(paramType.base, true) + of tyAlias: + result = liftingWalk(paramType.base) + of tySequence, tySet, tyArray, tyOpenArray, tyVar, tyLent, tyPtr, tyRef, tyProc: # XXX: this is a bit strange, but proc(s: seq) diff --git a/tests/statictypes/tstatictypes.nim b/tests/statictypes/tstatictypes.nim new file mode 100644 index 000000000..a646b61f7 --- /dev/null +++ b/tests/statictypes/tstatictypes.nim @@ -0,0 +1,17 @@ +discard """ +nimout: ''' +staticAlialProc instantiated with 4 +staticAlialProc instantiated with 6 +''' +""" + +type + StaticTypeAlias = static[int] + +proc staticAliasProc(s: StaticTypeAlias) = + static: echo "staticAlialProc instantiated with ", s + 1 + +staticAliasProc 1+2 +staticAliasProc 3 +staticAliasProc 5 + -- cgit 1.4.1-2-gfad0 From ab9969ed3be2d57fdeda170cc9960be7ba628149 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Sun, 22 Apr 2018 18:11:22 +0300 Subject: Bugfix: the size of an array may be a static tuple element --- compiler/semtypes.nim | 3 ++- tests/statictypes/tstatictypes.nim | 40 +++++++++++++++++++++++++++++++------- 2 files changed, 35 insertions(+), 8 deletions(-) (limited to 'tests') diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 19bb53209..6b27c8032 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -301,7 +301,8 @@ proc semArrayIndex(c: PContext, n: PNode): PType = localError(c.config, info, errOrdinalTypeExpected) result = makeRangeWithStaticExpr(c, e) if c.inGenericContext > 0: result.flags.incl tfUnresolved - elif e.kind in nkCallKinds and hasGenericArguments(e): + elif e.kind in (nkCallKinds + {nkBracketExpr}) and + hasGenericArguments(e): if not isOrdinalType(e.typ): localError(c.config, n[1].info, errOrdinalTypeExpected) # This is an int returning call, depending on an diff --git a/tests/statictypes/tstatictypes.nim b/tests/statictypes/tstatictypes.nim index a646b61f7..5234866fa 100644 --- a/tests/statictypes/tstatictypes.nim +++ b/tests/statictypes/tstatictypes.nim @@ -5,13 +5,39 @@ staticAlialProc instantiated with 6 ''' """ -type - StaticTypeAlias = static[int] +import macros -proc staticAliasProc(s: StaticTypeAlias) = - static: echo "staticAlialProc instantiated with ", s + 1 +proc plus(a, b: int): int = a + b -staticAliasProc 1+2 -staticAliasProc 3 -staticAliasProc 5 +when true: + type + StaticTypeAlias = static[int] + + proc staticAliasProc(s: StaticTypeAlias) = + static: echo "staticAlialProc instantiated with ", s + 1 + echo s + + staticAliasProc 1+2 + staticAliasProc 3 + staticAliasProc 5 + +when true: + type + ArrayWrapper1[S: static int] = object + data: array[S + 1, int] + + ArrayWrapper2[S: static[int]] = object + data: array[S.plus(2), int] + + ArrayWrapper3[S: static[(int, string)]] = object + data: array[S[0], int] + + var aw1: ArrayWrapper1[5] + var aw2: ArrayWrapper2[5] + var aw3: ArrayWrapper3[(10, "str")] + + static: + assert aw1.data.high == 5 + assert aw2.data.high == 6 + assert aw3.data.high == 9 -- cgit 1.4.1-2-gfad0 From a49b06a52a3c24258e9eb04593a2f83ae057755f Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Mon, 23 Apr 2018 17:23:14 +0300 Subject: Implement the `is` operator for the new static and typedesc type classes This also makes the first baby steps towards a sound treatment of higher-order kinds (type type int). Adds test cases showcasing the new features. * Also fixes breakage after the rebase --- compiler/parser.nim | 3 +- compiler/semdata.nim | 3 +- compiler/semexprs.nim | 81 +++++++++++++++++++++++++++--------- compiler/semtypes.nim | 7 +++- tests/metatype/ttypedesc1.nim | 34 +++++++++++---- tests/metatype/ttypedesc3.nim | 6 +-- tests/metatype/ttypeselectors.nim | 6 +-- tests/parser/ttypeclasses.nim | 39 ++++++++++++++---- tests/statictypes/tstatictypes.nim | 84 ++++++++++++++++++++++++++++++++++---- tests/types/tisopr.nim | 2 +- 10 files changed, 210 insertions(+), 55 deletions(-) (limited to 'tests') diff --git a/compiler/parser.nim b/compiler/parser.nim index 33ee8c9e6..5c99363c9 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -798,8 +798,7 @@ proc primarySuffix(p: var TParser, r: PNode, # `foo ref` or `foo ptr`. Unfortunately, these two are also # used as infix operators for the memory regions feature and # the current parsing rules don't play well here. - if mode == pmTypeDef or - (p.inPragma == 0 and (isUnary(p) or p.tok.tokType notin {tkOpr, tkDotDot})): + if p.inPragma == 0 and (isUnary(p) or p.tok.tokType notin {tkOpr, tkDotDot}): # actually parsing {.push hints:off.} as {.push(hints:off).} is a sweet # solution, but pragmas.nim can't handle that let a = result diff --git a/compiler/semdata.nim b/compiler/semdata.nim index c858b6839..aa0cb6e8e 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -288,7 +288,8 @@ proc makeTypeDesc*(c: PContext, typ: PType): PType = result.addSonSkipIntLit(typ) proc makeTypeSymNode*(c: PContext, typ: PType, info: TLineInfo): PNode = - let typedesc = makeTypeDesc(c, typ) + let typedesc = newTypeS(tyTypeDesc, c) + typedesc.addSonSkipIntLit(assertNotNil(c.config, typ)) let sym = newSym(skType, c.cache.idAnon, getCurrOwner(c), info, c.config.options).linkTo(typedesc) return newSymNode(sym, info) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 4b45ccf87..a072deb7d 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -194,7 +194,7 @@ proc semConv(c: PContext, n: PNode): PNode = var targetType = semTypeNode(c, n.sons[0], nil) if targetType.kind == tyTypeDesc: - internalAssert targetType.len > 0 + internalAssert c.config, targetType.len > 0 if targetType.base.kind == tyNone: return semTypeOf(c, n[1]) else: @@ -316,57 +316,98 @@ proc semSizeof(c: PContext, n: PNode): PNode = n.typ = getSysType(c.graph, n.info, tyInt) result = n +proc fixupStaticType(c: PContext, n: PNode) = + # This proc can be applied to evaluated expressions to assign + # them a static type. + # + # XXX: with implicit static, this should not be necessary, + # because the output type of operations such as `semConstExpr` + # should be a static type (as well as the type of any other + # expression that can be implicitly evaluated). For now, we + # apply this measure only in code that is enlightened to work + # with static types. + if n.typ.kind != tyStatic: + n.typ = newTypeWithSons(getCurrOwner(c), tyStatic, @[n.typ]) + n.typ.n = n # XXX: cycles like the one here look dangerous. + # Consider using `n.copyTree` + proc isOpImpl(c: PContext, n: PNode, flags: TExprFlags): PNode = - internalAssert c.config, n.sonsLen == 3 and - n[1].typ != nil and n[1].typ.kind == tyTypeDesc and + internalAssert c.config, + n.sonsLen == 3 and + n[1].typ != nil and n[2].kind in {nkStrLit..nkTripleStrLit, nkType} - let t1 = n[1].typ.skipTypes({tyTypeDesc}) + var + res = false + t1 = n[1].typ + t2 = n[2].typ + + if t1.kind == tyTypeDesc and t2.kind != tyTypeDesc: + t1 = t1.base if n[2].kind in {nkStrLit..nkTripleStrLit}: case n[2].strVal.normalize of "closure": let t = skipTypes(t1, abstractRange) - result = newIntNode(nkIntLit, ord(t.kind == tyProc and - t.callConv == ccClosure and - tfIterator notin t.flags)) + res = t.kind == tyProc and + t.callConv == ccClosure and + tfIterator notin t.flags else: - result = newIntNode(nkIntLit, 0) + res = false else: - var rhsOrigType = n[2].typ - var t2 = rhsOrigType.skipTypes({tyTypeDesc}) maybeLiftType(t2, c, n.info) var m: TCandidate initCandidate(c, m, t2) if efExplain in flags: m.diagnostics = @[] m.diagnosticsEnabled = true - let match = typeRel(m, t2, t1) >= isSubtype # isNone - result = newIntNode(nkIntLit, ord(match)) + res = typeRel(m, t2, t1) >= isSubtype # isNone + result = newIntNode(nkIntLit, ord(res)) result.typ = n.typ proc semIs(c: PContext, n: PNode, flags: TExprFlags): PNode = if sonsLen(n) != 3: localError(c.config, n.info, "'is' operator takes 2 arguments") + let boolType = getSysType(c.graph, n.info, tyBool) result = n - n.typ = getSysType(c.graph, n.info, tyBool) + n.typ = boolType + var liftLhs = true n.sons[1] = semExprWithType(c, n[1], {efDetermineType, efWantIterator}) if n[2].kind notin {nkStrLit..nkTripleStrLit}: let t2 = semTypeNode(c, n[2], nil) n.sons[2] = newNodeIT(nkType, n[2].info, t2) + if t2.kind == tyStatic: + let evaluated = tryConstExpr(c, n[1]) + if evaluated != nil: + c.fixupStaticType(evaluated) + n[1] = evaluated + else: + result = newIntNode(nkIntLit, 0) + result.typ = boolType + return + elif t2.kind == tyTypeDesc and + (t2.base.kind == tyNone or tfExplicit in t2.flags): + # When the right-hand side is an explicit type, we must + # not allow regular values to be matched against the type: + liftLhs = false - let lhsType = n[1].typ + var lhsType = n[1].typ if lhsType.kind != tyTypeDesc: - n.sons[1] = makeTypeSymNode(c, lhsType, n[1].info) - elif lhsType.base.kind == tyNone: - # this is a typedesc variable, leave for evals - return + if liftLhs: + n[1] = makeTypeSymNode(c, lhsType, n[1].info) + lhsType = n[1].typ + else: + if lhsType.base.kind == tyNone: + # this is a typedesc variable, leave for evals + return + if lhsType.base.containsGenericType: + # BUGFIX: don't evaluate this too early: ``T is void`` + return - # BUGFIX: don't evaluate this too early: ``T is void`` - if not n[1].typ.base.containsGenericType: result = isOpImpl(c, n, flags) + result = isOpImpl(c, n, flags) proc semOpAux(c: PContext, n: PNode) = const flags = {efDetermineType} diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 6b27c8032..64783f890 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -1472,8 +1472,11 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = of mSeq: result = semContainer(c, n, tySequence, "seq", prev) of mOpt: result = semContainer(c, n, tyOpt, "opt", prev) of mVarargs: result = semVarargs(c, n, prev) - of mTypeDesc, mTypeTy: result = makeTypeDesc(c, semTypeNode(c, n[1], nil)) - of mStaticTy: result = semStaticType(c, n[1], prev) + of mTypeDesc, mTypeTy: + result = makeTypeDesc(c, semTypeNode(c, n[1], nil)) + result.flags.incl tfExplicit + of mStaticTy: + result = semStaticType(c, n[1], prev) of mExpr: result = semTypeNode(c, n.sons[0], nil) if result != nil: diff --git a/tests/metatype/ttypedesc1.nim b/tests/metatype/ttypedesc1.nim index e9eee581f..837c8eccc 100644 --- a/tests/metatype/ttypedesc1.nim +++ b/tests/metatype/ttypedesc1.nim @@ -5,15 +5,16 @@ type x: T y: U -proc getTypeName(t: typedesc): string = t.name +proc getTypeName1(t: typedesc): string = t.name +proc getTypeName2(t: type): string = t.name -proc foo(T: typedesc[float], a: auto): string = +proc foo(T: type float, a: auto): string = result = "float " & $(a.len > 5) proc foo(T: typedesc[TFoo], a: int): string = result = "TFoo " & $(a) -proc foo(T: typedesc[int or bool]): string = +proc foo(T: type[int or bool]): string = var a: T a = 10 result = "int or bool " & ($a) @@ -23,8 +24,8 @@ template foo(T: typedesc[seq]): string = "seq" test "types can be used as proc params": # XXX: `check` needs to know that TFoo[int, float] is a type and # cannot be assigned for a local variable for later inspection - check ((string.getTypeName == "string")) - check ((getTypeName(int) == "int")) + check ((string.getTypeName1 == "string")) + check ((getTypeName2(int) == "int")) check ((foo(TFoo[int, float], 1000) == "TFoo 1000")) @@ -37,6 +38,25 @@ test "types can be used as proc params": check ((foo(seq[int]) == "seq")) check ((foo(seq[TFoo[bool, string]]) == "seq")) -when false: - proc foo(T: typedesc[seq], s: T) = nil +template accept(x) = + static: assert(compiles(x)) + +template reject(x) = + static: assert(not compiles(x)) + +var + si: seq[int] + ss: seq[string] + +proc foo(T: typedesc[seq], s: T) = + discard + +accept: + foo seq[int], si + +reject: + foo seq[string], si + +reject: + foo seq[int], ss diff --git a/tests/metatype/ttypedesc3.nim b/tests/metatype/ttypedesc3.nim index 9f19bd6e3..3d1cf2ec9 100644 --- a/tests/metatype/ttypedesc3.nim +++ b/tests/metatype/ttypedesc3.nim @@ -4,9 +4,9 @@ type Base = object of RootObj Child = object of Base -proc pr(T: typedesc[Base]) = echo "proc " & T.name -method me(T: typedesc[Base]) = echo "method " & T.name -iterator it(T: typedesc[Base]): auto = yield "yield " & T.name +proc pr(T: type[Base]) = echo "proc " & T.name +method me(T: type[Base]) = echo "method " & T.name +iterator it(T: type[Base]): auto = yield "yield " & T.name Base.pr Child.pr diff --git a/tests/metatype/ttypeselectors.nim b/tests/metatype/ttypeselectors.nim index 1209fe78f..2a2455adb 100644 --- a/tests/metatype/ttypeselectors.nim +++ b/tests/metatype/ttypeselectors.nim @@ -5,16 +5,16 @@ output: "8\n8\n4" import macros, typetraits -template selectType(x: int): typeDesc = +template selectType(x: int): type = when x < 10: int else: string -template simpleTypeTempl: typeDesc = +template simpleTypeTempl: type = string -macro typeFromMacro: typedesc = string +macro typeFromMacro: type = string # The tests below check that the result variable of the # selected type matches the literal types in the code: diff --git a/tests/parser/ttypeclasses.nim b/tests/parser/ttypeclasses.nim index 9f487c7a8..46fd20686 100644 --- a/tests/parser/ttypeclasses.nim +++ b/tests/parser/ttypeclasses.nim @@ -1,17 +1,42 @@ -discard """ - action: run -""" - type R = ref V = var D = distinct P = ptr + T = type + S = static + OBJ = object + TPL = tuple + SEQ = seq +var i: int var x: ref int var y: distinct int var z: ptr int +const C = @[1, 2, 3] + +static: + assert x is ref + assert y is distinct + assert z is ptr + assert C is static + assert C[1] is static[int] + assert C[0] is static[SomeInteger] + assert C isnot static[string] + assert C is SEQ|OBJ + assert C isnot OBJ|TPL + assert int is int + assert int is T + assert int is SomeInteger + assert seq[int] is type + assert seq[int] is type[seq] + assert seq[int] isnot type[seq[float]] + assert i isnot type[int] + assert type(i) is type[int] + assert x isnot T + assert y isnot S + assert z isnot enum + assert x isnot object + assert y isnot tuple + assert z isnot seq -doAssert x is ref -doAssert y is distinct -doAssert z is ptr \ No newline at end of file diff --git a/tests/statictypes/tstatictypes.nim b/tests/statictypes/tstatictypes.nim index 5234866fa..789bd7588 100644 --- a/tests/statictypes/tstatictypes.nim +++ b/tests/statictypes/tstatictypes.nim @@ -1,25 +1,91 @@ discard """ nimout: ''' -staticAlialProc instantiated with 4 -staticAlialProc instantiated with 6 +staticAlialProc instantiated with 358 +staticAlialProc instantiated with 368 +''' +output: ''' +16 +16 +b is 2 times a +17 ''' """ import macros +template ok(x) = assert(x) +template no(x) = assert(not x) + +template accept(x) = + static: assert(compiles(x)) + +template reject(x) = + static: assert(not compiles(x)) + proc plus(a, b: int): int = a + b +template isStatic(x: static): bool = true +template isStatic(x: auto): bool = false + +var v = 1 + when true: + # test that `isStatic` works as expected + const C = 2 + + static: + ok C.isStatic + ok isStatic(plus(1, 2)) + ok plus(C, 2).isStatic + + no isStatic(v) + no plus(1, v).isStatic + +when true: + # test that proc instantiation works as expected type StaticTypeAlias = static[int] - proc staticAliasProc(s: StaticTypeAlias) = - static: echo "staticAlialProc instantiated with ", s + 1 - echo s + proc staticAliasProc(a: StaticTypeAlias, + b: static[int], + c: static int) = + static: + assert a.isStatic and b.isStatic and c.isStatic + assert isStatic(a + plus(b, c)) + echo "staticAlialProc instantiated with ", a, b, c + + when b mod a == 0: + echo "b is ", b div a, " times a" + + echo a + b + c - staticAliasProc 1+2 - staticAliasProc 3 - staticAliasProc 5 + staticAliasProc 1+2, 5, 8 + staticAliasProc 3, 2+3, 9-1 + staticAliasProc 3, 3+3, 4+4 + +when true: + # test static coercions. normal cases that should work: + accept: + var s1 = static[int] plus(1, 2) + var s2 = static(plus(1,2)) + var s3 = static plus(1,2) + var s4 = static[SomeInteger](1 + 2) + + # the sub-script operator can be used only with types: + reject: + var just_static3 = static[plus(1,2)] + + # static coercion takes into account the type: + reject: + var x = static[string](plus(1, 2)) + reject: + var x = static[string] plus(1, 2) + reject: + var x = static[SomeFloat] plus(3, 4) + + # you cannot coerce a run-time variable + reject: + var x = static(v) when true: type @@ -35,7 +101,7 @@ when true: var aw1: ArrayWrapper1[5] var aw2: ArrayWrapper2[5] var aw3: ArrayWrapper3[(10, "str")] - + static: assert aw1.data.high == 5 assert aw2.data.high == 6 diff --git a/tests/types/tisopr.nim b/tests/types/tisopr.nim index 2f9dbf245..f05f443de 100644 --- a/tests/types/tisopr.nim +++ b/tests/types/tisopr.nim @@ -5,7 +5,7 @@ false false true true -no''' +yes''' """ proc IsVoid[T](): string = -- cgit 1.4.1-2-gfad0 From 5bcf8bcb598d2ca0162f50d2b7250a847026b2c9 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Tue, 12 Jun 2018 23:45:18 +0300 Subject: fixes #7222; fixes #5595; fixes #3747 * late instantiation for the generic procs' default param values * automatic mixin behaviour in concepts Other fixes: * don't render the automatically inserted default params in calls * better rendering of tyFromExpr --- compiler/ast.nim | 1 + compiler/renderer.nim | 6 +- compiler/sem.nim | 2 +- compiler/semcall.nim | 23 +++++- compiler/semexprs.nim | 30 +++++++ compiler/semgnrc.nim | 31 +++++--- compiler/seminst.nim | 37 +++++---- compiler/semtypes.nim | 35 +++++--- compiler/semtypinst.nim | 24 +++--- compiler/sigmatch.nim | 23 ++++-- compiler/types.nim | 5 +- tests/concepts/libs/trie.nim | 26 ++++++ tests/concepts/libs/trie_database.nim | 12 +++ tests/concepts/ttrieconcept.nim | 7 ++ tests/generics/tlateboundgenericparams.nim | 124 +++++++++++++++++++++++++++++ 15 files changed, 321 insertions(+), 65 deletions(-) create mode 100644 tests/concepts/libs/trie.nim create mode 100644 tests/concepts/libs/trie_database.nim create mode 100644 tests/concepts/ttrieconcept.nim create mode 100644 tests/generics/tlateboundgenericparams.nim (limited to 'tests') diff --git a/compiler/ast.nim b/compiler/ast.nim index d266e20b0..085a243b3 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -454,6 +454,7 @@ type nfPreventCg # this node should be ignored by the codegen nfBlockArg # this a stmtlist appearing in a call (e.g. a do block) nfFromTemplate # a top-level node returned from a template + nfDefaultParam # an automatically inserter default parameter TNodeFlags* = set[TNodeFlag] TTypeFlag* = enum # keep below 32 for efficiency reasons (now: beyond that) diff --git a/compiler/renderer.nim b/compiler/renderer.nim index ba87838db..3ce2e157d 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -387,8 +387,10 @@ proc lcomma(g: TSrcGen; n: PNode, start: int = 0, theEnd: int = - 1): int = assert(theEnd < 0) result = 0 for i in countup(start, sonsLen(n) + theEnd): - inc(result, lsub(g, n.sons[i])) - inc(result, 2) # for ``, `` + let param = n.sons[i] + if nfDefaultParam notin param.flags: + inc(result, lsub(g, param)) + inc(result, 2) # for ``, `` if result > 0: dec(result, 2) # last does not get a comma! diff --git a/compiler/sem.nim b/compiler/sem.nim index 3c3ffa194..afc794a37 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -51,7 +51,7 @@ proc indexTypesMatch(c: PContext, f, a: PType, arg: PNode): PNode proc semStaticExpr(c: PContext, n: PNode): PNode proc semStaticType(c: PContext, childNode: PNode, prev: PType): PType proc semTypeOf(c: PContext; n: PNode): PNode - +proc hasUnresolvedArgs(c: PContext, n: PNode): bool proc isArrayConstr(n: PNode): bool {.inline.} = result = n.kind == nkBracket and n.typ.skipTypes(abstractInst).kind == tyArray diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 5d3df064f..0de22cfb3 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -391,7 +391,21 @@ proc inferWithMetatype(c: PContext, formal: PType, result = copyTree(arg) result.typ = formal -proc semResolvedCall(c: PContext, n: PNode, x: TCandidate): PNode = +proc updateDefaultParams(call: PNode) = + # In generic procs, the default parameter may be unique for each + # instantiation (see tlateboundgenericparams). + # After a call is resolved, we need to re-assign any default value + # that was used during sigmatch. sigmatch is responsible for marking + # the default params with `nfDefaultParam` and `instantiateProcType` + # computes correctly the default values for each instantiation. + let calleeParams = call[0].sym.typ.n + for i in countdown(call.len - 1, 1): + if nfDefaultParam notin call[i].flags: + return + call[i] = calleeParams[i].sym.ast + +proc semResolvedCall(c: PContext, x: TCandidate, + n: PNode, flags: TExprFlags): PNode = assert x.state == csMatch var finalCallee = x.calleeSym markUsed(c.config, n.sons[0].info, finalCallee, c.graph.usageSym) @@ -424,8 +438,9 @@ proc semResolvedCall(c: PContext, n: PNode, x: TCandidate): PNode = result = x.call instGenericConvertersSons(c, result, x) - result.sons[0] = newSymNode(finalCallee, result.sons[0].info) + result[0] = newSymNode(finalCallee, result[0].info) result.typ = finalCallee.typ.sons[0] + updateDefaultParams(result) proc canDeref(n: PNode): bool {.inline.} = result = n.len >= 2 and (let t = n[1].typ; @@ -447,7 +462,7 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode, message(c.config, n.info, hintUserRaw, "Non-matching candidates for " & renderTree(n) & "\n" & candidates) - result = semResolvedCall(c, n, r) + result = semResolvedCall(c, r, n, flags) elif implicitDeref in c.features and canDeref(n): # try to deref the first argument and then try overloading resolution again: # @@ -458,7 +473,7 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode, # n.sons[1] = n.sons[1].tryDeref var r = resolveOverloads(c, n, nOrig, filter, flags, errors, efExplain in flags) - if r.state == csMatch: result = semResolvedCall(c, n, r) + if r.state == csMatch: result = semResolvedCall(c, r, n, flags) else: # get rid of the deref again for a better error message: n.sons[1] = n.sons[1].sons[0] diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index a072deb7d..82bd76136 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -542,6 +542,36 @@ proc fixAbstractType(c: PContext, n: PNode) = proc isAssignable(c: PContext, n: PNode; isUnsafeAddr=false): TAssignableResult = result = parampatterns.isAssignable(c.p.owner, n, isUnsafeAddr) +proc isUnresolvedSym(s: PSym): bool = + return s.kind == skGenericParam or + tfInferrableStatic in s.typ.flags or + (s.kind == skParam and s.typ.isMetaType) or + (s.kind == skType and + s.typ.flags * {tfGenericTypeParam, tfImplicitTypeParam} != {}) + +proc hasUnresolvedArgs(c: PContext, n: PNode): bool = + # Checks whether an expression depends on generic parameters that + # don't have bound values yet. E.g. this could happen in situations + # such as: + # type Slot[T] = array[T.size, byte] + # proc foo[T](x: default(T)) + # + # Both static parameter and type parameters can be unresolved. + case n.kind + of nkSym: + return isUnresolvedSym(n.sym) + of nkIdent, nkAccQuoted: + let ident = considerQuotedIdent(c.config, n) + let sym = searchInScopes(c, ident) + if sym != nil: + return isUnresolvedSym(sym) + else: + return false + else: + for i in 0.. 1: resetIdTable(cl.symMap) resetIdTable(cl.localCache) - result.sons[i] = replaceTypeVarsT(cl, result.sons[i]) - propagateToOwner(result, result.sons[i]) + let needsStaticSkipping = result[i].kind == tyFromExpr + result[i] = replaceTypeVarsT(cl, result[i]) + if needsStaticSkipping: + result[i] = result[i].skipTypes({tyStatic}) internalAssert c.config, originalParams[i].kind == nkSym - when true: - let oldParam = originalParams[i].sym - let param = copySym(oldParam) - param.owner = prc - param.typ = result.sons[i] - if oldParam.ast != nil: - param.ast = fitNode(c, param.typ, oldParam.ast, oldParam.ast.info) + let oldParam = originalParams[i].sym + let param = copySym(oldParam) + param.owner = prc + param.typ = result[i] + if oldParam.ast != nil: + var def = oldParam.ast.copyTree + if def.kind == nkCall: + for i in 1 ..< def.len: + def[i] = replaceTypeVarsN(cl, def[i]) + def = semExprWithType(c, def) + param.ast = fitNode(c, param.typ, def, def.info) + param.typ = param.ast.typ - # don't be lazy here and call replaceTypeVarsN(cl, originalParams[i])! - result.n.sons[i] = newSymNode(param) - addDecl(c, param) - else: - let param = replaceTypeVarsN(cl, originalParams[i]) - result.n.sons[i] = param - param.sym.owner = prc - addDecl(c, result.n.sons[i].sym) + result.n[i] = newSymNode(param) + result[i] = param.typ + propagateToOwner(result, result[i]) + addDecl(c, param) resetIdTable(cl.symMap) resetIdTable(cl.localCache) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 64783f890..85c6e3056 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -244,7 +244,7 @@ proc semRangeAux(c: PContext, n: PNode, prev: PType): PType = localError(c.config, n.info, "enum '$1' has holes" % typeToString(rangeT[0])) for i in 0..1: - if hasGenericArguments(range[i]): + if hasUnresolvedArgs(c, range[i]): result.n.addSon makeStaticExpr(c, range[i]) result.flags.incl tfUnresolved else: @@ -301,8 +301,7 @@ proc semArrayIndex(c: PContext, n: PNode): PType = localError(c.config, info, errOrdinalTypeExpected) result = makeRangeWithStaticExpr(c, e) if c.inGenericContext > 0: result.flags.incl tfUnresolved - elif e.kind in (nkCallKinds + {nkBracketExpr}) and - hasGenericArguments(e): + elif e.kind in (nkCallKinds + {nkBracketExpr}) and hasUnresolvedArgs(c, e): if not isOrdinalType(e.typ): localError(c.config, n[1].info, errOrdinalTypeExpected) # This is an int returning call, depending on an @@ -1006,13 +1005,11 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, prev: PType, kind: TSymKind; isType=false): PType = # for historical reasons (code grows) this is invoked for parameter # lists too and then 'isType' is false. - var cl: IntSet checkMinSonsLen(n, 1, c.config) result = newProcType(c, n.info, prev) - if genericParams != nil and sonsLen(genericParams) == 0: - cl = initIntSet() var check = initIntSet() var counter = 0 + for i in countup(1, n.len - 1): var a = n.sons[i] if a.kind != nkIdentDefs: @@ -1022,6 +1019,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, # pass over this instantiation: if a.kind == nkSym and sfFromGeneric in a.sym.flags: continue illFormedAst(a, c.config) + checkMinSonsLen(a, 3, c.config) var typ: PType = nil @@ -1030,26 +1028,38 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, length = sonsLen(a) hasType = a.sons[length-2].kind != nkEmpty hasDefault = a.sons[length-1].kind != nkEmpty + if hasType: typ = semParamType(c, a.sons[length-2], constraint) if hasDefault: - def = semExprWithType(c, a.sons[length-1]) - # check type compatibility between def.typ and typ: + def = a[^1] + block determineType: + if genericParams != nil and genericParams.len > 0: + def = semGenericStmt(c, def) + if hasUnresolvedArgs(c, def): + def.typ = makeTypeFromExpr(c, def.copyTree) + break determineType + + def = semExprWithType(c, def, {efDetermineType}) + if typ == nil: typ = def.typ - elif def != nil: - # and def.typ != nil and def.typ.kind != tyNone: + else: + # if def.typ != nil and def.typ.kind != tyNone: # example code that triggers it: # proc sort[T](cmp: proc(a, b: T): int = cmp) if not containsGenericType(typ): + # check type compatibility between def.typ and typ: def = fitNode(c, typ, def, def.info) + if not hasType and not hasDefault: if isType: localError(c.config, a.info, "':' expected") if kind in {skTemplate, skMacro}: typ = newTypeS(tyExpr, c) elif skipTypes(typ, {tyGenericInst, tyAlias, tySink}).kind == tyVoid: continue + for j in countup(0, length-3): var arg = newSymG(skParam, a.sons[j], c) if not hasType and not hasDefault and kind notin {skTemplate, skMacro}: @@ -1065,7 +1075,8 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, arg.position = counter arg.constraint = constraint inc(counter) - if def != nil and def.kind != nkEmpty: arg.ast = copyTree(def) + if def != nil and def.kind != nkEmpty: + arg.ast = copyTree(def) if containsOrIncl(check, arg.name.id): localError(c.config, a.sons[j].info, "attempt to redefine: '" & arg.name.s & "'") addSon(result.n, newSymNode(arg)) @@ -1320,7 +1331,7 @@ proc semTypeClass(c: PContext, n: PNode, prev: PType): PType = incl dummyParam.flags, sfUsed addDecl(c, dummyParam) - result.n.sons[3] = semConceptBody(c, n[3]) + result.n[3] = semConceptBody(c, n[3]) closeScope(c) proc semProcTypeWithScope(c: PContext, n: PNode, diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index a24972d04..f02c45e46 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -144,17 +144,6 @@ proc isTypeParam(n: PNode): bool = (n.sym.kind == skGenericParam or (n.sym.kind == skType and sfFromGeneric in n.sym.flags)) -proc hasGenericArguments*(n: PNode): bool = - if n.kind == nkSym: - return n.sym.kind == skGenericParam or - tfInferrableStatic in n.sym.typ.flags or - (n.sym.kind == skType and - n.sym.typ.flags * {tfGenericTypeParam, tfImplicitTypeParam} != {}) - else: - for i in 0.. Date: Wed, 13 Jun 2018 01:35:46 +0300 Subject: Support default type parameters progress on #7516 --- compiler/seminst.nim | 19 ++++++++++++++++--- compiler/semtypes.nim | 4 ++++ tests/metatype/ttypedesc2.nim | 14 ++++++++++++++ 3 files changed, 34 insertions(+), 3 deletions(-) (limited to 'tests') diff --git a/compiler/seminst.nim b/compiler/seminst.nim index 752a2b599..0047fd68e 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -247,26 +247,39 @@ proc instantiateProcType(c: PContext, pt: TIdTable, if i > 1: resetIdTable(cl.symMap) resetIdTable(cl.localCache) + + # take a note of the original type. If't a free type parameter + # we'll need to keep it unbount for the `fitNode` operation below... + var typeToFit = result[i] + let needsStaticSkipping = result[i].kind == tyFromExpr result[i] = replaceTypeVarsT(cl, result[i]) if needsStaticSkipping: result[i] = result[i].skipTypes({tyStatic}) + + # ...otherwise, we use the instantiated type in `fitNode` + if typeToFit.kind != tyTypeDesc or typeToFit.base.kind != tyNone: + typeToFit = result[i] + internalAssert c.config, originalParams[i].kind == nkSym let oldParam = originalParams[i].sym let param = copySym(oldParam) param.owner = prc param.typ = result[i] + + # The default value is instantiated and fitted against the final + # concrete param type. We avoid calling `replaceTypeVarsN` on the + # call head symbol, because this leads to infinite recursion. if oldParam.ast != nil: var def = oldParam.ast.copyTree if def.kind == nkCall: for i in 1 ..< def.len: def[i] = replaceTypeVarsN(cl, def[i]) def = semExprWithType(c, def) - param.ast = fitNode(c, param.typ, def, def.info) - param.typ = param.ast.typ + param.ast = fitNode(c, typeToFit, def, def.info) + param.typ = result[i] result.n[i] = newSymNode(param) - result[i] = param.typ propagateToOwner(result, result[i]) addDecl(c, param) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 85c6e3056..2f16151f2 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -1045,6 +1045,10 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, if typ == nil: typ = def.typ + if typ.kind == tyTypeDesc: + # default typedesc values are mapped to the unbound typedesc type: + typ = newTypeWithSons(c, tyTypeDesc, @[newTypeS(tyNone, c)]) + else: # if def.typ != nil and def.typ.kind != tyNone: # example code that triggers it: diff --git a/tests/metatype/ttypedesc2.nim b/tests/metatype/ttypedesc2.nim index 4b6cfe6bc..94a7367e7 100644 --- a/tests/metatype/ttypedesc2.nim +++ b/tests/metatype/ttypedesc2.nim @@ -34,3 +34,17 @@ when true: type Point[T] = tuple[x, y: T] proc origin(T: typedesc): Point[T] = discard discard origin(int) + +# https://github.com/nim-lang/Nim/issues/7516 +import typetraits + +proc hasDefault1(T: type = int): auto = return T.name +doAssert hasDefault1(int) == "int" +doAssert hasDefault1(string) == "string" +doAssert hasDefault1() == "int" + +proc hasDefault2(T = string): auto = return T.name +doAssert hasDefault2(int) == "int" +doAssert hasDefault2(string) == "string" +doAssert hasDefault2() == "string" + -- cgit 1.4.1-2-gfad0 From e719f211c634d2be7b5ae118db7051a2382a8e3e Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Thu, 14 Jun 2018 12:47:07 +0300 Subject: fix #6928; fix #7208 --- compiler/astalgo.nim | 9 +++++++++ compiler/seminst.nim | 7 ++++--- compiler/semtypes.nim | 3 +++ tests/generics/tlateboundgenericparams.nim | 21 +++++++++++++++++++++ 4 files changed, 37 insertions(+), 3 deletions(-) (limited to 'tests') diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index 0afe56bb7..a4a14405e 100644 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -31,6 +31,15 @@ when declared(echo): proc debug*(conf: ConfigRef; n: PType) {.deprecated.} proc debug*(conf: ConfigRef; n: PNode) {.deprecated.} + template debug*(x: PSym|PType|PNode) {.deprecated.} = + when compiles(c.config): + debug(c.config, x) + else: + error() + + template debug*(x: auto) {.deprecated.} = + echo x + template mdbg*: bool {.dirty.} = when compiles(c.module): c.module.fileIdx == c.config.projectMainIdx diff --git a/compiler/seminst.nim b/compiler/seminst.nim index 0047fd68e..0ad1fb872 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -248,8 +248,8 @@ proc instantiateProcType(c: PContext, pt: TIdTable, resetIdTable(cl.symMap) resetIdTable(cl.localCache) - # take a note of the original type. If't a free type parameter - # we'll need to keep it unbount for the `fitNode` operation below... + # take a note of the original type. If't a free type or static parameter + # we'll need to keep it unbound for the `fitNode` operation below... var typeToFit = result[i] let needsStaticSkipping = result[i].kind == tyFromExpr @@ -258,7 +258,8 @@ proc instantiateProcType(c: PContext, pt: TIdTable, result[i] = result[i].skipTypes({tyStatic}) # ...otherwise, we use the instantiated type in `fitNode` - if typeToFit.kind != tyTypeDesc or typeToFit.base.kind != tyNone: + if (typeToFit.kind != tyTypeDesc or typeToFit.base.kind != tyNone) and + (typeToFit.kind != tyStatic): typeToFit = result[i] internalAssert c.config, originalParams[i].kind == nkSym diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index f60b57d33..ff2820ec8 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -1056,6 +1056,9 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, if not containsGenericType(typ): # check type compatibility between def.typ and typ: def = fitNode(c, typ, def, def.info) + elif typ.kind == tyStatic: + def = semConstExpr(c, def) + def = fitNode(c, typ, def, def.info) if not hasType and not hasDefault: if isType: localError(c.config, a.info, "':' expected") diff --git a/tests/generics/tlateboundgenericparams.nim b/tests/generics/tlateboundgenericparams.nim index 02f99dc55..9f0580fd2 100644 --- a/tests/generics/tlateboundgenericparams.nim +++ b/tests/generics/tlateboundgenericparams.nim @@ -1,3 +1,11 @@ +discard """ + output: "1\n10\n1\n10" + nimout: ''' +bar instantiated with 1 +bar instantiated with 10 +''' +""" + import typetraits type @@ -122,3 +130,16 @@ when true: var p = getOrigin[float]() var rotated = p.rotate(2.1) + test 7: + proc bar(x: static[int]) = + static: echo "bar instantiated with ", x + echo x + + proc foo(x: static[int] = 1) = + bar(x) + + foo() + foo(10) + foo(1) + foo(10) + -- cgit 1.4.1-2-gfad0 From 31651ecd613b7fddea037cb0cc7d433bec6c902d Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Sat, 16 Jun 2018 03:51:31 +0300 Subject: allow referencing other parameters in default parameter values fix #7756 fix #1201 fix #7000 fix #3002 fix #1046 --- compiler/ast.nim | 8 ++- compiler/astalgo.nim | 42 ++++++++------- compiler/lowerings.nim | 12 +++++ compiler/sem.nim | 18 ++++--- compiler/semcall.nim | 4 +- compiler/seminst.nim | 26 ++++++++- compiler/semtypes.nim | 2 + compiler/sigmatch.nim | 3 +- compiler/transf.nim | 46 ++++++++++++++++ tests/misc/tparamsindefault.nim | 114 ++++++++++++++++++++++++++++++++++++++++ 10 files changed, 244 insertions(+), 31 deletions(-) create mode 100644 tests/misc/tparamsindefault.nim (limited to 'tests') diff --git a/compiler/ast.nim b/compiler/ast.nim index 085a243b3..6302c21b9 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -293,6 +293,10 @@ const # the compiler will avoid printing such names # in user messages. + sfHoisted* = sfForward + # an expression was hoised to an anonymous variable. + # the flag is applied to the var/let symbol + sfNoForward* = sfRegister # forward declarations are not required (per module) sfReorder* = sfForward @@ -455,6 +459,8 @@ type nfBlockArg # this a stmtlist appearing in a call (e.g. a do block) nfFromTemplate # a top-level node returned from a template nfDefaultParam # an automatically inserter default parameter + nfDefaultRefsParam # a default param value references another parameter + # the flag is applied to proc default values and to calls TNodeFlags* = set[TNodeFlag] TTypeFlag* = enum # keep below 32 for efficiency reasons (now: beyond that) @@ -972,7 +978,7 @@ const PersistentNodeFlags*: TNodeFlags = {nfBase2, nfBase8, nfBase16, nfDotSetter, nfDotField, nfIsRef, nfPreventCg, nfLL, - nfFromTemplate} + nfFromTemplate, nfDefaultRefsParam} namePos* = 0 patternPos* = 1 # empty except for term rewriting macros genericParamsPos* = 2 diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index a4a14405e..290ac05ee 100644 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -34,32 +34,36 @@ when declared(echo): template debug*(x: PSym|PType|PNode) {.deprecated.} = when compiles(c.config): debug(c.config, x) + elif compiles(c.graph.config): + debug(c.graph.config, x) else: error() template debug*(x: auto) {.deprecated.} = echo x -template mdbg*: bool {.dirty.} = - when compiles(c.module): - c.module.fileIdx == c.config.projectMainIdx - elif compiles(c.c.module): - c.c.module.fileIdx == c.c.config.projectMainIdx - elif compiles(m.c.module): - m.c.module.fileIdx == m.c.config.projectMainIdx - elif compiles(cl.c.module): - cl.c.module.fileIdx == cl.c.config.projectMainIdx - elif compiles(p): - when compiles(p.lex): - p.lex.fileIdx == p.lex.config.projectMainIdx + template mdbg*: bool {.deprecated.} = + when compiles(c.graph): + c.module.fileIdx == c.graph.config.projectMainIdx + elif compiles(c.module): + c.module.fileIdx == c.config.projectMainIdx + elif compiles(c.c.module): + c.c.module.fileIdx == c.c.config.projectMainIdx + elif compiles(m.c.module): + m.c.module.fileIdx == m.c.config.projectMainIdx + elif compiles(cl.c.module): + cl.c.module.fileIdx == cl.c.config.projectMainIdx + elif compiles(p): + when compiles(p.lex): + p.lex.fileIdx == p.lex.config.projectMainIdx + else: + p.module.module.fileIdx == p.config.projectMainIdx + elif compiles(m.module.fileIdx): + m.module.fileIdx == m.config.projectMainIdx + elif compiles(L.fileIdx): + L.fileIdx == L.config.projectMainIdx else: - p.module.module.fileIdx == p.config.projectMainIdx - elif compiles(m.module.fileIdx): - m.module.fileIdx == m.config.projectMainIdx - elif compiles(L.fileIdx): - L.fileIdx == L.config.projectMainIdx - else: - error() + error() # --------------------------- ident tables ---------------------------------- proc idTableGet*(t: TIdTable, key: PIdObj): RootRef diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim index 24a4f186e..1b17f620c 100644 --- a/compiler/lowerings.nim +++ b/compiler/lowerings.nim @@ -336,6 +336,18 @@ proc typeNeedsNoDeepCopy(t: PType): bool = if t.kind in {tyVar, tyLent, tySequence}: t = t.lastSon result = not containsGarbageCollectedRef(t) +proc hoistExpr*(varSection, expr: PNode, name: PIdent, owner: PSym): PSym = + result = newSym(skLet, name, owner, varSection.info, owner.options) + result.flags.incl sfHoisted + result.typ = expr.typ + + var varDef = newNodeI(nkIdentDefs, varSection.info, 3) + varDef.sons[0] = newSymNode(result) + varDef.sons[1] = newNodeI(nkEmpty, varSection.info) + varDef.sons[2] = expr + + varSection.add varDef + proc addLocalVar(g: ModuleGraph; varSection, varInit: PNode; owner: PSym; typ: PType; v: PNode; useShallowCopy=false): PSym = result = newSym(skTemp, getIdent(g.cache, genPrefix), owner, varSection.info, diff --git a/compiler/sem.nim b/compiler/sem.nim index afc794a37..299286545 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -73,6 +73,16 @@ template semIdeForTemplateOrGeneric(c: PContext; n: PNode; # echo "passing to safeSemExpr: ", renderTree(n) discard safeSemExpr(c, n) +proc fitNodePostMatch(c: PContext, formal: PType, arg: PNode): PNode = + result = arg + let x = result.skipConv + if x.kind in {nkPar, nkTupleConstr} and formal.kind != tyExpr: + changeType(c, x, formal, check=true) + else: + result = skipHiddenSubConv(result) + #result.typ = takeType(formal, arg.typ) + #echo arg.info, " picked ", result.typ.typeToString + proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode = if arg.typ.isNil: localError(c.config, arg.info, "expression has no type: " & @@ -88,13 +98,7 @@ proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode = result = copyTree(arg) result.typ = formal else: - let x = result.skipConv - if x.kind in {nkPar, nkTupleConstr} and formal.kind != tyExpr: - changeType(c, x, formal, check=true) - else: - result = skipHiddenSubConv(result) - #result.typ = takeType(formal, arg.typ) - #echo arg.info, " picked ", result.typ.typeToString + result = fitNodePostMatch(c, formal, result) proc inferWithMetatype(c: PContext, formal: PType, arg: PNode, coerceDistincts = false): PNode diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 0de22cfb3..67fe99232 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -402,7 +402,9 @@ proc updateDefaultParams(call: PNode) = for i in countdown(call.len - 1, 1): if nfDefaultParam notin call[i].flags: return - call[i] = calleeParams[i].sym.ast + let def = calleeParams[i].sym.ast + if nfDefaultRefsParam in def.flags: call.flags.incl nfDefaultRefsParam + call[i] = def proc semResolvedCall(c: PContext, x: TCandidate, n: PNode, flags: TExprFlags): PNode = diff --git a/compiler/seminst.nim b/compiler/seminst.nim index 0ad1fb872..fac04e3a0 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -220,6 +220,14 @@ proc instGenericContainer(c: PContext, info: TLineInfo, header: PType, result = replaceTypeVarsT(cl, header) closeScope(c) +proc referencesAnotherParam(n: PNode, p: PSym): bool = + if n.kind == nkSym: + return n.sym.kind == skParam and n.sym.owner == p + else: + for i in 0.. Date: Fri, 4 May 2018 15:44:36 +0100 Subject: Adds smaller code samples from Chapters 1-3 to the tester. --- tests/niminaction/Chapter1/various1.nim | 45 +++ tests/niminaction/Chapter2/explicit_discard.nim | 7 + tests/niminaction/Chapter2/no_def_eq.nim | 16 + tests/niminaction/Chapter2/no_iterator.nim | 7 + tests/niminaction/Chapter2/no_seq_type.nim | 6 + tests/niminaction/Chapter2/resultaccept.nim | 28 ++ tests/niminaction/Chapter2/resultreject.nim | 33 +++ tests/niminaction/Chapter2/various2.nim | 369 ++++++++++++++++++++++++ tests/niminaction/Chapter3/various3.nim | 93 ++++++ tests/niminaction/Chapter3/various3.nim.cfg | 1 + tests/testament/categories.nim | 38 ++- 11 files changed, 642 insertions(+), 1 deletion(-) create mode 100644 tests/niminaction/Chapter1/various1.nim create mode 100644 tests/niminaction/Chapter2/explicit_discard.nim create mode 100644 tests/niminaction/Chapter2/no_def_eq.nim create mode 100644 tests/niminaction/Chapter2/no_iterator.nim create mode 100644 tests/niminaction/Chapter2/no_seq_type.nim create mode 100644 tests/niminaction/Chapter2/resultaccept.nim create mode 100644 tests/niminaction/Chapter2/resultreject.nim create mode 100644 tests/niminaction/Chapter2/various2.nim create mode 100644 tests/niminaction/Chapter3/various3.nim create mode 100644 tests/niminaction/Chapter3/various3.nim.cfg (limited to 'tests') diff --git a/tests/niminaction/Chapter1/various1.nim b/tests/niminaction/Chapter1/various1.nim new file mode 100644 index 000000000..688180fd2 --- /dev/null +++ b/tests/niminaction/Chapter1/various1.nim @@ -0,0 +1,45 @@ +discard """ + exitCode: 0 + outputsub: "Woof!" +""" + +import strutils +echo("hello".to_upper()) +echo("world".toUpper()) + +type + Dog = object #<1> + age: int #<2> + +let dog = Dog(age: 3) #<3> + +proc showNumber(num: int | float) = + echo(num) + +showNumber(3.14) +showNumber(42) + +for i in 0 .. <10: + echo(i) + +block: # Block added due to clash. + type + Dog = object + + proc bark(self: Dog) = #<1> + echo("Woof!") + + let dog = Dog() + dog.bark() #<2> + +import sequtils, future, strutils +let list = @["Dominik Picheta", "Andreas Rumpf", "Desmond Hume"] +list.map( + (x: string) -> (string, string) => (x.split[0], x.split[1]) +).echo + +import strutils +let list1 = @["Dominik Picheta", "Andreas Rumpf", "Desmond Hume"] +for name in list1: + echo((name.split[0], name.split[1])) + diff --git a/tests/niminaction/Chapter2/explicit_discard.nim b/tests/niminaction/Chapter2/explicit_discard.nim new file mode 100644 index 000000000..3e94c335b --- /dev/null +++ b/tests/niminaction/Chapter2/explicit_discard.nim @@ -0,0 +1,7 @@ +discard """ + line: 7 + errormsg: "has to be discarded" +""" + +proc myProc(name: string): string = "Hello " & name +myProc("Dominik") \ No newline at end of file diff --git a/tests/niminaction/Chapter2/no_def_eq.nim b/tests/niminaction/Chapter2/no_def_eq.nim new file mode 100644 index 000000000..77f0a7dd8 --- /dev/null +++ b/tests/niminaction/Chapter2/no_def_eq.nim @@ -0,0 +1,16 @@ +discard """ + line: 16 + errormsg: "type mismatch" +""" + +type + Dog = object + name: string + + Cat = object + name: string + +let dog: Dog = Dog(name: "Fluffy") +let cat: Cat = Cat(name: "Fluffy") + +echo(dog == cat) \ No newline at end of file diff --git a/tests/niminaction/Chapter2/no_iterator.nim b/tests/niminaction/Chapter2/no_iterator.nim new file mode 100644 index 000000000..331d69480 --- /dev/null +++ b/tests/niminaction/Chapter2/no_iterator.nim @@ -0,0 +1,7 @@ +discard """ + line: 6 + errormsg: "type mismatch" +""" + +for i in 5: + echo i \ No newline at end of file diff --git a/tests/niminaction/Chapter2/no_seq_type.nim b/tests/niminaction/Chapter2/no_seq_type.nim new file mode 100644 index 000000000..493be270a --- /dev/null +++ b/tests/niminaction/Chapter2/no_seq_type.nim @@ -0,0 +1,6 @@ +discard """ + line: 6 + errormsg: "cannot infer the type of the sequence" +""" + +var list = @[] \ No newline at end of file diff --git a/tests/niminaction/Chapter2/resultaccept.nim b/tests/niminaction/Chapter2/resultaccept.nim new file mode 100644 index 000000000..7dd976b40 --- /dev/null +++ b/tests/niminaction/Chapter2/resultaccept.nim @@ -0,0 +1,28 @@ +discard """ + output: "" +""" + +# Page 35. + +proc implicit: string = + "I will be returned" + +proc discarded: string = + discard "I will not be returned" + +proc explicit: string = + return "I will be returned" + +proc resultVar: string = + result = "I will be returned" + +proc resultVar2: string = + result = "" + result.add("I will be ") + result.add("returned") + +doAssert implicit() == "I will be returned" +doAssert discarded() == nil +doAssert explicit() == "I will be returned" +doAssert resultVar() == "I will be returned" +doAssert resultVar2() == "I will be returned" \ No newline at end of file diff --git a/tests/niminaction/Chapter2/resultreject.nim b/tests/niminaction/Chapter2/resultreject.nim new file mode 100644 index 000000000..de59af7d9 --- /dev/null +++ b/tests/niminaction/Chapter2/resultreject.nim @@ -0,0 +1,33 @@ +discard """ + line: 27 + errormsg: "has to be discarded" +""" + +# Page 35. + +proc implicit: string = + "I will be returned" + +proc discarded: string = + discard "I will not be returned" + +proc explicit: string = + return "I will be returned" + +proc resultVar: string = + result = "I will be returned" + +proc resultVar2: string = + result = "" + result.add("I will be ") + result.add("returned") + +proc resultVar3: string = + result = "I am the result" + "I will cause an error" + +doAssert implicit() == "I will be returned" +doAssert discarded() == nil +doAssert explicit() == "I will be returned" +doAssert resultVar() == "I will be returned" +doAssert resultVar2() == "I will be returned" \ No newline at end of file diff --git a/tests/niminaction/Chapter2/various2.nim b/tests/niminaction/Chapter2/various2.nim new file mode 100644 index 000000000..3f6a3f453 --- /dev/null +++ b/tests/niminaction/Chapter2/various2.nim @@ -0,0 +1,369 @@ +discard """ + exitCode: 0 + outputsub: '''42 is greater than 0''' +""" + +if 42 >= 0: + echo "42 is greater than 0" + + +echo("Output: ", + 5) +echo(5 + + 5) +# --- Removed code that is supposed to fail here. Not going to test those. --- + +# Single-line comment +#[ +Multiline comment +]# +when false: + echo("Commented-out code") + +let decimal = 42 +let hex = 0x42 +let octal = 0o42 +let binary = 0b101010 + +let a: int16 = 42 +let b = 42'i8 + +let c = 1'f32 # --- Changed names here to avoid clashes --- +let d = 1.0e19 + +let e = false +let f = true + +let g = 'A' +let h = '\109' +let i = '\x79' + +let text = "The book title is \"Nim in Action\"" + +let filepath = "C:\\Program Files\\Nim" + +# --- Changed name here to avoid clashes --- +let filepath1 = r"C:\Program Files\Nim" + +let multiLine = """foo + bar + baz +""" +echo multiLine + +import strutils +# --- Changed name here to avoid clashes --- +let multiLine1 = """foo + bar + baz +""" +echo multiLine1.unindent +doAssert multiLine1.unindent == "foo\nbar\nbaz\n" + +proc fillString(): string = + result = "" + echo("Generating string") + for i in 0 .. 4: + result.add($i) #<1> + +const count = fillString() + +var + text1 = "hello" + number: int = 10 + isTrue = false + +var 火 = "Fire" +let ogień = true + +var `var` = "Hello" +echo(`var`) + +proc myProc(name: string): string = "Hello " & name +discard myProc("Dominik") + +proc bar(): int #<1> + +proc foo(): float = bar().float +proc bar(): int = foo().int + +proc noReturn() = echo("Hello") +proc noReturn2(): void = echo("Hello") + +proc noReturn3 = echo("Hello") + +proc message(recipient: string): auto = + "Hello " & recipient + +doAssert message("Dom") == "Hello Dom" + +proc max(a: int, b: int): int = + if a > b: a else: b + +doAssert max(5, 10) == 10 + +proc max2(a, b: int): int = + if a > b: a else: b + +proc genHello(name: string, surname = "Doe"): string = + "Hello " & name & " " & surname + +# -- Leaving these as asserts as that is in the original code, just in case +# -- somehow in the future `assert` is removed :) +assert genHello("Peter") == "Hello Peter Doe" +assert genHello("Peter", "Smith") == "Hello Peter Smith" + +proc genHello2(names: varargs[string]): string = + result = "" + for name in names: + result.add("Hello " & name & "\n") + +doAssert genHello2("John", "Bob") == "Hello John\nHello Bob\n" + +proc getUserCity(firstName, lastName: string): string = + case firstName + of "Damien": return "Tokyo" + of "Alex": return "New York" + else: return "Unknown" + +proc getUserCity(userID: int): string = + case userID + of 1: return "Tokyo" + of 2: return "New York" + else: return "Unknown" + +doAssert getUserCity("Damien", "Lundi") == "Tokyo" +doAssert getUserCity(2) == "New York" # -- Errata here: missing closing " + +import sequtils +let numbers = @[1, 2, 3, 4, 5, 6] +let odd = filter(numbers, proc (x: int): bool = x mod 2 != 0) +doAssert odd == @[1, 3, 5] + +import sequtils, future +let numbers1 = @[1, 2, 3, 4, 5, 6] +let odd1 = filter(numbers1, (x: int) -> bool => x mod 2 != 0) +assert odd1 == @[1, 3, 5] + +proc isValid(x: int, validator: proc (x: int): bool) = + if validator(x): echo(x, " is valid") + else: echo(x, " is NOT valid") + +import future +proc isValid2(x: int, validator: (x: int) -> bool) = + if validator(x): echo(x, " is valid") + else: echo(x, " is NOT valid") + +var list: array[3, int] +list[0] = 1 +list[1] = 42 +assert list[0] == 1 +assert list[1] == 42 +assert list[2] == 0 #<1> + +echo list.repr #<2> + +# echo list[500] + +var list2: array[-10 .. -9, int] +list2[-10] = 1 +list2[-9] = 2 + +var list3 = ["Hi", "There"] + +var list4 = ["My", "name", "is", "Dominik"] +for item in list4: + echo(item) + +for i in list4.low .. list4.high: + echo(list4[i]) + +var list5: seq[int] = @[] +doAssertRaises(IndexError): + list5[0] = 1 + +list5.add(1) + +assert list5[0] == 1 +doAssertRaises(IndexError): + echo list5[42] + +# -- Errata: var list: seq[int]; echo(list[0]). This now creates an exception, +# -- not a SIGSEGV. + +block: + var list = newSeq[string](3) + assert list[0] == nil + list[0] = "Foo" + list[1] = "Bar" + list[2] = "Baz" + + list.add("Lorem") + +block: + let list = @[4, 8, 15, 16, 23, 42] + for i in 0 .. 0 and age <= 10: + echo("You're still a child") +elif age > 10 and age < 18: + echo("You're a teenager") +else: + echo("You're an adult") + +let variable = "Arthur" +case variable +of "Arthur", "Zaphod", "Ford": + echo("Male") +of "Marvin": + echo("Robot") +of "Trillian": + echo("Female") +else: + echo("Unknown") + +let ageDesc = if age < 18: "Non-Adult" else: "Adult" + +block: + var i = 0 + while i < 3: + echo(i) + i.inc + +block label: + var i = 0 + while true: + while i < 5: + if i > 3: break label + i.inc + +iterator values(): int = + var i = 0 + while i < 5: + yield i + i.inc + +for value in values(): + echo(value) + +import os +for filename in walkFiles("*.nim"): + echo(filename) + +for item in @[1, 2, 3]: + echo(item) + +for i, value in @[1, 2, 3]: echo("Value at ", i, ": ", value) + +doAssertRaises(IOError): + proc second() = + raise newException(IOError, "Somebody set us up the bomb") + + proc first() = + second() + + first() + +block: + proc second() = + raise newException(IOError, "Somebody set us up the bomb") + + proc first() = + try: + second() + except: + echo("Cannot perform second action because: " & + getCurrentExceptionMsg()) + + first() + +block: + type + Person = object + name: string + age: int + + var person: Person + var person1 = Person(name: "Neo", age: 28) + +block: + type + PersonObj = object + name: string + age: int + PersonRef = ref PersonObj + + # proc setName(person: PersonObj) = + # person.name = "George" + + proc setName(person: PersonRef) = + person.name = "George" + +block: + type + Dog = object + name: string + + Cat = object + name: string + + let dog: Dog = Dog(name: "Fluffy") + let cat: Cat = Cat(name: "Fluffy") + +block: + type + Dog = tuple + name: string + + Cat = tuple + name: string + + let dog: Dog = (name: "Fluffy") + let cat: Cat = (name: "Fluffy") + + echo(dog == cat) + +block: + type + Point = tuple[x, y: int] + Point2 = (int, int) + + let pos: Point = (x: 100, y: 50) + doAssert pos == (100, 50) + + let pos1: Point = (x: 100, y: 50) + let (x, y) = pos1 #<1> + let (left, _) = pos1 + doAssert x == pos1[0] + doAssert y == pos1[1] + doAssert left == x + +block: + type + Color = enum + colRed, + colGreen, + colBlue + + let color: Color = colRed + +block: + type + Color {.pure.} = enum + red, green, blue + + let color = Color.red \ No newline at end of file diff --git a/tests/niminaction/Chapter3/various3.nim b/tests/niminaction/Chapter3/various3.nim new file mode 100644 index 000000000..478229b00 --- /dev/null +++ b/tests/niminaction/Chapter3/various3.nim @@ -0,0 +1,93 @@ +import threadpool +proc foo: string = "Dog" +var x: FlowVar[string] = spawn foo() +assert(^x == "Dog") + +block: + type + Box = object + case empty: bool + of false: + contents: string + else: + discard + + var obj = Box(empty: false, contents: "Hello") + assert obj.contents == "Hello" + + var obj2 = Box(empty: true) + doAssertRaises(FieldError): + echo(obj2.contents) + +import json +assert parseJson("null").kind == JNull +assert parseJson("true").kind == JBool +assert parseJson("42").kind == JInt +assert parseJson("3.14").kind == JFloat +assert parseJson("\"Hi\"").kind == JString +assert parseJson("""{ "key": "value" }""").kind == JObject +assert parseJson("[1, 2, 3, 4]").kind == JArray + +import json +let data = """ + {"username": "Dominik"} +""" + +let obj = parseJson(data) +assert obj.kind == JObject +assert obj["username"].kind == JString +assert obj["username"].str == "Dominik" + +block: + proc count10(): int = + for i in 0 .. <10: + result.inc + assert count10() == 10 + +type + Point = tuple[x, y: int] + +var point = (5, 10) +var point2 = (x: 5, y: 10) + +type + Human = object + name: string + age: int + +var jeff = Human(name: "Jeff", age: 23) +var amy = Human(name: "Amy", age: 20) + +import asyncdispatch + +var future = newFuture[int]() +doAssert(not future.finished) + +future.callback = + proc (future: Future[int]) = + echo("Future is no longer empty, ", future.read) + +future.complete(42) + +import asyncdispatch, asyncfile + +when false: + var file = openAsync("") + let dataFut = file.readAll() + dataFut.callback = + proc (future: Future[string]) = + echo(future.read()) + + asyncdispatch.runForever() + +import asyncdispatch, asyncfile, os + +proc readFiles() {.async.} = + # --- Changed to getTempDir here. + var file = openAsync(getTempDir() / "test.txt", fmReadWrite) + let data = await file.readAll() + echo(data) + await file.write("Hello!\n") + +waitFor readFiles() + diff --git a/tests/niminaction/Chapter3/various3.nim.cfg b/tests/niminaction/Chapter3/various3.nim.cfg new file mode 100644 index 000000000..6c1ded992 --- /dev/null +++ b/tests/niminaction/Chapter3/various3.nim.cfg @@ -0,0 +1 @@ +threads:on \ No newline at end of file diff --git a/tests/testament/categories.nim b/tests/testament/categories.nim index 84e536636..f513090a4 100644 --- a/tests/testament/categories.nim +++ b/tests/testament/categories.nim @@ -266,8 +266,17 @@ proc testNimInAction(r: var TResults, cat: Category, options: string) = testSpec r, makeTest(filename, options, cat, actionCompile), targetCPP let tests = [ + "niminaction/Chapter1/various1", + "niminaction/Chapter2/various2", + "niminaction/Chapter2/resultaccept", + "niminaction/Chapter2/resultreject", + "niminaction/Chapter2/explicit_discard", + "niminaction/Chapter2/no_def_eq", + "niminaction/Chapter2/no_iterator", + "niminaction/Chapter2/no_seq_type", "niminaction/Chapter3/ChatApp/src/server", "niminaction/Chapter3/ChatApp/src/client", + "niminaction/Chapter3/various3", "niminaction/Chapter6/WikipediaStats/concurrency_regex", "niminaction/Chapter6/WikipediaStats/concurrency", "niminaction/Chapter6/WikipediaStats/naive", @@ -278,8 +287,34 @@ proc testNimInAction(r: var TResults, cat: Category, options: string) = "niminaction/Chapter7/Tweeter/src/tweeter", "niminaction/Chapter7/Tweeter/src/createDatabase", "niminaction/Chapter7/Tweeter/tests/database_test", - "niminaction/Chapter8/sdl/sdl_test", + "niminaction/Chapter8/sdl/sdl_test" ] + + # Verify that the files have not been modified. Death shall fall upon + # whoever edits these hashes without dom96's permission, j/k. But please only + # edit when making a conscious breaking change, also please try to make your + # commit message clear so I can easily compile an errata later. + var testHashes: seq[string] = @[] + + for test in tests: + testHashes.add(getMD5(readFile("tests" / test.addFileExt("nim")).string)) + + let refHashes = @[ + "51afdfa84b3ca3d810809d6c4e5037ba", "30f07e4cd5eaec981f67868d4e91cfcf", + "d14e7c032de36d219c9548066a97e846", "2e40bfd5daadb268268727da91bb4e81", + "c5d3853ed0aba04bf6d35ba28a98dca0", "058603145ff92d46c009006b06e5b228", + "7b94a029b94ddb7efafddd546c965ff6", "586d74514394e49f2370dfc01dd9e830", + "e1901837b757c9357dc8d259fd0ef0f6", "097670c7ae12e825debaf8ec3995227b", + "a8cb7b78cc78d28535ab467361db5d6e", "bfaec2816a1848991b530c1ad17a0184", + "47cb71bb4c1198d6d29cdbee05aa10b9", "87e4436809f9d73324cfc4f57f116770", + "7b7db5cddc8cf8fa9b6776eef1d0a31d", "e6e40219f0f2b877869b738737b7685e", + "6532ee87d819f2605a443d5e94f9422a", "9a8fe78c588d08018843b64b57409a02", + "03a801275b8b76b4170c870cd0da079d", "20bb7d3e2d38d43b0cb5fcff4909a4a8", + "af6844598f534fab6942abfa4dfe9ab2", "2a7a17f84f6503d9bc89a5ab8feea127" + ] + doAssert testHashes == refHashes, "Nim in Action tests were changed." + + # Run the tests. for testfile in tests: test "tests/" & testfile & ".nim", actionCompile @@ -291,6 +326,7 @@ proc testNimInAction(r: var TResults, cat: Category, options: string) = + # ------------------------- manyloc ------------------------------------------- #proc runSpecialTests(r: var TResults, options: string) = # for t in ["lib/packages/docutils/highlite"]: -- cgit 1.4.1-2-gfad0 From dbcdc4331a2d87aafa06ce49d50277474595064d Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 17 Jun 2018 14:16:01 +0200 Subject: testament: minor code formating change --- tests/testament/tester.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/testament/tester.nim b/tests/testament/tester.nim index 0185156ec..0764b6363 100644 --- a/tests/testament/tester.nim +++ b/tests/testament/tester.nim @@ -129,7 +129,7 @@ proc callCCompiler(cmdTemplate, filename, options: string, let c = parseCmdLine(cmdTemplate % ["target", targetToCmd[target], "options", options, "file", filename.quoteShell, "filedir", filename.getFileDir()]) - var p = startProcess(command="gcc", args=c[5.. ^1], + var p = startProcess(command="gcc", args=c[5 .. ^1], options={poStdErrToStdOut, poUsePath}) let outp = p.outputStream var x = newStringOfCap(120) -- cgit 1.4.1-2-gfad0 From 03b073d541e899585951fb51896ce4440f94387b Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Sat, 5 May 2018 21:14:48 +0100 Subject: Workaround VM bug in strutils --- lib/pure/strutils.nim | 5 +++-- tests/testament/categories.nim | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) (limited to 'tests') diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index 055e91faf..ab34a0b2d 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -17,8 +17,9 @@ import parseutils from math import pow, round, floor, log10 from algorithm import reverse -from unicode import toLower, toUpper -export toLower, toUpper +when defined(nimVmExportFixed): + from unicode import toLower, toUpper + export toLower, toUpper {.deadCodeElim: on.} # dce option deprecated diff --git a/tests/testament/categories.nim b/tests/testament/categories.nim index f513090a4..9affbc159 100644 --- a/tests/testament/categories.nim +++ b/tests/testament/categories.nim @@ -293,13 +293,13 @@ proc testNimInAction(r: var TResults, cat: Category, options: string) = # Verify that the files have not been modified. Death shall fall upon # whoever edits these hashes without dom96's permission, j/k. But please only # edit when making a conscious breaking change, also please try to make your - # commit message clear so I can easily compile an errata later. + # commit message clear and notify me so I can easily compile an errata later. var testHashes: seq[string] = @[] for test in tests: testHashes.add(getMD5(readFile("tests" / test.addFileExt("nim")).string)) - let refHashes = @[ + const refHashes = @[ "51afdfa84b3ca3d810809d6c4e5037ba", "30f07e4cd5eaec981f67868d4e91cfcf", "d14e7c032de36d219c9548066a97e846", "2e40bfd5daadb268268727da91bb4e81", "c5d3853ed0aba04bf6d35ba28a98dca0", "058603145ff92d46c009006b06e5b228", -- cgit 1.4.1-2-gfad0 From db68bbe4f7e3ed2c6321e46d9b4d4977f1855a4e Mon Sep 17 00:00:00 2001 From: gemath <33119724+gemath@users.noreply.github.com> Date: Tue, 19 Jun 2018 17:13:33 +0000 Subject: Pegs AST read access (#8050) * Make PEG AST nodes readable from outside the module. * Added a test module for the pegs stdlib module. * Edited changelog. * Renamed ``sons`` iterator to ``items``, added ``pairs``, inlined both. * Updated entry and moved it to the right category. --- changelog.md | 3 ++ lib/pure/pegs.nim | 23 +++++++++++++-- tests/stdlib/tpegs.nim | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 102 insertions(+), 2 deletions(-) create mode 100644 tests/stdlib/tpegs.nim (limited to 'tests') diff --git a/changelog.md b/changelog.md index 4067cb693..4a490dfdd 100644 --- a/changelog.md +++ b/changelog.md @@ -107,6 +107,9 @@ use the Nim VM in a native Nim application. - Added the parameter ``val`` for the ``CritBitTree[T].incl`` proc. - The proc ``tgamma`` was renamed to ``gamma``. ``tgamma`` is deprecated. +- The ``pegs`` module now exports getters for the fields of its ``Peg`` and ``NonTerminal`` + object types. ``Peg``s with child nodes now have the standard ``items`` and ``pairs`` + iterators. ### Language additions diff --git a/lib/pure/pegs.nim b/lib/pure/pegs.nim index 39c5790ed..d16527a56 100644 --- a/lib/pure/pegs.nim +++ b/lib/pure/pegs.nim @@ -32,7 +32,7 @@ const ## can be captured. More subpatterns cannot be captured! type - PegKind = enum + PegKind* = enum pkEmpty, pkAny, ## any character (.) pkAnyRune, ## any Unicode character (_) @@ -67,7 +67,7 @@ type pkRule, ## a <- b pkList, ## a, b pkStartAnchor ## ^ --> Internal DSL: startAnchor() - NonTerminalFlag = enum + NonTerminalFlag* = enum ntDeclared, ntUsed NonTerminalObj = object ## represents a non terminal symbol name: string ## the name of the symbol @@ -86,6 +86,25 @@ type else: sons: seq[Peg] NonTerminal* = ref NonTerminalObj +proc name*(nt: NonTerminal): string = nt.name +proc line*(nt: NonTerminal): int = nt.line +proc col*(nt: NonTerminal): int = nt.col +proc flags*(nt: NonTerminal): set[NonTerminalFlag] = nt.flags +proc rule*(nt: NonTerminal): Peg = nt.rule + +proc kind*(p: Peg): PegKind = p.kind +proc term*(p: Peg): string = p.term +proc ch*(p: Peg): char = p.ch +proc charChoice*(p: Peg): ref set[char] = p.charChoice +proc nt*(p: Peg): NonTerminal = p.nt +proc index*(p: Peg): range[0..MaxSubpatterns] = p.index +iterator items*(p: Peg): Peg {.inline.} = + for s in p.sons: + yield s +iterator pairs*(p: Peg): (int, Peg) {.inline.} = + for i in 0 ..< p.sons.len: + yield (i, p.sons[i]) + proc term*(t: string): Peg {.nosideEffect, rtl, extern: "npegs$1Str".} = ## constructs a PEG from a terminal string if t.len != 1: diff --git a/tests/stdlib/tpegs.nim b/tests/stdlib/tpegs.nim new file mode 100644 index 000000000..e5b709a66 --- /dev/null +++ b/tests/stdlib/tpegs.nim @@ -0,0 +1,78 @@ +discard """ + output: ''' +pkNonTerminal: Sum @(2, 3) + pkSequence: (Product (('+' / '-') Product)*) + pkNonTerminal: Product @(3, 7) + pkSequence: (Value (('*' / '/') Value)*) + pkNonTerminal: Value @(4, 5) + pkOrderedChoice: (([0-9] [0-9]*) / ('(' Expr ')')) + pkSequence: ([0-9] [0-9]*) + pkCharChoice: [0-9] + pkGreedyRepSet: [0-9]* + pkSequence: ('(' Expr ')') + pkChar: '(' + pkNonTerminal: Expr @(1, 4) + pkNonTerminal: Sum @(2, 3) + pkChar: ')' + pkGreedyRep: (('*' / '/') Value)* + pkSequence: (('*' / '/') Value) + pkOrderedChoice: ('*' / '/') + pkChar: '*' + pkChar: '/' + pkNonTerminal: Value @(4, 5) + pkGreedyRep: (('+' / '-') Product)* + pkSequence: (('+' / '-') Product) + pkOrderedChoice: ('+' / '-') + pkChar: '+' + pkChar: '-' + pkNonTerminal: Product @(3, 7) +''' +""" + +import strutils, streams +import pegs + +const + indent = " " + +let + pegSrc = """ +Expr <- Sum +Sum <- Product (('+' / '-') Product)* +Product <- Value (('*' / '/') Value)* +Value <- [0-9]+ / '(' Expr ')' + """ + pegAst: Peg = pegSrc.peg + +var + outp = newStringStream() + processed: seq[string] = @[] + +proc prt(outp: Stream, kind: PegKind, s: string; level: int = 0) = + outp.writeLine indent.repeat(level) & "$1: $2" % [$kind, s] + +proc recLoop(p: Peg, level: int = 0) = + case p.kind + of pkEmpty..pkWhitespace: + discard + of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle: + outp.prt(p.kind, $p, level) + of pkChar, pkGreedyRepChar: + outp.prt(p.kind, $p, level) + of pkCharChoice, pkGreedyRepSet: + outp.prt(p.kind, $p, level) + of pkNonTerminal: + outp.prt(p.kind, + "$1 @($3, $4)" % [p.nt.name, $p.nt.rule.kind, $p.nt.line, $p.nt.col], level) + if not(p.nt.name in processed): + processed.add p.nt.name + p.nt.rule.recLoop level+1 + of pkBackRef..pkBackRefIgnoreStyle: + outp.prt(p.kind, $p, level) + else: + outp.prt(p.kind, $p, level) + for s in items(p): + s.recLoop level+1 + +pegAst.recLoop +echo outp.data \ No newline at end of file -- cgit 1.4.1-2-gfad0 From fb62dd1fae7a405d80f0d4257ddb1f4d48e204f0 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Mon, 18 Jun 2018 14:30:18 +0200 Subject: Fix constant folding for shl/not Since the source and destination types are the same the result should be trimmed to fit. --- compiler/semfold.nim | 11 ++++++++-- tests/arithm/tnot.nim | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++ tests/arithm/tshl.nim | 34 ++++++++++++++++++++++++++++++ 3 files changed, 101 insertions(+), 2 deletions(-) create mode 100644 tests/arithm/tnot.nim create mode 100644 tests/arithm/tshl.nim (limited to 'tests') diff --git a/compiler/semfold.nim b/compiler/semfold.nim index 10a223ea2..eceb10470 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -211,7 +211,12 @@ proc evalOp(m: TMagic, n, a, b, c: PNode; g: ModuleGraph): PNode = of mUnaryMinusF64: result = newFloatNodeT(- getFloat(a), n, g) of mNot: result = newIntNodeT(1 - getInt(a), n, g) of mCard: result = newIntNodeT(nimsets.cardSet(g.config, a), n, g) - of mBitnotI: result = newIntNodeT(not getInt(a), n, g) + of mBitnotI: + case skipTypes(n.typ, abstractRange).kind + of tyUInt..tyUInt64: + result = newIntNodeT((not getInt(a)) and lastOrd(g.config, a.typ, fixedUnsigned=true), n, g) + else: + result = newIntNodeT(not getInt(a), n, g) of mLengthArray: result = newIntNodeT(lengthOrd(g.config, a.typ), n, g) of mLengthSeq, mLengthOpenArray, mXLenSeq, mLengthStr, mXLenStr: if a.kind == nkNilLit: @@ -250,8 +255,10 @@ proc evalOp(m: TMagic, n, a, b, c: PNode; g: ModuleGraph): PNode = of tyInt8: result = newIntNodeT(int8(getInt(a)) shl int8(getInt(b)), n, g) of tyInt16: result = newIntNodeT(int16(getInt(a)) shl int16(getInt(b)), n, g) of tyInt32: result = newIntNodeT(int32(getInt(a)) shl int32(getInt(b)), n, g) - of tyInt64, tyInt, tyUInt..tyUInt64: + of tyInt64, tyInt: result = newIntNodeT(`shl`(getInt(a), getInt(b)), n, g) + of tyUInt..tyUInt64: + result = newIntNodeT(`shl`(getInt(a), getInt(b)) and lastOrd(g.config, a.typ, fixedUnsigned=true), n, g) else: internalError(g.config, n.info, "constant folding for shl") of mShrI: case skipTypes(n.typ, abstractRange).kind diff --git a/tests/arithm/tnot.nim b/tests/arithm/tnot.nim new file mode 100644 index 000000000..6a4877b2c --- /dev/null +++ b/tests/arithm/tnot.nim @@ -0,0 +1,58 @@ +discard """ + output: ''' +-5 +-5 +-5 +-5 +4 +4 +4 +4 +251 +65531 +4294967291 +18446744073709551611 +4 +4 +4 +4 +''' +""" + +# Signed types +block: + const t0: int8 = not 4 + const t1: int16 = not 4 + const t2: int32 = not 4 + const t3: int64 = not 4 + const t4: int8 = not -5 + const t5: int16 = not -5 + const t6: int32 = not -5 + const t7: int64 = not -5 + echo t0 + echo t1 + echo t2 + echo t3 + echo t4 + echo t5 + echo t6 + echo t7 + +# Unsigned types +block: + const t0: uint8 = not 4'u8 + const t1: uint16 = not 4'u16 + const t2: uint32 = not 4'u32 + const t3: uint64 = not 4'u64 + const t4: uint8 = not 251'u8 + const t5: uint16 = not 65531'u16 + const t6: uint32 = not 4294967291'u32 + const t7: uint64 = not 18446744073709551611'u64 + echo t0 + echo t1 + echo t2 + echo t3 + echo t4 + echo t5 + echo t6 + echo t7 diff --git a/tests/arithm/tshl.nim b/tests/arithm/tshl.nim new file mode 100644 index 000000000..0aa46d021 --- /dev/null +++ b/tests/arithm/tshl.nim @@ -0,0 +1,34 @@ +discard """ + output: ''' +0 +0 +1 +1 +0 +0 +0 +1 +''' +""" + +# Signed types +block: + const t0: int8 = 1'i8 shl 8 + const t1: int16 = 1'i16 shl 16 + const t2: int32 = 1'i32 shl 32 + const t3: int64 = 1'i64 shl 64 + echo t0 + echo t1 + echo t2 + echo t3 + +# Unsigned types +block: + const t0: uint8 = 1'u8 shl 8 + const t1: uint16 = 1'u16 shl 16 + const t2: uint32 = 1'u32 shl 32 + const t3: uint64 = 1'u64 shl 64 + echo t0 + echo t1 + echo t2 + echo t3 -- cgit 1.4.1-2-gfad0 From af66258dca695d932e76ea31bdee2b2f185139cb Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Thu, 21 Jun 2018 22:21:24 +0200 Subject: Discriminate gensym'd type names in sigHash The root cause of #7905 lies in the codegen phase. The two template instantiations generate two different MyType types with different members but same t.sym.name leading the caching mechanism to confuse the two. Fixes #7905 --- compiler/sighashes.nim | 6 ++++-- tests/types/t7905.nim | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 tests/types/t7905.nim (limited to 'tests') diff --git a/compiler/sighashes.nim b/compiler/sighashes.nim index 0b95387cd..720d1848d 100644 --- a/compiler/sighashes.nim +++ b/compiler/sighashes.nim @@ -189,10 +189,11 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]) = c.hashTypeSym(t.sym) else: c.hashSym(t.sym) - if sfAnon in t.sym.flags: + if {sfAnon, sfGenSym} * t.sym.flags != {}: # generated object names can be identical, so we need to # disambiguate furthermore by hashing the field types and names: # mild hack to prevent endless recursions (makes nimforum compile again): + let wasAnon = sfAnon in t.sym.flags excl t.sym.flags, sfAnon let n = t.n for i in 0 ..< n.len: @@ -200,7 +201,8 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]) = let s = n[i].sym c.hashSym s c.hashType s.typ, flags - incl t.sym.flags, sfAnon + if wasAnon: + incl t.sym.flags, sfAnon else: c &= t.id if t.len > 0 and t.sons[0] != nil: diff --git a/tests/types/t7905.nim b/tests/types/t7905.nim new file mode 100644 index 000000000..ddb371039 --- /dev/null +++ b/tests/types/t7905.nim @@ -0,0 +1,18 @@ +discard """ + output: ''' +(member: "hello world") +(member: 123.456) +''' +""" + +template foobar(arg: typed): untyped = + type + MyType = object + member: type(arg) + + var myVar: MyType + myVar.member = arg + echo myVar + +foobar("hello world") +foobar(123.456'f64) -- cgit 1.4.1-2-gfad0 From e39baf46fc523245a7d73b263808b944c6f225db Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Fri, 22 Jun 2018 15:09:35 +0200 Subject: Don't blow up with recursive objects --- compiler/sighashes.nim | 7 +++---- tests/types/t7905.nim | 15 +++++++++++++++ 2 files changed, 18 insertions(+), 4 deletions(-) (limited to 'tests') diff --git a/compiler/sighashes.nim b/compiler/sighashes.nim index 720d1848d..0bf2b8459 100644 --- a/compiler/sighashes.nim +++ b/compiler/sighashes.nim @@ -193,16 +193,15 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]) = # generated object names can be identical, so we need to # disambiguate furthermore by hashing the field types and names: # mild hack to prevent endless recursions (makes nimforum compile again): - let wasAnon = sfAnon in t.sym.flags - excl t.sym.flags, sfAnon + let oldFlags = t.sym.flags + t.sym.flags = t.sym.flags - {sfAnon, sfGenSym} let n = t.n for i in 0 ..< n.len: assert n[i].kind == nkSym let s = n[i].sym c.hashSym s c.hashType s.typ, flags - if wasAnon: - incl t.sym.flags, sfAnon + t.sym.flags = oldFlags else: c &= t.id if t.len > 0 and t.sons[0] != nil: diff --git a/tests/types/t7905.nim b/tests/types/t7905.nim index ddb371039..ef75bb86c 100644 --- a/tests/types/t7905.nim +++ b/tests/types/t7905.nim @@ -2,6 +2,8 @@ discard """ output: ''' (member: "hello world") (member: 123.456) +(member: "hello world", x: ...) +(member: 123.456, x: ...) ''' """ @@ -16,3 +18,16 @@ template foobar(arg: typed): untyped = foobar("hello world") foobar(123.456'f64) + +template foobarRec(arg: typed): untyped = + type + MyType = object + member: type(arg) + x: ref MyType + + var myVar: MyType + myVar.member = arg + echo myVar + +foobarRec("hello world") +foobarRec(123.456'f64) -- cgit 1.4.1-2-gfad0 From bfa3d62cc1a9485cc7030216f5859258131ea5bb Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Sun, 24 Jun 2018 15:13:34 +0200 Subject: More concept fixes Fixes #7705, #7703, #7702 --- compiler/semexprs.nim | 4 ++-- compiler/semfold.nim | 2 +- compiler/types.nim | 2 +- tests/concepts/treversable.nim | 31 +++++++++++++++++++++++++++++++ 4 files changed, 35 insertions(+), 4 deletions(-) create mode 100644 tests/concepts/treversable.nim (limited to 'tests') diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 9d7c493a7..d7b5667b9 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -271,7 +271,7 @@ proc semLowHigh(c: PContext, n: PNode, m: TMagic): PNode = localError(c.config, n.info, errXExpectsTypeOrValue % opToStr[m]) else: n.sons[1] = semExprWithType(c, n.sons[1], {efDetermineType}) - var typ = skipTypes(n.sons[1].typ, abstractVarRange + {tyTypeDesc}) + var typ = skipTypes(n.sons[1].typ, abstractVarRange + {tyTypeDesc, tyUserTypeClassInst}) case typ.kind of tySequence, tyString, tyCString, tyOpenArray, tyVarargs: n.typ = getSysType(c.graph, n.info, tyInt) @@ -1261,7 +1261,7 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode = # make sure we don't evaluate generic macros/templates n.sons[0] = semExprWithType(c, n.sons[0], {efNoEvaluateGeneric}) - let arr = skipTypes(n.sons[0].typ, {tyGenericInst, + let arr = skipTypes(n.sons[0].typ, {tyGenericInst, tyUserTypeClassInst, tyVar, tyLent, tyPtr, tyRef, tyAlias, tySink}) case arr.kind of tyArray, tyOpenArray, tyVarargs, tySequence, tyString, diff --git a/compiler/semfold.nim b/compiler/semfold.nim index eceb10470..2f495bc7f 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -619,7 +619,7 @@ proc getConstExpr(m: PSym, n: PNode; g: ModuleGraph): PNode = of mLow: result = newIntNodeT(firstOrd(g.config, n.sons[1].typ), n, g) of mHigh: - if skipTypes(n.sons[1].typ, abstractVar).kind notin + if skipTypes(n.sons[1].typ, abstractVar+{tyUserTypeClassInst}).kind notin {tySequence, tyString, tyCString, tyOpenArray, tyVarargs}: result = newIntNodeT(lastOrd(g.config, skipTypes(n[1].typ, abstractVar)), n, g) else: diff --git a/compiler/types.nim b/compiler/types.nim index 98343c688..a1bdc7730 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -615,7 +615,7 @@ proc firstOrd*(conf: ConfigRef; t: PType): BiggestInt = else: assert(t.n.sons[0].kind == nkSym) result = t.n.sons[0].sym.position - of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tyStatic, tyInferred: + of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tyStatic, tyInferred, tyUserTypeClassInst: result = firstOrd(conf, lastSon(t)) of tyOrdinal: if t.len > 0: result = firstOrd(conf, lastSon(t)) diff --git a/tests/concepts/treversable.nim b/tests/concepts/treversable.nim new file mode 100644 index 000000000..6ebc077d9 --- /dev/null +++ b/tests/concepts/treversable.nim @@ -0,0 +1,31 @@ +# issue 7705, 7703, 7702 +discard """ + output: ''' +z +e + ''' +""" + +type + Reversable*[T] = concept a + a[int] is T + a.high is int + a.len is int + a.low is int + +proc get[T](s: Reversable[T], n: int): T = + s[n] + +proc hi[T](s: Reversable[T]): int = + s.high + +proc lo[T](s: Reversable[T]): int = + s.low + +iterator reverse*[T](s: Reversable[T]): T = + assert hi(s) - lo(s) == len(s) - 1 + for z in hi(s).countdown(lo(s)): + yield s.get(z) + +for s in @["e", "z"].reverse: + echo s -- cgit 1.4.1-2-gfad0 From 95436893061176d51b1c11fdf5418b3ed17a8455 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Sun, 24 Jun 2018 18:27:40 +0200 Subject: Make `static` blocks introduce their own scope Treat the static block as a normal block, don't leak any identifier in the outer scope. Fixes #5958 --- compiler/semstmts.nim | 2 ++ tests/global/t5958.nim | 9 +++++++++ 2 files changed, 11 insertions(+) create mode 100644 tests/global/t5958.nim (limited to 'tests') diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 292238dc9..fe4318de5 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1772,7 +1772,9 @@ proc semStaticStmt(c: PContext, n: PNode): PNode = #echo "semStaticStmt" #writeStackTrace() inc c.inStaticContext + openScope(c) let a = semStmt(c, n.sons[0]) + closeScope(c) dec c.inStaticContext n.sons[0] = a evalStaticStmt(c.module, c.graph, a, c.p.owner) diff --git a/tests/global/t5958.nim b/tests/global/t5958.nim new file mode 100644 index 000000000..5abcad4a9 --- /dev/null +++ b/tests/global/t5958.nim @@ -0,0 +1,9 @@ +discard """ + line: 9 + errormsg: "undeclared identifier: 'a'" +""" + +static: + var a = 1 + +echo a -- cgit 1.4.1-2-gfad0 From f559e62e45b4be227d494e75fe82f571ee410840 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Mon, 25 Jun 2018 15:56:13 +0200 Subject: Adjust some tests to make them pass The non-scoped behaviour of static blocks was exploited by those tests, replace all the variables declared whithin one with compileTime marked ones. --- tests/pragmas/treorder.nim | 3 +-- tests/vm/tnimnode.nim | 20 +++++++++----------- tests/vm/tvmmisc.nim | 4 ++-- 3 files changed, 12 insertions(+), 15 deletions(-) (limited to 'tests') diff --git a/tests/pragmas/treorder.nim b/tests/pragmas/treorder.nim index 6a6bbff4d..1006af527 100644 --- a/tests/pragmas/treorder.nim +++ b/tests/pragmas/treorder.nim @@ -71,5 +71,4 @@ macro make(arg: untyped): untyped = proc first(i: int): void = make(second) -static: - var ss: string = "" \ No newline at end of file +var ss {.compileTime.}: string = "" \ No newline at end of file diff --git a/tests/vm/tnimnode.nim b/tests/vm/tnimnode.nim index 188bac9bf..4210ab41d 100644 --- a/tests/vm/tnimnode.nim +++ b/tests/vm/tnimnode.nim @@ -4,14 +4,12 @@ proc assertEq(arg0,arg1: string): void = if arg0 != arg1: raiseAssert("strings not equal:\n" & arg0 & "\n" & arg1) -static: - # a simple assignment of stmtList to another variable - var node: NimNode - # an assignment of stmtList into an array - var nodeArray: array[1, NimNode] - # an assignment of stmtList into a seq - var nodeSeq = newSeq[NimNode](2) - +# a simple assignment of stmtList to another variable +var node {.compileTime.}: NimNode +# an assignment of stmtList into an array +var nodeArray {.compileTime.}: array[1, NimNode] +# an assignment of stmtList into a seq +var nodeSeq {.compileTime.} = newSeq[NimNode](2) proc checkNode(arg: NimNode; name: string): void {. compileTime .} = echo "checking ", name @@ -35,10 +33,10 @@ proc checkNode(arg: NimNode; name: string): void {. compileTime .} = echo "OK" -static: - # the root node that is used to generate the Ast - var stmtList: NimNode +# the root node that is used to generate the Ast +var stmtList {.compileTime.}: NimNode +static: stmtList = newStmtList(nnkDiscardStmt.newTree(newEmptyNode())) checkNode(stmtList, "direct construction") diff --git a/tests/vm/tvmmisc.nim b/tests/vm/tvmmisc.nim index 4af824cf4..80f5aeee0 100644 --- a/tests/vm/tvmmisc.nim +++ b/tests/vm/tvmmisc.nim @@ -19,9 +19,9 @@ block: var x = default(type(0)) # #6379 -static: - import algorithm +import algorithm +static: var numArray = [1, 2, 3, 4, -1] numArray.sort(cmp) assert numArray == [-1, 1, 2, 3, 4] -- cgit 1.4.1-2-gfad0 From 236bc06b5f1a1a1fc4a711007aed0b60b6656a4f Mon Sep 17 00:00:00 2001 From: Oscar Nihlgård Date: Tue, 26 Jun 2018 21:18:55 +0200 Subject: Improve vm support for ref types --- compiler/vm.nim | 47 ++++++++++++++++++++++++++--------------------- compiler/vmdef.nim | 1 - tests/vm/tnilref.nim | 7 +++++++ tests/vm/tref.nim | 47 ++++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 79 insertions(+), 23 deletions(-) create mode 100644 tests/vm/tnilref.nim (limited to 'tests') diff --git a/compiler/vm.nim b/compiler/vm.nim index 3e33e8256..b16eb0fd4 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -204,22 +204,14 @@ proc asgnComplex(x: var TFullReg, y: TFullReg) = of rkRegisterAddr: x.regAddr = y.regAddr of rkNodeAddr: x.nodeAddr = y.nodeAddr -proc putIntoNode(n: var PNode; x: TFullReg) = +proc writeField(n: var PNode, x: TFullReg) = case x.kind of rkNone: discard of rkInt: n.intVal = x.intVal of rkFloat: n.floatVal = x.floatVal - of rkNode: - if nfIsRef in x.node.flags: - n = x.node - else: - let destIsRef = nfIsRef in n.flags - n[] = x.node[] - # Ref-ness must be kept for the destination - if destIsRef: - n.flags.incl nfIsRef - of rkRegisterAddr: putIntoNode(n, x.regAddr[]) - of rkNodeAddr: n[] = x.nodeAddr[][] + of rkNode: n = x.node + of rkRegisterAddr: writeField(n, x.regAddr[]) + of rkNodeAddr: n = x.nodeAddr[] proc putIntoReg(dest: var TFullReg; n: PNode) = case n.kind @@ -498,9 +490,6 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = asgnComplex(regs[ra], regs[instr.regB]) of opcAsgnRef: asgnRef(regs[ra], regs[instr.regB]) - of opcRegToNode: - decodeB(rkNode) - putIntoNode(regs[ra].node, regs[rb]) of opcNodeToReg: let ra = instr.regA let rb = instr.regB @@ -559,7 +548,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = else: stackTrace(c, tos, pc, errIndexOutOfBounds) elif idx <% arr.len: - putIntoNode(arr.sons[idx], regs[rc]) + writeField(arr.sons[idx], regs[rc]) else: stackTrace(c, tos, pc, errIndexOutOfBounds) of opcLdObj: @@ -579,9 +568,9 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = if dest.kind == nkNilLit: stackTrace(c, tos, pc, errNilAccess) elif dest.sons[shiftedRb].kind == nkExprColonExpr: - putIntoNode(dest.sons[shiftedRb].sons[1], regs[rc]) + writeField(dest.sons[shiftedRb].sons[1], regs[rc]) else: - putIntoNode(dest.sons[shiftedRb], regs[rc]) + writeField(dest.sons[shiftedRb], regs[rc]) of opcWrStrIdx: decodeBC(rkNode) let idx = regs[rb].intVal.int @@ -624,9 +613,24 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = let ra = instr.regA let rc = instr.regC case regs[ra].kind - of rkNodeAddr: putIntoNode(regs[ra].nodeAddr[], regs[rc]) + of rkNodeAddr: + # XXX: Workaround for vmgen bug: + let n = regs[rc].regToNode + if (nfIsRef in regs[ra].nodeAddr[].flags or + regs[ra].nodeAddr[].kind == nkNilLit) and nfIsRef notin n.flags: + if regs[ra].nodeAddr[].kind == nkNilLit: + stackTrace(c, tos, pc, errNilAccess) + regs[ra].nodeAddr[][] = n[] + regs[ra].nodeAddr[].flags.incl nfIsRef + else: + regs[ra].nodeAddr[] = n of rkRegisterAddr: regs[ra].regAddr[] = regs[rc] - of rkNode: putIntoNode(regs[ra].node, regs[rc]) + of rkNode: + if regs[ra].node.kind == nkNilLit: + stackTrace(c, tos, pc, errNilAccess) + assert nfIsRef in regs[ra].node.flags + regs[ra].node[] = regs[rc].regToNode[] + regs[ra].node.flags.incl nfIsRef else: stackTrace(c, tos, pc, errNilAccess) of opcAddInt: decodeBC(rkInt) @@ -1211,6 +1215,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcNBindSym: decodeBx(rkNode) regs[ra].node = copyTree(c.constants.sons[rbx]) + regs[ra].node.flags.incl nfIsRef of opcNChild: decodeBC(rkNode) let idx = regs[rc].intVal.int @@ -1572,7 +1577,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = var sym = newSym(k.TSymKind, getIdent(c.cache, name), c.module.owner, c.debug[pc]) incl(sym.flags, sfGenSym) regs[ra].node = newSymNode(sym) - + regs[ra].node.flags.incl nfIsRef of opcNccValue: decodeB(rkInt) let destKey = regs[rb].node.strVal diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim index cec61ade5..f7466b392 100644 --- a/compiler/vmdef.nim +++ b/compiler/vmdef.nim @@ -36,7 +36,6 @@ type opcAsgnFloat, opcAsgnRef, opcAsgnComplex, - opcRegToNode, opcNodeToReg, opcLdArr, # a = b[c] diff --git a/tests/vm/tnilref.nim b/tests/vm/tnilref.nim new file mode 100644 index 000000000..5e27cf0cb --- /dev/null +++ b/tests/vm/tnilref.nim @@ -0,0 +1,7 @@ +discard """ + errormsg: "attempt to access a nil address" +""" + +static: + var s: ref int + s[] = 1 \ No newline at end of file diff --git a/tests/vm/tref.nim b/tests/vm/tref.nim index 517a67fb0..27b7bf313 100644 --- a/tests/vm/tref.nim +++ b/tests/vm/tref.nim @@ -9,4 +9,49 @@ static: b[5] = 'c' doAssert a[] == "Hellocworld" - doAssert b[] == "Hellocworld" \ No newline at end of file + doAssert b[] == "Hellocworld" + + proc notGlobal() = + var + a: ref string + b: ref string + new a + + a[] = "Hello world" + b = a + + b[5] = 'c' + doAssert a[] == "Hellocworld" + doAssert b[] == "Hellocworld" + notGlobal() + +static: # bug 6081 + block: + type Obj = object + field: ref int + var i: ref int + new(i) + var r = Obj(field: i) + var rr = r + r.field = nil + doAssert rr.field != nil + + proc foo() = # Proc to avoid special global logic + var s: seq[ref int] + var i: ref int + new(i) + s.add(i) + var head = s[0] + s[0] = nil + doAssert head != nil + + foo() + +static: + + block: # global alias + var s: ref int + new(s) + var ss = s + s[] = 1 + doAssert ss[] == 1 \ No newline at end of file -- cgit 1.4.1-2-gfad0