From 170cb4f4cbe734d6fa30415ddfea725cbe35f84e Mon Sep 17 00:00:00 2001 From: Federico Ceratto Date: Wed, 21 Aug 2019 19:01:17 +0100 Subject: [PATCH 01/61] Update OpenSSL example (#11896) * Update OpenSSL example Fix privkey filename. Bump up RSA key size. Add ECDSA example. --- lib/pure/net.nim | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/pure/net.nim b/lib/pure/net.nim index 8c4229c88e..c337680f1d 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -530,8 +530,12 @@ when defineSsl: ## ## The last two parameters specify the certificate file path and the key file ## path, a server socket will most likely not work without these. + ## ## Certificates can be generated using the following command: - ## ``openssl req -x509 -nodes -days 365 -newkey rsa:1024 -keyout mycert.pem -out mycert.pem``. + ## - ``openssl req -x509 -nodes -days 365 -newkey rsa:4096 -keyout mykey.pem -out mycert.pem`` + ## or using ECDSA: + ## - ``openssl ecparam -out mykey.pem -name secp256k1 -genkey`` + ## - ``openssl req -new -key mykey.pem -x509 -nodes -days 365 -out mycert.pem`` var newCTX: SSL_CTX case protVersion of protSSLv23: From 9eee15f6683ad50a881adbfa7bfd4ca9b2b717bc Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 21 Aug 2019 13:22:39 +0200 Subject: [PATCH 02/61] thavlak: code style changes --- tests/gc/thavlak.nim | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/tests/gc/thavlak.nim b/tests/gc/thavlak.nim index 2d8df7c1a3..a25421f104 100644 --- a/tests/gc/thavlak.nim +++ b/tests/gc/thavlak.nim @@ -110,7 +110,7 @@ proc setNestingLevel(self: ref SimpleLoop, level: int) = self.nestingLevel = level if level == 0: self.isRoot = true -var loop_counter: int = 0 +var loopCounter: int = 0 type Lsg = object @@ -119,8 +119,8 @@ type proc createNewLoop(self: var Lsg): ref SimpleLoop = result = newSimpleLoop() - loop_counter += 1 - result.counter = loop_counter + loopCounter += 1 + result.counter = loopCounter proc addLoop(self: var Lsg, l: ref SimpleLoop) = self.loops.add l @@ -170,13 +170,13 @@ proc union(self: ref UnionFindNode, unionFindNode: ref UnionFindNode) = const - BB_TOP = 0 # uninitialized - BB_NONHEADER = 1 # a regular BB - BB_REDUCIBLE = 2 # reducible loop - BB_SELF = 3 # single BB loop - BB_IRREDUCIBLE = 4 # irreducible loop - BB_DEAD = 5 # a dead BB - BB_LAST = 6 # Sentinel + BB_TOP = 0 # uninitialized + BB_NONHEADER = 1 # a regular BB + BB_REDUCIBLE = 2 # reducible loop + BB_SELF = 3 # single BB loop + BB_IRREDUCIBLE = 4 # irreducible loop + BB_DEAD = 5 # a dead BB + BB_LAST = 6 # Sentinel # # Marker for uninitialized nodes. UNVISITED = -1 @@ -196,7 +196,9 @@ proc newHavlakLoopFinder(cfg: Cfg, lsg: Lsg): HavlakLoopFinder = proc isAncestor(w: int, v: int, last: seq[int]): bool = w <= v and v <= last[w] -proc dfs(currentNode: ref BasicBlock, nodes: var seq[ref UnionFindNode], number: var Table[ref BasicBlock, int], last: var seq[int], current: int): int = +proc dfs(currentNode: ref BasicBlock, nodes: var seq[ref UnionFindNode], + number: var Table[ref BasicBlock, int], + last: var seq[int], current: int): int = var stack = @[(currentNode, current)] while stack.len > 0: let (currentNode, current) = stack.pop() @@ -215,13 +217,13 @@ proc findLoops(self: var HavlakLoopFinder): int = if startNode == nil: return 0 var size = self.cfg.getNumNodes - var nonBackPreds = newSeq[HashSet[int]]() - var backPreds = newSeq[seq[int]]() - var number = initTable[ref BasicBlock, int]() - var header = newSeq[int](size) - var types = newSeq[int](size) - var last = newSeq[int](size) - var nodes = newSeq[ref UnionFindNode]() + var nonBackPreds = newSeq[HashSet[int]]() + var backPreds = newSeq[seq[int]]() + var number = initTable[ref BasicBlock, int]() + var header = newSeq[int](size) + var types = newSeq[int](size) + var last = newSeq[int](size) + var nodes = newSeq[ref UnionFindNode]() for i in 1..size: nonBackPreds.add initSet[int](1) From 085fbcea6f054ec638250853c3e85a0e51c3f3bd Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 22 Aug 2019 18:57:41 +0200 Subject: [PATCH 03/61] fixes #10854 --- compiler/semexprs.nim | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index b80c4c8cb6..8048111cde 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -2398,11 +2398,9 @@ proc semTupleFieldsConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = typ.n = newNodeI(nkRecList, n.info) # nkIdentDefs var ids = initIntSet() for i in 0 ..< sonsLen(n): - if n[i].kind != nkExprColonExpr or n[i][0].kind notin {nkSym, nkIdent}: + if n[i].kind != nkExprColonExpr: illFormedAst(n.sons[i], c.config) - var id: PIdent - if n.sons[i].sons[0].kind == nkIdent: id = n.sons[i].sons[0].ident - else: id = n.sons[i].sons[0].sym.name + let id = considerQuotedIdent(c, n[i][0]) if containsOrIncl(ids, id.id): localError(c.config, n.sons[i].info, errFieldInitTwice % id.s) n.sons[i].sons[1] = semExprWithType(c, n.sons[i].sons[1], From 9aad99bdfd90394634b97a1c9b005d1c86c9c7e2 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 22 Aug 2019 19:08:35 +0200 Subject: [PATCH 04/61] closes #7117 --- tests/template/template_various.nim | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/tests/template/template_various.nim b/tests/template/template_various.nim index 36fa42050e..ac7e91fa2c 100644 --- a/tests/template/template_various.nim +++ b/tests/template/template_various.nim @@ -9,6 +9,7 @@ bar7 10 4true 132 +20 ''' """ @@ -192,7 +193,7 @@ block ttempl: block ttempl4: - template `:=`(name, val: untyped): typed = + template `:=`(name, val: untyped) = var name = val ha := 1 * 4 @@ -211,7 +212,7 @@ block ttempl5: discard # Call parse_to_close - template get_next_ident: typed = + template get_next_ident = discard "{something}".parse_to_close(0, open = '{', close = '}') get_next_ident() @@ -231,3 +232,16 @@ block ttempl5: block templreturntype: template `=~` (a: int, b: int): bool = false var foo = 2 =~ 3 + +# bug #7117 +template parse9(body: untyped): untyped = + + template val9(arg: string): int {.inject.} = + var b: bool + if b: 10 + else: 20 + + body + +parse9: + echo val9("1") From f28a47ea7b9c579b172653c15dc2cc054adf599a Mon Sep 17 00:00:00 2001 From: Palash Nigam Date: Fri, 23 Aug 2019 11:12:33 +0530 Subject: [PATCH 05/61] fixes #11834 (#12000) --- lib/pure/complex.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/complex.nim b/lib/pure/complex.nim index 442bf62dbb..7c669cd6db 100644 --- a/lib/pure/complex.nim +++ b/lib/pure/complex.nim @@ -372,7 +372,7 @@ when isMainModule: doAssert((a+b) == z) doAssert((a+b) =~ 0.0) doAssert((a/b) == m1) - doAssert((1.0/a) == complex(0.2, -0.4)) + doAssert((1.0/a) =~ complex(0.2, -0.4)) doAssert((a*b) == complex(3.0, -4.0)) doAssert(10.0*a == tt) doAssert(a*10.0 == tt) From 806f3b592f87f71ce8fb9a01ce024c4423efe108 Mon Sep 17 00:00:00 2001 From: lenoil98 Date: Fri, 23 Aug 2019 07:50:50 -0400 Subject: [PATCH 06/61] Add build support for FreeBSD PowerPC64 --- tools/niminst/buildsh.nimf | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tools/niminst/buildsh.nimf b/tools/niminst/buildsh.nimf index 9fa0c8d309..464c545942 100644 --- a/tools/niminst/buildsh.nimf +++ b/tools/niminst/buildsh.nimf @@ -154,7 +154,14 @@ case $ucpu in fi mycpu="powerpc64" ;; *power*|*ppc* ) - mycpu="powerpc" ;; + if [ "$myos" = "freebsd" ] ; then + COMP_FLAGS="$COMP_FLAGS -m64" + LINK_FLAGS="$LINK_FLAGS -m64" + mycpu=`uname -p` + else + mycpu="powerpc" + fi + ;; *ia64*) mycpu="ia64" ;; *m68k*) From e1539c7344622cd7e604546a7205d6288e40ee4d Mon Sep 17 00:00:00 2001 From: lenoil98 Date: Fri, 23 Aug 2019 07:54:02 -0400 Subject: [PATCH 07/61] Add build support for FreeBSD PowerPC64 --- tools/niminst/makefile.nimf | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tools/niminst/makefile.nimf b/tools/niminst/makefile.nimf index 0f2c1920e3..ad9d55c03f 100644 --- a/tools/niminst/makefile.nimf +++ b/tools/niminst/makefile.nimf @@ -120,6 +120,11 @@ ifeq ($(ucpu),ppc64) endif ifeq ($(ucpu),powerpc) mycpu = powerpc + ifeq ($(myos),freebsd) + mycpu = $(shell sh -c 'uname -p | tr "[:upper:]" "[:lower:]"') + CFLAGS += -m64 + LDFLAGS += -m64 + endif endif ifeq ($(ucpu),ppc) mycpu = ppc From 2212a9045883ceb66fac1a738347d846dc7e56ec Mon Sep 17 00:00:00 2001 From: lenoil98 Date: Fri, 23 Aug 2019 07:56:56 -0400 Subject: [PATCH 08/61] Add build support for FreeBSD PowerPC64 --- compiler/installer.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/installer.ini b/compiler/installer.ini index 909ba28dbf..e1ebbb8968 100644 --- a/compiler/installer.ini +++ b/compiler/installer.ini @@ -9,7 +9,7 @@ Platforms: """ linux: i386;ia64;alpha;amd64;powerpc64;arm;sparc;sparc64;m68k;mips;mipsel;mips64;mips64el;powerpc;powerpc64el;arm64;riscv64 macosx: i386;amd64;powerpc64 solaris: i386;amd64;sparc;sparc64 - freebsd: i386;amd64 + freebsd: i386;amd64;powerpc64 netbsd: i386;amd64 openbsd: i386;amd64 dragonfly: i386;amd64 From b07694cd90ab7c6eb4660971ddb818b461d4eed8 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 23 Aug 2019 16:15:02 +0200 Subject: [PATCH 09/61] new gensym handling (#11985) * new .gensym implementation * make astspec test green again * introduce a --useVersion switch to group compatibility switches * fixes #10180 * fixes #11494 * fixes #11483 * object constructor fields and named parameters are also not gensym'ed * disabled broken package --- changelog.md | 7 + compiler/commands.nim | 9 ++ compiler/evaltempl.nim | 14 +- compiler/main.nim | 2 +- compiler/options.nim | 1 + compiler/semexprs.nim | 3 +- compiler/semtempl.nim | 62 +++++--- compiler/suggest.nim | 6 +- compiler/vm.nim | 2 +- doc/advopt.txt | 2 +- doc/manual.rst | 39 +++++ lib/core/locks.nim | 4 +- lib/system/alloc.nim | 12 +- testament/important_packages.nim | 8 +- tests/astspec/tastspec.nim | 217 ++++++++++++++++++--------- tests/template/tmore_regressions.nim | 44 ++++++ tests/template/tparams_gensymed.nim | 15 ++ tests/template/tredefinition.nim | 13 ++ 18 files changed, 346 insertions(+), 114 deletions(-) create mode 100644 tests/template/tmore_regressions.nim create mode 100644 tests/template/tredefinition.nim diff --git a/changelog.md b/changelog.md index 41d371b601..f29a4c69db 100644 --- a/changelog.md +++ b/changelog.md @@ -10,6 +10,13 @@ to UTF-8. Use the new switch `-d:nimDontSetUtf8CodePage` to disable this feature. +- The language definition and compiler are now stricter about ``gensym``'ed + symbols in hygienic templates. See the section in the + [manual](https://nim-lang.org/docs/manual.html#templates-hygiene-in-templates) + for further details. Use the compiler switch `--useVersion:0.19` for a + transition period. + + ### Breaking changes in the standard library diff --git a/compiler/commands.nim b/compiler/commands.nim index 3874ea38fb..662df9c84d 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -788,6 +788,15 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; of "expandmacro": expectArg(conf, switch, arg, pass, info) conf.macrosToExpand[arg] = "T" + of "useversion": + expectArg(conf, switch, arg, pass, info) + case arg + of "0.19": + conf.globalOptions.incl optNimV019 + of "1.0": + discard "the default" + else: + localError(conf, info, "unknown Nim version; currently supported values are: {0.19, 1.0}") of "": conf.projectName = "-" else: diff --git a/compiler/evaltempl.nim b/compiler/evaltempl.nim index d3d3e5f779..d941f6c464 100644 --- a/compiler/evaltempl.nim +++ b/compiler/evaltempl.nim @@ -10,7 +10,7 @@ ## Template evaluation engine. Now hygienic. import - strutils, options, ast, astalgo, msgs, renderer, lineinfos + strutils, options, ast, astalgo, msgs, renderer, lineinfos, idents type TemplCtx = object @@ -20,6 +20,7 @@ type mapping: TIdTable # every gensym'ed symbol needs to be mapped to some # new symbol config: ConfigRef + ic: IdentCache proc copyNode(ctx: TemplCtx, a, b: PNode): PNode = result = copyNode(a) @@ -52,7 +53,11 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) = #if x.kind == skParam and x.owner.kind == skModule: # internalAssert c.config, false idTablePut(c.mapping, s, x) - result.add newSymNode(x, if c.instLines: actual.info else: templ.info) + if sfGenSym in s.flags and optNimV019 notin c.config.globalOptions: + result.add newIdentNode(getIdent(c.ic, x.name.s & "`gensym" & $x.id), + if c.instLines: actual.info else: templ.info) + else: + result.add newSymNode(x, if c.instLines: actual.info else: templ.info) else: result.add copyNode(c, templ, actual) of nkNone..nkIdent, nkType..nkNilLit: # atom @@ -160,7 +165,9 @@ proc wrapInComesFrom*(info: TLineInfo; sym: PSym; res: PNode): PNode = result.typ = res.typ proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym; - conf: ConfigRef; fromHlo=false): PNode = + conf: ConfigRef; + ic: IdentCache; + fromHlo=false): PNode = inc(conf.evalTemplateCounter) if conf.evalTemplateCounter > evalTemplateLimit: globalError(conf, n.info, errTemplateInstantiationTooNested) @@ -172,6 +179,7 @@ proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym; ctx.owner = tmpl ctx.genSymOwner = genSymOwner ctx.config = conf + ctx.ic = ic initIdTable(ctx.mapping) let body = tmpl.getBody diff --git a/compiler/main.nim b/compiler/main.nim index 8cd8a52d4e..877b82dd93 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -18,7 +18,7 @@ import sem, idents, passes, extccomp, cgen, json, nversion, platform, nimconf, passaux, depends, vm, idgen, - parser, modules, + modules, modulegraphs, tables, rod, lineinfos, pathutils when not defined(leanCompiler): diff --git a/compiler/options.nim b/compiler/options.nim index 75eec47560..52ecd61bc4 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -84,6 +84,7 @@ type # please make sure we have under 32 options optDynlibOverrideAll optNimV2 optMultiMethods + optNimV019 TGlobalOptions* = set[TGlobalOption] diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 8048111cde..57d9aae4bc 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -30,7 +30,7 @@ proc semTemplateExpr(c: PContext, n: PNode, s: PSym, # Note: This is n.info on purpose. It prevents template from creating an info # context when called from an another template pushInfoContext(c.config, n.info, s.detailedInfo) - result = evalTemplate(n, s, getCurrOwner(c), c.config, efFromHlo in flags) + result = evalTemplate(n, s, getCurrOwner(c), c.config, c.cache, efFromHlo in flags) if efNoSemCheck notin flags: result = semAfterMacroCall(c, n, result, s, flags) popInfoContext(c.config) @@ -1236,6 +1236,7 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode = result = newSymNode(s, n.info) else: let info = getCallLineInfo(n) + #if efInCall notin flags: markUsed(c, info, s) onUse(info, s) result = newSymNode(s, info) diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index ddc0667e44..907d2174e4 100644 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -47,7 +47,8 @@ type TSymChoiceRule = enum scClosed, scOpen, scForceOpen -proc symChoice(c: PContext, n: PNode, s: PSym, r: TSymChoiceRule): PNode = +proc symChoice(c: PContext, n: PNode, s: PSym, r: TSymChoiceRule; + isField = false): PNode = var a: PSym o: TOverloadIter @@ -63,9 +64,12 @@ proc symChoice(c: PContext, n: PNode, s: PSym, r: TSymChoiceRule): PNode = # XXX this makes more sense but breaks bootstrapping for now: # (s.kind notin routineKinds or s.magic != mNone): # for instance 'nextTry' is both in tables.nim and astalgo.nim ... - result = newSymNode(s, info) - markUsed(c, info, s) - onUse(info, s) + if not isField or sfGenSym notin s.flags: + result = newSymNode(s, info) + markUsed(c, info, s) + onUse(info, s) + else: + result = n else: # semantic checking requires a type; ``fitNode`` deals with it # appropriately @@ -74,7 +78,7 @@ proc symChoice(c: PContext, n: PNode, s: PSym, r: TSymChoiceRule): PNode = result = newNodeIT(kind, info, newTypeS(tyNone, c)) a = initOverloadIter(o, c, n) while a != nil: - if a.kind != skModule: + if a.kind != skModule and (not isField or sfGenSym notin s.flags): incl(a.flags, sfUsed) addSon(result, newSymNode(a, info)) onUse(info, a) @@ -119,6 +123,7 @@ type owner: PSym cursorInBody: bool # only for nimsuggest scopeN: int + noGenSym: int template withBracketExpr(ctx, x, body: untyped) = body @@ -228,7 +233,7 @@ proc addLocalDecl(c: var TemplCtx, n: var PNode, k: TSymKind) = else: replaceIdentBySym(c.c, n, ident) -proc semTemplSymbol(c: PContext, n: PNode, s: PSym): PNode = +proc semTemplSymbol(c: PContext, n: PNode, s: PSym; isField: bool): PNode = incl(s.flags, sfUsed) # we do not call onUse here, as the identifier is not really # resolved here. We will fixup the used identifiers later. @@ -237,15 +242,18 @@ proc semTemplSymbol(c: PContext, n: PNode, s: PSym): PNode = # Introduced in this pass! Leave it as an identifier. result = n of OverloadableSyms: - result = symChoice(c, n, s, scOpen) + result = symChoice(c, n, s, scOpen, isField) of skGenericParam: - result = newSymNodeTypeDesc(s, n.info) + if isField: result = n + else: result = newSymNodeTypeDesc(s, n.info) of skParam: result = n of skType: - result = newSymNodeTypeDesc(s, n.info) + if isField: result = n + else: result = newSymNodeTypeDesc(s, n.info) else: - result = newSymNode(s, n.info) + if isField: result = n + else: result = newSymNode(s, n.info) proc semRoutineInTemplName(c: var TemplCtx, n: PNode): PNode = result = n @@ -322,22 +330,23 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode = if n.ident.id in c.toInject: return n let s = qualifiedLookUp(c.c, n, {}) if s != nil: - if s.owner == c.owner and s.kind == skParam: + if s.owner == c.owner and s.kind == skParam and + (sfGenSym notin s.flags or c.noGenSym == 0): incl(s.flags, sfUsed) result = newSymNode(s, n.info) onUse(n.info, s) elif contains(c.toBind, s.id): - result = symChoice(c.c, n, s, scClosed) + result = symChoice(c.c, n, s, scClosed, c.noGenSym > 0) elif contains(c.toMixin, s.name.id): - result = symChoice(c.c, n, s, scForceOpen) - elif s.owner == c.owner and sfGenSym in s.flags: + result = symChoice(c.c, n, s, scForceOpen, c.noGenSym > 0) + elif s.owner == c.owner and sfGenSym in s.flags and c.noGenSym == 0: # template tmp[T](x: var seq[T]) = # var yz: T incl(s.flags, sfUsed) result = newSymNode(s, n.info) onUse(n.info, s) else: - result = semTemplSymbol(c.c, n, s) + result = semTemplSymbol(c.c, n, s, c.noGenSym > 0) of nkBind: result = semTemplBody(c, n.sons[0]) of nkBindStmt: @@ -524,12 +533,27 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode = onUse(n.info, s) return newSymNode(s, n.info) elif contains(c.toBind, s.id): - return symChoice(c.c, n, s, scClosed) + return symChoice(c.c, n, s, scClosed, c.noGenSym > 0) elif contains(c.toMixin, s.name.id): - return symChoice(c.c, n, s, scForceOpen) + return symChoice(c.c, n, s, scForceOpen, c.noGenSym > 0) else: - return symChoice(c.c, n, s, scOpen) - result = semTemplBodySons(c, n) + return symChoice(c.c, n, s, scOpen, c.noGenSym > 0) + if n.kind == nkDotExpr: + result = n + result.sons[0] = semTemplBody(c, n.sons[0]) + inc c.noGenSym + result.sons[1] = semTemplBody(c, n.sons[1]) + dec c.noGenSym + else: + result = semTemplBodySons(c, n) + of nkExprColonExpr, nkExprEqExpr: + if n.len == 2: + inc c.noGenSym + result.sons[0] = semTemplBody(c, n.sons[0]) + dec c.noGenSym + result.sons[1] = semTemplBody(c, n.sons[1]) + else: + result = semTemplBodySons(c, n) else: result = semTemplBodySons(c, n) diff --git a/compiler/suggest.nim b/compiler/suggest.nim index dc01916d1d..9680bc8465 100644 --- a/compiler/suggest.nim +++ b/compiler/suggest.nim @@ -261,15 +261,15 @@ proc getQuality(s: PSym): range[0..100] = if exp.kind in {tyUntyped, tyTyped, tyGenericParam, tyAnything}: return 50 return 100 -template wholeSymTab(cond, section: untyped) = +template wholeSymTab(cond, section: untyped) {.dirty.} = var isLocal = true var scopeN = 0 for scope in walkScopes(c.currentScope): if scope == c.topLevelScope: isLocal = false dec scopeN for item in scope.symbols: - let it {.inject.} = item - var pm {.inject.}: PrefixMatch + let it = item + var pm: PrefixMatch if cond: outputs.add(symToSuggest(c.config, it, isLocal = isLocal, section, info, getQuality(it), pm, c.inTypeContext > 0, scopeN)) diff --git a/compiler/vm.nim b/compiler/vm.nim index 75f6cc1c34..31dec418fd 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -1137,7 +1137,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = let node = regs[rb+i].regToNode node.info = c.debug[pc] macroCall.add(node) - var a = evalTemplate(macroCall, prc, genSymOwner, c.config) + var a = evalTemplate(macroCall, prc, genSymOwner, c.config, c.cache) if a.kind == nkStmtList and a.len == 1: a = a[0] a.recSetFlagIsRef ensureKind(rkNode) diff --git a/doc/advopt.txt b/doc/advopt.txt index f5359c0c07..e3d7f018c4 100644 --- a/doc/advopt.txt +++ b/doc/advopt.txt @@ -122,7 +122,7 @@ Advanced options: enable experimental language feature --legacy:$2 enable obsolete/legacy language feature - legacy code. + --useVersion:0.19|1.0 emulate Nim version X of the Nim compiler --newruntime use an alternative runtime that uses destructors and that uses a shared heap via -d:useMalloc --profiler:on|off enable profiling; requires `import nimprof`, and diff --git a/doc/manual.rst b/doc/manual.rst index e1fb2fa59e..7dad444233 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -4899,6 +4899,45 @@ no semantics outside of a template definition and cannot be abstracted over: To get rid of hygiene in templates, one can use the `dirty`:idx: pragma for a template. ``inject`` and ``gensym`` have no effect in ``dirty`` templates. +``gensym``'ed symbols cannot be used as ``field`` in the ``x.field`` syntax. +Nor can they be used in the ``ObjectConstruction(field: value)`` +and ``namedParameterCall(field = value)`` syntactic constructs. + +The reason for this is that code like + +.. code-block:: nim + :test: "nim c $1" + + type + T = object + f: int + + template tmp(x: T) = + let f = 34 + echo x.f, T(f: 4) + + +should work as expected. + +However, this means that the method call syntax is not available for +``gensym``'ed symbols: + +.. code-block:: nim + :test: "nim c $1" + :status: 1 + + template tmp(x) = + type + T {.gensym.} = int + + echo x.T # invalid: instead use: 'echo T(x)'. + + tmp(12) + + +**Note**: The Nim compiler prior to version 1 was more lenient about this +requirement. Use the ``--useVersion:0.19`` switch for a transition period. + Limitations of the method call syntax diff --git a/lib/core/locks.nim b/lib/core/locks.nim index d6d579ba07..0143957ce3 100644 --- a/lib/core/locks.nim +++ b/lib/core/locks.nim @@ -60,11 +60,11 @@ template withLock*(a: Lock, body: untyped) = ## Acquires the given lock, executes the statements in body and ## releases the lock after the statements finish executing. mixin acquire, release - a.acquire() + acquire(a) {.locks: [a].}: try: body finally: - a.release() + release(a) {.pop.} diff --git a/lib/system/alloc.nim b/lib/system/alloc.nim index 9c47d9de99..efbd95089c 100644 --- a/lib/system/alloc.nim +++ b/lib/system/alloc.nim @@ -984,7 +984,7 @@ when defined(nimTypeNames): # ---------------------- thread memory region ------------------------------- -template instantiateForRegion(allocator: untyped) = +template instantiateForRegion(allocator: untyped) {.dirty.} = {.push stackTrace: off.} when defined(fulldebug): @@ -1006,8 +1006,8 @@ template instantiateForRegion(allocator: untyped) = proc dealloc(p: pointer) = dealloc(allocator, p) - proc realloc(p: pointer, newsize: Natural): pointer = - result = realloc(allocator, p, newsize) + proc realloc(p: pointer, newSize: Natural): pointer = + result = realloc(allocator, p, newSize) when false: proc countFreeMem(): int = @@ -1054,13 +1054,13 @@ template instantiateForRegion(allocator: untyped) = else: dealloc(p) - proc reallocShared(p: pointer, newsize: Natural): pointer = + proc reallocShared(p: pointer, newSize: Natural): pointer = when hasThreadSupport: acquireSys(heapLock) - result = realloc(sharedHeap, p, newsize) + result = realloc(sharedHeap, p, newSize) releaseSys(heapLock) else: - result = realloc(p, newsize) + result = realloc(p, newSize) when hasThreadSupport: template sharedMemStatsShared(v: int) = diff --git a/testament/important_packages.nim b/testament/important_packages.nim index 93aeb1bbca..a96310ee50 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -5,9 +5,9 @@ template pkg(name: string; cmd = "nimble test"; hasDeps = false; url = ""): unty var packages*: seq[tuple[name, cmd: string; hasDeps: bool; url: string]] = @[] -pkg "argparse" +#pkg "argparse" pkg "arraymancer", "nim c -r src/arraymancer.nim", true -pkg "ast_pattern_matching", "nim c -r tests/test1.nim" +pkg "ast_pattern_matching", "nim c -r --useVersion=0.19 tests/test1.nim" pkg "binaryheap", "nim c -r binaryheap.nim" pkg "blscurve", "", true pkg "bncurve", "", true @@ -61,7 +61,7 @@ pkg "npeg" pkg "ormin", "nim c -o:orminn ormin.nim", true pkg "parsetoml" pkg "patty" -pkg "plotly", "nim c examples/all.nim", true +pkg "plotly", "nim c --useVersion:0.19 examples/all.nim", true pkg "protobuf", "nim c -o:protobuff -r src/protobuf.nim", true pkg "regex", "nim c src/regex", true pkg "result", "nim c -r result.nim" @@ -71,7 +71,7 @@ pkg "sdl2_nim", "nim c -r sdl2/sdl.nim" pkg "snip", "", false, "https://github.com/genotrance/snip" pkg "stint", "nim c -o:stintt -r stint.nim" pkg "strunicode", "nim c -r src/strunicode.nim", true -pkg "telebot", "nim c -o:tbot -r telebot.nim", true +pkg "telebot", "nim c -o:tbot --useVersion:0.19 -r telebot.nim", true pkg "tiny_sqlite" pkg "unicodedb" pkg "unicodeplus", "", true diff --git a/tests/astspec/tastspec.nim b/tests/astspec/tastspec.nim index 82c32f1302..f9e35804dd 100644 --- a/tests/astspec/tastspec.nim +++ b/tests/astspec/tastspec.nim @@ -6,6 +6,74 @@ action: compile import ../ast_pattern_matching +template expectNimNode(arg: untyped): NimNode = arg + ## This template here is just to be injected by `myquote`, so that + ## a nice error message appears when the captured symbols are not of + ## type `NimNode`. + +proc substitudeComments(symbols, values, n: NimNode): NimNode = + ## substitudes all nodes of kind nnkCommentStmt to parameter + ## symbols. Consumes the argument `n`. + if n.kind == nnkCommentStmt: + values.add newCall(bindSym"newCommentStmtNode", newLit(n.strVal)) + # Gensym doesn't work for parameters. These identifiers won't + # clash unless an argument is constructed to clash here. + symbols.add ident("comment" & $values.len & "_XObBdOnh6meCuJK2smZV") + return symbols[^1] + for i in 0 ..< n.len: + n[i] = substitudeComments(symbols, values, n[i]) + return n + +macro myquote*(args: varargs[untyped]): untyped = + expectMinLen(args, 1) + + # This is a workaround for #10430 where comments are removed in + # template expansions. This workaround lifts all comments + # statements to be arguments of the temporary template. + + let extraCommentSymbols = newNimNode(nnkBracket) + let extraCommentGenExpr = newNimNode(nnkBracket) + let body = substitudeComments( + extraCommentSymbols, extraCommentGenExpr, args[^1] + ) + + let formalParams = nnkFormalParams.newTree(ident"untyped") + for i in 0 ..< args.len-1: + formalParams.add nnkIdentDefs.newTree( + args[i], ident"untyped", newEmptyNode() + ) + for sym in extraCommentSymbols: + formalParams.add nnkIdentDefs.newTree( + sym, ident"untyped", newEmptyNode() + ) + + let templateSym = genSym(nskTemplate) + let templateDef = nnkTemplateDef.newTree( + templateSym, + newEmptyNode(), + newEmptyNode(), + formalParams, + nnkPragma.newTree(ident"dirty"), + newEmptyNode(), + args[^1] + ) + + let templateCall = newCall(templateSym) + for i in 0 ..< args.len-1: + let symName = args[i] + # identifiers and quoted identifiers are allowed. + if symName.kind == nnkAccQuoted: + symName.expectLen 1 + symName[0].expectKind nnkIdent + else: + symName.expectKind nnkIdent + templateCall.add newCall(bindSym"expectNimNode", symName) + for expr in extraCommentGenExpr: + templateCall.add expr + let getAstCall = newCall(bindSym"getAst", templateCall) + result = newStmtList(templateDef, getAstCall) + + macro testAddrAst(arg: typed): bool = arg.expectKind nnkStmtListExpr arg[0].expectKind(nnkVarSection) @@ -49,34 +117,35 @@ static: echo "OK" - testPattern nnkIntLit(intVal = 42) , 42 - testPattern nnkInt8Lit(intVal = 42) , 42'i8 - testPattern nnkInt16Lit(intVal = 42) , 42'i16 - testPattern nnkInt32Lit(intVal = 42) , 42'i32 - testPattern nnkInt64Lit(intVal = 42) , 42'i64 - testPattern nnkUInt8Lit(intVal = 42) , 42'u8 - testPattern nnkUInt16Lit(intVal = 42) , 42'u16 - testPattern nnkUInt32Lit(intVal = 42) , 42'u32 - testPattern nnkUInt64Lit(intVal = 42) , 42'u64 - #testPattern nnkFloat64Lit(floatVal = 42.0) , 42.0 - testPattern nnkFloat32Lit(floatVal = 42.0) , 42.0'f32 - #testPattern nnkFloat64Lit(floatVal = 42.0) , 42.0'f64 - testPattern nnkStrLit(strVal = "abc") , "abc" - testPattern nnkRStrLit(strVal = "abc") , r"abc" - testPattern nnkTripleStrLit(strVal = "abc") , """abc""" - testPattern nnkCharLit(intVal = 32) , ' ' - testPattern nnkNilLit() , nil - testPattern nnkIdent(strVal = "myIdentifier") , myIdentifier + testPattern nnkIntLit(intVal = 42), 42 + testPattern nnkInt8Lit(intVal = 42), 42'i8 + testPattern nnkInt16Lit(intVal = 42), 42'i16 + testPattern nnkInt32Lit(intVal = 42), 42'i32 + testPattern nnkInt64Lit(intVal = 42), 42'i64 + testPattern nnkUInt8Lit(intVal = 42), 42'u8 + testPattern nnkUInt16Lit(intVal = 42), 42'u16 + testPattern nnkUInt32Lit(intVal = 42), 42'u32 + testPattern nnkUInt64Lit(intVal = 42), 42'u64 + #testPattern nnkFloat64Lit(floatVal = 42.0), 42.0 + testPattern nnkFloat32Lit(floatVal = 42.0), 42.0'f32 + #testPattern nnkFloat64Lit(floatVal = 42.0), 42.0'f64 + testPattern nnkStrLit(strVal = "abc"), "abc" + testPattern nnkRStrLit(strVal = "abc"), r"abc" + testPattern nnkTripleStrLit(strVal = "abc"), """abc""" + testPattern nnkCharLit(intVal = 32), ' ' + testPattern nnkNilLit(), nil + testPattern nnkIdent(strVal = "myIdentifier"), myIdentifier + + testPatternFail nnkInt8Lit(intVal = 42), 42'i16 + testPatternFail nnkInt16Lit(intVal = 42), 42'i8 - testPatternFail nnkInt8Lit(intVal = 42) , 42'i16 - testPatternFail nnkInt16Lit(intVal = 42) , 42'i8 # this should be just `block` but it doesn't work that way anymore because of VM. macro scope(arg: untyped): untyped = let procSym = genSym(nskProc) result = quote do: - proc `procSym`(): void {.compileTime.} = + proc `procSym`() {.compileTime.} = `arg` `procSym`() @@ -85,7 +154,7 @@ static: ## Command call scope: - let ast = quote do: + let ast = myquote: echo "abc", "xyz" ast.matchAst: @@ -95,7 +164,7 @@ static: ## Call with ``()`` scope: - let ast = quote do: + let ast = myquote: echo("abc", "xyz") ast.matchAst: @@ -140,7 +209,7 @@ static: scope: - let ast = quote do: + let ast = myquote: ? "xyz" ast.matchAst(err): @@ -155,7 +224,7 @@ static: scope: - let ast = quote do: + let ast = myquote: proc identifier* ast[0].matchAst(err): @@ -185,7 +254,7 @@ static: ## Call with raw string literal scope: - let ast = quote do: + let ast = myquote: echo"abc" @@ -230,7 +299,7 @@ static: scope: - let ast = quote do: + let ast = myquote: cast[T](x) ast.matchAst: @@ -242,7 +311,7 @@ static: scope: - let ast = quote do: + let ast = myquote: x.y ast.matchAst: @@ -264,7 +333,7 @@ static: scope: - let ast = quote do: + let ast = myquote: (1, 2, (3)) ast.matchAst: @@ -276,7 +345,7 @@ static: scope: - let ast = quote do: + let ast = myquote: {1, 2, 3} ast.matchAst: @@ -285,7 +354,7 @@ static: scope: - let ast = quote do: + let ast = myquote: {a: 3, b: 5} ast.matchAst: @@ -300,7 +369,7 @@ static: scope: - let ast = quote do: + let ast = myquote: [1, 2, 3] ast.matchAst: @@ -312,7 +381,7 @@ static: scope: - let ast = quote do: + let ast = myquote: 1..3 ast.matchAst: @@ -328,7 +397,7 @@ static: scope: - let ast = quote do: + let ast = myquote: if cond1: expr1 elif cond2: expr2 else: expr3 ast.matchAst: @@ -343,7 +412,7 @@ static: scope: - let ast = quote do: + let ast = myquote: ## This is a comment ## This is part of the first comment stmt1 @@ -357,12 +426,12 @@ static: ): echo "ok" else: - echo "NOT OK!!!" + echo "warning!" echo ast.treeRepr echo "TEST causes no fail, because of a regression in Nim." scope: - let ast = quote do: + let ast = myquote: {.emit: "#include ".} ast.matchAst: @@ -375,7 +444,7 @@ static: echo "ok" scope: - let ast = quote do: + let ast = myquote: {.pragma: cdeclRename, cdecl.} ast.matchAst: @@ -391,7 +460,7 @@ static: scope: - let ast = quote do: + let ast = myquote: if cond1: stmt1 elif cond2: @@ -413,7 +482,7 @@ static: scope: - let ast = quote do: + let ast = myquote: x = 42 ast.matchAst: @@ -423,7 +492,7 @@ static: scope: - let ast = quote do: + let ast = myquote: stmt1 stmt2 stmt3 @@ -439,7 +508,7 @@ static: scope: - let ast = quote do: + let ast = myquote: case expr1 of expr2, expr3..expr4: stmt1 @@ -464,7 +533,7 @@ static: scope: - let ast = quote do: + let ast = myquote: while expr1: stmt1 @@ -477,7 +546,7 @@ static: scope: - let ast = quote do: + let ast = myquote: for ident1, ident2 in expr1: stmt1 @@ -490,7 +559,7 @@ static: scope: - let ast = quote do: + let ast = myquote: try: stmt1 except e1, e2: @@ -517,7 +586,7 @@ static: scope: - let ast = quote do: + let ast = myquote: return expr1 ast.matchAst: @@ -528,7 +597,7 @@ static: ## Continue statement scope: - let ast = quote do: + let ast = myquote: continue ast.matchAst: @@ -539,7 +608,7 @@ static: scope: - let ast = quote do: + let ast = myquote: break otherLocation ast.matchAst: @@ -550,10 +619,12 @@ static: scope: - let ast = quote do: + template blockStatement {.dirty.} = block name: discard + let ast = getAst(blockStatement()) + ast.matchAst: of nnkBlockStmt(ident"name", nnkStmtList): echo "ok" @@ -562,7 +633,7 @@ static: scope: - let ast = quote do: + let ast = myquote: asm """some asm""" ast.matchAst: @@ -576,7 +647,7 @@ static: scope: - let ast = quote do: + let ast = myquote: import math ast.matchAst: @@ -585,7 +656,7 @@ static: scope: - let ast = quote do: + let ast = myquote: import math except pow ast.matchAst: @@ -594,7 +665,7 @@ static: scope: - let ast = quote do: + let ast = myquote: import strutils as su ast.matchAst: @@ -611,7 +682,7 @@ static: scope: - let ast = quote do: + let ast = myquote: from math import pow ast.matchAst: @@ -622,7 +693,7 @@ static: scope: - let ast = quote do: + let ast = myquote: export unsigned ast.matchAst: @@ -631,7 +702,7 @@ static: scope: - let ast = quote do: + let ast = myquote: export math except pow # we're going to implement our own exponentiation ast.matchAst: @@ -642,7 +713,7 @@ static: scope: - let ast = quote do: + let ast = myquote: include blocks ast.matchAst: @@ -653,7 +724,7 @@ static: scope: - let ast = quote do: + let ast = myquote: var a = 3 ast.matchAst: @@ -670,7 +741,7 @@ static: scope: - let ast = quote do: + let ast = myquote: let a = 3 ast.matchAst: @@ -687,7 +758,7 @@ static: scope: - let ast = quote do: + let ast = myquote: const a = 3 ast.matchAst: @@ -704,7 +775,7 @@ static: scope: - let ast = quote do: + let ast = myquote: type A = int ast.matchAst: @@ -719,7 +790,7 @@ static: scope: - let ast = quote do: + let ast = myquote: type MyInt = distinct int ast.peelOff({nnkTypeSection}).matchAst: @@ -735,7 +806,7 @@ static: scope: - let ast = quote do: + let ast = myquote: type A[T] = expr1 ast.matchAst: @@ -757,7 +828,7 @@ static: scope: - let ast = quote do: + let ast = myquote: type IO = object of RootObj ast.peelOff(nnkTypeSection).matchAst: @@ -840,7 +911,7 @@ static: scope: - let ast = quote do: + let ast = myquote: type X = enum First @@ -853,7 +924,7 @@ static: scope: - let ast = quote do: + let ast = myquote: type Con = concept x,y,z (x & y & z) is string @@ -865,7 +936,7 @@ static: scope: - let astX = quote do: + let astX = myquote: type A[T: static[int]] = object @@ -880,7 +951,7 @@ static: scope: - let ast = quote do: + let ast = myquote: type MyProc[T] = proc(x: T) ast.peelOff({nnkStmtList, nnkTypeSection}).matchAst(err): @@ -952,7 +1023,7 @@ static: proc hello*[T: SomeInteger](x: int = 3, y: float32): int {.inline.} = discard scope: - var ast = quote do: + var ast = myquote: proc foobar(a, b: int): void ast = ast[3] @@ -971,7 +1042,7 @@ static: scope: - let ast = quote do: + let ast = myquote: proc hello(): var int ast[3].matchAst: # subAst @@ -986,7 +1057,7 @@ static: scope: - let ast = quote do: + let ast = myquote: iterator nonsense[T](x: seq[T]): float {.closure.} = discard @@ -998,7 +1069,7 @@ static: scope: - let ast = quote do: + let ast = myquote: converter toBool(x: float): bool ast.matchAst: @@ -1008,7 +1079,7 @@ static: ## Template declaration scope: - let ast = quote do: + let ast = myquote: template optOpt{expr1}(a: int): int ast.matchAst: diff --git a/tests/template/tmore_regressions.nim b/tests/template/tmore_regressions.nim new file mode 100644 index 0000000000..8b4b5fa4c7 --- /dev/null +++ b/tests/template/tmore_regressions.nim @@ -0,0 +1,44 @@ +discard """ +output: '''0 + +0.0''' +""" + +# bug #11494 +import macros + +macro staticForEach(arr: untyped, body: untyped): untyped = + result = newNimNode(nnkStmtList) + + arr.expectKind(nnkBracket) + for n in arr: + let b = copyNimTree(body) + result.add quote do: + block: + type it {.inject.} = `n` + `b` + +template forEveryMatchingEntity*() = + staticForEach([int, string, float]): + var a: it + echo a + +forEveryMatchingEntity() + + +# bug #11483 +proc main = + template first(body) = + template second: var int = + var o: int + var i = addr(o) + i[] + + body + + first: + second = 5 + second = 6 + +main() + diff --git a/tests/template/tparams_gensymed.nim b/tests/template/tparams_gensymed.nim index b19ed7afce..f7a02efa09 100644 --- a/tests/template/tparams_gensymed.nim +++ b/tests/template/tparams_gensymed.nim @@ -8,6 +8,7 @@ output: ''' 1 2 3 +wth ''' """ # bug #1915 @@ -130,3 +131,17 @@ template test() = doAssert(foo.len == 3) test() + +# regression found in PMunch's parser generator + +proc namedcall(arg: string) = + discard + +macro m(): untyped = + result = quote do: + (proc (arg: string) = + namedcall(arg = arg) + echo arg) + +let meh = m() +meh("wth") diff --git a/tests/template/tredefinition.nim b/tests/template/tredefinition.nim new file mode 100644 index 0000000000..8efc5ae2f8 --- /dev/null +++ b/tests/template/tredefinition.nim @@ -0,0 +1,13 @@ +discard """ + errormsg: "redefinition of 'a`gensym" + line: 9 +""" +# bug #10180 +proc f() = + template t() = + var a = 1 + var a = 2 + echo a + t() + +f() From fbb2763204ada801f3935e9cbab80abab9a7e945 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 23 Aug 2019 18:58:55 +0200 Subject: [PATCH 10/61] fixes #11958 (#12013) --- compiler/plugins/locals.nim | 23 +++++++++++------------ tests/misc/tlocals.nim | 36 +++++++++++++++++++++++++++++++++++- 2 files changed, 46 insertions(+), 13 deletions(-) diff --git a/compiler/plugins/locals.nim b/compiler/plugins/locals.nim index 08d836f7d7..73f6eaa19e 100644 --- a/compiler/plugins/locals.nim +++ b/compiler/plugins/locals.nim @@ -17,25 +17,24 @@ proc semLocals*(c: PContext, n: PNode): PNode = var tupleType = newTypeS(tyTuple, c) result = newNodeIT(nkPar, n.info, tupleType) tupleType.n = newNodeI(nkRecList, n.info) + let owner = getCurrOwner(c) # for now we skip openarrays ... for scope in walkScopes(c.currentScope): if scope == c.topLevelScope: break for it in items(scope.symbols): - # XXX parameters' owners are wrong for generics; this caused some pain - # for closures too; we should finally fix it. - #if it.owner != c.p.owner: return result if it.kind in skLocalVars and it.typ.skipTypes({tyGenericInst, tyVar}).kind notin {tyVarargs, tyOpenArray, tyTypeDesc, tyStatic, tyUntyped, tyTyped, tyEmpty}: - var field = newSym(skField, it.name, getCurrOwner(c), n.info) - field.typ = it.typ.skipTypes({tyVar}) - field.position = counter - inc(counter) + if it.owner == owner: + var field = newSym(skField, it.name, owner, n.info) + field.typ = it.typ.skipTypes({tyVar}) + field.position = counter + inc(counter) - addSon(tupleType.n, newSymNode(field)) - addSonSkipIntLit(tupleType, field.typ) + addSon(tupleType.n, newSymNode(field)) + addSonSkipIntLit(tupleType, field.typ) - var a = newSymNode(it, result.info) - if it.typ.skipTypes({tyGenericInst}).kind == tyVar: a = newDeref(a) - result.add(a) + var a = newSymNode(it, result.info) + if it.typ.skipTypes({tyGenericInst}).kind == tyVar: a = newDeref(a) + result.add(a) diff --git a/tests/misc/tlocals.nim b/tests/misc/tlocals.nim index e6c73313d1..ad9e7d0323 100644 --- a/tests/misc/tlocals.nim +++ b/tests/misc/tlocals.nim @@ -1,5 +1,7 @@ discard """ - output: '''(x: "string here", a: 1)''' + output: '''(x: "string here", a: 1) +b is 5 +x is 12''' """ proc simple[T](a: T) = @@ -28,3 +30,35 @@ proc test(baz: int, qux: var int): int = var x1 = 456 discard test(123, x1) + +# bug #11958 +proc foo() = + var a = 5 + proc bar() {.nimcall.} = + var b = 5 + for k, v in fieldpairs(locals()): + echo k, " is ", v + + bar() +foo() + + +proc foo2() = + var a = 5 + proc bar2() {.nimcall.} = + for k, v in fieldpairs(locals()): + echo k, " is ", v + + bar2() +foo2() + + +proc foo3[T](y: T) = + var a = 5 + proc bar2[T](x: T) {.nimcall.} = + for k, v in fieldpairs(locals()): + echo k, " is ", v + + bar2(y) + +foo3(12) From 547fcd69c3a689b7db8dc3e462659b71f791ab46 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 23 Aug 2019 21:34:22 +0200 Subject: [PATCH 11/61] rdstdin: remove cruft that shouldn't have been exported or added (#12014) --- lib/impure/rdstdin.nim | 40 ---------------------------------------- 1 file changed, 40 deletions(-) diff --git a/lib/impure/rdstdin.nim b/lib/impure/rdstdin.nim index 742db0cc6b..b78c0d8cf7 100644 --- a/lib/impure/rdstdin.nim +++ b/lib/impure/rdstdin.nim @@ -33,46 +33,6 @@ when defined(Windows): stdout.write(prompt) result = readLine(stdin, line) - import winlean - - const - VK_SHIFT* = 16 - VK_CONTROL* = 17 - VK_MENU* = 18 - KEY_EVENT* = 1 - - type - KEY_EVENT_RECORD = object - bKeyDown: WINBOOL - wRepeatCount: uint16 - wVirtualKeyCode: uint16 - wVirtualScanCode: uint16 - unicodeChar: uint16 - dwControlKeyState: uint32 - INPUT_RECORD = object - eventType*: int16 - reserved*: int16 - event*: KEY_EVENT_RECORD - safetyBuffer: array[0..5, DWORD] - - proc readConsoleInputW*(hConsoleInput: Handle, lpBuffer: var INPUT_RECORD, - nLength: uint32, - lpNumberOfEventsRead: var uint32): WINBOOL{. - stdcall, dynlib: "kernel32", importc: "ReadConsoleInputW".} - - proc getch(): uint16 = - let hStdin = getStdHandle(STD_INPUT_HANDLE) - var - irInputRecord: INPUT_RECORD - dwEventsRead: uint32 - - while readConsoleInputW(hStdin, irInputRecord, 1, dwEventsRead) != 0: - if irInputRecord.eventType == KEY_EVENT and - irInputRecord.event.wVirtualKeyCode notin {VK_SHIFT, VK_MENU, VK_CONTROL}: - result = irInputRecord.event.unicodeChar - discard readConsoleInputW(hStdin, irInputRecord, 1, dwEventsRead) - return result - elif defined(genode): proc readLineFromStdin*(prompt: string): TaintedString {. tags: [ReadIOEffect, WriteIOEffect].} = From f2e8c39e851b1d1f55d387d80ae3d9f598a6ef0e Mon Sep 17 00:00:00 2001 From: Viktor Kirilov Date: Fri, 23 Aug 2019 23:50:34 +0300 Subject: [PATCH 12/61] - adding _actual as a suffix only for calls to an actual proc and not through a global function pointer - fixes https://github.com/nim-lang/Nim/issues/11996 (#12007) - adding forward declarations for reloadable functions within a module - fix compilation errors when 2 such functions reference each other - fixes https://github.com/nim-lang/Nim/issues/11608 - preserve permissions of copied executable binaries --- compiler/ccgcalls.nim | 6 +++--- compiler/ccgtypes.nim | 2 +- compiler/cgen.nim | 7 ++++++- lib/nimhcr.nim | 2 +- 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim index 9201af4663..2ae56863b3 100644 --- a/compiler/ccgcalls.nim +++ b/compiler/ccgcalls.nim @@ -184,8 +184,8 @@ template genParamLoop(params) {.dirty.} = if params != nil: add(params, ~", ") add(params, genArgNoParam(p, ri.sons[i])) -proc addActualPrefixForHCR(res: var Rope, module: PSym, sym: PSym) = - if sym.flags * {sfImportc, sfNonReloadable} == {} and +proc addActualSuffixForHCR(res: var Rope, module: PSym, sym: PSym) = + if sym.flags * {sfImportc, sfNonReloadable} == {} and sym.loc.k == locProc and (sym.typ.callConv == ccInline or sym.owner.id == module.id): res = res & "_actual".rope @@ -203,7 +203,7 @@ proc genPrefixCall(p: BProc, le, ri: PNode, d: var TLoc) = genParamLoop(params) var callee = rdLoc(op) if p.hcrOn and ri.sons[0].kind == nkSym: - callee.addActualPrefixForHCR(p.module.module, ri.sons[0].sym) + callee.addActualSuffixForHCR(p.module.module, ri.sons[0].sym) fixupCall(p, le, ri, d, callee, params) proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) = diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 28a9bf0281..584e69b30f 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -956,7 +956,7 @@ proc genProcHeader(m: BModule, prc: PSym, asPtr: bool = false): Rope = fillLoc(prc.loc, locProc, prc.ast[namePos], mangleName(m, prc), OnUnknown) genProcParams(m, prc.typ, rettype, params, check) # handle the 2 options for hotcodereloading codegen - function pointer - # (instead of forward declaration) or header for function budy with "_actual" postfix + # (instead of forward declaration) or header for function body with "_actual" postfix let asPtrStr = rope(if asPtr: "_PTR" else: "") var name = prc.loc.r if isReloadable(m, prc) and not asPtr: diff --git a/compiler/cgen.nim b/compiler/cgen.nim index e961530f2b..0330034854 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -784,7 +784,7 @@ proc cgsym(m: BModule, name: string): Rope = rawMessage(m.config, errGenerated, "system module needs: " & name) result = sym.loc.r if m.hcrOn and sym != nil and sym.kind in {skProc..skIterator}: - result.addActualPrefixForHCR(m.module, sym) + result.addActualSuffixForHCR(m.module, sym) proc generateHeaders(m: BModule) = add(m.s[cfsHeaders], "\L#include \"nimbase.h\"\L") @@ -1033,6 +1033,11 @@ proc genProcAux(m: BModule, prc: PSym) = generatedProc = ropecg(p.module, "$N$1 {$n$2$3$4}$N$N", [header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts)]) else: + if m.hcrOn and isReloadable(m, prc): + # Add forward declaration for "_actual"-suffixed functions defined in the same module (or inline). + # This fixes the use of methods and also the case when 2 functions within the same module + # call each other using directly the "_actual" versions (an optimization) - see issue #11608 + addf(m.s[cfsProcHeaders], "$1;\n", [header]) generatedProc = ropecg(p.module, "$N$1 {$N", [header]) add(generatedProc, initGCFrame(p)) if optStackTrace in prc.options: diff --git a/lib/nimhcr.nim b/lib/nimhcr.nim index 79f3fd350e..201d4f5ad1 100644 --- a/lib/nimhcr.nim +++ b/lib/nimhcr.nim @@ -421,7 +421,7 @@ when defined(createNimHcr): modules.add(name, newModuleDesc()) let copiedName = name & ".copy." & dllExt - copyFile(name, copiedName) + copyFileWithPermissions(name, copiedName) let lib = loadLib(copiedName) assert lib != nil From ce7f29e8e6628df209ecebb566f9cefc14243aaf Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sat, 24 Aug 2019 06:25:47 +0200 Subject: [PATCH 13/61] fixes #11833 (#12018) --- compiler/injectdestructors.nim | 2 -- lib/core/seqs.nim | 2 +- lib/system.nim | 34 +++++++++++++++------------ tests/destructor/tnewruntime_misc.nim | 8 +++++++ tests/destructor/tv2_cast.nim | 3 +-- tests/errmsgs/twrong_at_operator.nim | 4 ++-- 6 files changed, 31 insertions(+), 22 deletions(-) diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index 87f09da59c..7c2a15ce3c 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -542,8 +542,6 @@ proc pArg(arg: PNode; c: var Con; isSink: bool): PNode = branch = copyNode(arg[i]) branch.add pArgIfTyped(arg[i][0]) result.add branch - elif isAnalysableFieldAccess(arg, c.owner) and isLastRead(arg, c): - result = destructiveMoveVar(arg, c) else: # an object that is not temporary but passed to a 'sink' parameter # results in a copy. diff --git a/lib/core/seqs.nim b/lib/core/seqs.nim index 2892e4d8a5..01839a4dea 100644 --- a/lib/core/seqs.nim +++ b/lib/core/seqs.nim @@ -202,7 +202,7 @@ when false: assert i < x.len x.data[i] = y - proc `@`*[T](elems: openArray[T]): NimSeqV2[T] = + proc `@`*[T](elems: sink openArray[T]): NimSeqV2[T] = result.cap = elems.len result.len = elems.len result.data = cast[type(result.data)](alloc(result.cap * sizeof(T))) diff --git a/lib/system.nim b/lib/system.nim index 7e23b5188f..09280d6070 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1794,21 +1794,25 @@ proc cmp*(x, y: string): int {.noSideEffect, procvar.} ## **Note**: The precise result values depend on the used C runtime library and ## can differ between operating systems! -proc `@`* [IDX, T](a: array[IDX, T]): seq[T] {. - magic: "ArrToSeq", noSideEffect.} - ## Turns an array into a sequence. - ## - ## This most often useful for constructing - ## sequences with the array constructor: ``@[1, 2, 3]`` has the type - ## ``seq[int]``, while ``[1, 2, 3]`` has the type ``array[0..2, int]``. - ## - ## .. code-block:: Nim - ## let - ## a = [1, 3, 5] - ## b = "foo" - ## - ## echo @a # => @[1, 3, 5] - ## echo @b # => @['f', 'o', 'o'] +when defined(nimHasDefault): + proc `@`* [IDX, T](a: sink array[IDX, T]): seq[T] {. + magic: "ArrToSeq", noSideEffect.} + ## Turns an array into a sequence. + ## + ## This most often useful for constructing + ## sequences with the array constructor: ``@[1, 2, 3]`` has the type + ## ``seq[int]``, while ``[1, 2, 3]`` has the type ``array[0..2, int]``. + ## + ## .. code-block:: Nim + ## let + ## a = [1, 3, 5] + ## b = "foo" + ## + ## echo @a # => @[1, 3, 5] + ## echo @b # => @['f', 'o', 'o'] +else: + proc `@`* [IDX, T](a: array[IDX, T]): seq[T] {. + magic: "ArrToSeq", noSideEffect.} when defined(nimHasDefault): proc default*(T: typedesc): T {.magic: "Default", noSideEffect.} diff --git a/tests/destructor/tnewruntime_misc.nim b/tests/destructor/tnewruntime_misc.nim index c5f978a98d..d6c03b9b02 100644 --- a/tests/destructor/tnewruntime_misc.nim +++ b/tests/destructor/tnewruntime_misc.nim @@ -75,5 +75,13 @@ proc selfAssign = selfAssign() +# bug #11833 +type FooAt = object + +proc testWrongAt() = + var x = @[@[FooAt()]] + +testWrongAt() + let (a, d) = allocCounters() discard cprintf("%ld new: %ld\n", a - unpairedEnvAllocs() - d, allocs) diff --git a/tests/destructor/tv2_cast.nim b/tests/destructor/tv2_cast.nim index 8a4d69ef0b..9c05b2ae17 100644 --- a/tests/destructor/tv2_cast.nim +++ b/tests/destructor/tv2_cast.nim @@ -2,7 +2,6 @@ discard """ cmd: '''nim c --newruntime $file''' output: '''@[1] @[116, 101, 115, 116] -test @[1953719668, 875770417]''' """ @@ -13,7 +12,7 @@ echo cast[seq[uint8]](@[1]) echo cast[seq[uint8]]("test") discard cast[string](@[116'u8, 101, 115, 116]) -echo cast[string](@[116'u8, 101, 115, 116]) +#echo cast[string](@[116'u8, 101, 115, 116, 0]) var a = cast[seq[uint32]]("test1234") a.setLen(2) echo a diff --git a/tests/errmsgs/twrong_at_operator.nim b/tests/errmsgs/twrong_at_operator.nim index 7ce0770034..edf584094f 100644 --- a/tests/errmsgs/twrong_at_operator.nim +++ b/tests/errmsgs/twrong_at_operator.nim @@ -8,9 +8,9 @@ proc `@`[T](a: openArray[T]): seq[T] first type mismatch at position: 1 required type for a: openarray[T] but expression '[int]' is of type: array[0..0, type int] -proc `@`[IDX, T](a: array[IDX, T]): seq[T] +proc `@`[IDX, T](a: sink array[IDX, T]): seq[T] first type mismatch at position: 1 - required type for a: array[IDX, T] + required type for a: sink array[IDX, T] but expression '[int]' is of type: array[0..0, type int] expression: @[int] From 73dd73f9610f5ce8e6c85e9cc7aed7aff4d9ce1e Mon Sep 17 00:00:00 2001 From: nc-x Date: Mon, 26 Aug 2019 22:50:24 +0530 Subject: [PATCH 14/61] [ci skip] Add note to nimhcr.nim (#12050) --- lib/nimhcr.nim | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/nimhcr.nim b/lib/nimhcr.nim index 201d4f5ad1..396747527a 100644 --- a/lib/nimhcr.nim +++ b/lib/nimhcr.nim @@ -111,6 +111,7 @@ # - ARM support for the trampolines # - investigate: # - soon the system module might be importing other modules - the init order...? +# (revert https://github.com/nim-lang/Nim/pull/11971 when working on this) # - rethink the closure iterators # - ability to keep old versions of dynamic libraries alive # - because of async server code From 5df7d72afe28fb332ad5205377d56b05245e54a9 Mon Sep 17 00:00:00 2001 From: Viktor Kirilov Date: Mon, 26 Aug 2019 21:08:18 +0300 Subject: [PATCH 15/61] HCR: tests for the fixes for #11608 and #11996 (#12047) * tests for #11996 and #11608 * removed unnecessary comment --- tests/dll/nimhcr_2_1.nim | 15 +++++++++++++++ tests/dll/nimhcr_integration.nim | 1 + 2 files changed, 16 insertions(+) diff --git a/tests/dll/nimhcr_2_1.nim b/tests/dll/nimhcr_2_1.nim index a13b4c6819..faafb1f765 100644 --- a/tests/dll/nimhcr_2_1.nim +++ b/tests/dll/nimhcr_2_1.nim @@ -13,3 +13,18 @@ echo a.str beforeCodeReload: echo " 2: before!" + +# testing a construct of 2 functions in the same module which reference each other +# https://github.com/nim-lang/Nim/issues/11608 +proc rec_1(depth: int) +proc rec_2(depth: int) = + rec_1(depth + 1) +proc rec_1(depth: int) = + if depth < 3: + rec_2(depth) + else: + echo("max mutual recursion reached!") + +# https://github.com/nim-lang/Nim/issues/11996 +let rec_2_func_ref = rec_2 +rec_2_func_ref(0) diff --git a/tests/dll/nimhcr_integration.nim b/tests/dll/nimhcr_integration.nim index 40ba90f723..9bae64ef4c 100644 --- a/tests/dll/nimhcr_integration.nim +++ b/tests/dll/nimhcr_integration.nim @@ -29,6 +29,7 @@ main: hasAnyModuleChanged? true 0: before - improved! main: before 2: random string +max mutual recursion reached! 1 bar 0: after - closure iterator: 0 From 931ee0ca827de982bb39c9b53ad0ea874abe94ba Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 26 Aug 2019 20:12:22 +0200 Subject: [PATCH 16/61] fixes #7258 (#12054) --- compiler/semtypinst.nim | 26 +++++++++++++++++++++++++- tests/statictypes/tstatictypes.nim | 12 ++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index 950ec8e6b6..7e45876580 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -260,12 +260,32 @@ proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym = #result = PSym(idTableGet(cl.symMap, s)) #if result == nil: + #[ + + We cannot naively check for symbol recursions, because otherwise + object types A, B whould share their fields! + + import tables + + type + Table[S, T] = object + x: S + y: T + + G[T] = object + inodes: Table[int, T] # A + rnodes: Table[T, int] # B + + var g: G[string] + + ]# result = copySym(s) incl(result.flags, sfFromGeneric) #idTablePut(cl.symMap, s, result) result.owner = s.owner result.typ = replaceTypeVarsT(cl, s.typ) - result.ast = replaceTypeVarsN(cl, s.ast) + if result.kind != skType: + result.ast = replaceTypeVarsN(cl, s.ast) proc lookupTypeVar(cl: var TReplTypeVars, t: PType): PType = result = cl.typeMap.lookup(t) @@ -685,6 +705,10 @@ proc recomputeFieldPositions*(t: PType; obj: PNode; currPosition: var int) = proc generateTypeInstance*(p: PContext, pt: TIdTable, info: TLineInfo, t: PType): PType = + # Given `t` like Foo[T] + # pt: Table with type mappings: T -> int + # Desired result: Foo[int] + # proc (x: T = 0); T -> int ----> proc (x: int = 0) var typeMap = initLayeredTypeMap(pt) var cl = initTypeVars(p, addr(typeMap), info, nil) pushInfoContext(p.config, info) diff --git a/tests/statictypes/tstatictypes.nim b/tests/statictypes/tstatictypes.nim index 2a4ab0c637..c95e499d7a 100644 --- a/tests/statictypes/tstatictypes.nim +++ b/tests/statictypes/tstatictypes.nim @@ -8,6 +8,7 @@ output: ''' 16 b is 2 times a 17 +['\x00', '\x00', '\x00', '\x00'] ''' """ @@ -154,3 +155,14 @@ block: const uk = MicroKernel(a: 5.5, b: 1) tFunc[uk]() + + +# bug #7258 +type + StringValue*[LEN: static[Natural]] = array[LEN+Natural(2),char] + StringValue16* = StringValue[2] + +var + s: StringValue16 + +echo s From 88814bbe98fe43ca4f3c168b7d77ebb820838e7a Mon Sep 17 00:00:00 2001 From: Palash Nigam Date: Mon, 26 Aug 2019 23:43:41 +0530 Subject: [PATCH 17/61] fixes #11832 (#12049) --- tests/parallel/tpi.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/parallel/tpi.nim b/tests/parallel/tpi.nim index dcb9b8fc5e..1abed6f239 100644 --- a/tests/parallel/tpi.nim +++ b/tests/parallel/tpi.nim @@ -9,9 +9,9 @@ proc term(k: float): float = 4 * math.pow(-1, k) / (2*k + 1) proc piU(n: int): float = var ch = newSeq[FlowVar[float]](n+1) - for k in 0..n: + for k in 0..ch.high: ch[k] = spawn term(float(k)) - for k in 0..n: + for k in 0..ch.high: result += ^ch[k] proc piS(n: int): float = From ae9bb4474bdfb6740cbf50875febdf9689718601 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arne=20D=C3=B6ring?= Date: Mon, 26 Aug 2019 20:14:21 +0200 Subject: [PATCH 18/61] fixes #12033 (#12039) --- compiler/semstmts.nim | 4 ++++ tests/proc/tillegalreturntype.nim | 13 +++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 07aaf0ecaf..0d9d1af398 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1904,6 +1904,10 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, discard popProcCon(c) else: + if s.kind in {skProc, skFunc} and s.typ[0] != nil and s.typ[0].kind == tyUntyped: + # `auto` is represented as `tyUntyped` at this point in compilation. + localError(c.config, n[paramsPos][0].info, "return type 'auto' cannot be used in forward declarations") + if s.kind == skMethod: semMethodPrototype(c, s, n) if proto != nil: localError(c.config, n.info, errImplOfXexpected % proto.name.s) if {sfImportc, sfBorrow, sfError} * s.flags == {} and s.magic == mNone: diff --git a/tests/proc/tillegalreturntype.nim b/tests/proc/tillegalreturntype.nim index be9e2147e2..e26c44bead 100644 --- a/tests/proc/tillegalreturntype.nim +++ b/tests/proc/tillegalreturntype.nim @@ -1,8 +1,11 @@ discard """ cmd: "nim check $file" errmsg: "" - nimout: '''tillegalreturntype.nim(8, 11) Error: return type 'typed' is only valid for macros and templates -tillegalreturntype.nim(11, 11) Error: return type 'untyped' is only valid for macros and templates''' + nimout: ''' +tillegalreturntype.nim(11, 11) Error: return type 'typed' is only valid for macros and templates +tillegalreturntype.nim(14, 11) Error: return type 'untyped' is only valid for macros and templates +tillegalreturntype.nim(17, 41) Error: return type 'auto' cannot be used in forward declarations +''' """ proc x(): typed = @@ -10,3 +13,9 @@ proc x(): typed = proc y(): untyped = discard + +proc test_proc[T, U](arg1: T, arg2: U): auto + +proc test_proc[T, U](arg1: T, arg2: U): auto = + echo "Proc has been called" + return arg1 / arg2 From 8117a949c58ce108b3de2f926602b6d57dddcbdf Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Mon, 26 Aug 2019 19:27:44 +0100 Subject: [PATCH 19/61] Remove old and unused parseBody/parseResponse procs in httpclient. (#11949) --- lib/pure/httpclient.nim | 132 +--------------------------------------- 1 file changed, 2 insertions(+), 130 deletions(-) diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim index 5a98c5ede5..f89a928aba 100644 --- a/lib/pure/httpclient.nim +++ b/lib/pure/httpclient.nim @@ -1,7 +1,7 @@ # # # Nim's Runtime Library -# (c) Copyright 2016 Dominik Picheta, Andreas Rumpf +# (c) Copyright 2019 Nim Contributors # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -175,7 +175,7 @@ ## let client = newHttpClient(maxRedirects = 0) ## -import net, strutils, uri, parseutils, strtabs, base64, os, mimetypes, +import net, strutils, uri, parseutils, base64, os, mimetypes, math, random, httpcore, times, tables, streams, std/monotimes import asyncnet, asyncdispatch, asyncfile import nativesockets @@ -276,134 +276,6 @@ proc fileError(msg: string) = e.msg = msg raise e -proc parseChunks(s: Socket, timeout: int): string = - result = "" - var ri = 0 - while true: - var chunkSizeStr = "" - var chunkSize = 0 - s.readLine(chunkSizeStr, timeout) - var i = 0 - if chunkSizeStr == "": - httpError("Server terminated connection prematurely") - while i < chunkSizeStr.len: - case chunkSizeStr[i] - of '0'..'9': - chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('0')) - of 'a'..'f': - chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('a') + 10) - of 'A'..'F': - chunkSize = chunkSize shl 4 or (ord(chunkSizeStr[i]) - ord('A') + 10) - of ';': - # http://tools.ietf.org/html/rfc2616#section-3.6.1 - # We don't care about chunk-extensions. - break - else: - httpError("Invalid chunk size: " & chunkSizeStr) - inc(i) - if chunkSize <= 0: - s.skip(2, timeout) # Skip \c\L - break - result.setLen(ri+chunkSize) - var bytesRead = 0 - while bytesRead != chunkSize: - let ret = recv(s, addr(result[ri]), chunkSize-bytesRead, timeout) - ri += ret - bytesRead += ret - s.skip(2, timeout) # Skip \c\L - # Trailer headers will only be sent if the request specifies that we want - # them: http://tools.ietf.org/html/rfc2616#section-3.6.1 - -proc parseBody(s: Socket, headers: HttpHeaders, httpVersion: string, timeout: int): string = - result = "" - if headers.getOrDefault"Transfer-Encoding" == "chunked": - result = parseChunks(s, timeout) - else: - # -REGION- Content-Length - # (http://tools.ietf.org/html/rfc2616#section-4.4) NR.3 - var contentLengthHeader = headers.getOrDefault"Content-Length" - if contentLengthHeader != "": - var length = contentLengthHeader.parseInt() - if length > 0: - result = newString(length) - var received = 0 - while true: - if received >= length: break - let r = s.recv(addr(result[received]), length-received, timeout) - if r == 0: break - received += r - if received != length: - httpError("Got invalid content length. Expected: " & $length & - " got: " & $received) - else: - # (http://tools.ietf.org/html/rfc2616#section-4.4) NR.4 TODO - - # -REGION- Connection: Close - # (http://tools.ietf.org/html/rfc2616#section-4.4) NR.5 - let implicitConnectionClose = - httpVersion == "1.0" or - # This doesn't match the HTTP spec, but it fixes issues for non-conforming servers. - (httpVersion == "1.1" and headers.getOrDefault"Connection" == "") - if headers.getOrDefault"Connection" == "close" or implicitConnectionClose: - var buf = "" - while true: - buf = newString(4000) - let r = s.recv(addr(buf[0]), 4000, timeout) - if r == 0: break - buf.setLen(r) - result.add(buf) - -proc parseResponse(s: Socket, getBody: bool, timeout: int): Response = - new result - var parsedStatus = false - var linei = 0 - var fullyRead = false - var line = "" - result.headers = newHttpHeaders() - while true: - line = "" - linei = 0 - s.readLine(line, timeout) - if line == "": break # We've been disconnected. - if line == "\c\L": - fullyRead = true - break - if not parsedStatus: - # Parse HTTP version info and status code. - var le = skipIgnoreCase(line, "HTTP/", linei) - if le <= 0: httpError("invalid http version") - inc(linei, le) - le = skipIgnoreCase(line, "1.1", linei) - if le > 0: result.version = "1.1" - else: - le = skipIgnoreCase(line, "1.0", linei) - if le <= 0: httpError("unsupported http version") - result.version = "1.0" - inc(linei, le) - # Status code - linei.inc skipWhitespace(line, linei) - result.status = line[linei .. ^1] - parsedStatus = true - else: - # Parse headers - var name = "" - var le = parseUntil(line, name, ':', linei) - if le <= 0: httpError("invalid headers") - inc(linei, le) - if line[linei] != ':': httpError("invalid headers") - inc(linei) # Skip : - - result.headers.add(name, line[linei.. ^1].strip()) - # Ensure the server isn't trying to DoS us. - if result.headers.len > headerLimit: - httpError("too many headers") - - if not fullyRead: - httpError("Connection was closed before full request has been made") - if getBody: - result.body = parseBody(s, result.headers, result.version, timeout) - else: - result.body = "" when not defined(ssl): type SSLContext = ref object From d8d2a9e98504374c106623f29d95ed8d80146f74 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 25 Aug 2019 12:46:15 +0200 Subject: [PATCH 20/61] manual: better documentation for implicit generics --- doc/manual.rst | 100 ++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 82 insertions(+), 18 deletions(-) diff --git a/doc/manual.rst b/doc/manual.rst index 7dad444233..a40e83809a 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -4410,13 +4410,10 @@ more complex type classes: # create a type class that will match all tuple and object types type RecordType = tuple or object - proc printFields(rec: RecordType) = + proc printFields[T: RecordType](rec: T) = for key, value in fieldPairs(rec): echo key, " = ", value -Procedures utilizing type classes in such manner are considered to be -`implicitly generic`:idx:. They will be instantiated once for each unique -combination of param types used within the program. Whilst the syntax of type classes appears to resemble that of ADTs/algebraic data types in ML-like languages, it should be understood that type classes are static @@ -4442,6 +4439,26 @@ as `type constraints`:idx: of the generic type parameter: onlyIntOrString(5.0, 0.0) # type mismatch onlyIntOrString("xy", 50) # invalid as 'T' cannot be both at the same time + +Implicit generics +----------------- + +A type class can be used directly as the parameter's type. + +.. code-block:: nim + + # create a type class that will match all tuple and object types + type RecordType = tuple or object + + proc printFields(rec: RecordType) = + for key, value in fieldPairs(rec): + echo key, " = ", value + + +Procedures utilizing type classes in such manner are considered to be +`implicitly generic`:idx:. They will be instantiated once for each unique +combination of param types used within the program. + By default, during overload resolution each named type class will bind to exactly one concrete type. We call such type classes `bind once`:idx: types. Here is an example taken directly from the system module to illustrate this: @@ -4470,26 +4487,73 @@ the dot syntax: proc `[]`(m: Matrix, row, col: int): Matrix.T = m.data[col * high(Matrix.Columns) + row] -Alternatively, the `type` operator can be used over the proc params for similar -effect when anonymous or distinct type classes are used. -When a generic type is instantiated with a type class instead of a concrete -type, this results in another more specific type class: +Here are more examples that illustrate implicit generics: .. code-block:: nim - seq[ref object] # Any sequence storing references to any object type - type T1 = auto - proc foo(s: seq[T1], e: T1) - # seq[T1] is the same as just `seq`, but T1 will be allowed to bind - # to a single type, while the signature is being matched + proc p(t: Table; k: Table.Key): Table.Value - Matrix[Ordinal] # Any Matrix instantiation using integer values + # is roughly the same as: -As seen in the previous example, in such instantiations, it's not necessary to -supply all type parameters of the generic type, because any missing ones will -be inferred to have the equivalent of the `any` type class and thus they will -match anything without discrimination. + proc p[Key, Value](t: Table[Key, Value]; k: Key): Value + + +.. code-block:: nim + + proc p(a: Table, b: Table) + + # is roughly the same as: + + proc p[Key, Value](a, b: Table[Key, Value]) + + +.. code-block:: nim + + proc p(a: Table, b: distinct Table) + + # is roughly the same as: + + proc p[Key, Value, KeyB, ValueB](a: Table[Key, Value], b: Table[KeyB, ValueB]) + + +`typedesc` used as a parameter type also introduces an implicit +generic. `typedesc` has its own set of rules: + +.. code-block:: nim + + proc p(a: typedesc) + + # is roughly the same as: + + proc p[T](a: typedesc[T]) + + +`typedesc` is a "bind many" type class: + +.. code-block:: nim + + proc p(a, b: typedesc) + + # is roughly the same as: + + proc p[T, T2](a: typedesc[T], b: typedesc[T2]) + + +A parameter of type `typedesc` is itself usable as a type. If it is used +as a type, it's the underlying type. (In other words, one level +of "typedesc"-ness is stripped off: + +.. code-block:: nim + + proc p(a: typedesc; b: a) = discard + + # is roughly the same as: + proc p[T](a: typedesc[T]; b: T) = discard + + # hence this is a valid call: + p(int, 4) + # as parameter 'a' requires a type, but 'b' requires a value. Generic inference restrictions From 0c41fd0f508e861c4b7eb947026ad3a4947d9c23 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 25 Aug 2019 12:54:17 +0200 Subject: [PATCH 21/61] semtypes.nim: little refactoring for liftParamType --- compiler/semtypes.nim | 46 +++++++++++++++++++------------------------ 1 file changed, 20 insertions(+), 26 deletions(-) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 3038a1a6be..a45a5e78c6 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -928,21 +928,12 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, result = typeClass addDecl(c, s) - # XXX: There are codegen errors if this is turned into a nested proc - template liftingWalk(typ: PType, anonFlag = false): untyped = + template recurse(typ: PType, anonFlag = false): untyped = liftParamType(c, procKind, genericParams, typ, paramName, info, anonFlag) - #proc liftingWalk(paramType: PType, anon = false): PType = var paramTypId = if not anon and paramType.sym != nil: paramType.sym.name else: nil - template maybeLift(typ: PType): untyped = - let lifted = liftingWalk(typ) - (if lifted != nil: lifted else: typ) - - template addImplicitGeneric(e): untyped = - addImplicitGenericImpl(c, e, paramTypId) - case paramType.kind: of tyAnything: result = addImplicitGenericImpl(c, newTypeS(tyGenericParam, c), nil) @@ -952,10 +943,12 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, # this is a concrete static value return if tfUnresolved in paramType.flags: return # already lifted - let base = paramType.base.maybeLift + + let lifted = recurse(paramType.base) + let base = (if lifted != nil: lifted else: paramType.base) if base.isMetaType and procKind == skMacro: localError(c.config, info, errMacroBodyDependsOnGenericTypes % paramName) - result = addImplicitGeneric(c.newTypeWithSons(tyStatic, @[base])) + result = addImplicitGenericImpl(c, c.newTypeWithSons(tyStatic, @[base]), paramTypId) if result != nil: result.flags.incl({tfHasStatic, tfUnresolved}) of tyTypeDesc: @@ -968,15 +961,15 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, paramTypId = nil let t = c.newTypeWithSons(tyTypeDesc, @[paramType.base]) incl t.flags, tfCheckedForDestructor - result = addImplicitGeneric(t) + result = addImplicitGenericImpl(c, t, paramTypId) of tyDistinct: if paramType.sonsLen == 1: # disable the bindOnce behavior for the type class - result = liftingWalk(paramType.base, true) + result = recurse(paramType.base, true) of tyAlias, tyOwned: - result = liftingWalk(paramType.base) + result = recurse(paramType.base) of tySequence, tySet, tyArray, tyOpenArray, tyVar, tyLent, tyPtr, tyRef, tyProc: @@ -989,12 +982,12 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, if paramType.kind == tySequence and paramType.lastSon.kind == tyNone: let typ = c.newTypeWithSons(tyBuiltInTypeClass, @[newTypeS(paramType.kind, c)]) - result = addImplicitGeneric(typ) + result = addImplicitGenericImpl(c, typ, paramTypId) else: for i in 0 ..< paramType.len: if paramType.sons[i] == paramType: globalError(c.config, info, errIllegalRecursionInTypeX % typeToString(paramType)) - var lifted = liftingWalk(paramType.sons[i]) + var lifted = recurse(paramType.sons[i]) if lifted != nil: paramType.sons[i] = lifted result = paramType @@ -1014,29 +1007,29 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, if paramType.lastSon.kind == tyUserTypeClass: result.kind = tyUserTypeClassInst result.rawAddSon paramType.lastSon - return addImplicitGeneric(result) + return addImplicitGenericImpl(c, result, paramTypId) let x = instGenericContainer(c, paramType.sym.info, result, allowMetaTypes = true) result = newTypeWithSons(c, tyCompositeTypeClass, @[paramType, x]) #result = newTypeS(tyCompositeTypeClass, c) #for i in 0.. Date: Sun, 25 Aug 2019 13:01:27 +0200 Subject: [PATCH 22/61] semtypes.nim: more refactorings for liftParamType --- compiler/semtypes.nim | 72 ++++++++++++++++++++++--------------------- 1 file changed, 37 insertions(+), 35 deletions(-) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index a45a5e78c6..0e2421e8b9 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -899,35 +899,37 @@ template shouldHaveMeta(t) = internalAssert c.config, tfHasMeta in t.flags # result.lastSon.flags.incl tfHasMeta +proc addImplicitGeneric(c: PContext; typeClass: PType, typId: PIdent; + info: TLineInfo; genericParams: PNode; + paramName: string): PType = + if genericParams == nil: + # This happens with anonymous proc types appearing in signatures + # XXX: we need to lift these earlier + return + let finalTypId = if typId != nil: typId + else: getIdent(c.cache, paramName & ":type") + # is this a bindOnce type class already present in the param list? + for i in 0 ..< genericParams.len: + if genericParams.sons[i].sym.name.id == finalTypId.id: + return genericParams.sons[i].typ + + let owner = if typeClass.sym != nil: typeClass.sym + else: getCurrOwner(c) + var s = newSym(skType, finalTypId, owner, info) + if sfExplain in owner.flags: s.flags.incl sfExplain + if typId == nil: s.flags.incl(sfAnon) + s.linkTo(typeClass) + typeClass.flags.incl tfImplicitTypeParam + s.position = genericParams.len + genericParams.addSon(newSymNode(s)) + result = typeClass + addDecl(c, s) + proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, paramType: PType, paramName: string, info: TLineInfo, anon = false): PType = if paramType == nil: return # (e.g. proc return type) - proc addImplicitGenericImpl(c: PContext; typeClass: PType, typId: PIdent): PType = - if genericParams == nil: - # This happens with anonymous proc types appearing in signatures - # XXX: we need to lift these earlier - return - let finalTypId = if typId != nil: typId - else: getIdent(c.cache, paramName & ":type") - # is this a bindOnce type class already present in the param list? - for i in 0 ..< genericParams.len: - if genericParams.sons[i].sym.name.id == finalTypId.id: - return genericParams.sons[i].typ - - let owner = if typeClass.sym != nil: typeClass.sym - else: getCurrOwner(c) - var s = newSym(skType, finalTypId, owner, info) - if sfExplain in owner.flags: s.flags.incl sfExplain - if typId == nil: s.flags.incl(sfAnon) - s.linkTo(typeClass) - typeClass.flags.incl tfImplicitTypeParam - s.position = genericParams.len - genericParams.addSon(newSymNode(s)) - result = typeClass - addDecl(c, s) - template recurse(typ: PType, anonFlag = false): untyped = liftParamType(c, procKind, genericParams, typ, paramName, info, anonFlag) @@ -936,7 +938,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, case paramType.kind: of tyAnything: - result = addImplicitGenericImpl(c, newTypeS(tyGenericParam, c), nil) + result = addImplicitGeneric(c, newTypeS(tyGenericParam, c), nil, info, genericParams, paramName) of tyStatic: if paramType.base.kind != tyNone and paramType.n != nil: @@ -948,7 +950,8 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, let base = (if lifted != nil: lifted else: paramType.base) if base.isMetaType and procKind == skMacro: localError(c.config, info, errMacroBodyDependsOnGenericTypes % paramName) - result = addImplicitGenericImpl(c, c.newTypeWithSons(tyStatic, @[base]), paramTypId) + result = addImplicitGeneric(c, c.newTypeWithSons(tyStatic, @[base]), + paramTypId, info, genericParams, paramName) if result != nil: result.flags.incl({tfHasStatic, tfUnresolved}) of tyTypeDesc: @@ -961,7 +964,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, paramTypId = nil let t = c.newTypeWithSons(tyTypeDesc, @[paramType.base]) incl t.flags, tfCheckedForDestructor - result = addImplicitGenericImpl(c, t, paramTypId) + result = addImplicitGeneric(c, t, paramTypId, info, genericParams, paramName) of tyDistinct: if paramType.sonsLen == 1: @@ -982,7 +985,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, if paramType.kind == tySequence and paramType.lastSon.kind == tyNone: let typ = c.newTypeWithSons(tyBuiltInTypeClass, @[newTypeS(paramType.kind, c)]) - result = addImplicitGenericImpl(c, typ, paramTypId) + result = addImplicitGeneric(c, typ, paramTypId, info, genericParams, paramName) else: for i in 0 ..< paramType.len: if paramType.sons[i] == paramType: @@ -1007,20 +1010,20 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, if paramType.lastSon.kind == tyUserTypeClass: result.kind = tyUserTypeClassInst result.rawAddSon paramType.lastSon - return addImplicitGenericImpl(c, result, paramTypId) + return addImplicitGeneric(c, result, paramTypId, info, genericParams, paramName) let x = instGenericContainer(c, paramType.sym.info, result, allowMetaTypes = true) result = newTypeWithSons(c, tyCompositeTypeClass, @[paramType, x]) #result = newTypeS(tyCompositeTypeClass, c) #for i in 0.. Date: Mon, 26 Aug 2019 23:19:55 +0200 Subject: [PATCH 23/61] unicode.nim: fixed spacing to something sane --- lib/pure/unicode.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/unicode.nim b/lib/pure/unicode.nim index 35c3642218..33b720f623 100644 --- a/lib/pure/unicode.nim +++ b/lib/pure/unicode.nim @@ -388,7 +388,7 @@ proc runeStrAtPos*(s: string, pos: Natural): string = ## * `runeAtPos proc <#runeAtPos,string,int>`_ ## * `fastRuneAt template <#fastRuneAt.t,string,int,untyped>`_ let o = runeOffset(s, pos) - s[o.. (o+runeLenAt(s, o)-1)] + s[o .. (o+runeLenAt(s, o)-1)] proc runeSubStr*(s: string, pos: int, len: int = int.high): string = ## Returns the UTF-8 substring starting at code point ``pos`` From b044523c5adbcb5a2612b6b42933221365b6f8f1 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 27 Aug 2019 10:07:42 +0200 Subject: [PATCH 24/61] fixes 9195 (#12055) --- compiler/semtypes.nim | 6 +++++- compiler/sigmatch.nim | 8 ++++++-- tests/metatype/ttypedesc2.nim | 24 ++++++++++++++++++++++++ 3 files changed, 35 insertions(+), 3 deletions(-) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 0e2421e8b9..4d628e6ec7 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -1040,6 +1040,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, of tyGenericInvocation: for i in 1 ..< paramType.len: + #if paramType[i].kind != tyTypeDesc: let lifted = recurse(paramType.sons[i]) if lifted != nil: paramType.sons[i] = lifted @@ -1355,7 +1356,10 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType = for i in 1 ..< m.call.len: var typ = m.call[i].typ - if typ.kind == tyTypeDesc and typ.sons[0].kind == tyNone: + # is this a 'typedesc' *parameter*? If so, use the typedesc type, + # unstripped. + if m.call[i].kind == nkSym and m.call[i].sym.kind == skParam and + typ.kind == tyTypeDesc and containsGenericType(typ): isConcrete = false addToResult(typ) else: diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 952c8f991e..327537595c 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -1654,8 +1654,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, of tyGenericParam: var x = PType(idTableGet(c.bindings, f)) if x == nil: - if c.callee.kind == tyGenericBody and - f.kind == tyGenericParam and not c.typedescMatched: + if c.callee.kind == tyGenericBody and not c.typedescMatched: # XXX: The fact that generic types currently use tyGenericParam for # their parameters is really a misnomer. tyGenericParam means "match # any value" and what we need is "match any type", which can be encoded @@ -1700,6 +1699,11 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, c.inheritancePenalty = oldInheritancePenalty - c.inheritancePenalty - 100 * ord(result == isEqual) result = isGeneric + elif a.kind == tyTypeDesc: + # somewhat special typing rule, the following is illegal: + # proc p[T](x: T) + # p(int) + result = isNone else: result = isGeneric diff --git a/tests/metatype/ttypedesc2.nim b/tests/metatype/ttypedesc2.nim index 94a7367e78..37399784b8 100644 --- a/tests/metatype/ttypedesc2.nim +++ b/tests/metatype/ttypedesc2.nim @@ -48,3 +48,27 @@ doAssert hasDefault2(int) == "int" doAssert hasDefault2(string) == "string" doAssert hasDefault2() == "string" + +# bug #9195 +type + Error = enum + erA, erB, erC + Result[T, U] = object + x: T + u: U + PB = object + +proc decodeUVarint*(itzzz: typedesc[SomeUnsignedInt], + data: openArray[char]): Result[itzzz, Error] = + result = Result[itzzz, Error](x: 0, u: erC) + +discard decodeUVarint(uint32, "abc") + +type + X = object + Y[T] = object + +proc testObj(typ: typedesc[object]): Y[typ] = + discard + +discard testObj(X) From ddc155af64c2e7762c0efa33f8cc9fe9150f5d21 Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 27 Aug 2019 10:20:12 +0200 Subject: [PATCH 25/61] fixes #12040 --- config/config.nims | 1 + 1 file changed, 1 insertion(+) diff --git a/config/config.nims b/config/config.nims index 03daa59702..24c7901681 100644 --- a/config/config.nims +++ b/config/config.nims @@ -2,3 +2,4 @@ when defined(nimHasCppDefine): cppDefine "errno" + cppDefine "unix" From 1ccff0324c74051da445852b31932aa2bbdb0c7a Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 27 Aug 2019 12:13:06 +0200 Subject: [PATCH 26/61] manual: more precise rules about evaluation order --- doc/manual.rst | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/doc/manual.rst b/doc/manual.rst index a40e83809a..f0945f78dd 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -721,6 +721,44 @@ Rationale: Consistency with overloaded assignment or assignment-like operations, ``a = b`` can be read as ``performSomeCopy(a, b)``. +However, the concept of "order of evaluation" is only applicable after the code +was normalized: The normalization involves template expansions and argument +reorderings that have been passed to named parameters: + +.. code-block:: nim + :test: "nim c $1" + + var s = "" + + proc p(): int = + s.add "p" + result = 5 + + proc q(): int = + s.add "q" + result = 3 + + # Evaluation order is 'b' before 'a' due to template + # expansion's semantics. + template swapArgs(a, b): untyped = + b + a + + doAssert swapArgs(p() + q(), q() - p()) == 6 + doAssert s == "qppq" + + # Evaluation order is not influenced by named parameters: + proc construct(first, second: int) = + discard + + # 'p' is evaluated before 'q'! + construct(second = q(), first = p()) + + doAssert s == "qppqpq" + + +Rationale: This is far easier to implement than hypothetical alternatives. + + Constants and Constant Expressions ================================== From 66a885615242ca45c19a4efa169ee1296de5061e Mon Sep 17 00:00:00 2001 From: Jacek Sieka Date: Tue, 27 Aug 2019 14:27:44 +0200 Subject: [PATCH 27/61] abort is noreturn (#12061) --- lib/system/ansi_c.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/system/ansi_c.nim b/lib/system/ansi_c.nim index 76c78a3b9e..16e7a14d60 100644 --- a/lib/system/ansi_c.nim +++ b/lib/system/ansi_c.nim @@ -30,7 +30,7 @@ proc c_strcmp*(a, b: cstring): cint {. proc c_strlen*(a: cstring): csize {. importc: "strlen", header: "", noSideEffect.} proc c_abort*() {. - importc: "abort", header: "", noSideEffect.} + importc: "abort", header: "", noSideEffect, noreturn.} when defined(linux) and defined(amd64): From def623490345e12b12a07efcc37f81d27926f0ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oscar=20Nihlg=C3=A5rd?= Date: Wed, 7 Aug 2019 20:34:52 +0200 Subject: [PATCH 28/61] Fix several float range issues --- compiler/semexprs.nim | 6 ++++++ compiler/semstmts.nim | 3 +++ compiler/semtypes.nim | 6 ++++++ compiler/sigmatch.nim | 7 ++++--- tests/range/trange.nim | 11 +++++++++++ 5 files changed, 30 insertions(+), 3 deletions(-) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 57d9aae4bc..735f2646f3 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -513,6 +513,12 @@ proc changeType(c: PContext; n: PNode, newType: PType, check: bool) = if value < firstOrd(c.config, newType) or value > lastOrd(c.config, newType): localError(c.config, n.info, "cannot convert " & $value & " to " & typeToString(newType)) + of nkFloatLit..nkFloat64Lit: + if check: + echo newType.kind + if not floatRangeCheck(n.floatVal, newType): + localError(c.config, n.info, "cannot convert " & $n.floatVal & + " to " & typeToString(newType)) else: discard n.typ = newType diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 0d9d1af398..9c67fa565f 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -292,6 +292,9 @@ proc fitRemoveHiddenConv(c: PContext, typ: PType, n: PNode): PNode = result = newFloatNode(nkFloatLit, BiggestFloat r1.intVal) result.info = n.info result.typ = typ + if not floatRangeCheck(result.floatVal, typ): + localError(c.config, n.info, "cannot convert " & $result.floatVal & + " to " & typeToString(typ)) else: changeType(c, r1, typ, check=true) result = r1 diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 4d628e6ec7..16cc89e137 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -10,6 +10,8 @@ # this module does the semantic checking of type declarations # included from sem.nim +import math + const errStringOrIdentNodeExpected = "string or ident node expected" errStringLiteralExpected = "string literal expected" @@ -236,6 +238,10 @@ proc semRangeAux(c: PContext, n: PNode, prev: PType): PType = else: result.n.addSon semConstExpr(c, range[i]) + if (result.n[0].kind in {nkFloatLit..nkFloat64Lit} and classify(result.n[0].floatVal) == fcNan) or + (result.n[1].kind in {nkFloatLit..nkFloat64Lit} and classify(result.n[1].floatVal) == fcNan): + localError(c.config, n.info, "NaN is not a valid start or end for a range") + if weakLeValue(result.n[0], result.n[1]) == impNo: localError(c.config, n.info, "range is empty") diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 327537595c..429d41f7fb 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -414,9 +414,10 @@ proc isConvertibleToRange(f, a: PType): bool = of tyUInt16: result = a.kind in {tyUInt8, tyUInt16, tyUInt} of tyUInt32: result = a.kind in {tyUInt8, tyUInt16, tyUInt32, tyUInt} else: result = false - elif f.kind in {tyFloat..tyFloat128} and - a.kind in {tyFloat..tyFloat128}: - result = true + elif f.kind in {tyFloat..tyFloat128}: + # `isIntLit` is correct and should be used above as well, see PR: + # https://github.com/nim-lang/Nim/pull/11197 + result = isIntLit(a) or a.kind in {tyFloat..tyFloat128} proc handleFloatRange(f, a: PType): TTypeRelation = if a.kind == f.kind: diff --git a/tests/range/trange.nim b/tests/range/trange.nim index 41804d0f26..9df0e6975c 100644 --- a/tests/range/trange.nim +++ b/tests/range/trange.nim @@ -118,3 +118,14 @@ block: x3 = R32(4) doAssert $x1 & $x2 & $x3 == "444" + +block: + var x1: range[0'f..1'f] = 1 + const x2: range[0'f..1'f] = 1 + var x5: range[0'f32..1'f32] = 1'f64 + const x6: range[0'f32..1'f32] = 1'f64 + + reject: + const x: range[0'f..1'f] = 2'f + reject: + const x: range[0'f..1'f] = 2 From 329e169e964951b39cee863dd715277166497213 Mon Sep 17 00:00:00 2001 From: narimiran Date: Tue, 27 Aug 2019 14:49:32 +0200 Subject: [PATCH 29/61] address the comments --- compiler/sem.nim | 1 + compiler/semexprs.nim | 7 ++----- compiler/semstmts.nim | 3 +-- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/compiler/sem.nim b/compiler/sem.nim index 119393e258..194f88809d 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -444,6 +444,7 @@ proc semAfterMacroCall(c: PContext, call, macroResult: PNode, const errMissingGenericParamsForTemplate = "'$1' has unspecified generic parameters" + errFloatToString = "cannot convert '$1' to '$2'" proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym, flags: TExprFlags = {}): PNode = diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 735f2646f3..765110d561 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -514,11 +514,8 @@ proc changeType(c: PContext; n: PNode, newType: PType, check: bool) = localError(c.config, n.info, "cannot convert " & $value & " to " & typeToString(newType)) of nkFloatLit..nkFloat64Lit: - if check: - echo newType.kind - if not floatRangeCheck(n.floatVal, newType): - localError(c.config, n.info, "cannot convert " & $n.floatVal & - " to " & typeToString(newType)) + if check and not floatRangeCheck(n.floatVal, newType): + localError(c.config, n.info, errFloatToString % [$n.floatVal, typeToString(newType)]) else: discard n.typ = newType diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 9c67fa565f..5a9c926471 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -293,8 +293,7 @@ proc fitRemoveHiddenConv(c: PContext, typ: PType, n: PNode): PNode = result.info = n.info result.typ = typ if not floatRangeCheck(result.floatVal, typ): - localError(c.config, n.info, "cannot convert " & $result.floatVal & - " to " & typeToString(typ)) + localError(c.config, n.info, errFloatToString % [$n.floatVal, typeToString(typ)]) else: changeType(c, r1, typ, check=true) result = r1 From 3f5599b3448737df3a15c47b3d65212e1cb3c4ad Mon Sep 17 00:00:00 2001 From: narimiran Date: Tue, 27 Aug 2019 15:03:06 +0200 Subject: [PATCH 30/61] disable testing of 'norm' package --- testament/important_packages.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index a96310ee50..d10e9c5b30 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -56,7 +56,7 @@ pkg "nimquery" pkg "nimsl", "", true pkg "nimsvg" pkg "nimx", "nim c --threads:on test/main.nim", true -pkg "norm", "nim c -r tests/testsqlite.nim", true +# pkg "norm", "nim c -r tests/testsqlite.nim", true pkg "npeg" pkg "ormin", "nim c -o:orminn ormin.nim", true pkg "parsetoml" From 8df745a42ed49533903e5e6034385c5815b47383 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 27 Aug 2019 16:15:37 +0200 Subject: [PATCH 31/61] fixes #12056 (#12063) --- compiler/ast.nim | 2 +- compiler/semfold.nim | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index 62a3492cce..45c8d5ace3 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -1059,7 +1059,7 @@ template `[]`*(n: Indexable, i: BackwardsIndex): Indexable = n[n.len - i.int] template `[]=`*(n: Indexable, i: BackwardsIndex; x: Indexable) = n[n.len - i.int] = x when defined(useNodeIds): - const nodeIdToDebug* = 2322967# 2322968 + const nodeIdToDebug* = -1 # 2322968 var gNodeId: int proc newNode*(kind: TNodeKind): PNode = diff --git a/compiler/semfold.nim b/compiler/semfold.nim index 38b29344f8..564d7f0676 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -50,7 +50,10 @@ proc newIntNodeT*(intVal: Int128, n: PNode; g: ModuleGraph): PNode = result.info = n.info proc newFloatNodeT*(floatVal: BiggestFloat, n: PNode; g: ModuleGraph): PNode = - result = newFloatNode(nkFloatLit, floatVal) + if n.typ.skipTypes(abstractInst).kind == tyFloat32: + result = newFloatNode(nkFloat32Lit, floatVal) + else: + result = newFloatNode(nkFloatLit, floatVal) result.typ = n.typ result.info = n.info @@ -176,7 +179,7 @@ proc evalOp(m: TMagic, n, a, b, c: PNode; g: ModuleGraph): PNode = of mOrd: result = newIntNodeT(getOrdValue(a), n, g) of mChr: result = newIntNodeT(getInt(a), n, g) of mUnaryMinusI, mUnaryMinusI64: result = foldUnarySub(getInt(a), n, g) - of mUnaryMinusF64: result = newFloatNodeT(- getFloat(a), n, g) + of mUnaryMinusF64: result = newFloatNodeT(-getFloat(a), n, g) of mNot: result = newIntNodeT(One - getInt(a), n, g) of mCard: result = newIntNodeT(nimsets.cardSet(g.config, a), n, g) of mBitnotI: From d8177a398059334a7d36c1ba0d98b39516815edd Mon Sep 17 00:00:00 2001 From: Artem V L Date: Tue, 27 Aug 2019 16:16:32 +0200 Subject: [PATCH 32/61] Typos fixed, handleRefillChar() described (#12062) --- lib/pure/lexbase.nim | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/pure/lexbase.nim b/lib/pure/lexbase.nim index 11ec45a372..0ef4a147a1 100644 --- a/lib/pure/lexbase.nim +++ b/lib/pure/lexbase.nim @@ -100,7 +100,7 @@ proc fillBaseLexer(L: var BaseLexer, pos: int): int = result = 0 proc handleCR*(L: var BaseLexer, pos: int): int = - ## Call this if you scanned over '\c' in the buffer; it returns the the + ## Call this if you scanned over '\c' in the buffer; it returns the ## position to continue the scanning from. `pos` must be the position ## of the '\c'. assert(L.buf[pos] == '\c') @@ -111,7 +111,7 @@ proc handleCR*(L: var BaseLexer, pos: int): int = L.lineStart = result proc handleLF*(L: var BaseLexer, pos: int): int = - ## Call this if you scanned over '\L' in the buffer; it returns the the + ## Call this if you scanned over '\L' in the buffer; it returns the ## position to continue the scanning from. `pos` must be the position ## of the '\L'. assert(L.buf[pos] == '\L') @@ -120,7 +120,8 @@ proc handleLF*(L: var BaseLexer, pos: int): int = L.lineStart = result proc handleRefillChar*(L: var BaseLexer, pos: int): int = - ## To be documented. + ## Call this if a terminator character other than a new line is scanned + ## at `pos`; it returns the position to continue the scanning from. assert(L.buf[pos] in L.refillChars) result = fillBaseLexer(L, pos) #L.lastNL := result-1; // BUGFIX: was: result; From 114da04cbbaa7930a38ba8ca59229447aee7b40f Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 27 Aug 2019 19:18:56 +0200 Subject: [PATCH 33/61] fixes #12029; finish the 'unused import' feature (#12064) --- changelog.md | 4 ++++ compiler/condsyms.nim | 1 + compiler/modules.nim | 1 - compiler/pragmas.nim | 2 +- compiler/renderer.nim | 5 +++++ compiler/sem.nim | 3 ++- doc/manual.rst | 11 +++++++++++ tests/tools/dontmentionme.nim | 3 +++ tests/tools/tunused_imports.nim | 2 +- 9 files changed, 28 insertions(+), 4 deletions(-) create mode 100644 tests/tools/dontmentionme.nim diff --git a/changelog.md b/changelog.md index f29a4c69db..749959e3c6 100644 --- a/changelog.md +++ b/changelog.md @@ -58,6 +58,10 @@ - The Nim compiler now does not recompile the Nim project via ``nim c -r`` if no dependent Nim file changed. This feature can be overridden by the ``--forceBuild`` command line option. +- The Nim compiler now warns about unused module imports. You can use a + top level ``{.used.}`` pragma in the module that you want to be importable + without producing this warning. + ### Compiler changes diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index ce295c8b93..15a6254723 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -97,3 +97,4 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimFixedOwned") defineSymbol("nimHasStyleChecks") + defineSymbol("nimHasUsed") diff --git a/compiler/modules.nim b/compiler/modules.nim index 13845e6e9e..02b4d8ac42 100644 --- a/compiler/modules.nim +++ b/compiler/modules.nim @@ -50,7 +50,6 @@ proc partialInitModule(result: PSym; graph: ModuleGraph; fileIdx: FileIndex; fil setLen(graph.modules, int(fileIdx) + 1) graph.modules[result.position] = result - incl(result.flags, sfUsed) initStrTable(result.tab) strTableAdd(result.tab, result) # a module knows itself strTableAdd(packSym.tab, result) diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 9e3976e733..12b0cff874 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -49,7 +49,7 @@ const wDeprecated, wFloatChecks, wInfChecks, wNanChecks, wPragma, wEmit, wUnroll, wLinearScanEnd, wPatterns, wTrMacros, wEffects, wNoForward, wReorder, wComputedGoto, - wInjectStmt, wDeprecated, wExperimental, wThis} + wInjectStmt, wDeprecated, wExperimental, wThis, wUsed} lambdaPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl, wNoSideEffect, wSideEffect, wNoreturn, wDynlib, wHeader, wDeprecated, wExtern, wThread, wImportCpp, wImportObjC, wAsmNoStackFrame, diff --git a/compiler/renderer.nim b/compiler/renderer.nim index cfc414aaf1..dfe66de035 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -9,6 +9,11 @@ # This module implements the renderer of the standard Nim representation. +when defined(nimHasUsed): + # 'import renderer' is so useful for debugging + # that Nim shouldn't produce a warning for that: + {.used.} + import lexer, options, idents, strutils, ast, msgs, lineinfos diff --git a/compiler/sem.nim b/compiler/sem.nim index 119393e258..a13fd138b7 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -614,7 +614,8 @@ proc myProcess(context: PPassContext, n: PNode): PNode = proc reportUnusedModules(c: PContext) = for i in 0..high(c.unusedImports): - message(c.config, c.unusedImports[i][1], warnUnusedImportX, c.unusedImports[i][0].name.s) + if sfUsed notin c.unusedImports[i][0].flags: + message(c.config, c.unusedImports[i][1], warnUnusedImportX, c.unusedImports[i][0].name.s) proc myClose(graph: ModuleGraph; context: PPassContext, n: PNode): PNode = var c = PContext(context) diff --git a/doc/manual.rst b/doc/manual.rst index f0945f78dd..1f8f23310b 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -6276,6 +6276,17 @@ is particularly useful when the symbol was generated by a macro: implementArithOps(int) echoAdd 3, 5 +``used`` can also be used as a top level statement to mark a module as "used". +This prevents the "Unused import" warning: + +.. code-block:: nim + + # module: debughelper.nim + when defined(nimHasUsed): + # 'import debughelper' is so useful for debugging + # that Nim shouldn't produce a warning for that import, + # even if currently unused: + {.used.} experimental pragma diff --git a/tests/tools/dontmentionme.nim b/tests/tools/dontmentionme.nim new file mode 100644 index 0000000000..218823aca3 --- /dev/null +++ b/tests/tools/dontmentionme.nim @@ -0,0 +1,3 @@ +{.used.} + +proc nothing* = echo "nothing to see here" diff --git a/tests/tools/tunused_imports.nim b/tests/tools/tunused_imports.nim index c9cfcfe906..1c5732c83e 100644 --- a/tests/tools/tunused_imports.nim +++ b/tests/tools/tunused_imports.nim @@ -10,7 +10,7 @@ tunused_imports.nim(25, 8) Warning: imported and not used: 'strutils' [UnusedImp {.warning: "BEGIN".} -import net +import net, dontmentionme echo AF_UNIX From 00d46ca1c04935b7b4fe16e7f72204b46f5651fa Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 27 Aug 2019 20:05:40 +0200 Subject: [PATCH 34/61] fixes #12024 (#12065) --- compiler/semcall.nim | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 24547bccd9..cf83c599ec 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -176,6 +176,8 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors): filterOnlyFirst = true break + var maybeWrongSpace = false + var candidates = "" var skipped = 0 for err in errors: @@ -218,11 +220,17 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors): if got != nil: effectProblem(wanted, got, candidates) of kUnknown: discard "do not break 'nim check'" candidates.add "\n" + if err.firstMismatch.arg == 1 and nArg.kind == nkTupleConstr and + n.kind == nkCommand: + maybeWrongSpace = true for diag in err.diagnostics: candidates.add(diag & "\n") if skipped > 0: candidates.add($skipped & " other mismatching symbols have been " & "suppressed; compile with --showAllMismatches:on to see them\n") + if maybeWrongSpace: + candidates.add("maybe misplaced space between " & renderTree(n[0]) & " and '(' \n") + result = (prefer, candidates) const From eff0837ff40e4a5f5659ff02a56d9936bcbd7bcd Mon Sep 17 00:00:00 2001 From: Vindaar Date: Tue, 27 Aug 2019 22:23:47 +0200 Subject: [PATCH 35/61] fixes #12015 by also checking kind of `typeNode` (#12016) * fixes #12015 by also checking kind of `typeNode` If a tuple field is aliased it'll appear the same as a ref type in a call to `getType` if only for the kind of the resulting `NimNode` is checked (that is a `nnkBracketExpr`) * fix test case due to #12017 and add more realistic test case Adds an additional test case, which includes generics and is closer to the real failure I encountered * remove previous fix and fix differently after all The previous fix was incomplete, because it failed for generics. Note that the `of "tuple"` is not actually needed, the `nnkBracketExpr` branch in the `else` branch would catch it too, but I decided to introduce it for clarity. However, the latter is actually needed, because it seems for aliases of `seq` we end up in it. * update comment about global `%` proc in json test --- lib/pure/json.nim | 17 +++++++++++++---- tests/stdlib/tjsonmacro.nim | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 4 deletions(-) diff --git a/lib/pure/json.nim b/lib/pure/json.nim index 1ef08f547e..23b23b4a46 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -1369,11 +1369,20 @@ proc createConstructor(typeSym, jsonNode: NimNode): NimNode = for `forLoopI` in 0 ..< `jsonNode`.len: list[`forLoopI`] =`constructorNode`; list ) - + of "tuple": + let typeNode = getTypeImpl(typeSym) + result = createConstructor(typeNode, jsonNode) else: - # Generic type. + # Generic type or some `seq[T]` alias let obj = getType(typeSym) - result = processType(typeSym, obj, jsonNode, false) + case obj.kind + of nnkBracketExpr: + # probably a `seq[T]` alias + let typeNode = getTypeImpl(typeSym) + result = createConstructor(typeNode, jsonNode) + else: + # generic type + result = processType(typeSym, obj, jsonNode, false) of nnkSym: # Handle JsonNode. if ($typeSym).cmpIgnoreStyle("jsonnode") == 0: @@ -1385,7 +1394,7 @@ proc createConstructor(typeSym, jsonNode: NimNode): NimNode = if typeNode.typeKind == ntyDistinct: result = createConstructor(typeNode, jsonNode) elif obj.kind == nnkBracketExpr: - # When `Sym "Foo"` turns out to be a `ref object`. + # When `Sym "Foo"` turns out to be a `ref object` or `tuple` result = createConstructor(obj, jsonNode) else: result = processType(typeSym, obj, jsonNode, false) diff --git a/tests/stdlib/tjsonmacro.nim b/tests/stdlib/tjsonmacro.nim index 0521d558c6..85530065d6 100644 --- a/tests/stdlib/tjsonmacro.nim +++ b/tests/stdlib/tjsonmacro.nim @@ -517,6 +517,42 @@ when true: doAssert v.name == "smith" doAssert MyRef(w).name == "smith" +# bug #12015 +# The definition of the `%` proc needs to be here, since the `% c` calls below +# can only find our custom `%` proc for `Pix` if defined in global scope. +type + Pix = tuple[x, y: uint8, ch: uint16] +proc `%`(p: Pix): JsonNode = + result = %* { "x" : % p.x, + "y" : % p.y, + "ch" : % p.ch } +block: + type + Cluster = object + works: tuple[x, y: uint8, ch: uint16] # working + fails: Pix # previously broken + + let data = (x: 123'u8, y: 53'u8, ch: 1231'u16) + let c = Cluster(works: data, fails: data) + let cFromJson = (% c).to(Cluster) + doAssert c == cFromJson + +block: + # bug related to #12015 + type + PixInt = tuple[x, y, ch: int] + SomePix = Pix | PixInt + Cluster[T: SomePix] = seq[T] + ClusterObject[T: SomePix] = object + data: Cluster[T] + RecoEvent[T: SomePix] = object + cluster: seq[ClusterObject[T]] + + let data = @[(x: 123'u8, y: 53'u8, ch: 1231'u16)] + var c = RecoEvent[Pix](cluster: @[ClusterObject[Pix](data: data)]) + let cFromJson = (% c).to(RecoEvent[Pix]) + doAssert c == cFromJson + # TODO: when the issue with the limeted vm registers is solved, the # exact same test as above should be evaluated at compile time as # well, to ensure that the vm functionality won't diverge from the From d564130a3b2596161847d695329ad009e693358a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arne=20D=C3=B6ring?= Date: Tue, 27 Aug 2019 23:18:46 +0200 Subject: [PATCH 36/61] Fix to int to biggest int (#12066) * fix to(Biggest)Int * kill toFloat magics as well --- compiler/ast.nim | 4 ---- compiler/ccgexprs.nim | 10 +--------- compiler/jsgen.nim | 8 -------- compiler/semfold.nim | 3 --- compiler/sigmatch.nim | 2 +- compiler/vmgen.nim | 3 +-- lib/nimbase.h | 12 ------------ lib/system.nim | 34 +++++++++++++++++----------------- tests/system/tsystem_misc.nim | 15 +++++++++++++++ tools/dochack/fuzzysearch.nim | 16 ++++++++-------- 10 files changed, 43 insertions(+), 64 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index 45c8d5ace3..ddf81c8744 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -622,8 +622,6 @@ type mUnaryMinusI, mUnaryMinusI64, mAbsI, mNot, mUnaryPlusI, mBitnotI, mUnaryPlusF64, mUnaryMinusF64, mAbsF64, - mToFloat, mToBiggestFloat, - mToInt, mToBiggestInt, mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr, mAnd, mOr, @@ -692,8 +690,6 @@ const mEqRef, mEqProc, mEqUntracedRef, mLePtr, mLtPtr, mEqCString, mXor, mUnaryMinusI, mUnaryMinusI64, mAbsI, mNot, mUnaryPlusI, mBitnotI, mUnaryPlusF64, mUnaryMinusF64, mAbsF64, - mToFloat, mToBiggestFloat, - mToInt, mToBiggestInt, mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr, mAnd, mOr, diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 6e203dad5c..a882a3cee3 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -666,14 +666,6 @@ proc unaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mAbsF64: applyFormat("($1 < 0? -($1) : ($1))") # BUGFIX: fabs() makes problems for Tiny C - of mToFloat: - applyFormat("((double) ($1))") - of mToBiggestFloat: - applyFormat("((double) ($1))") - of mToInt: - applyFormat("float64ToInt32($1)") - of mToBiggestInt: - applyFormat("float64ToInt64($1)") else: assert false, $op @@ -2094,7 +2086,7 @@ proc genEnumToStr(p: BProc, e: PNode, d: var TLoc) = proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = case op of mOr, mAnd: genAndOr(p, e, d, op) - of mNot..mToBiggestInt: unaryArith(p, e, d, op) + of mNot..mAbsF64: unaryArith(p, e, d, op) of mUnaryMinusI..mAbsI: unaryArithOverflow(p, e, d, op) of mAddF64..mDivF64: binaryFloatArith(p, e, d, op) of mShrI..mXor: binaryArith(p, e, d, op) diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index f858bd32c1..5391a451b3 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -431,10 +431,6 @@ const # magic checked op; magic unchecked op; ["", ""], # UnaryPlusF64 ["", ""], # UnaryMinusF64 ["", ""], # AbsF64 - ["", ""], # ToFloat - ["", ""], # ToBiggestFloat - ["", ""], # ToInt - ["", ""], # ToBiggestInt ["nimCharToStr", "nimCharToStr"], ["nimBoolToStr", "nimBoolToStr"], ["cstrToNimstr", "cstrToNimstr"], @@ -612,10 +608,6 @@ proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic) = of mUnaryPlusF64: applyFormat("+($1)", "+($1)") of mUnaryMinusF64: applyFormat("-($1)", "-($1)") of mAbsF64: applyFormat("Math.abs($1)", "Math.abs($1)") - of mToFloat: applyFormat("$1", "$1") - of mToBiggestFloat: applyFormat("$1", "$1") - of mToInt: applyFormat("Math.trunc($1)", "Math.trunc($1)") - of mToBiggestInt: applyFormat("Math.trunc($1)", "Math.trunc($1)") of mCharToStr: applyFormat("nimCharToStr($1)", "nimCharToStr($1)") of mBoolToStr: applyFormat("nimBoolToStr($1)", "nimBoolToStr($1)") of mIntToStr: applyFormat("cstrToNimstr(($1)+\"\")", "cstrToNimstr(($1)+\"\")") diff --git a/compiler/semfold.nim b/compiler/semfold.nim index 564d7f0676..bfb5077d6a 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -196,10 +196,7 @@ proc evalOp(m: TMagic, n, a, b, c: PNode; g: ModuleGraph): PNode = else: result = newIntNodeT(toInt128(sonsLen(a)), n, g) of mUnaryPlusI, mUnaryPlusF64: result = a # throw `+` away - of mToFloat, mToBiggestFloat: - result = newFloatNodeT(toFloat64(getInt(a)), n, g) # XXX: Hides overflow/underflow - of mToInt, mToBiggestInt: result = newIntNodeT(system.toInt(getFloat(a)), n, g) of mAbsF64: result = newFloatNodeT(abs(getFloat(a)), n, g) of mAbsI: result = foldAbs(getInt(a), n, g) of mUnaryLt: result = foldSub(getOrdValue(a), One, n, g) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 327537595c..e4bf13e60e 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -873,7 +873,7 @@ proc inferStaticParam*(c: var TCandidate, lhs: PNode, rhs: BiggestInt): bool = of mUnaryMinusI: return inferStaticParam(c, lhs[1], -rhs) - of mUnaryPlusI, mToInt, mToBiggestInt: + of mUnaryPlusI: return inferStaticParam(c, lhs[1], rhs) else: discard diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 9cd3b841ef..15ddfe4968 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -1058,8 +1058,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = let t = skipTypes(n.typ, abstractVar-{tyTypeDesc}) if t.kind in {tyUInt8..tyUInt32} or (t.kind == tyUInt and t.size < 8): c.gABC(n, opcNarrowU, dest, TRegister(t.size*8)) - of mToFloat, mToBiggestFloat, mToInt, - mToBiggestInt, mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, + of mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr: genConv(c, n, n.sons[1], dest) of mEqStr, mEqCString: genBinaryABC(c, n, dest, opcEqStr) diff --git a/lib/nimbase.h b/lib/nimbase.h index a4bde0bd48..eadc34f664 100644 --- a/lib/nimbase.h +++ b/lib/nimbase.h @@ -374,18 +374,6 @@ typedef char* NCSTRING; # define NIM_IMAN 0 #endif -static N_INLINE(NI, float64ToInt32)(double x) { - /* nowadays no hack necessary anymore */ - return x >= 0 ? (NI)(x+0.5) : (NI)(x-0.5); -} - -static N_INLINE(NI32, float32ToInt32)(float x) { - /* nowadays no hack necessary anymore */ - return x >= 0 ? (NI32)(x+0.5) : (NI32)(x-0.5); -} - -#define float64ToInt64(x) ((NI64) (x)) - #define NIM_STRLIT_FLAG ((NU)(1) << ((NIM_INTBITS) - 2)) /* This has to be the same as system.strlitFlag! */ #define STRING_LITERAL(name, str, length) \ diff --git a/lib/system.nim b/lib/system.nim index 09280d6070..974c39a678 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1132,57 +1132,57 @@ proc chr*(u: range[0..255]): char {.magic: "Chr", noSideEffect.} # built-in operators when defined(nimNoZeroExtendMagic): - proc ze*(x: int8): int = + proc ze*(x: int8): int {.deprecated.} = ## zero extends a smaller integer type to ``int``. This treats `x` as ## unsigned. ## **Deprecated since version 0.19.9**: Use unsigned integers instead. cast[int](uint(cast[uint8](x))) - proc ze*(x: int16): int = + proc ze*(x: int16): int {.deprecated.} = ## zero extends a smaller integer type to ``int``. This treats `x` as ## unsigned. ## **Deprecated since version 0.19.9**: Use unsigned integers instead. cast[int](uint(cast[uint16](x))) - proc ze64*(x: int8): int64 = + proc ze64*(x: int8): int64 {.deprecated.} = ## zero extends a smaller integer type to ``int64``. This treats `x` as ## unsigned. ## **Deprecated since version 0.19.9**: Use unsigned integers instead. cast[int64](uint64(cast[uint8](x))) - proc ze64*(x: int16): int64 = + proc ze64*(x: int16): int64 {.deprecated.} = ## zero extends a smaller integer type to ``int64``. This treats `x` as ## unsigned. ## **Deprecated since version 0.19.9**: Use unsigned integers instead. cast[int64](uint64(cast[uint16](x))) - proc ze64*(x: int32): int64 = + proc ze64*(x: int32): int64 {.deprecated.} = ## zero extends a smaller integer type to ``int64``. This treats `x` as ## unsigned. ## **Deprecated since version 0.19.9**: Use unsigned integers instead. cast[int64](uint64(cast[uint32](x))) - proc ze64*(x: int): int64 = + proc ze64*(x: int): int64 {.deprecated.} = ## zero extends a smaller integer type to ``int64``. This treats `x` as ## unsigned. Does nothing if the size of an ``int`` is the same as ``int64``. ## (This is the case on 64 bit processors.) ## **Deprecated since version 0.19.9**: Use unsigned integers instead. cast[int64](uint64(cast[uint](x))) - proc toU8*(x: int): int8 = + proc toU8*(x: int): int8 {.deprecated.} = ## treats `x` as unsigned and converts it to a byte by taking the last 8 bits ## from `x`. ## **Deprecated since version 0.19.9**: Use unsigned integers instead. cast[int8](x) - proc toU16*(x: int): int16 = + proc toU16*(x: int): int16 {.deprecated.} = ## treats `x` as unsigned and converts it to an ``int16`` by taking the last ## 16 bits from `x`. ## **Deprecated since version 0.19.9**: Use unsigned integers instead. cast[int16](x) - proc toU32*(x: int64): int32 = + proc toU32*(x: int64): int32 {.deprecated.} = ## treats `x` as unsigned and converts it to an ``int32`` by taking the ## last 32 bits from `x`. ## **Deprecated since version 0.19.9**: Use unsigned integers instead. @@ -2281,8 +2281,7 @@ type # these work for most platforms: PInt64* = ptr int64 ## An alias for ``ptr int64``. PInt32* = ptr int32 ## An alias for ``ptr int32``. -proc toFloat*(i: int): float {. - magic: "ToFloat", noSideEffect, importc: "toFloat".} +proc toFloat*(i: int): float {.noSideEffect, inline.} = ## Converts an integer `i` into a ``float``. ## ## If the conversion fails, `ValueError` is raised. @@ -2294,13 +2293,13 @@ proc toFloat*(i: int): float {. ## b = 3.7 ## ## echo a.toFloat + b # => 5.7 + float(i) -proc toBiggestFloat*(i: BiggestInt): BiggestFloat {. - magic: "ToBiggestFloat", noSideEffect, importc: "toBiggestFloat".} +proc toBiggestFloat*(i: BiggestInt): BiggestFloat {.noSideEffect, inline.} = ## Same as `toFloat <#toFloat,int>`_ but for ``BiggestInt`` to ``BiggestFloat``. + BiggestFloat(i) -proc toInt*(f: float): int {. - magic: "ToInt", noSideEffect, importc: "toInt".} +proc toInt*(f: float): int {.noSideEffect.} = ## Converts a floating point number `f` into an ``int``. ## ## Conversion rounds `f` half away from 0, see @@ -2314,10 +2313,11 @@ proc toInt*(f: float): int {. ## doAssert toInt(0.49) == 0 ## doAssert toInt(0.5) == 1 ## doAssert toInt(-0.5) == -1 # rounding is symmetrical + if f >= 0: int(f+0.5) else: int(f-0.5) -proc toBiggestInt*(f: BiggestFloat): BiggestInt {. - magic: "ToBiggestInt", noSideEffect, importc: "toBiggestInt".} +proc toBiggestInt*(f: BiggestFloat): BiggestInt {.noSideEffect.} = ## Same as `toInt <#toInt,float>`_ but for ``BiggestFloat`` to ``BiggestInt``. + if f >= 0: BiggestInt(f+0.5) else: BiggestInt(f-0.5) proc addQuitProc*(quitProc: proc() {.noconv.}) {. importc: "atexit", header: "".} diff --git a/tests/system/tsystem_misc.nim b/tests/system/tsystem_misc.nim index a20e6b3bf3..9dcd9ac9fa 100644 --- a/tests/system/tsystem_misc.nim +++ b/tests/system/tsystem_misc.nim @@ -159,3 +159,18 @@ block: # `$`*[T: tuple|object](x: T) x2:float doAssert $Foo(x:2) == "(x: 2, x2: 0.0)" doAssert $() == "()" + + +# this is a call indirection to prevent `toInt` to be resolved at compile time. +proc testToInt(arg: float64, a: int, b: BiggestInt) = + doAssert toInt(arg) == a + doAssert toBiggestInt(arg) == b + +testToInt(0.45, 0, 0) # should round towards 0 +testToInt(-0.45, 0, 0) # should round towards 0 +testToInt(0.5, 1, 1) # should round away from 0 +testToInt(-0.5, -1, -1) # should round away from 0 +testToInt(13.37, 13, 13) # should round towards 0 +testToInt(-13.37, -13, -13) # should round towards 0 +testToInt(7.8, 8, 8) # should round away from 0 +testToInt(-7.8, -8, -8) # should round away from 0 diff --git a/tools/dochack/fuzzysearch.nim b/tools/dochack/fuzzysearch.nim index 69f9fce3c2..40575b998c 100644 --- a/tools/dochack/fuzzysearch.nim +++ b/tools/dochack/fuzzysearch.nim @@ -17,8 +17,8 @@ const ## This is to weight function signatures and descriptions over module titles. -type - ScoreCard = enum +type + ScoreCard = enum StartMatch = -100 ## Start matching. LeadingCharDiff = -3 ## An unmatched, leading character was found. CharDiff = -1 ## An unmatched character was found. @@ -58,19 +58,19 @@ proc fuzzyMatch*(pattern, str: cstring) : tuple[score: int, matched: bool] = if strChar in {'_', ' ', '.'}: strIndex += 1 continue - + # Since this algorithm will be used to search against Nim documentation, # the below logic prioritizes headers. if not headerMatched and strChar == ':': headerMatched = true scoreState = StartMatch - score = toInt(floor(HeadingScaleFactor * toFloat(score))) + score = int(floor(HeadingScaleFactor * float(score))) patIndex = 0 strIndex += 1 continue if strChar == patternChar: - case scoreState + case scoreState of StartMatch, WordBoundryMatch: scoreState = LeadingCharMatch @@ -84,7 +84,7 @@ proc fuzzyMatch*(pattern, str: cstring) : tuple[score: int, matched: bool] = if scoreState == LeadingCharMatch: score += ord(LeadingCharMatch) - + var onBoundary = (patIndex == high(pattern)) if not onBoundary and strIndex < high(str): let @@ -95,7 +95,7 @@ proc fuzzyMatch*(pattern, str: cstring) : tuple[score: int, matched: bool] = nextStrChar notin {'a'..'z'} and nextStrChar != nextPatternChar ) - + if onBoundary: transition(WordBoundryMatch) @@ -115,7 +115,7 @@ proc fuzzyMatch*(pattern, str: cstring) : tuple[score: int, matched: bool] = patIndex += 1 else: - case scoreState + case scoreState of StartMatch: transition(LeadingCharDiff) From 20dec10722eecb1e9b9ee43a3e1cda7533837c72 Mon Sep 17 00:00:00 2001 From: Clyybber Date: Wed, 28 Aug 2019 00:02:48 +0200 Subject: [PATCH 37/61] Refactored injectdestructors.nim (#11926) One improvement over #devel is visible in the transformation of getEnv. With this approach we move to result whenever possible. --- compiler/injectdestructors.nim | 725 ++++++++++++++++----------------- 1 file changed, 342 insertions(+), 383 deletions(-) diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index 7c2a15ce3c..cd5fc2f11e 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -138,9 +138,6 @@ import strutils, options, dfa, lowerings, tables, modulegraphs, msgs, lineinfos, parampatterns, sighashes -const - InterestingSyms = {skVar, skResult, skLet, skForVar, skTemp} - type Con = object owner: PSym @@ -217,43 +214,6 @@ proc isLastRead(n: PNode; c: var Con): bool = dbg: echo "ugh ", c.otherRead.isNil, " ", result - when false: - let s = n.sym - var pcs: seq[int] = @[instr+1] - var takenGotos: IntSet - var takenForks = initIntSet() - while pcs.len > 0: - var pc = pcs.pop - - takenGotos = initIntSet() - while pc < c.g.len: - case c.g[pc].kind - of def: - if c.g[pc].sym == s: - # the path lead to a redefinition of 's' --> abandon it. - break - inc pc - of use: - if c.g[pc].sym == s: - c.otherRead = c.g[pc].n - return false - inc pc - of goto: - # we must leave endless loops eventually: - if not takenGotos.containsOrIncl(pc): - pc = pc + c.g[pc].dest - else: - inc pc - of fork: - # we follow the next instruction but push the dest onto our "work" stack: - if not takenForks.containsOrIncl(pc): - pcs.add pc + c.g[pc].dest - inc pc - of InstrKind.join: - inc pc - #echo c.graph.config $ n.info, " last read here!" - return true - proc initialized(code: ControlFlowGraph; pc: int, init, uninit: var IntSet; comesFrom: int): int = ## Computes the set of definitely initialized variables accross all code paths @@ -290,9 +250,6 @@ proc initialized(code: ControlFlowGraph; pc: int, inc pc return pc -template interestingSym(s: PSym): bool = - s.owner == c.owner and s.kind in InterestingSyms and hasDestructor(s.typ) - template isUnpackedTuple(s: PSym): bool = ## we move out all elements of unpacked tuples, ## hence unpacked tuples themselves don't need to be destroyed @@ -353,8 +310,8 @@ proc canBeMoved(t: PType): bool {.inline.} = let t = t.skipTypes({tyGenericInst, tyAlias, tySink}) result = t.kind != tyRef and t.attachedOps[attachedSink] != nil -proc genSink(c: Con; t: PType; dest, ri: PNode): PNode = - let t = t.skipTypes({tyGenericInst, tyAlias, tySink}) +proc genSink(c: Con; dest, ri: PNode): PNode = + let t = dest.typ.skipTypes({tyGenericInst, tyAlias, tySink}) let k = if t.attachedOps[attachedSink] != nil: attachedSink else: attachedAsgn if t.attachedOps[k] != nil: @@ -365,20 +322,20 @@ proc genSink(c: Con; t: PType; dest, ri: PNode): PNode = # we generate a fast assignment in this case: result = newTree(nkFastAsgn, dest) -proc genCopy(c: var Con; t: PType; dest, ri: PNode): PNode = +proc genCopyNoCheck(c: Con; dest, ri: PNode): PNode = + let t = dest.typ.skipTypes({tyGenericInst, tyAlias, tySink}) + result = genOp(c, t, attachedAsgn, dest, ri) + +proc genCopy(c: var Con; dest, ri: PNode): PNode = + let t = dest.typ if tfHasOwned in t.flags: # try to improve the error message here: if c.otherRead == nil: discard isLastRead(ri, c) checkForErrorPragma(c, t, ri, "=") - let t = t.skipTypes({tyGenericInst, tyAlias, tySink}) - result = genOp(c, t, attachedAsgn, dest, ri) + genCopyNoCheck(c, dest, ri) -proc genCopyNoCheck(c: Con; t: PType; dest, ri: PNode): PNode = - let t = t.skipTypes({tyGenericInst, tyAlias, tySink}) - result = genOp(c, t, attachedAsgn, dest, ri) - -proc genDestroy(c: Con; t: PType; dest: PNode): PNode = - let t = t.skipTypes({tyGenericInst, tyAlias, tySink}) +proc genDestroy(c: Con; dest: PNode): PNode = + let t = dest.typ.skipTypes({tyGenericInst, tyAlias, tySink}) result = genOp(c, t, attachedDestructor, dest, nil) proc addTopVar(c: var Con; v: PNode) = @@ -390,20 +347,10 @@ proc getTemp(c: var Con; typ: PType; info: TLineInfo): PNode = result = newSymNode(sym) c.addTopVar(result) -proc p(n: PNode; c: var Con): PNode - -template recurse(n, dest) = - for i in 0.. 0 and n.typ != nil and isDangerousSeq(n.typ): @@ -467,19 +396,151 @@ proc containsConstSeq(n: PNode): bool = of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv: result = containsConstSeq(n[1]) of nkObjConstr, nkClosure: - for i in 1 ..< n.len: + for i in 1.. 0: + ri = genDefaultCall(v.typ, c, v.info) + if ri.kind != nkEmpty: + let r = moveOrCopy(v, ri, c) + result.add r + else: + result.add keepVar(n, it, c) + of nkCallKinds: + let parameters = n[0].typ + let L = if parameters != nil: parameters.len else: 0 + for i in 1.. 0 and isDangerousSeq(ri.typ): - result = genCopy(c, dest.typ, dest, ri) + result = genCopy(c, dest, ri) else: - result = genSink(c, dest.typ, dest, ri) - let ri2 = copyTree(ri) - for i in 0.. 0: - ri = genDefaultCall(v.typ, c, v.info) - if ri.kind != nkEmpty: - let r = moveOrCopy(v, ri, c) - result.add r - else: - result.add keepVar(n, it, c) - of nkCallKinds: - let parameters = n[0].typ - let L = if parameters != nil: parameters.len else: 0 - for i in 1 ..< n.len: - n.sons[i] = pArg(n[i], c, i < L and isSinkTypeForParam(parameters[i])) - if n.typ != nil and hasDestructor(n.typ): - discard "produce temp creation" - result = newNodeIT(nkStmtListExpr, n.info, n.typ) - let tmp = getTemp(c, n.typ, n.info) - var sinkExpr = genSink(c, n.typ, tmp, n) - sinkExpr.add n - result.add sinkExpr - result.add tmp - c.destroys.add genDestroy(c, n.typ, tmp) - else: - result = n - of nkAsgn, nkFastAsgn: - if hasDestructor(n[0].typ) and n[1].kind notin {nkProcDef, nkDo, nkLambda}: - # rule (self-assignment-removal): - if n[1].kind == nkSym and n[0].kind == nkSym and n[0].sym == n[1].sym: - result = newNodeI(nkEmpty, n.info) - else: - result = moveOrCopy(n[0], n[1], c) - else: - result = copyNode(n) - recurse(n, result) - of nkNone..nkNilLit, nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, - nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo, nkFuncDef: - result = n - of nkCast, nkHiddenStdConv, nkHiddenSubConv, nkConv: - result = copyNode(n) - # Destination type - result.add n[0] - # Analyse the inner expression - result.add p(n[1], c) - of nkWhen: - # This should be a "when nimvm" node. - result = copyTree(n) - result[1][0] = p(result[1][0], c) - of nkRaiseStmt: - if optNimV2 in c.graph.config.globalOptions and n[0].kind != nkEmpty: - if n[0].kind in nkCallKinds: - let call = copyNode(n[0]) - recurse(n[0], call) - result = copyNode(n) - result.add call - else: - let t = n[0].typ - let tmp = getTemp(c, t, n.info) - var m = genCopyNoCheck(c, t, tmp, n[0]) - - m.add p(n[0], c) - result = newTree(nkStmtList, genWasMoved(tmp, c), m) - var toDisarm = n[0] - if toDisarm.kind == nkStmtListExpr: toDisarm = toDisarm.lastSon - if toDisarm.kind == nkSym and toDisarm.sym.owner == c.owner: - result.add genWasMoved(toDisarm, c) - result.add newTree(nkRaiseStmt, tmp) - else: - result = copyNode(n) - recurse(n, result) - of nkForStmt, nkParForStmt, nkWhileStmt: - inc c.inLoop - result = copyNode(n) - recurse(n, result) - dec c.inLoop - else: - result = copyNode(n) - recurse(n, result) - proc extractDestroysForTemporaries(c: Con, destroys: PNode): PNode = result = newNodeI(nkStmtList, destroys.info) - for i in 0 ..< destroys.len: + for i in 0.. 0: result.add c.topLevelVars if c.destroys.len > 0: - reverseDestroys(c.destroys) + c.destroys.sons = reverseDestroys(c.destroys.sons) if owner.kind == skModule: result.add newTryFinally(body, extractDestroysForTemporaries(c, c.destroys)) g.globalDestructors.add c.destroys @@ -900,6 +860,5 @@ proc injectDestructorCalls*(g: ModuleGraph; owner: PSym; n: PNode): PNode = result.add body dbg: - echo "------------------------------------" - echo owner.name.s, " transformed to: " + echo ">---------transformed-to--------->" echo result From d4af0554c43fc6db8c1078169be0c216a00c1628 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 28 Aug 2019 10:25:13 +0200 Subject: [PATCH 38/61] remove unicode.Rune16 without deprecation period; fixes https://github.com/nim-lang/RFCs/issues/151 (#12072) --- changelog.md | 12 ++++++++++++ lib/pure/unicode.nim | 5 ----- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/changelog.md b/changelog.md index 749959e3c6..61e6711db9 100644 --- a/changelog.md +++ b/changelog.md @@ -19,6 +19,18 @@ ### Breaking changes in the standard library +- We removed `unicode.Rune16` without any deprecation period as the name + was wrong (see the [RFC](https://github.com/nim-lang/RFCs/issues/151) for details) + and we didn't find any usages of it in the wild. If you still need it, add this + piece of code to your project: + +```nim + +type + Rune16* = distinct int16 + +``` + ### Breaking changes in the compiler diff --git a/lib/pure/unicode.nim b/lib/pure/unicode.nim index 33b720f623..30d8053344 100644 --- a/lib/pure/unicode.nim +++ b/lib/pure/unicode.nim @@ -31,11 +31,6 @@ type ## Type that can hold a single Unicode code point. ## ## A Rune may be composed with other Runes to a character on the screen. - Rune16* = distinct int16 ## \ - ## Type that can hold a single UTF-16 encoded character. - ## - ## A single Rune16 may not be enough to hold an arbitrary Unicode code point. - template ones(n: untyped): untyped = ((1 shl n)-1) From 84351da9d85867083de9376f7e08b1c840be6bc0 Mon Sep 17 00:00:00 2001 From: Ico Doornekamp Date: Wed, 28 Aug 2019 12:05:07 +0200 Subject: [PATCH 39/61] Fixes #12044 (#12071) --- lib/system/assertions.nim | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/system/assertions.nim b/lib/system/assertions.nim index 0deb957c97..2131394e03 100644 --- a/lib/system/assertions.nim +++ b/lib/system/assertions.nim @@ -27,12 +27,12 @@ proc failedAssertImpl*(msg: string) {.raises: [], tags: [].} = Hide(raiseAssert)(msg) template assertImpl(cond: bool, msg: string, expr: string, enabled: static[bool]) = - const - loc = instantiationInfo(fullPaths = compileOption("excessiveStackTrace")) - ploc = $loc - bind instantiationInfo - mixin failedAssertImpl when enabled: + const + loc = instantiationInfo(fullPaths = compileOption("excessiveStackTrace")) + ploc = $loc + bind instantiationInfo + mixin failedAssertImpl {.line: loc.}: if not cond: failedAssertImpl(ploc & " `" & expr & "` " & msg) From c9f49cbc0a9314585ca70b3365e49ab82dfdcc13 Mon Sep 17 00:00:00 2001 From: cooldome Date: Wed, 28 Aug 2019 11:07:46 +0100 Subject: [PATCH 40/61] lift destructor for openarray (#12073) * destroy for sink openarray --- compiler/ast.nim | 4 ++-- compiler/ccgtypes.nim | 4 ++-- compiler/liftdestructors.nim | 8 ++++++-- compiler/sem.nim | 10 ++++------ tests/destructor/tmove_objconstr.nim | 17 ++++++++++++++++- 5 files changed, 30 insertions(+), 13 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index ddf81c8744..6e5006bb75 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -1496,14 +1496,14 @@ proc propagateToOwner*(owner, elem: PType) = if tfHasAsgn in elem.flags: let o2 = owner.skipTypes({tyGenericInst, tyAlias, tySink}) if o2.kind in {tyTuple, tyObject, tyArray, - tySequence, tyOpt, tySet, tyDistinct}: + tySequence, tyOpt, tySet, tyDistinct, tyOpenArray, tyVarargs}: o2.flags.incl tfHasAsgn owner.flags.incl tfHasAsgn if tfHasOwned in elem.flags: let o2 = owner.skipTypes({tyGenericInst, tyAlias, tySink}) if o2.kind in {tyTuple, tyObject, tyArray, - tySequence, tyOpt, tySet, tyDistinct}: + tySequence, tyOpt, tySet, tyDistinct, tyOpenArray, tyVarargs}: o2.flags.incl tfHasOwned owner.flags.incl tfHasOwned diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 584e69b30f..1e38267801 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -469,7 +469,7 @@ proc genProcParams(m: BModule, t: PType, rettype, params: var Rope, add(params, param.loc.r) # declare the len field for open arrays: var arr = param.typ - if arr.kind in {tyVar, tyLent}: arr = arr.lastSon + if arr.kind in {tyVar, tyLent, tySink}: arr = arr.lastSon var j = 0 while arr.kind in {tyOpenArray, tyVarargs}: # this fixes the 'sort' bug: @@ -477,7 +477,7 @@ proc genProcParams(m: BModule, t: PType, rettype, params: var Rope, # need to pass hidden parameter: addf(params, ", NI $1Len_$2", [param.loc.r, j.rope]) inc(j) - arr = arr.sons[0] + arr = arr.sons[0].skipTypes({tySink}) if t.sons[0] != nil and isInvalidReturnType(m.config, t.sons[0]): var arr = t.sons[0] if params != nil: add(params, ", ") diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim index abd294e5ab..71f0baecf1 100644 --- a/compiler/liftdestructors.nim +++ b/compiler/liftdestructors.nim @@ -506,7 +506,11 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) = of tyTuple: fillBodyTup(c, t, body, x, y) of tyVarargs, tyOpenArray: - localError(c.g.config, c.info, "cannot copy openArray") + if c.kind == attachedDestructor: + forallElements(c, t, body, x, y) + else: + discard "cannot copy openArray" + of tyFromExpr, tyProxy, tyBuiltInTypeClass, tyUserTypeClass, tyUserTypeClassInst, tyCompositeTypeClass, tyAnd, tyOr, tyNot, tyAnything, tyGenericParam, tyGenericBody, tyNil, tyUntyped, tyTyped, @@ -626,7 +630,7 @@ proc createTypeBoundOps(g: ModuleGraph; c: PContext; orig: PType; info: TLineInf var canon = g.canonTypes.getOrDefault(h) var overwrite = false if canon == nil: - let typ = orig.skipTypes({tyGenericInst, tyAlias}) + let typ = orig.skipTypes({tyGenericInst, tyAlias, tySink}) g.canonTypes[h] = typ canon = typ elif canon != orig: diff --git a/compiler/sem.nim b/compiler/sem.nim index 7c47dac22d..70a84501f3 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -77,14 +77,12 @@ template semIdeForTemplateOrGeneric(c: PContext; n: PNode; discard safeSemExpr(c, n) proc fitNodePostMatch(c: PContext, formal: PType, arg: PNode): PNode = - result = arg - let x = result.skipConv + let x = arg.skipConv if x.kind in {nkPar, nkTupleConstr, nkCurly} and formal.kind != tyUntyped: changeType(c, x, formal, check=true) - else: - result = skipHiddenSubConv(result) - #result.typ = takeType(formal, arg.typ) - #echo arg.info, " picked ", result.typ.typeToString + result = arg + result = skipHiddenSubConv(result) + proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode = if arg.typ.isNil: diff --git a/tests/destructor/tmove_objconstr.nim b/tests/destructor/tmove_objconstr.nim index bfc819ceb1..be92d1503c 100644 --- a/tests/destructor/tmove_objconstr.nim +++ b/tests/destructor/tmove_objconstr.nim @@ -175,4 +175,19 @@ proc myfuncLoop(x: int): MySeqNonCopyable = var cc = newMySeq(i, 5.0) result = cc -discard myfuncLoop(3) \ No newline at end of file +discard myfuncLoop(3) + +#------------------------------------------------------------ +# Move into table via openarray +#------------------------------------------------------------ + +type + TableNonCopyable = object + x: seq[(string, MySeqNonCopyable)] + +proc toTable(pairs: sink openArray[(string, MySeqNonCopyable)]): TableNonCopyable = + discard + + +let mytable = {"a": newMySeq(2, 5.0)}.toTable + From 42d2e68bca2d7fce65591ed7c4697ed1ecf86026 Mon Sep 17 00:00:00 2001 From: Jacek Sieka Date: Wed, 28 Aug 2019 12:08:54 +0200 Subject: [PATCH 41/61] only store finally block in exception stack (#11876) --- compiler/ccgstmts.nim | 20 +++++++++++--------- compiler/cgendata.nim | 2 +- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index 337b369043..bfd053f2d3 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -199,7 +199,7 @@ proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int) = # Called by return and break stmts. # Deals with issues faced when jumping out of try/except/finally stmts, - var stack = newSeq[tuple[n: PNode, inExcept: bool]](0) + var stack = newSeq[tuple[fin: PNode, inExcept: bool]](0) for i in 1 .. howManyTrys: let tryStmt = p.nestedTryStmts.pop @@ -214,9 +214,9 @@ proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int) = # Find finally-stmt for this try-stmt # and generate a copy of its sons - var finallyStmt = lastSon(tryStmt.n) - if finallyStmt.kind == nkFinally: - genStmts(p, finallyStmt.sons[0]) + var finallyStmt = tryStmt.fin + if finallyStmt != nil: + genStmts(p, finallyStmt[0]) # push old elements again: for i in countdown(howManyTrys-1, 0): @@ -675,8 +675,8 @@ proc genRaiseStmt(p: BProc, t: PNode) = if p.nestedTryStmts.len > 0 and p.nestedTryStmts[^1].inExcept: # if the current try stmt have a finally block, # we must execute it before reraising - var finallyBlock = p.nestedTryStmts[^1].n[^1] - if finallyBlock.kind == nkFinally: + let finallyBlock = p.nestedTryStmts[^1].fin + if finallyBlock != nil: genSimpleBlock(p, finallyBlock[0]) if t[0].kind != nkEmpty: var a: TLoc @@ -924,7 +924,8 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) = getTemp(p, t.typ, d) genLineDir(p, t) discard cgsym(p.module, "popCurrentExceptionEx") - add(p.nestedTryStmts, (t, false)) + let fin = if t[^1].kind == nkFinally: t[^1] else: nil + add(p.nestedTryStmts, (fin, false)) startBlock(p, "try {$n") expr(p, t[0], d) endBlock(p) @@ -1021,8 +1022,9 @@ proc genTry(p: BProc, t: PNode, d: var TLoc) = else: linefmt(p, cpsStmts, "$1.status = setjmp($1.context);$n", [safePoint]) startBlock(p, "if ($1.status == 0) {$n", [safePoint]) - var length = sonsLen(t) - add(p.nestedTryStmts, (t, quirkyExceptions)) + let length = sonsLen(t) + let fin = if t[^1].kind == nkFinally: t[^1] else: nil + add(p.nestedTryStmts, (fin, quirkyExceptions)) expr(p, t.sons[0], d) if not quirkyExceptions: linefmt(p, cpsStmts, "#popSafePoint();$n", []) diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim index bd35f2b87e..47b0f23ec8 100644 --- a/compiler/cgendata.nim +++ b/compiler/cgendata.nim @@ -73,7 +73,7 @@ type noSafePoints*: bool # the proc doesn't use safe points in exception handling lastLineInfo*: TLineInfo # to avoid generating excessive 'nimln' statements currLineInfo*: TLineInfo # AST codegen will make this superfluous - nestedTryStmts*: seq[tuple[n: PNode, inExcept: bool]] + nestedTryStmts*: seq[tuple[fin: PNode, inExcept: bool]] # in how many nested try statements we are # (the vars must be volatile then) # bool is true when are in the except part of a try block From 21fc8b4d4d82637fab831cffed0e5b995ec7f16c Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 28 Aug 2019 19:36:29 +0200 Subject: [PATCH 42/61] refactor sizealignoffset (#12077) * small refactoring * refactor computeObjectOffsetFoldFunction with AccumObject * refactor packed object offstes fold function * refactor compute union object offsets fold function * merge normal/packed object offset fold function * compiletime offsetof in c++ inheritance objects * enable c++ inheritance offsetof tests * correct alignment for big sets/enums on weird 32bit platforms * uncomputedSize -> unknownSize * workaround for travis * fixes win32 alignment problems --- compiler/ast.nim | 1 + compiler/options.nim | 10 + compiler/pragmas.nim | 10 +- compiler/semtypes.nim | 9 +- compiler/sizealignoffsetimpl.nim | 345 +++++++++++++------------------ tests/misc/tsizeof.nim | 53 +++-- 6 files changed, 198 insertions(+), 230 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index 6e5006bb75..71d19be30b 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -899,6 +899,7 @@ type size*: BiggestInt # the size of the type in bytes # -1 means that the size is unkwown align*: int16 # the type's alignment requirements + paddingAtEnd*: int16 # lockLevel*: TLockLevel # lock level as required for deadlock checking loc*: TLoc typeInst*: PType # for generic instantiations the tyGenericInst that led to this diff --git a/compiler/options.nim b/compiler/options.nim index 52ecd61bc4..8b5ade7270 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -719,3 +719,13 @@ proc `$`*(c: IdeCmd): string = of ideOutline: "outline" of ideKnown: "known" of ideMsg: "msg" + +proc floatInt64Align*(conf: ConfigRef): int16 = + ## Returns either 4 or 8 depending on reasons. + if conf.target.targetCPU == cpuI386: + #on Linux/BSD i386, double are aligned to 4bytes (except with -malign-double) + if conf.target.targetOS != osWindows: + # on i386 for all known POSIX systems, 64bits ints are aligned + # to 4bytes (except with -malign-double) + return 4 + return 8 diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 12b0cff874..000066b8e9 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -816,12 +816,12 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, if sym.typ == nil: invalidPragma(c, it) var size = expectIntLit(c, it) case size - of 1, 2, 4, 8: + of 1, 2, 4: sym.typ.size = size - if size == 8 and c.config.target.targetCPU == cpuI386: - sym.typ.align = 4 - else: - sym.typ.align = int16(size) + sym.typ.align = int16 size + of 8: + sym.typ.size = 8 + sym.typ.align = floatInt64Align(c.config) else: localError(c.config, it.info, "size may only be 1, 2, 4 or 8") of wNodecl: diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 16cc89e137..6e542237b4 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -1819,13 +1819,8 @@ proc setMagicType(conf: ConfigRef; m: PSym, kind: TTypeKind, size: int) = # FIXME: proper support for clongdouble should be added. # long double size can be 8, 10, 12, 16 bytes depending on platform & compiler - if conf.target.targetCPU == cpuI386 and size == 8: - #on Linux/BSD i386, double are aligned to 4bytes (except with -malign-double) - if conf.target.targetOS != osWindows: - if kind in {tyFloat64, tyFloat, tyInt, tyUInt, tyInt64, tyUInt64}: - # on i386 for all known POSIX systems, 64bits ints are aligned - # to 4bytes (except with -malign-double) - m.typ.align = 4 + if kind in {tyFloat64, tyFloat, tyInt, tyUInt, tyInt64, tyUInt64} and size == 8: + m.typ.align = int16(conf.floatInt64Align) proc setMagicIntegral(conf: ConfigRef; m: PSym, kind: TTypeKind, size: int) = setMagicType(conf, m, kind, size) diff --git a/compiler/sizealignoffsetimpl.nim b/compiler/sizealignoffsetimpl.nim index 6028ba5eef..66b8ba1c93 100644 --- a/compiler/sizealignoffsetimpl.nim +++ b/compiler/sizealignoffsetimpl.nim @@ -7,6 +7,7 @@ # ## code owner: Arne Döring ## e-mail: arne.doering@gmx.net +## included from types.nim proc align(address, alignment: BiggestInt): BiggestInt = result = (address + (alignment - 1)) and not (alignment - 1) @@ -14,7 +15,6 @@ proc align(address, alignment: BiggestInt): BiggestInt = proc align(address, alignment: int): int = result = (address + (alignment - 1)) and not (alignment - 1) - const ## a size is concidered "unknown" when it is an imported type from C ## or C++. @@ -39,6 +39,13 @@ proc inc(arg: var OffsetAccum; value: int) = else: arg.offset += value +proc alignmentMax(a,b: int): int = + if unlikely(a == szIllegalRecursion or b == szIllegalRecursion): raiseIllegalTypeRecursion() + if a == szUnknownSize or b == szUnknownSize: + szUnknownSize + else: + max(a,b) + proc align(arg: var OffsetAccum; value: int) = if unlikely(value == szIllegalRecursion): raiseIllegalTypeRecursion() if value == szUnknownSize or arg.maxAlign == szUnknownSize or arg.offset == szUnknownSize: @@ -48,11 +55,22 @@ proc align(arg: var OffsetAccum; value: int) = arg.maxAlign = max(value, arg.maxAlign) arg.offset = align(arg.offset, value) -proc finish(arg: var OffsetAccum) = - if arg.maxAlign == szUnknownSize or arg.offset == szUnknownSize: +proc mergeBranch(arg: var OffsetAccum; value: OffsetAccum) = + if value.maxAlign == szUnknownSize or arg.maxAlign == szUnknownSize or + value.offset == szUnknownSize or arg.offset == szUnknownSize: + arg.maxAlign = szUnknownSize arg.offset = szUnknownSize else: - arg.offset = align(arg.offset, arg.maxAlign) + arg.offset = max(arg.offset, value.offset) + arg.maxAlign = max(arg.maxAlign, value.maxAlign) + +proc finish(arg: var OffsetAccum): int = + if arg.maxAlign == szUnknownSize or arg.offset == szUnknownSize: + result = szUnknownSize + arg.offset = szUnknownSize + else: + result = align(arg.offset, arg.maxAlign) - arg.offset + arg.offset += result proc computeSizeAlign(conf: ConfigRef; typ: PType) @@ -93,154 +111,69 @@ proc setOffsetsToUnknown(n: PNode) = for i in 0 ..< safeLen(n): setOffsetsToUnknown(n[i]) -proc computeObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode, - initialOffset: BiggestInt): tuple[offset, align: BiggestInt] = +proc computeObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode, packed: bool, accum: var OffsetAccum): void = ## ``offset`` is the offset within the object, after the node has been written, no padding bytes added ## ``align`` maximum alignment from all sub nodes assert n != nil if n.typ != nil and n.typ.size == szIllegalRecursion: - result.offset = szIllegalRecursion - result.align = szIllegalRecursion - return - - result.align = 1 + raiseIllegalTypeRecursion() case n.kind of nkRecCase: assert(n.sons[0].kind == nkSym) - let (kindOffset, kindAlign) = computeObjectOffsetsFoldFunction(conf, n.sons[0], initialOffset) - - var maxChildAlign: BiggestInt = if initialOffset == szUnknownSize: szUnknownSize else: 0 - for i in 1 ..< sonsLen(n): - let child = n.sons[i] - case child.kind - of nkOfBranch, nkElse: - # offset parameter cannot be known yet, it needs to know the alignment first - let align = computeSubObjectAlign(conf, n.sons[i].lastSon) - if align == szIllegalRecursion: - result.offset = szIllegalRecursion - result.align = szIllegalRecursion - return - if align == szUnknownSize or maxChildAlign == szUnknownSize: - maxChildAlign = szUnknownSize + computeObjectOffsetsFoldFunction(conf, n.sons[0], packed, accum) + var maxChildAlign: int = if accum.offset == szUnknownSize: szUnknownSize else: 1 + if not packed: + for i in 1 ..< sonsLen(n): + let child = n.sons[i] + case child.kind + of nkOfBranch, nkElse: + # offset parameter cannot be known yet, it needs to know the alignment first + let align = int(computeSubObjectAlign(conf, n.sons[i].lastSon)) + maxChildAlign = alignmentMax(maxChildAlign, align) else: - maxChildAlign = max(maxChildAlign, align) - else: - internalError(conf, "computeObjectOffsetsFoldFunction(record case branch)") + internalError(conf, "computeObjectOffsetsFoldFunction(record case branch)") if maxChildAlign == szUnknownSize: setOffsetsToUnknown(n) - result.align = szUnknownSize - result.offset = szUnknownSize + accum.offset = szUnknownSize + accum.maxAlign = szUnknownSize else: # the union neds to be aligned first, before the offsets can be assigned - let kindUnionOffset = align(kindOffset, maxChildAlign) - var maxChildOffset: BiggestInt = 0 + accum.align(maxChildAlign) + let accumRoot = accum # copy, because each branch should start af the same offset for i in 1 ..< sonsLen(n): - let (offset, align) = computeObjectOffsetsFoldFunction(conf, n.sons[i].lastSon, kindUnionOffset) - maxChildOffset = max(maxChildOffset, offset) - result.align = max(kindAlign, maxChildAlign) - result.offset = maxChildOffset + var branchAccum = accumRoot + computeObjectOffsetsFoldFunction(conf, n.sons[i].lastSon, packed, branchAccum) + accum.mergeBranch(branchAccum) of nkRecList: - result.align = 1 # maximum of all member alignments - var offset = initialOffset for i, child in n.sons: - let (newOffset, align) = computeObjectOffsetsFoldFunction(conf, child, offset) - if newOffset == szIllegalRecursion: - result.offset = szIllegalRecursion - result.align = szIllegalRecursion - return - elif newOffset == szUnknownSize or offset == szUnknownSize: - # if anything is unknown, the rest becomes unknown as well - offset = szUnknownSize - result.align = szUnknownSize - else: - offset = newOffset - result.align = max(result.align, align) - # final alignment - if offset == szUnknownSize: - result.offset = szUnknownSize - else: - result.offset = align(offset, result.align) + computeObjectOffsetsFoldFunction(conf, child, packed, accum) of nkSym: var size = szUnknownSize var align = szUnknownSize if n.sym.bitsize == 0: # 0 represents bitsize not set computeSizeAlign(conf, n.sym.typ) size = n.sym.typ.size.int - align = n.sym.typ.align.int - - result.align = align - if initialOffset == szUnknownSize or size == szUnknownSize or align == szUnknownSize: - n.sym.offset = szUnknownSize - result.offset = szUnknownSize - else: - n.sym.offset = align(initialOffset, align).int - result.offset = n.sym.offset + n.sym.typ.size + align = if packed: 1 else: n.sym.typ.align.int + accum.align(align) + n.sym.offset = accum.offset + accum.inc(size) else: - result.align = szUnknownSize - result.offset = szUnknownSize + accum.maxAlign = szUnknownSize + accum.offset = szUnknownSize -proc computePackedObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode, initialOffset: BiggestInt, debug: bool): BiggestInt = - ## ``result`` is the offset within the object, after the node has been written, no padding bytes added +proc computeUnionObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode; accum: var OffsetAccum) = + ## ``accum.offset`` will the offset from the larget member of the union. case n.kind of nkRecCase: - assert(n.sons[0].kind == nkSym) - let kindOffset = computePackedObjectOffsetsFoldFunction(conf, n.sons[0], initialOffset, debug) - # the union neds to be aligned first, before the offsets can be assigned - let kindUnionOffset = kindOffset - var maxChildOffset: BiggestInt = kindUnionOffset - for i in 1 ..< sonsLen(n): - let offset = computePackedObjectOffsetsFoldFunction(conf, n.sons[i].lastSon, kindUnionOffset, debug) - if offset == szIllegalRecursion: - return szIllegalRecursion - if offset == szUnknownSize or maxChildOffset == szUnknownSize: - maxChildOffset = szUnknownSize - else: - maxChildOffset = max(maxChildOffset, offset) - result = maxChildOffset - of nkRecList: - result = initialOffset - for i, child in n.sons: - result = computePackedObjectOffsetsFoldFunction(conf, child, result, debug) - if result == szIllegalRecursion: - break - of nkSym: - var size = szUnknownSize - if n.sym.bitsize == 0: - computeSizeAlign(conf, n.sym.typ) - size = n.sym.typ.size.int - - if initialOffset == szUnknownSize or size == szUnknownSize: - n.sym.offset = szUnknownSize - result = szUnknownSize - else: - n.sym.offset = int(initialOffset) - result = initialOffset + n.sym.typ.size - else: - result = szUnknownSize - -proc computeUnionObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode, debug: bool): tuple[offset, align: BiggestInt] = - ## ``result`` is the offset from the larget member of the union. - case n.kind - of nkRecCase: - result.offset = szUnknownSize - result.align = szUnknownSize + accum.offset = szUnknownSize + accum.maxAlign = szUnknownSize localError(conf, n.info, "Illegal use of ``case`` in union type.") - #internalError(conf, "Illegal use of ``case`` in union type.") of nkRecList: - var maxChildOffset: BiggestInt = 0 + let accumRoot = accum # copy, because each branch should start af the same offset for i, child in n.sons: - let (offset, align) = computeUnionObjectOffsetsFoldFunction(conf, child, debug) - if offset == szIllegalRecursion or align == szIllegalRecursion: - result.offset = szIllegalRecursion - result.align = szIllegalRecursion - elif offset == szUnknownSize or align == szUnknownSize: - result.offset = szUnknownSize - result.align = szUnknownSize - else: - assert offset != szUncomputedSize - assert align != szUncomputedSize - result.offset = max(result.offset, offset) - result.align = max(result.align, align) + var branchAccum = accumRoot + computeUnionObjectOffsetsFoldFunction(conf, child, branchAccum) + accum.mergeBranch(branchAccum) of nkSym: var size = szUnknownSize var align = szUnknownSize @@ -248,17 +181,12 @@ proc computeUnionObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode, debug: boo computeSizeAlign(conf, n.sym.typ) size = n.sym.typ.size.int align = n.sym.typ.align.int - - result.align = align - if size == szUnknownSize: - n.sym.offset = szUnknownSize - result.offset = szUnknownSize - else: - n.sym.offset = 0 - result.offset = n.sym.typ.size + accum.align(align) + n.sym.offset = accum.offset + accum.inc(size) else: - result.offset = szUnknownSize - result.align = szUnknownSize + accum.maxAlign = szUnknownSize + accum.offset = szUnknownSize proc computeSizeAlign(conf: ConfigRef; typ: PType) = ## computes and sets ``size`` and ``align`` members of ``typ`` @@ -288,8 +216,7 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) = # mark computation in progress typ.size = szIllegalRecursion typ.align = szIllegalRecursion - - var maxAlign, sizeAccum, length: BiggestInt + typ.paddingAtEnd = 0 var tk = typ.kind case tk @@ -299,26 +226,23 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) = else: typ.size = conf.target.ptrSize typ.align = int16(conf.target.ptrSize) - of tyNil: typ.size = conf.target.ptrSize typ.align = int16(conf.target.ptrSize) - of tyString: if conf.selectedGC == gcDestructors: typ.size = conf.target.ptrSize * 2 else: typ.size = conf.target.ptrSize typ.align = int16(conf.target.ptrSize) - of tyCString, tySequence, tyPtr, tyRef, tyVar, tyLent, tyOpenArray: let base = typ.lastSon if base == typ: # this is not the correct location to detect ``type A = ptr A`` typ.size = szIllegalRecursion typ.align = szIllegalRecursion + typ.paddingAtEnd = szIllegalRecursion return - typ.align = int16(conf.target.ptrSize) if typ.kind == tySequence and conf.selectedGC == gcDestructors: typ.size = conf.target.ptrSize * 2 @@ -340,12 +264,13 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) = computeSizeAlign(conf, base) typ.size = 0 typ.align = base.align + of tyEnum: if firstOrd(conf, typ) < Zero: typ.size = 4 # use signed int32 typ.align = 4 else: - length = toInt64(lastOrd(conf, typ)) # BUGFIX: use lastOrd! + let length = toInt64(lastOrd(conf, typ)) # BUGFIX: use lastOrd! if length + 1 < `shl`(1, 8): typ.size = 1 typ.align = 1 @@ -357,30 +282,37 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) = typ.align = 4 else: typ.size = 8 - typ.align = 8 + typ.align = int16(conf.floatInt64Align) of tySet: if typ.sons[0].kind == tyGenericParam: typ.size = szUncomputedSize - typ.align = szUncomputedSize # in original version this was 1 + typ.align = szUncomputedSize else: - length = toInt64(lengthOrd(conf, typ.sons[0])) + let length = toInt64(lengthOrd(conf, typ.sons[0])) if length <= 8: typ.size = 1 + typ.align = 1 elif length <= 16: typ.size = 2 + typ.align = 2 elif length <= 32: typ.size = 4 + typ.align = 4 elif length <= 64: typ.size = 8 + typ.align = int16(conf.floatInt64Align) elif align(length, 8) mod 8 == 0: typ.size = align(length, 8) div 8 + typ.align = int16(conf.floatInt64Align) else: typ.size = align(length, 8) div 8 + 1 - typ.align = int16(typ.size) + typ.align = int16(conf.floatInt64Align) of tyRange: computeSizeAlign(conf, typ.sons[0]) typ.size = typ.sons[0].size typ.align = typ.sons[0].align + typ.paddingAtEnd = typ.sons[0].paddingAtEnd + of tyTuple: try: var accum = OffsetAccum(maxAlign: 1) @@ -392,112 +324,121 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) = let sym = typ.n[i].sym sym.offset = accum.offset accum.inc(int(child.size)) - accum.finish + typ.paddingAtEnd = int16(accum.finish()) typ.size = accum.offset typ.align = int16(accum.maxAlign) except IllegalTypeRecursionError: + typ.paddingAtEnd = szIllegalRecursion typ.size = szIllegalRecursion typ.align = szIllegalRecursion + of tyObject: - var headerSize: BiggestInt - var headerAlign: int16 - if typ.sons[0] != nil: - # compute header size - if conf.cmd == cmdCompileToCpp: - # if the target is C++ the members of this type are written - # into the padding byets at the end of the parent type. At the - # moment it is not supported to calculate that. - headerSize = szUnknownSize - headerAlign = szUncomputedSize - else: - var st = typ.sons[0] - while st.kind in skipPtrs: - st = st.sons[^1] - computeSizeAlign(conf, st) - if st.size == szIllegalRecursion: - typ.size = st.size - typ.align = st.align - return - headerSize = st.size - headerAlign = st.align - elif isObjectWithTypeFieldPredicate(typ): - # this branch is taken for RootObj - headerSize = conf.target.intSize - headerAlign = conf.target.intSize.int16 - else: - headerSize = 0 - headerAlign = 1 - let (offset, align) = + try: + var accum = + if typ.sons[0] != nil: + # compute header size + var st = typ.sons[0] + while st.kind in skipPtrs: + st = st.sons[^1] + computeSizeAlign(conf, st) + if conf.cmd == cmdCompileToCpp: + OffsetAccum( + offset: int(st.size) - int(st.paddingAtEnd), + maxAlign: st.align + ) + else: + OffsetAccum( + offset: int(st.size), + maxAlign: st.align + ) + elif isObjectWithTypeFieldPredicate(typ): + # this branch is taken for RootObj + OffsetAccum( + offset: conf.target.intSize, + maxAlign: conf.target.intSize + ) + else: + OffsetAccum(maxAlign: 1) if tfUnion in typ.flags: if tfPacked in typ.flags: let info = if typ.sym != nil: typ.sym.info else: unknownLineInfo() - localError(conf, info, "type may not be packed and union at the same time.") - (BiggestInt(szUnknownSize), BiggestInt(szUnknownSize)) + localError(conf, info, "union type may not be packed.") + accum = OffsetAccum(offset: szUnknownSize, maxAlign: szUnknownSize) + elif accum.offset != 0: + let info = if typ.sym != nil: typ.sym.info else: unknownLineInfo() + localError(conf, info, "union type may not have an object header") + accum = OffsetAccum(offset: szUnknownSize, maxAlign: szUnknownSize) else: - computeUnionObjectOffsetsFoldFunction(conf, typ.n, false) + computeUnionObjectOffsetsFoldFunction(conf, typ.n, accum) elif tfPacked in typ.flags: - (computePackedObjectOffsetsFoldFunction(conf, typ.n, headerSize, false), BiggestInt(1)) + accum.maxAlign = 1 + computeObjectOffsetsFoldFunction(conf, typ.n, true, accum) else: - computeObjectOffsetsFoldFunction(conf, typ.n, headerSize) - if offset == szIllegalRecursion: + computeObjectOffsetsFoldFunction(conf, typ.n, false, accum) + let paddingAtEnd = int16(accum.finish()) + if typ.sym != nil and + typ.sym.flags * {sfCompilerProc, sfImportc} == {sfImportc}: + typ.size = szUnknownSize + typ.align = szUnknownSize + typ.paddingAtEnd = szUnknownSize + else: + typ.size = accum.offset + typ.align = int16(accum.maxAlign) + typ.paddingAtEnd = paddingAtEnd + except IllegalTypeRecursionError: typ.size = szIllegalRecursion typ.align = szIllegalRecursion - return - if offset == szUnknownSize or ( - typ.sym != nil and - typ.sym.flags * {sfCompilerProc, sfImportc} == {sfImportc}): - typ.size = szUnknownSize - typ.align = szUnknownSize - return - # header size is already in size from computeObjectOffsetsFoldFunction - # maxAlign is probably not changed at all from headerAlign - if tfPacked in typ.flags: - typ.size = offset - typ.align = 1 - else: - typ.align = int16(max(align, headerAlign)) - typ.size = align(offset, typ.align) + typ.paddingAtEnd = szIllegalRecursion of tyInferred: if typ.len > 1: computeSizeAlign(conf, typ.lastSon) typ.size = typ.lastSon.size typ.align = typ.lastSon.align + typ.paddingAtEnd = typ.lastSon.paddingAtEnd of tyGenericInst, tyDistinct, tyGenericBody, tyAlias, tySink, tyOwned: computeSizeAlign(conf, typ.lastSon) typ.size = typ.lastSon.size typ.align = typ.lastSon.align + typ.paddingAtEnd = typ.lastSon.paddingAtEnd of tyTypeClasses: if typ.isResolvedUserTypeClass: computeSizeAlign(conf, typ.lastSon) typ.size = typ.lastSon.size typ.align = typ.lastSon.align + typ.paddingAtEnd = typ.lastSon.paddingAtEnd else: - typ.size = szUncomputedSize - typ.align = szUncomputedSize + typ.size = szUnknownSize + typ.align = szUnknownSize + typ.paddingAtEnd = szUnknownSize of tyTypeDesc: computeSizeAlign(conf, typ.base) typ.size = typ.base.size typ.align = typ.base.align + typ.paddingAtEnd = typ.base.paddingAtEnd of tyForward: # is this really illegal recursion, or maybe just unknown? typ.size = szIllegalRecursion typ.align = szIllegalRecursion + typ.paddingAtEnd = szIllegalRecursion of tyStatic: if typ.n != nil: computeSizeAlign(conf, typ.lastSon) typ.size = typ.lastSon.size typ.align = typ.lastSon.align + typ.paddingAtEnd = typ.lastSon.paddingAtEnd else: - typ.size = szUncomputedSize - typ.align = szUncomputedSize + typ.size = szUnknownSize + typ.align = szUnknownSize + typ.paddingAtEnd = szUnknownSize else: - typ.size = szUncomputedSize - typ.align = szUncomputedSize + typ.size = szUnknownSize + typ.align = szUnknownSize + typ.paddingAtEnd = szUnknownSize template foldSizeOf*(conf: ConfigRef; n: PNode; fallback: PNode): PNode = let config = conf diff --git a/tests/misc/tsizeof.nim b/tests/misc/tsizeof.nim index 9cbe9aeb0c..a4633021b2 100644 --- a/tests/misc/tsizeof.nim +++ b/tests/misc/tsizeof.nim @@ -1,4 +1,5 @@ discard """ + targets: "c cpp" output: ''' body executed body executed @@ -7,6 +8,14 @@ macros api OK ''' """ +# This is for travis. The keyword ``alignof`` only exists in ``c++11`` +# and newer. On travis gcc does not default to c++11 yet. +when defined(cpp) and not defined(windows): + {.passC: "-std=c++11".} + +# Object offsets are different for inheritance objects when compiling +# to c++. + type TMyEnum = enum tmOne, tmTwo, tmThree, tmFour @@ -143,6 +152,14 @@ type ValueA ValueB + # Must have more than 32 elements so that set[MyEnum33] will become compile to an int64. + MyEnum33 {.pure.} = enum + Value1, Value2, Value3, Value4, Value5, Value6, + Value7, Value8, Value9, Value10, Value11, Value12, + Value13, Value14, Value15, Value16, Value17, Value18, + Value19, Value20, Value21, Value22, Value23, Value24, + Value25, Value26, Value27, Value28, Value29, Value30, + Value31, Value32, Value33 proc transformObjectconfigPacked(arg: NimNode): NimNode = let debug = arg.kind == nnkPragmaExpr @@ -296,6 +313,10 @@ testinstance: b: int8 c: int8 + PaddingOfSetEnum33 = object + cause: int8 + theSet: set[MyEnum33] + Bazing {.objectconfig.} = object of RootObj a: int64 # TODO test on 32 bit system @@ -328,6 +349,7 @@ testinstance: var g : RecursiveStuff var ro : RootObj var go : GenericObject[int64] + var po : PaddingOfSetEnum33 var e1: Enum1 @@ -346,16 +368,16 @@ testinstance: else: doAssert sizeof(SimpleAlignment) > 10 - testSizeAlignOf(t,a,b,c,d,e,f,g,ro,go, e1, e2, e4, e8, eoa, eob) + testSizeAlignOf(t,a,b,c,d,e,f,g,ro,go,po, e1, e2, e4, e8, eoa, eob) - when not defined(cpp): - type - WithBitsize {.objectconfig.} = object - bitfieldA {.bitsize: 16.}: uint32 - bitfieldB {.bitsize: 16.}: uint32 - var wbs: WithBitsize - testSize(wbs) + type + WithBitsize {.objectconfig.} = object + bitfieldA {.bitsize: 16.}: uint32 + bitfieldB {.bitsize: 16.}: uint32 + + var wbs: WithBitsize + testSize(wbs) testOffsetOf(TrivialType, x) testOffsetOf(TrivialType, y) @@ -383,11 +405,13 @@ testinstance: testOffsetOf(Foobar, c) - when not defined(cpp): - testOffsetOf(Bazing, a) - testOffsetOf(InheritanceA, a) - testOffsetOf(InheritanceB, b) - testOffsetOf(InheritanceC, c) + testOffsetOf(PaddingOfSetEnum33, cause) + testOffsetOf(PaddingOfSetEnum33, theSet) + + testOffsetOf(Bazing, a) + testOffsetOf(InheritanceA, a) + testOffsetOf(InheritanceB, b) + testOffsetOf(InheritanceC, c) testOffsetOf(EnumObjectA, a) testOffsetOf(EnumObjectA, b) @@ -619,9 +643,6 @@ doAssert offsetof(MyPackedCaseObject, val3) == 13 doAssert offsetof(MyPackedCaseObject, val4) == 9 doAssert offsetof(MyPackedCaseObject, val5) == 13 -reject: - const off4 = offsetof(MyPackedCaseObject, val1) - reject: const off5 = offsetof(MyPackedCaseObject, val2) From 5f7a6aff06e80fb27f5edb855a4592a8d935906b Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 29 Aug 2019 00:35:05 +0200 Subject: [PATCH 43/61] fixes #11941 (#12079) --- compiler/sem.nim | 9 +++++++-- compiler/semtypes.nim | 2 +- tests/template/template_issues.nim | 15 +++++++++++++++ 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/compiler/sem.nim b/compiler/sem.nim index 70a84501f3..ecf5e4f855 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -403,7 +403,13 @@ proc semAfterMacroCall(c: PContext, call, macroResult: PNode, if s.typ.sons[0] == nil: result = semStmt(c, result, flags) else: - case s.typ.sons[0].kind + var retType = s.typ.sons[0] + if retType.kind == tyTypeDesc and tfUnresolved in retType.flags and + retType.len == 1: + # bug #11941: template fails(T: type X, v: auto): T + # does not mean we expect a tyTypeDesc. + retType = retType[0] + case retType.kind of tyUntyped: # Not expecting a type here allows templates like in ``tmodulealias.in``. result = semExpr(c, result, flags) @@ -421,7 +427,6 @@ proc semAfterMacroCall(c: PContext, call, macroResult: PNode, result.typ = makeTypeDesc(c, typ) #result = symNodeFromType(c, typ, n.info) else: - var retType = s.typ.sons[0] if s.ast[genericParamsPos] != nil and retType.isMetaType: # The return type may depend on the Macro arguments # e.g. template foo(T: typedesc): seq[T] diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 6e542237b4..9fec57b154 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -1799,7 +1799,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = of nkStmtListType: result = semStmtListType(c, n, prev) of nkBlockType: result = semBlockType(c, n, prev) else: - localError(c.config, n.info, errTypeExpected) + localError(c.config, n.info, "type expected, but got: " & renderTree(n)) result = newOrPrevType(tyError, prev, c) n.typ = result dec c.inTypeContext diff --git a/tests/template/template_issues.nim b/tests/template/template_issues.nim index dd545d1e28..b7dd2a1a7c 100644 --- a/tests/template/template_issues.nim +++ b/tests/template/template_issues.nim @@ -6,6 +6,7 @@ output: ''' a hi Hello, World! +(e: 42) ''' """ @@ -220,3 +221,17 @@ block t5235: outer: test("Hello, World!") + + +# bug #11941 +type X = object + e: int + +proc works(T: type X, v: auto): T = T(e: v) +template fails(T: type X, v: auto): T = T(e: v) + +var + w = X.works(42) + x = X.fails(42) + +echo x From cd106cf68071a3249d32d4ffc2948cd5fe6c1795 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 29 Aug 2019 07:43:35 +0200 Subject: [PATCH 44/61] fixes #12074 (#12080) --- lib/system.nim | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/system.nim b/lib/system.nim index 974c39a678..6d43d672a6 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -2681,6 +2681,7 @@ when defined(nimNewRoof): ## ## for i in countup(2, 9, 3): ## echo i # => 2; 5; 8 + mixin inc when T is IntLikeForCount: var res = int(a) while res <= int(b): @@ -2701,6 +2702,7 @@ when defined(nimNewRoof): ## .. code-block:: Nim ## for i in 3 .. 7: ## echo i # => 3; 4; 5; 6; 7 + mixin inc when T is IntLikeForCount: var res = int(a) while res <= int(b): @@ -2730,6 +2732,7 @@ when defined(nimNewRoof): dotdotImpl(uint32) iterator `..<`*[T](a, b: T): T {.inline.} = + mixin inc var i = T(a) while i < b: yield i @@ -2785,6 +2788,7 @@ else: ## .. code-block:: Nim ## for i in 3 .. 7: ## echo i # => 3; 4; 5; 6; 7 + mixin inc when T is IntLikeForCount: var res = int(a) while res <= int(b): @@ -2797,6 +2801,7 @@ else: inc(res) iterator `..<`*[S, T](a: S, b: T): T {.inline.} = + mixin inc var i = T(a) while i < b: yield i From 9203d3a982446990467f4ddcfdc33d2cc5d91882 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 29 Aug 2019 07:49:58 +0200 Subject: [PATCH 45/61] fixes 5870 (#11704) * fixes #5870 * make tclosure test green again * this check is correct but breaks some Nimble packages --- compiler/semstmts.nim | 2 +- compiler/types.nim | 50 ++++++++++++++++++++++------------------ lib/system/iterators.nim | 4 ++-- tests/errmsgs/t5870.nim | 17 ++++++++++++++ 4 files changed, 47 insertions(+), 26 deletions(-) create mode 100644 tests/errmsgs/t5870.nim diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 5a9c926471..b22dc7952f 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1880,7 +1880,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, else: pushProcCon(c, s) if n.sons[genericParamsPos].kind == nkEmpty or usePseudoGenerics: - if not usePseudoGenerics: paramsTypeCheck(c, s.typ) + if not usePseudoGenerics and s.magic == mNone: paramsTypeCheck(c, s.typ) c.p.wasForwarded = proto != nil maybeAddResult(c, s, n) diff --git a/compiler/types.nim b/compiler/types.nim index 61fbffd60e..5e157272ea 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -1188,7 +1188,9 @@ type TTypeAllowedFlag* = enum taField, taHeap, - taConcept + taConcept, + taIsOpenArray, + taNoUntyped TTypeAllowedFlags* = set[TTypeAllowedFlag] @@ -1204,8 +1206,8 @@ proc typeAllowedNode(marker: var IntSet, n: PNode, kind: TSymKind, of nkNone..nkNilLit: discard else: - if n.kind == nkRecCase and kind in {skProc, skFunc, skConst}: - return n[0].typ + #if n.kind == nkRecCase and kind in {skProc, skFunc, skConst}: + # return n[0].typ for i in 0 ..< sonsLen(n): let it = n.sons[i] result = typeAllowedNode(marker, it, kind, flags) @@ -1240,28 +1242,29 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind, case t2.kind of tyVar, tyLent: if taHeap notin flags: result = t2 # ``var var`` is illegal on the heap - of tyOpenArray, tyUncheckedArray: + of tyOpenArray: + if kind != skParam or taIsOpenArray in flags: result = t + else: result = typeAllowedAux(marker, t2.sons[0], kind, flags+{taIsOpenArray}) + of tyUncheckedArray: if kind != skParam: result = t - else: result = typeAllowedAux(marker, t2.sons[0], skParam, flags) + else: result = typeAllowedAux(marker, t2.sons[0], kind, flags) else: if kind notin {skParam, skResult}: result = t else: result = typeAllowedAux(marker, t2, kind, flags) of tyProc: - if kind == skConst and t.callConv == ccClosure: - result = t - else: - for i in 1 ..< sonsLen(t): - result = typeAllowedAux(marker, t.sons[i], skParam, flags) - if result != nil: break - if result.isNil and t.sons[0] != nil: - result = typeAllowedAux(marker, t.sons[0], skResult, flags) + let f = if kind in {skProc, skFunc}: flags+{taNoUntyped} else: flags + for i in 1 ..< sonsLen(t): + result = typeAllowedAux(marker, t.sons[i], skParam, f) + if result != nil: break + if result.isNil and t.sons[0] != nil: + result = typeAllowedAux(marker, t.sons[0], skResult, flags) of tyTypeDesc: # XXX: This is still a horrible idea... result = nil + of tyUntyped, tyTyped: + if kind notin {skParam, skResult} or taNoUntyped in flags: result = t of tyStatic: if kind notin {skParam}: result = t - of tyUntyped, tyTyped: - if kind notin {skParam, skResult}: result = t of tyVoid: if taField notin flags: result = t of tyTypeClasses: @@ -1286,30 +1289,31 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind, if skipTypes(t.sons[0], abstractInst-{tyTypeDesc}).kind notin {tyChar, tyEnum, tyInt..tyFloat128, tyUInt8..tyUInt32}: result = t of tyOpenArray, tyVarargs, tySink: - if kind != skParam: + # you cannot nest openArrays/sinks/etc. + if kind != skParam or taIsOpenArray in flags: result = t else: - result = typeAllowedAux(marker, t.sons[0], skVar, flags) + result = typeAllowedAux(marker, t.sons[0], kind, flags+{taIsOpenArray}) of tyUncheckedArray: if kind != skParam and taHeap notin flags: result = t else: - result = typeAllowedAux(marker, lastSon(t), kind, flags) + result = typeAllowedAux(marker, lastSon(t), kind, flags-{taHeap}) of tySequence, tyOpt: if t.sons[0].kind != tyEmpty: - result = typeAllowedAux(marker, t.sons[0], skVar, flags+{taHeap}) + result = typeAllowedAux(marker, t.sons[0], kind, flags+{taHeap}) elif kind in {skVar, skLet}: result = t.sons[0] of tyArray: if t.sons[1].kind != tyEmpty: - result = typeAllowedAux(marker, t.sons[1], skVar, flags) + result = typeAllowedAux(marker, t.sons[1], kind, flags) elif kind in {skVar, skLet}: result = t.sons[1] of tyRef: if kind == skConst: result = t - else: result = typeAllowedAux(marker, t.lastSon, skVar, flags+{taHeap}) + else: result = typeAllowedAux(marker, t.lastSon, kind, flags+{taHeap}) of tyPtr: - result = typeAllowedAux(marker, t.lastSon, skVar, flags+{taHeap}) + result = typeAllowedAux(marker, t.lastSon, kind, flags+{taHeap}) of tySet: for i in 0 ..< sonsLen(t): result = typeAllowedAux(marker, t.sons[i], kind, flags) @@ -1333,7 +1337,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind, result = nil of tyOwned: if t.len == 1 and t.sons[0].skipTypes(abstractInst).kind in {tyRef, tyPtr, tyProc}: - result = typeAllowedAux(marker, t.lastSon, skVar, flags+{taHeap}) + result = typeAllowedAux(marker, t.lastSon, kind, flags+{taHeap}) else: result = t diff --git a/lib/system/iterators.nim b/lib/system/iterators.nim index dafd56cb39..117ec123d9 100644 --- a/lib/system/iterators.nim +++ b/lib/system/iterators.nim @@ -224,7 +224,7 @@ iterator fields*[T: tuple|object](x: T): RootObj {. ## **Warning**: This really transforms the 'for' and unrolls the loop. ## The current implementation also has a bug ## that affects symbol binding in the loop body. -iterator fields*[S:tuple|object, T:tuple|object](x: S, y: T): tuple[a,b: untyped] {. +iterator fields*[S:tuple|object, T:tuple|object](x: S, y: T): tuple[a, b: RootObj] {. magic: "Fields", noSideEffect.} ## Iterates over every field of `x` and `y`. ## @@ -266,7 +266,7 @@ iterator fieldPairs*[T: tuple|object](x: T): RootObj {. ## loop body. iterator fieldPairs*[S: tuple|object, T: tuple|object](x: S, y: T): tuple[ - a, b: untyped] {. + a, b: RootObj] {. magic: "FieldPairs", noSideEffect.} ## Iterates over every field of `x` and `y`. ## diff --git a/tests/errmsgs/t5870.nim b/tests/errmsgs/t5870.nim new file mode 100644 index 0000000000..bcbc9cca9d --- /dev/null +++ b/tests/errmsgs/t5870.nim @@ -0,0 +1,17 @@ +discard """ +errormsg: "invalid type for const: seq[SomeRefObj]" +line: 14 +""" + +# bug #5870 +type SomeRefObj = ref object of RootObj + someIntMember: int + +proc createSomeRefObj(v: int): SomeRefObj= + result.new() + result.someIntMember = v + +const compileTimeSeqOfRefObjs = @[createSomeRefObj(100500), createSomeRefObj(2)] + +for i in 0..1: + echo compileTimeSeqOfRefObjs[i].someIntMember From d0e5bd2305db719b0c9acb6a017c8aa579a2f246 Mon Sep 17 00:00:00 2001 From: Araq Date: Thu, 29 Aug 2019 12:10:31 +0200 Subject: [PATCH 46/61] fixes a critical type checking regression --- compiler/types.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/types.nim b/compiler/types.nim index 5e157272ea..1c21fffb16 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -1254,7 +1254,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind, of tyProc: let f = if kind in {skProc, skFunc}: flags+{taNoUntyped} else: flags for i in 1 ..< sonsLen(t): - result = typeAllowedAux(marker, t.sons[i], skParam, f) + result = typeAllowedAux(marker, t.sons[i], skParam, f-{taIsOpenArray}) if result != nil: break if result.isNil and t.sons[0] != nil: result = typeAllowedAux(marker, t.sons[0], skResult, flags) From 2a3b0563141f2cfdee4cbeef85167c25610ff71f Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 29 Aug 2019 19:09:54 +0200 Subject: [PATCH 47/61] fixes #12042 (#12083) * fixes #12042 * make tests green again --- compiler/ccgcalls.nim | 12 +++++++++++- compiler/ccgexprs.nim | 2 +- compiler/semstmts.nim | 2 +- tests/openarray/tptrarrayderef.nim | 15 +++++++++++++++ 4 files changed, 28 insertions(+), 3 deletions(-) diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim index 2ae56863b3..4efd33d1ea 100644 --- a/compiler/ccgcalls.nim +++ b/compiler/ccgcalls.nim @@ -79,9 +79,19 @@ proc genBoundsCheck(p: BProc; arr, a, b: TLoc) proc openArrayLoc(p: BProc, n: PNode): Rope = var a: TLoc - let q = skipConv(n) + var q = skipConv(n) + var skipped = false + while q.kind == nkStmtListExpr and q.len > 0: + skipped = true + q = q.lastSon if getMagic(q) == mSlice: # magic: pass slice to openArray: + if skipped: + q = skipConv(n) + while q.kind == nkStmtListExpr and q.len > 0: + for i in 0..q.len-2: + genStmts(p, q[i]) + q = q.lastSon var b, c: TLoc initLocExpr(p, q[1], a) initLocExpr(p, q[2], b) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index a882a3cee3..ea4cd4acaf 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -2268,7 +2268,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mAccessEnv: unaryExpr(p, e, d, "$1.ClE_0") of mSlice: localError(p.config, e.info, "invalid context for 'toOpenArray'; " & - " 'toOpenArray' is only valid within a call expression") + "'toOpenArray' is only valid within a call expression") else: when defined(debugMagics): echo p.prc.name.s, " ", p.prc.id, " ", p.prc.flags, " ", p.prc.ast[genericParamsPos].kind diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index b22dc7952f..5a0aac40e3 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -293,7 +293,7 @@ proc fitRemoveHiddenConv(c: PContext, typ: PType, n: PNode): PNode = result.info = n.info result.typ = typ if not floatRangeCheck(result.floatVal, typ): - localError(c.config, n.info, errFloatToString % [$n.floatVal, typeToString(typ)]) + localError(c.config, n.info, errFloatToString % [$result.floatVal, typeToString(typ)]) else: changeType(c, r1, typ, check=true) result = r1 diff --git a/tests/openarray/tptrarrayderef.nim b/tests/openarray/tptrarrayderef.nim index b75bc08c4b..5e77430d1f 100644 --- a/tests/openarray/tptrarrayderef.nim +++ b/tests/openarray/tptrarrayderef.nim @@ -1,6 +1,7 @@ discard """ output: '''[1, 2, 3, 4] 3 +['1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C'] OK ''' """ @@ -66,4 +67,18 @@ var doAssert y1 == ([1, 2], 3) doAssert y2 == [1, 2, 3, 4] +template newOpenArray(x: var string, size: int): openArray[char] = + var z = 1 + toOpenArray(x, z, size) + +template doSomethingAndCreate(x: var string): openArray[char] = + let size = 12 + newOpenArray(x, size) + +proc sinkk(x: openArray[char]) = + echo x + +var xArrayDeref = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" +sinkk doSomethingAndCreate(xArrayDeref) + echo "OK" From 029dcc6259b8f0e6ee0216a936636509f7f530ff Mon Sep 17 00:00:00 2001 From: Clyybber Date: Fri, 30 Aug 2019 06:41:26 +0200 Subject: [PATCH 48/61] fix #12037 (#12089) --- compiler/dfa.nim | 17 +++-------------- tests/destructor/t12037.nim | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+), 14 deletions(-) create mode 100644 tests/destructor/t12037.nim diff --git a/compiler/dfa.nim b/compiler/dfa.nim index 7315e74bce..2805e42fb9 100644 --- a/compiler/dfa.nim +++ b/compiler/dfa.nim @@ -607,14 +607,9 @@ proc aliases(obj, field: PNode): bool = if sameTrees(obj, n): return true case n.kind of nkDotExpr, nkCheckedFieldExpr, nkHiddenSubConv, nkHiddenStdConv, - nkObjDownConv, nkObjUpConv, nkHiddenDeref, nkDerefExpr: + nkObjDownConv, nkObjUpConv, nkHiddenAddr, nkAddr, nkBracketExpr, + nkHiddenDeref, nkDerefExpr: n = n[0] - of nkBracketExpr: - let x = n[0] - if x.typ != nil and x.typ.skipTypes(abstractInst).kind == tyTuple: - n = x - else: - break else: break return false @@ -652,7 +647,7 @@ proc isAnalysableFieldAccess*(orig: PNode; owner: PSym): bool = while true: case n.kind of nkDotExpr, nkCheckedFieldExpr, nkHiddenSubConv, nkHiddenStdConv, - nkObjDownConv, nkObjUpConv: + nkObjDownConv, nkObjUpConv, nkHiddenAddr, nkAddr, nkBracketExpr: n = n[0] of nkHiddenDeref, nkDerefExpr: # We "own" sinkparam[].loc but not ourVar[].location as it is a nasty @@ -660,12 +655,6 @@ proc isAnalysableFieldAccess*(orig: PNode; owner: PSym): bool = n = n[0] return n.kind == nkSym and n.sym.owner == owner and (isSinkParam(n.sym) or n.sym.typ.skipTypes(abstractInst-{tyOwned}).kind in {tyOwned}) - of nkBracketExpr: - let x = n[0] - if x.typ != nil and x.typ.skipTypes(abstractInst).kind == tyTuple: - n = x - else: - break else: break # XXX Allow closure deref operations here if we know diff --git a/tests/destructor/t12037.nim b/tests/destructor/t12037.nim new file mode 100644 index 0000000000..8d50262d6f --- /dev/null +++ b/tests/destructor/t12037.nim @@ -0,0 +1,18 @@ +discard """ + cmd: '''nim c --newruntime $file''' + output: ''' +showing original type, length, and contents seq[int] 1 @[42] +copy length and contents 1 @[42] +''' +""" + +proc test() = + var sq1 = @[42] + echo "showing original type, length, and contents ", sq1.typeof, " ", sq1.len, " ", sq1 + doAssert cast[int](sq1[0].unsafeAddr) != 0 + var sq2 = sq1 # copy of original + echo "copy length and contents ", sq2.len, " ", sq2 + doAssert cast[int](sq2[0].unsafeAddr) != 0 + doAssert cast[int](sq1[0].unsafeAddr) != 0 + +test() From 25581c796f519cb41d8b40b48bf160678339b87f Mon Sep 17 00:00:00 2001 From: narimiran Date: Fri, 30 Aug 2019 08:47:58 +0200 Subject: [PATCH 49/61] enable testing of more packages --- testament/important_packages.nim | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index d10e9c5b30..6317139d1c 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -5,9 +5,10 @@ template pkg(name: string; cmd = "nimble test"; hasDeps = false; url = ""): unty var packages*: seq[tuple[name, cmd: string; hasDeps: bool; url: string]] = @[] -#pkg "argparse" +pkg "argparse" pkg "arraymancer", "nim c -r src/arraymancer.nim", true pkg "ast_pattern_matching", "nim c -r --useVersion=0.19 tests/test1.nim" +pkg "bigints" pkg "binaryheap", "nim c -r binaryheap.nim" pkg "blscurve", "", true pkg "bncurve", "", true @@ -56,7 +57,7 @@ pkg "nimquery" pkg "nimsl", "", true pkg "nimsvg" pkg "nimx", "nim c --threads:on test/main.nim", true -# pkg "norm", "nim c -r tests/testsqlite.nim", true +pkg "norm", "nim c -r tests/tsqlite.nim", true pkg "npeg" pkg "ormin", "nim c -o:orminn ormin.nim", true pkg "parsetoml" From 82d5e773e3cccbca6632ff7cbfcec055ab4b915b Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 30 Aug 2019 15:43:07 +0200 Subject: [PATCH 50/61] make testament a tool we ship with Nim; fixes #12084 (#12088) * make testament a tool we ship with Nim; fixes #12084 * moved config to fit * adapt testament tests to use the testament binary --- changelog.md | 3 +++ compiler/installer.ini | 1 + koch.nim | 13 ++++++++----- testament/categories.nim | 12 ++++++++++++ testament/{tester.nim => testament.nim} | 18 ++++++++++++++---- .../{tester.nim.cfg => testament.nim.cfg} | 0 tests/testament/tshouldfail.nim | 2 +- 7 files changed, 39 insertions(+), 10 deletions(-) rename testament/{tester.nim => testament.nim} (97%) rename testament/{tester.nim.cfg => testament.nim.cfg} (100%) diff --git a/changelog.md b/changelog.md index 61e6711db9..b7c2214c9c 100644 --- a/changelog.md +++ b/changelog.md @@ -73,6 +73,9 @@ type - The Nim compiler now warns about unused module imports. You can use a top level ``{.used.}`` pragma in the module that you want to be importable without producing this warning. +- The "testament" testing tool's name was changed + from `tester` to `testament` and is generally available as a tool to run Nim + tests automatically. ### Compiler changes diff --git a/compiler/installer.ini b/compiler/installer.ini index e1ebbb8968..e2a9380f34 100644 --- a/compiler/installer.ini +++ b/compiler/installer.ini @@ -87,6 +87,7 @@ Files: "bin/nimble.exe" Files: "bin/vccexe.exe" Files: "bin/nimgrab.exe" Files: "bin/nimpretty.exe" +Files: "bin/testament.exe" Files: "koch.exe" Files: "finish.exe" diff --git a/koch.nim b/koch.nim index 57c490449c..5c1f2cafa4 100644 --- a/koch.nim +++ b/koch.nim @@ -176,6 +176,7 @@ proc bundleWinTools(args: string) = nimCompile("tools/nimgrab.nim", options = "-d:ssl " & args) nimCompile("tools/nimgrep.nim", options = args) bundleC2nim(args) + nimCompile("testament/testament.nim", options = args) when false: # not yet a tool worth including nimCompile(r"tools\downloader.nim", @@ -217,6 +218,8 @@ proc buildTools(args: string = "") = options = "-d:release " & args) nimCompileFold("Compile nimfind", "tools/nimfind.nim", options = "-d:release " & args) + nimCompileFold("Compile testament", "testament/testament.nim", + options = "-d:release " & args) proc nsis(latest: bool; args: string) = bundleNimbleExe(latest, args) @@ -417,8 +420,8 @@ proc winRelease*() = template `|`(a, b): string = (if a.len > 0: a else: b) proc tests(args: string) = - nimexec "cc --opt:speed testament/tester" - let tester = quoteShell(getCurrentDir() / "testament/tester".exe) + nimexec "cc --opt:speed testament/testament" + let tester = quoteShell(getCurrentDir() / "testament/testament".exe) let success = tryExec tester & " " & (args|"all") if not success: quit("tests failed", QuitFailure) @@ -482,7 +485,7 @@ proc runCI(cmd: string) = kochExecFold("boot -d:release -d:nimHasLibFFI", "boot -d:release -d:nimHasLibFFI") if getEnv("NIM_TEST_PACKAGES", "false") == "true": - execFold("Test selected Nimble packages", "nim c -r testament/tester cat nimble-packages") + execFold("Test selected Nimble packages", "nim c -r testament/testament cat nimble-packages") else: buildTools() # altenatively, kochExec "tools --toolsNoNimble" @@ -490,10 +493,10 @@ proc runCI(cmd: string) = execFold("Test nimscript", "nim e tests/test_nimscript.nims") when defined(windows): # note: will be over-written below - execFold("Compile tester", "nim c -d:nimCoroutines --os:genode -d:posix --compileOnly testament/tester") + execFold("Compile tester", "nim c -d:nimCoroutines --os:genode -d:posix --compileOnly testament/testament") # main bottleneck here - execFold("Run tester", "nim c -r -d:nimCoroutines testament/tester --pedantic all -d:nimCoroutines") + execFold("Run tester", "nim c -r -d:nimCoroutines testament/testament --pedantic all -d:nimCoroutines") execFold("Run nimdoc tests", "nim c -r nimdoc/tester") execFold("Run nimpretty tests", "nim c -r nimpretty/tester.nim") diff --git a/testament/categories.nim b/testament/categories.nim index 745abebe3b..d5cf79e3a5 100644 --- a/testament/categories.nim +++ b/testament/categories.nim @@ -717,3 +717,15 @@ proc processCategory(r: var TResults, cat: Category, inc testsRun if testsRun == 0: echo "[Warning] - Invalid category specified \"", cat.string, "\", no tests were run" + +proc processPattern(r: var TResults, pattern, options: string; simulate: bool) = + var testsRun = 0 + for name in walkPattern(pattern): + if simulate: + echo "Detected test: ", name + else: + var test = makeTest(name, options, Category"pattern") + testSpec r, test + inc testsRun + if testsRun == 0: + echo "no tests were found for pattern: ", pattern diff --git a/testament/tester.nim b/testament/testament.nim similarity index 97% rename from testament/tester.nim rename to testament/testament.nim index c32228ca83..5d0213ce5c 100644 --- a/testament/tester.nim +++ b/testament/testament.nim @@ -1,6 +1,6 @@ # # -# Nim Tester +# Nim Testament # (c) Copyright 2017 Andreas Rumpf # # See the file "copying.txt", included in this @@ -25,9 +25,10 @@ const resultsFile = "testresults.html" #jsonFile = "testresults.json" # not used Usage = """Usage: - tester [options] command [arguments] + testament [options] command [arguments] Command: + p|pat|pattern run all the tests matching the given pattern all run all tests c|cat|category run all the tests of a certain category r|run run single test file @@ -435,6 +436,10 @@ proc testSpecHelper(r: var TResults, test: TTest, expected: TSpec, target: TTarg if isJsTarget: exeCmd = nodejs args = concat(@[exeFile], args) + elif defined(posix) and not exeFile.contains('/'): + # "security" in Posix is actually just a euphemism + # for "unproductive arbitrary shit" + exeCmd = "./" & exeFile else: exeCmd = exeFile var (_, buf, exitCode) = execCmdEx2(exeCmd, args, input = expected.input) @@ -594,7 +599,7 @@ proc main() = var p = initOptParser() p.next() - while p.kind == cmdLongoption: + while p.kind in {cmdLongoption, cmdShortOption}: case p.key.string.normalize of "print", "verbose": optPrintResults = true of "failing": optFailing = true @@ -646,7 +651,7 @@ proc main() = of "all": #processCategory(r, Category"megatest", p.cmdLineRest.string, testsDir, runJoinableTests = false) - var myself = quoteShell(findExe("testament" / "tester")) + var myself = quoteShell(findExe("testament" / "testament")) if targetsStr.len > 0: myself &= " " & quoteShell("--targets:" & targetsStr) @@ -694,6 +699,11 @@ proc main() = var cat = Category(p.key) p.next processCategory(r, cat, p.cmdLineRest.string, testsDir, runJoinableTests = false) + of "p", "pat", "pattern": + skips = loadSkipFrom(skipFrom) + let pattern = p.key + p.next + processPattern(r, pattern, p.cmdLineRest.string, simulate) of "r", "run": # at least one directory is required in the path, to use as a category name let pathParts = split(p.key.string, {DirSep, AltSep}) diff --git a/testament/tester.nim.cfg b/testament/testament.nim.cfg similarity index 100% rename from testament/tester.nim.cfg rename to testament/testament.nim.cfg diff --git a/tests/testament/tshouldfail.nim b/tests/testament/tshouldfail.nim index ebf941fab6..64f4b3838b 100644 --- a/tests/testament/tshouldfail.nim +++ b/tests/testament/tshouldfail.nim @@ -1,5 +1,5 @@ discard """ -cmd: "testament/tester --directory:testament --colors:off --backendLogging:off --nim:../compiler/nim category shouldfail" +cmd: "testament/testament --directory:testament --colors:off --backendLogging:off --nim:../compiler/nim category shouldfail" action: compile nimout: ''' FAIL: tests/shouldfail/tccodecheck.nim C From f9600b7207e45573ee066ec7c9145df113ff5b99 Mon Sep 17 00:00:00 2001 From: Clyybber Date: Sat, 31 Aug 2019 07:44:53 +0200 Subject: [PATCH 51/61] Remove ENDB (#12095) --- compiler/ccgstmts.nim | 26 -- compiler/cgen.nim | 45 +-- compiler/cgendata.nim | 2 - compiler/commands.nim | 24 +- compiler/jsgen.nim | 6 +- compiler/options.nim | 1 - compiler/pragmas.nim | 15 +- compiler/wordrecg.nim | 8 +- doc/endb.rst | 203 ---------- lib/system.nim | 7 - lib/system/debugger.nim | 303 --------------- lib/system/endb.nim | 579 ----------------------------- lib/system/excpt.nim | 12 +- tests/gc/gcleak4.nim | 2 - tests/misc/thallo.nim | 1 - tests/objects/tobjects_various.nim | 1 - 16 files changed, 19 insertions(+), 1216 deletions(-) delete mode 100644 doc/endb.rst delete mode 100644 lib/system/debugger.nim delete mode 100644 lib/system/endb.nim diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index bfd053f2d3..ffeeb0db94 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -1161,36 +1161,10 @@ proc genEmit(p: BProc, t: PNode) = genLineDir(p, t) line(p, cpsStmts, s) -proc genBreakPoint(p: BProc, t: PNode) = - var name: string - if optEndb in p.options: - if t.kind == nkExprColonExpr: - assert(t.sons[1].kind in {nkStrLit..nkTripleStrLit}) - name = normalize(t.sons[1].strVal) - else: - inc(p.module.g.breakPointId) - name = "bp" & $p.module.g.breakPointId - genLineDir(p, t) # BUGFIX - appcg(p.module, p.module.g.breakpoints, - "#dbgRegisterBreakpoint($1, (NCSTRING)$2, (NCSTRING)$3);$n", [ - toLinenumber(t.info), makeCString(toFilename(p.config, t.info)), - makeCString(name)]) - -proc genWatchpoint(p: BProc, n: PNode) = - if optEndb notin p.options: return - var a: TLoc - initLocExpr(p, n.sons[1], a) - let typ = skipTypes(n.sons[1].typ, abstractVarRange) - lineCg(p, cpsStmts, "#dbgRegisterWatchpoint($1, (NCSTRING)$2, $3);$n", - [addrLoc(p.config, a), makeCString(renderTree(n.sons[1])), - genTypeInfo(p.module, typ, n.info)]) - proc genPragma(p: BProc, n: PNode) = for it in n.sons: case whichPragma(it) of wEmit: genEmit(p, it) - of wBreakpoint: genBreakPoint(p, it) - of wWatchPoint: genWatchpoint(p, it) of wInjectStmt: var p = newProc(nil, p.module) p.options = p.options - {optLineTrace, optStackTrace} diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 0330034854..6e79b2d7b3 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -264,13 +264,7 @@ proc genLineDir(p: BProc, t: PNode) = if optEmbedOrigSrc in p.config.globalOptions: 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, t.info): - linefmt(p, cpsStmts, "#endb($1, $2);$N", - [line, makeCString(toFilename(p.config, t.info))]) - elif ({optLineTrace, optStackTrace} * p.options == - {optLineTrace, optStackTrace}) and + if ({optLineTrace, optStackTrace} * p.options == {optLineTrace, optStackTrace}) and (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", @@ -479,19 +473,6 @@ proc deinitGCFrame(p: BProc): Rope = result = ropecg(p.module, "if (((NU)&GCFRAME_) < 4096) #nimGCFrame(&GCFRAME_);$n", []) -proc localDebugInfo(p: BProc, s: PSym, retType: PType) = - if {optStackTrace, optEndb} * p.options != {optStackTrace, optEndb}: return - # XXX work around a bug: No type information for open arrays possible: - if skipTypes(s.typ, abstractVar).kind in {tyOpenArray, tyVarargs}: return - var a = "&" & s.loc.r - if s.kind == skParam and ccgIntroducedPtr(p.config, s, retType): a = s.loc.r - lineF(p, cpsInit, - "FR_.s[$1].address = (void*)$3; FR_.s[$1].typ = $4; FR_.s[$1].name = $2;$n", - [p.maxFrameLen.rope, makeCString(normalize(s.name.s)), a, - genTypeInfo(p.module, s.loc.t, s.info)]) - inc(p.maxFrameLen) - inc p.blocks[p.blocks.len-1].frameLen - proc localVarDecl(p: BProc; n: PNode): Rope = let s = n.sym if s.loc.k == locNone: @@ -515,7 +496,6 @@ proc assignLocalVar(p: BProc, n: PNode) = let nl = if optLineDir in p.config.options: "" else: "\L" let decl = localVarDecl(p, n) & ";" & nl line(p, cpsLocals, decl) - localDebugInfo(p, n.sym, nil) include ccgthreadvars @@ -562,17 +542,10 @@ proc assignGlobalVar(p: BProc, n: PNode) = if p.withinLoop > 0: # fixes tests/run/tzeroarray: resetLoc(p, s.loc) - if p.module.module.options * {optStackTrace, optEndb} == - {optStackTrace, optEndb}: - appcg(p.module, p.module.s[cfsDebugInit], - "#dbgRegisterGlobal($1, &$2, $3);$n", - [makeCString(normalize(s.owner.name.s & '.' & s.name.s)), - s.loc.r, genTypeInfo(p.module, s.typ, n.info)]) proc assignParam(p: BProc, s: PSym, retType: PType) = assert(s.loc.r != nil) scopeMangledParam(p, s) - localDebugInfo(p, s, retType) proc fillProcLoc(m: BModule; n: PNode) = let sym = n.sym @@ -689,7 +662,7 @@ proc loadDynamicLib(m: BModule, lib: PLib) = [loadlib, genStringLiteral(m, lib.path)]) else: var p = newProc(nil, m) - p.options = p.options - {optStackTrace, optEndb} + p.options = p.options - {optStackTrace} var dest: TLoc initLoc(dest, locTemp, lib.path, OnStack) dest.r = getTempName(m) @@ -1318,7 +1291,6 @@ proc genMainProc(m: BModule) = PreMainBody = "$N" & "void PreMainInner(void) {$N" & "$2" & - "$3" & "}$N$N" & PosixCmdLine & "void PreMain(void) {$N" & @@ -1408,17 +1380,11 @@ proc genMainProc(m: BModule) = elif m.config.target.targetOS == osGenode: m.includeHeader("") - if optEndb in m.config.options: - for i in 0.. - (Without any typed command) repeat the previous debugger command. - If there is no previous command, ``step_into`` is assumed. - -Executing Commands -================== - -``s``, ``step_into`` - Single step, stepping into routine calls. - -``n``, ``step_over`` - Single step, without stepping into routine calls. - -``f``, ``skip_current`` - Continue execution until the current routine finishes. - -``c``, ``continue`` - Continue execution until the next breakpoint. - -``i``, ``ignore`` - Continue execution, ignore all breakpoints. This effectively quits - the debugger and runs the program until it finishes. - - -Breakpoint Commands -=================== - -``b``, ``setbreak`` [fromline [toline]] [file] - Set a new breakpoint for the given file - and line numbers. If no file is given, the current execution point's - filename is used. If the filename has no extension, ``.nim`` is - appended for your convenience. - If no line numbers are given, the current execution point's - line is used. If both ``fromline`` and ``toline`` are given the - breakpoint contains a line number range. Some examples if it is still - unclear: - - * ``b 12 15 thallo`` creates a breakpoint that - will be triggered if the instruction pointer reaches one of the - lines 12-15 in the file ``thallo.nim``. - * ``b 12 thallo`` creates a breakpoint that - will be triggered if the instruction pointer reaches the - line 12 in the file ``thallo.nim``. - * ``b 12`` creates a breakpoint that - will be triggered if the instruction pointer reaches the - line 12 in the current file. - * ``b`` creates a breakpoint that - will be triggered if the instruction pointer reaches the - current line in the current file again. - -``breakpoints`` - Display the entire breakpoint list. - -``disable`` - Disable a breakpoint. It remains disabled until you turn it on again - with the ``enable`` command. - -``enable`` - Enable a breakpoint. - -Often it happens when debugging that you keep retyping the breakpoints again -and again because they are lost when you restart your program. This is not -necessary: A special pragma has been defined for this: - - -The ``breakpoint`` pragma -------------------------- - -The ``breakpoint`` pragma is syntactically a statement. It can be used -to mark the *following line* as a breakpoint: - -.. code-block:: Nim - write("1") - {.breakpoint: "before_write_2".} - write("2") - -The name of the breakpoint here is ``before_write_2``. Of course the -breakpoint's name is optional - the compiler will generate one for you -if you leave it out. - -Code for the ``breakpoint`` pragma is only generated if the debugger -is turned on, so you don't need to remove it from your source code after -debugging. - - -The ``watchpoint`` pragma -------------------------- - -The ``watchpoint`` pragma is syntactically a statement. It can be used -to mark a location as a watchpoint: - -.. code-block:: Nim - var a: array[0..20, int] - - {.watchpoint: a[3].} - for i in 0 .. 20: a[i] = i - -ENDB then writes a stack trace whenever the content of the location ``a[3]`` -changes. The current implementation only tracks a hash value of the location's -contents and so locations that are not word sized may encounter false -negatives in very rare cases. - -Code for the ``watchpoint`` pragma is only generated if the debugger -is turned on, so you don't need to remove it from your source code after -debugging. - -Due to the primitive implementation watchpoints are even slower than -breakpoints: After *every* executed Nim code line it is checked whether the -location changed. - - -Data Display Commands -===================== - -``e``, ``eval`` - Evaluate the expression . Note that ENDB has no full-blown expression - evaluator built-in. So expressions are limited: - - * To display global variables prefix their names with their - owning module: ``nim1.globalVar`` - * To display local variables or parameters just type in - their name: ``localVar``. If you want to inspect variables that are not - in the current stack frame, use the ``up`` or ``down`` command. - - Unfortunately, only inspecting variables is possible at the moment. Maybe - a future version will implement a full-blown Nim expression evaluator, - but this is not easy to do and would bloat the debugger's code. - - Since displaying the whole data structures is often not needed and - painfully slow, the debugger uses a *maximal display depth* concept for - displaying. - - You can alter the maximal display depth with the ``maxdisplay`` - command. - -``maxdisplay`` - Sets the maximal display depth to the given integer value. A value of 0 - means there is no maximal display depth. Default is 3. - -``o``, ``out`` - Evaluate the expression and store its string representation into a - file named . If the file does not exist, it will be created, - otherwise it will be opened for appending. - -``w``, ``where`` - Display the current execution point. - -``u``, ``up`` - Go up in the call stack. - -``d``, ``down`` - Go down in the call stack. - -``stackframe`` [file] - Displays the content of the current stack frame in ``stdout`` or - appends it to the file, depending on whether a file is given. - -``callstack`` - Display the entire call stack (but not its content). - -``l``, ``locals`` - Display the available local variables in the current stack frame. - -``g``, ``globals`` - Display all the global variables that are available for inspection. diff --git a/lib/system.nim b/lib/system.nim index 6d43d672a6..16a5e03fe5 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -3639,10 +3639,6 @@ when not defined(JS): #and not defined(nimscript): if result == 0: result = x.len - y.len - when not defined(nimscript) and hostOS != "standalone": - when defined(endb): - proc endbStep() - when declared(newSeq): proc cstringArrayToSeq*(a: cstringArray, len: Natural): seq[string] = ## Converts a ``cstringArray`` to a ``seq[string]``. `a` is supposed to be @@ -3816,9 +3812,6 @@ when not defined(JS): #and not defined(nimscript): currException = exc {.push stack_trace: off, profiler:off.} - when defined(endb) and not defined(nimscript): - include "system/debugger" - when (defined(profiler) or defined(memProfiler)) and not defined(nimscript): include "system/profiler" {.pop.} # stacktrace diff --git a/lib/system/debugger.nim b/lib/system/debugger.nim deleted file mode 100644 index fd0ae23997..0000000000 --- a/lib/system/debugger.nim +++ /dev/null @@ -1,303 +0,0 @@ -# -# -# Nim's Runtime Library -# (c) Copyright 2013 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -## This file implements basic features for any debugger. - -type - VarSlot* {.compilerproc, final.} = object ## a slot in a frame - address*: pointer ## the variable's address - typ*: PNimType ## the variable's type - name*: cstring ## the variable's name; for globals this is "module.name" - - PExtendedFrame = ptr ExtendedFrame - ExtendedFrame = object # If the debugger is enabled the compiler - # provides an extended frame. Of course - # only slots that are - # needed are allocated and not 10_000, - # except for the global data description. - f: TFrame - slots: array[0..10_000, VarSlot] - -var - dbgGlobalData: ExtendedFrame # this reserves much space, but - # for now it is the most practical way - -proc dbgRegisterGlobal(name: cstring, address: pointer, - typ: PNimType) {.compilerproc.} = - let i = dbgGlobalData.f.len - if i >= high(dbgGlobalData.slots): - #debugOut("[Warning] cannot register global ") - return - dbgGlobalData.slots[i].name = name - dbgGlobalData.slots[i].typ = typ - dbgGlobalData.slots[i].address = address - inc(dbgGlobalData.f.len) - -proc getLocal*(frame: PFrame; slot: int): VarSlot {.inline.} = - ## retrieves the meta data for the local variable at `slot`. CAUTION: An - ## invalid `slot` value causes a corruption! - result = cast[PExtendedFrame](frame).slots[slot] - -proc getGlobalLen*(): int {.inline.} = - ## gets the number of registered globals. - result = dbgGlobalData.f.len - -proc getGlobal*(slot: int): VarSlot {.inline.} = - ## retrieves the meta data for the global variable at `slot`. CAUTION: An - ## invalid `slot` value causes a corruption! - result = dbgGlobalData.slots[slot] - -# ------------------- breakpoint support ------------------------------------ - -type - Breakpoint* = object ## represents a break point - low*, high*: int ## range from low to high; if disabled - ## both low and high are set to their negative values - filename*: cstring ## the filename of the breakpoint - -var - dbgBP: array[0..127, Breakpoint] # breakpoints - dbgBPlen: int - dbgBPbloom: int64 # we use a bloom filter to speed up breakpoint checking - - dbgFilenames*: array[0..300, cstring] ## registered filenames; - ## 'nil' terminated - dbgFilenameLen: int - -proc dbgRegisterFilename(filename: cstring) {.compilerproc.} = - # XXX we could check for duplicates here for DLL support - dbgFilenames[dbgFilenameLen] = filename - inc dbgFilenameLen - -proc dbgRegisterBreakpoint(line: int, - filename, name: cstring) {.compilerproc.} = - let x = dbgBPlen - if x >= high(dbgBP): - #debugOut("[Warning] cannot register breakpoint") - return - inc(dbgBPlen) - dbgBP[x].filename = filename - dbgBP[x].low = line - dbgBP[x].high = line - dbgBPbloom = dbgBPbloom or line - -proc addBreakpoint*(filename: cstring, lo, hi: int): bool = - let x = dbgBPlen - if x >= high(dbgBP): return false - inc(dbgBPlen) - result = true - dbgBP[x].filename = filename - dbgBP[x].low = lo - dbgBP[x].high = hi - for line in lo..hi: dbgBPbloom = dbgBPbloom or line - -const - FileSystemCaseInsensitive = defined(windows) or defined(dos) or defined(os2) - -proc fileMatches(c, bp: cstring): bool = - # bp = breakpoint filename - # c = current filename - # we consider it a match if bp is a suffix of c - # and the character for the suffix does not exist or - # is one of: \ / : - # depending on the OS case does not matter! - var blen: int = bp.len - var clen: int = c.len - if blen > clen: return false - # check for \ / : - if clen-blen-1 >= 0 and c[clen-blen-1] notin {'\\', '/', ':'}: - return false - var i = 0 - while i < blen: - var x = bp[i] - var y = c[i+clen-blen] - when FileSystemCaseInsensitive: - if x >= 'A' and x <= 'Z': x = chr(ord(x) - ord('A') + ord('a')) - if y >= 'A' and y <= 'Z': y = chr(ord(y) - ord('A') + ord('a')) - if x != y: return false - inc(i) - return true - -proc canonFilename*(filename: cstring): cstring = - ## returns 'nil' if the filename cannot be found. - for i in 0 .. dbgFilenameLen-1: - result = dbgFilenames[i] - if fileMatches(result, filename): return result - result = nil - -iterator listBreakpoints*(): ptr Breakpoint = - ## lists all breakpoints. - for i in 0..dbgBPlen-1: yield addr(dbgBP[i]) - -proc isActive*(b: ptr Breakpoint): bool = b.low > 0 -proc flip*(b: ptr Breakpoint) = - ## enables or disables 'b' depending on its current state. - b.low = -b.low; b.high = -b.high - -proc checkBreakpoints*(filename: cstring, line: int): ptr Breakpoint = - ## in which breakpoint (if any) we are. - if (dbgBPbloom and line) != line: return nil - for b in listBreakpoints(): - if line >= b.low and line <= b.high and filename == b.filename: return b - -# ------------------- watchpoint support ------------------------------------ - -type - Hash = int - Watchpoint {.pure, final.} = object - name: cstring - address: pointer - typ: PNimType - oldValue: Hash - -var - watchpoints: array[0..99, Watchpoint] - watchpointsLen: int - -proc `!&`(h: Hash, val: int): Hash {.inline.} = - result = h +% val - result = result +% result shl 10 - result = result xor (result shr 6) - -proc `!$`(h: Hash): Hash {.inline.} = - result = h +% h shl 3 - result = result xor (result shr 11) - result = result +% result shl 15 - -proc hash(data: pointer, size: int): Hash = - var h: Hash = 0 - var p = cast[cstring](data) - var i = 0 - var s = size - while s > 0: - h = h !& ord(p[i]) - inc(i) - dec(s) - result = !$h - -proc hashGcHeader(data: pointer): Hash = - const headerSize = sizeof(int)*2 - result = hash(cast[pointer](cast[int](data) -% headerSize), headerSize) - -proc genericHashAux(dest: pointer, mt: PNimType, shallow: bool, - h: Hash): Hash -proc genericHashAux(dest: pointer, n: ptr TNimNode, shallow: bool, - h: Hash): Hash = - var d = cast[ByteAddress](dest) - case n.kind - of nkSlot: - result = genericHashAux(cast[pointer](d +% n.offset), n.typ, shallow, h) - of nkList: - result = h - for i in 0..n.len-1: - result = result !& genericHashAux(dest, n.sons[i], shallow, result) - of nkCase: - result = h !& hash(cast[pointer](d +% n.offset), n.typ.size) - var m = selectBranch(dest, n) - if m != nil: result = genericHashAux(dest, m, shallow, result) - of nkNone: sysAssert(false, "genericHashAux") - -proc genericHashAux(dest: pointer, mt: PNimType, shallow: bool, - h: Hash): Hash = - sysAssert(mt != nil, "genericHashAux 2") - case mt.kind - of tyString: - var x = cast[PPointer](dest)[] - result = h - if x != nil: - let s = cast[NimString](x) - when defined(trackGcHeaders): - result = result !& hashGcHeader(x) - else: - result = result !& hash(x, s.len) - of tySequence: - var x = cast[PPointer](dest) - var dst = cast[ByteAddress](cast[PPointer](dest)[]) - result = h - if dst != 0: - when defined(trackGcHeaders): - result = result !& hashGcHeader(cast[PPointer](dest)[]) - else: - for i in 0..cast[PGenericSeq](dst).len-1: - result = result !& genericHashAux( - cast[pointer](dst +% i*% mt.base.size +% GenericSeqSize), - mt.base, shallow, result) - of tyObject, tyTuple: - # we don't need to copy m_type field for tyObject, as they are equal anyway - result = genericHashAux(dest, mt.node, shallow, h) - of tyArray, tyArrayConstr: - let d = cast[ByteAddress](dest) - result = h - for i in 0..(mt.size div mt.base.size)-1: - result = result !& genericHashAux(cast[pointer](d +% i*% mt.base.size), - mt.base, shallow, result) - of tyRef: - when defined(trackGcHeaders): - var s = cast[PPointer](dest)[] - if s != nil: - result = result !& hashGcHeader(s) - else: - if shallow: - result = h !& hash(dest, mt.size) - else: - result = h - var s = cast[PPointer](dest)[] - if s != nil: - result = result !& genericHashAux(s, mt.base, shallow, result) - else: - result = h !& hash(dest, mt.size) # hash raw bits - -proc genericHash(dest: pointer, mt: PNimType): int = - result = genericHashAux(dest, mt, false, 0) - -proc dbgRegisterWatchpoint(address: pointer, name: cstring, - typ: PNimType) {.compilerproc.} = - let L = watchPointsLen - for i in 0 .. pred(L): - if watchPoints[i].name == name: - # address may have changed: - watchPoints[i].address = address - return - if L >= watchPoints.high: - #debugOut("[Warning] cannot register watchpoint") - return - watchPoints[L].name = name - watchPoints[L].address = address - watchPoints[L].typ = typ - watchPoints[L].oldValue = genericHash(address, typ) - inc watchPointsLen - -proc dbgUnregisterWatchpoints*() = - watchPointsLen = 0 - -var - dbgLineHook*: proc () {.nimcall.} - ## set this variable to provide a procedure that should be called before - ## each executed instruction. This should only be used by debuggers! - ## Only code compiled with the ``debugger:on`` switch calls this hook. - - dbgWatchpointHook*: proc (watchpointName: cstring) {.nimcall.} - -proc checkWatchpoints = - let L = watchPointsLen - for i in 0 .. pred(L): - let newHash = genericHash(watchPoints[i].address, watchPoints[i].typ) - if newHash != watchPoints[i].oldValue: - dbgWatchpointHook(watchPoints[i].name) - watchPoints[i].oldValue = newHash - -proc endb(line: int, file: cstring) {.compilerproc, noinline.} = - # This proc is called before every Nim code line! - if framePtr == nil: return - if dbgWatchpointHook != nil: checkWatchpoints() - framePtr.line = line # this is done here for smaller code size! - framePtr.filename = file - if dbgLineHook != nil: dbgLineHook() - -include "system/endb" diff --git a/lib/system/endb.nim b/lib/system/endb.nim deleted file mode 100644 index 6c99f8d12b..0000000000 --- a/lib/system/endb.nim +++ /dev/null @@ -1,579 +0,0 @@ -# -# -# Nim's Runtime Library -# (c) Copyright 2013 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -# This file implements the embedded debugger that can be linked -# with the application. Mostly we do not use dynamic memory here as that -# would interfere with the GC and trigger ON/OFF errors if the -# user program corrupts memory. Unfortunately, for dispaying -# variables we use the ``system.repr()`` proc which uses Nim -# strings and thus allocates memory from the heap. Pity, but -# I do not want to implement ``repr()`` twice. - -const - EndbBeg = "*** endb" - EndbEnd = "***\n" - -type - StaticStr = object - len: int - data: array[0..100, char] - - BreakpointFilename = object - b: ptr Breakpoint - filename: StaticStr - - DbgState = enum - dbOff, # debugger is turned off - dbStepInto, # debugger is in tracing mode - dbStepOver, - dbSkipCurrent, - dbQuiting, # debugger wants to quit - dbBreakpoints # debugger is only interested in breakpoints - -var - dbgUser: StaticStr # buffer for user input; first command is ``step_into`` - # needs to be global cause we store the last command - # in it - dbgState: DbgState # state of debugger - dbgSkipToFrame: PFrame # frame to be skipped to - - maxDisplayRecDepth: int = 5 # do not display too much data! - - brkPoints: array[0..127, BreakpointFilename] - -proc setLen(s: var StaticStr, newLen=0) = - s.len = newLen - s.data[newLen] = '\0' - -proc add(s: var StaticStr, c: char) = - if s.len < high(s.data)-1: - s.data[s.len] = c - s.data[s.len+1] = '\0' - inc s.len - -proc add(s: var StaticStr, c: cstring) = - var i = 0 - while c[i] != '\0': - add s, c[i] - inc i - -proc assign(s: var StaticStr, c: cstring) = - setLen(s) - add s, c - -proc `==`(a, b: StaticStr): bool = - if a.len == b.len: - for i in 0 .. a.len-1: - if a.data[i] != b.data[i]: return false - return true - -proc `==`(a: StaticStr, b: cstring): bool = - result = c_strcmp(unsafeAddr a.data, b) == 0 - -proc write(f: CFilePtr, s: cstring) = c_fputs(s, f) -proc writeLine(f: CFilePtr, s: cstring) = - c_fputs(s, f) - c_fputs("\n", f) - -proc write(f: CFilePtr, s: StaticStr) = - write(f, cstring(unsafeAddr s.data)) - -proc write(f: CFilePtr, i: int) = - when sizeof(int) == 8: - discard c_fprintf(f, "%lld", i) - else: - discard c_fprintf(f, "%ld", i) - -proc close(f: CFilePtr): cint {. - importc: "fclose", header: "", discardable.} - -proc c_fgetc(stream: CFilePtr): cint {. - importc: "fgetc", header: "".} -proc c_ungetc(c: cint, f: CFilePtr): cint {. - importc: "ungetc", header: "", discardable.} - -var - cstdin* {.importc: "stdin", header: "".}: CFilePtr - -proc listBreakPoints() = - write(cstdout, EndbBeg) - write(cstdout, "| Breakpoints:\n") - for b in listBreakpoints(): - write(cstdout, abs(b.low)) - if b.high != b.low: - write(cstdout, "..") - write(cstdout, abs(b.high)) - write(cstdout, " ") - write(cstdout, b.filename) - if b.isActive: - write(cstdout, " [disabled]\n") - else: - write(cstdout, "\n") - write(cstdout, EndbEnd) - -proc openAppend(filename: cstring): CFilePtr = - proc fopen(filename, mode: cstring): CFilePtr {.importc: "fopen", header: "".} - - result = fopen(filename, "ab") - if result != nil: - write(result, "----------------------------------------\n") - -proc dbgRepr(p: pointer, typ: PNimType): string = - var cl: ReprClosure - initReprClosure(cl) - cl.recDepth = maxDisplayRecDepth - # locks for the GC turned out to be a bad idea... - # inc(recGcLock) - result = "" - reprAux(result, p, typ, cl) - # dec(recGcLock) - deinitReprClosure(cl) - -proc writeVariable(stream: CFilePtr, slot: VarSlot) = - write(stream, slot.name) - write(stream, " = ") - writeLine(stream, dbgRepr(slot.address, slot.typ)) - -proc listFrame(stream: CFilePtr, f: PFrame) = - write(stream, EndbBeg) - write(stream, "| Frame (") - write(stream, f.len) - write(stream, " slots):\n") - for i in 0 .. f.len-1: - writeLine(stream, getLocal(f, i).name) - write(stream, EndbEnd) - -proc listLocals(stream: CFilePtr, f: PFrame) = - write(stream, EndbBeg) - write(stream, "| Frame (") - write(stream, f.len) - write(stream, " slots):\n") - for i in 0 .. f.len-1: - writeVariable(stream, getLocal(f, i)) - write(stream, EndbEnd) - -proc listGlobals(stream: CFilePtr) = - write(stream, EndbBeg) - write(stream, "| Globals:\n") - for i in 0 .. getGlobalLen()-1: - writeLine(stream, getGlobal(i).name) - write(stream, EndbEnd) - -proc debugOut(msg: cstring) = - # the *** *** markers are for easy recognition of debugger - # output for external frontends. - write(cstdout, EndbBeg) - write(cstdout, "| ") - write(cstdout, msg) - write(cstdout, EndbEnd) - -proc dbgFatal(msg: cstring) = - debugOut(msg) - dbgAborting = true # the debugger wants to abort - quit(1) - -proc dbgShowCurrentProc(dbgFramePointer: PFrame) = - if dbgFramePointer != nil: - write(cstdout, "*** endb| now in proc: ") - write(cstdout, dbgFramePointer.procname) - write(cstdout, " ***\n") - else: - write(cstdout, "*** endb| (proc name not available) ***\n") - -proc dbgShowExecutionPoint() = - write(cstdout, "*** endb| ") - write(cstdout, framePtr.filename) - write(cstdout, "(") - write(cstdout, framePtr.line) - write(cstdout, ") ") - write(cstdout, framePtr.procname) - write(cstdout, " ***\n") - -proc scanAndAppendWord(src: cstring, a: var StaticStr, start: int): int = - result = start - # skip whitespace: - while src[result] in {'\t', ' '}: inc(result) - while true: - case src[result] - of 'a'..'z', '0'..'9': add(a, src[result]) - of '_': discard # just skip it - of 'A'..'Z': add(a, chr(ord(src[result]) - ord('A') + ord('a'))) - else: break - inc(result) - -proc scanWord(src: cstring, a: var StaticStr, start: int): int = - setlen(a) - result = scanAndAppendWord(src, a, start) - -proc scanFilename(src: cstring, a: var StaticStr, start: int): int = - result = start - setLen a - while src[result] in {'\t', ' '}: inc(result) - while src[result] notin {'\t', ' ', '\0'}: - add(a, src[result]) - inc(result) - -proc scanNumber(src: cstring, a: var int, start: int): int = - result = start - a = 0 - while src[result] in {'\t', ' '}: inc(result) - while true: - case src[result] - of '0'..'9': a = a * 10 + ord(src[result]) - ord('0') - of '_': discard # skip underscores (nice for long line numbers) - else: break - inc(result) - -proc dbgHelp() = - debugOut(""" -list of commands (see the manual for further help): - GENERAL -h, help display this help message -q, quit quit the debugger and the program - repeat the previous debugger command - EXECUTING -s, step single step, stepping into routine calls -n, next single step, without stepping into routine calls -f, skipcurrent continue execution until the current routine finishes -c, continue, r, run continue execution until the next breakpoint -i, ignore continue execution, ignore all breakpoints - BREAKPOINTS -b, break [fromline [toline]] [file] - set a new breakpoint for line and file - if line or file are omitted the current one is used -breakpoints display the entire breakpoint list -toggle fromline [file] enable or disable a breakpoint -filenames list all valid filenames - DATA DISPLAY -e, eval evaluate the expression -o, out evaluate and write it to -w, where display the current execution point -stackframe [file] display current stack frame [and write it to file] -u, up go up in the call stack -d, down go down in the call stack -bt, backtrace display the entire call stack -l, locals display available local variables -g, globals display available global variables -maxdisplay set the display's recursion maximum -""") - -proc invalidCommand() = - debugOut("[Warning] invalid command ignored (type 'h' for help) ") - -proc hasExt(s: cstring): bool = - # returns true if s has a filename extension - var i = 0 - while s[i] != '\0': - if s[i] == '.': return true - inc i - -proc parseBreakpoint(s: cstring, start: int): Breakpoint = - var dbgTemp: StaticStr - var i = scanNumber(s, result.low, start) - if result.low == 0: result.low = framePtr.line - i = scanNumber(s, result.high, i) - if result.high == 0: result.high = result.low - i = scanFilename(s, dbgTemp, i) - if dbgTemp.len != 0: - if not hasExt(addr dbgTemp.data): add(dbgTemp, ".nim") - result.filename = canonFilename(addr dbgTemp.data) - if result.filename.isNil: - debugOut("[Warning] no breakpoint could be set; unknown filename ") - return - else: - result.filename = framePtr.filename - -proc createBreakPoint(s: cstring, start: int) = - let br = parseBreakpoint(s, start) - if not br.filename.isNil: - if not addBreakpoint(br.filename, br.low, br.high): - debugOut("[Warning] no breakpoint could be set; out of breakpoint space ") - -proc breakpointToggle(s: cstring, start: int) = - var a = parseBreakpoint(s, start) - if not a.filename.isNil: - var b = checkBreakpoints(a.filename, a.low) - if not b.isNil: b.flip - else: debugOut("[Warning] unknown breakpoint ") - -proc dbgEvaluate(stream: CFilePtr, s: cstring, start: int, f: PFrame) = - var dbgTemp: StaticStr - var i = scanWord(s, dbgTemp, start) - while s[i] in {' ', '\t'}: inc(i) - var v: VarSlot - if s[i] == '.': - inc(i) - add(dbgTemp, '.') - i = scanAndAppendWord(s, dbgTemp, i) - for i in 0 .. getGlobalLen()-1: - let v = getGlobal(i) - if c_strcmp(v.name, addr dbgTemp.data) == 0: - writeVariable(stream, v) - else: - for i in 0 .. f.len-1: - let v = getLocal(f, i) - if c_strcmp(v.name, addr dbgTemp.data) == 0: - writeVariable(stream, v) - -proc dbgOut(s: cstring, start: int, currFrame: PFrame) = - var dbgTemp: StaticStr - var i = scanFilename(s, dbgTemp, start) - if dbgTemp.len == 0: - invalidCommand() - return - var stream = openAppend(addr dbgTemp.data) - if stream == nil: - debugOut("[Warning] could not open or create file ") - return - dbgEvaluate(stream, s, i, currFrame) - close(stream) - -proc dbgStackFrame(s: cstring, start: int, currFrame: PFrame) = - var dbgTemp: StaticStr - var i = scanFilename(s, dbgTemp, start) - if dbgTemp.len == 0: - # just write it to cstdout: - listFrame(cstdout, currFrame) - else: - var stream = openAppend(addr dbgTemp.data) - if stream == nil: - debugOut("[Warning] could not open or create file ") - return - listFrame(stream, currFrame) - close(stream) - -proc readLine(f: CFilePtr, line: var StaticStr): bool = - while true: - var c = c_fgetc(f) - if c < 0'i32: - if line.len > 0: break - else: return false - if c == 10'i32: break # LF - if c == 13'i32: # CR - c = c_fgetc(f) # is the next char LF? - if c != 10'i32: discard c_ungetc(c, f) # no, put the character back - break - add line, chr(int(c)) - result = true - -proc listFilenames() = - write(cstdout, EndbBeg) - write(cstdout, "| Files:\n") - var i = 0 - while true: - let x = dbgFilenames[i] - if x.isNil: break - write(cstdout, x) - write(cstdout, "\n") - inc i - write(cstdout, EndbEnd) - -proc dbgWriteStackTrace(f: PFrame) -proc commandPrompt() = - # if we return from this routine, user code executes again - var - again = true - dbgFramePtr = framePtr # for going down and up the stack - dbgDown = 0 # how often we did go down - dbgTemp: StaticStr - - while again: - write(cstdout, "*** endb| >>") - let oldLen = dbgUser.len - dbgUser.len = 0 - if not readLine(cstdin, dbgUser): break - if dbgUser.len == 0: dbgUser.len = oldLen - # now look what we have to do: - var i = scanWord(addr dbgUser.data, dbgTemp, 0) - template `?`(x: untyped): untyped = dbgTemp == cstring(x) - if ?"s" or ?"step": - dbgState = dbStepInto - again = false - elif ?"n" or ?"next": - dbgState = dbStepOver - dbgSkipToFrame = framePtr - again = false - elif ?"f" or ?"skipcurrent": - dbgState = dbSkipCurrent - dbgSkipToFrame = framePtr.prev - again = false - elif ?"c" or ?"continue" or ?"r" or ?"run": - dbgState = dbBreakpoints - again = false - elif ?"i" or ?"ignore": - dbgState = dbOff - again = false - elif ?"h" or ?"help": - dbgHelp() - elif ?"q" or ?"quit": - dbgState = dbQuiting - dbgAborting = true - again = false - quit(1) # BUGFIX: quit with error code > 0 - elif ?"e" or ?"eval": - var - prevState = dbgState - prevSkipFrame = dbgSkipToFrame - dbgState = dbSkipCurrent - dbgEvaluate(cstdout, addr dbgUser.data, i, dbgFramePtr) - dbgState = prevState - dbgSkipToFrame = prevSkipFrame - elif ?"o" or ?"out": - dbgOut(addr dbgUser.data, i, dbgFramePtr) - elif ?"stackframe": - dbgStackFrame(addr dbgUser.data, i, dbgFramePtr) - elif ?"w" or ?"where": - dbgShowExecutionPoint() - elif ?"l" or ?"locals": - var - prevState = dbgState - prevSkipFrame = dbgSkipToFrame - dbgState = dbSkipCurrent - listLocals(cstdout, dbgFramePtr) - dbgState = prevState - dbgSkipToFrame = prevSkipFrame - elif ?"g" or ?"globals": - var - prevState = dbgState - prevSkipFrame = dbgSkipToFrame - dbgState = dbSkipCurrent - listGlobals(cstdout) - dbgState = prevState - dbgSkipToFrame = prevSkipFrame - elif ?"u" or ?"up": - if dbgDown <= 0: - debugOut("[Warning] cannot go up any further ") - else: - dbgFramePtr = framePtr - for j in 0 .. dbgDown-2: # BUGFIX - dbgFramePtr = dbgFramePtr.prev - dec(dbgDown) - dbgShowCurrentProc(dbgFramePtr) - elif ?"d" or ?"down": - if dbgFramePtr != nil: - inc(dbgDown) - dbgFramePtr = dbgFramePtr.prev - dbgShowCurrentProc(dbgFramePtr) - else: - debugOut("[Warning] cannot go down any further ") - elif ?"bt" or ?"backtrace": - dbgWriteStackTrace(framePtr) - elif ?"b" or ?"break": - createBreakPoint(addr dbgUser.data, i) - elif ?"breakpoints": - listBreakPoints() - elif ?"toggle": - breakpointToggle(addr dbgUser.data, i) - elif ?"filenames": - listFilenames() - elif ?"maxdisplay": - var parsed: int - i = scanNumber(addr dbgUser.data, parsed, i) - if dbgUser.data[i-1] in {'0'..'9'}: - if parsed == 0: maxDisplayRecDepth = -1 - else: maxDisplayRecDepth = parsed - else: - invalidCommand() - else: invalidCommand() - -proc endbStep() = - # we get into here if an unhandled exception has been raised - # XXX: do not allow the user to run the program any further? - # XXX: BUG: the frame is lost here! - dbgShowExecutionPoint() - commandPrompt() - -proc dbgWriteStackTrace(f: PFrame) = - const - firstCalls = 32 - var - it = f - i = 0 - total = 0 - tempFrames: array[0..127, PFrame] - # setup long head: - while it != nil and i <= high(tempFrames)-firstCalls: - tempFrames[i] = it - inc(i) - inc(total) - it = it.prev - # go up the stack to count 'total': - var b = it - while it != nil: - inc(total) - it = it.prev - var skipped = 0 - if total > len(tempFrames): - # skip N - skipped = total-i-firstCalls+1 - for j in 1..skipped: - if b != nil: b = b.prev - # create '...' entry: - tempFrames[i] = nil - inc(i) - # setup short tail: - while b != nil and i <= high(tempFrames): - tempFrames[i] = b - inc(i) - b = b.prev - for j in countdown(i-1, 0): - if tempFrames[j] == nil: - write(cstdout, "(") - write(cstdout, skipped) - write(cstdout, " calls omitted) ...") - else: - write(cstdout, tempFrames[j].filename) - if tempFrames[j].line > 0: - write(cstdout, "(") - write(cstdout, tempFrames[j].line) - write(cstdout, ")") - write(cstdout, " ") - write(cstdout, tempFrames[j].procname) - write(cstdout, "\n") - -proc checkForBreakpoint = - let b = checkBreakpoints(framePtr.filename, framePtr.line) - if b != nil: - write(cstdout, "*** endb| reached ") - write(cstdout, framePtr.filename) - write(cstdout, "(") - write(cstdout, framePtr.line) - write(cstdout, ") ") - write(cstdout, framePtr.procname) - write(cstdout, " ***\n") - commandPrompt() - -proc lineHookImpl() {.nimcall.} = - case dbgState - of dbStepInto: - # we really want the command prompt here: - dbgShowExecutionPoint() - commandPrompt() - of dbSkipCurrent, dbStepOver: # skip current routine - if framePtr == dbgSkipToFrame: - dbgShowExecutionPoint() - commandPrompt() - else: - # breakpoints are wanted though (I guess) - checkForBreakpoint() - of dbBreakpoints: - # debugger is only interested in breakpoints - checkForBreakpoint() - else: discard - -proc watchpointHookImpl(name: cstring) {.nimcall.} = - dbgWriteStackTrace(framePtr) - debugOut(name) - -proc initDebugger {.inline.} = - dbgState = dbStepInto - dbgUser.len = 1 - dbgUser.data[0] = 's' - dbgWatchpointHook = watchpointHookImpl - dbgLineHook = lineHookImpl diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim index 0898ad0fda..1eb4cedc8e 100644 --- a/lib/system/excpt.nim +++ b/lib/system/excpt.nim @@ -38,10 +38,7 @@ proc showErrorMessage(data: cstring) {.gcsafe.} = writeToStdErr(data) proc quitOrDebug() {.inline.} = - when defined(endb): - endbStep() # call the debugger - else: - quit(1) + quit(1) proc chckIndx(i, a, b: int): int {.inline, compilerproc, benign.} proc chckRange(i, a, b: int): int {.inline, compilerproc, benign.} @@ -469,10 +466,6 @@ proc nimFrame(s: PFrame) {.compilerRtl, inl.} = framePtr = s if s.calldepth == nimCallDepthLimit: callDepthLimitReached() -when defined(endb): - var - dbgAborting: bool # whether the debugger wants to abort - when defined(cpp) and appType != "lib" and not defined(js) and not defined(nimscript) and hostOS != "standalone" and not defined(noCppExceptions): @@ -515,8 +508,6 @@ when not defined(noSignalHandler) and not defined(useNimRtl): elif s == SIGSEGV: action("SIGSEGV: Illegal storage access. (Attempt to read from nil?)\n") elif s == SIGABRT: - when defined(endb): - if dbgAborting: return # the debugger wants to abort action("SIGABRT: Abnormal termination.\n") elif s == SIGFPE: action("SIGFPE: Arithmetic error.\n") elif s == SIGILL: action("SIGILL: Illegal operation.\n") @@ -546,7 +537,6 @@ when not defined(noSignalHandler) and not defined(useNimRtl): msg = y processSignal(sign, asgn) showErrorMessage(msg) - when defined(endb): dbgAborting = true quit(1) # always quit when SIGABRT proc registerSignalHandler() = diff --git a/tests/gc/gcleak4.nim b/tests/gc/gcleak4.nim index e9b17e5572..35b8ce1121 100644 --- a/tests/gc/gcleak4.nim +++ b/tests/gc/gcleak4.nim @@ -27,13 +27,11 @@ method eval(e: ref TPlusExpr): int = proc newLit(x: int): ref TLiteral = new(result) - {.watchpoint: result.} result.x = x result.op1 = $getOccupiedMem() proc newPlus(a, b: ref TExpr): ref TPlusExpr = new(result) - {.watchpoint: result.} result.a = a result.b = b result.op2 = $getOccupiedMem() diff --git a/tests/misc/thallo.nim b/tests/misc/thallo.nim index f61ce2ef23..8dac560235 100644 --- a/tests/misc/thallo.nim +++ b/tests/misc/thallo.nim @@ -48,7 +48,6 @@ echo(["a", "b", "c", "d"].len) for x in items(["What's", "your", "name", "?", ]): echo(x) var `name` = readLine(stdin) -{.breakpoint.} echo("Hi " & thallo.name & "!\n") debug(name) diff --git a/tests/objects/tobjects_various.nim b/tests/objects/tobjects_various.nim index a6c4628afb..315193de98 100644 --- a/tests/objects/tobjects_various.nim +++ b/tests/objects/tobjects_various.nim @@ -17,7 +17,6 @@ block tobject2: z: int # added a field proc getPoint( p: var TPoint2d) = - {.breakpoint.} writeLine(stdout, p.x) var p: TPoint3d From 9ae0dd611f77c4cd7122e6ac77e0278c1bafbbd7 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Fri, 30 Aug 2019 23:26:45 -0700 Subject: [PATCH 52/61] typeToString can now show (recursively) resolved type aliases; fixes #8569 #8083 #8570 (#11678) * nested typeToString * typeToString: preferResolved * add test * fix test * preferMixed * fix tests --- compiler/semmagic.nim | 8 + compiler/types.nim | 432 ++++++++++++++------------- tests/errmsgs/tsigmatch.nim | 2 +- tests/errmsgs/twrong_at_operator.nim | 2 +- tests/metatype/ttypetraits2.nim | 29 ++ tests/openarray/t8259.nim | 2 +- 6 files changed, 272 insertions(+), 203 deletions(-) diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index 7f6bf8fa32..ed0c12a954 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -141,6 +141,14 @@ proc evalTypeTrait(c: PContext; traitCall: PNode, operand: PType, context: PSym) return typeWithSonsResult(tyAnd, @[operand, operand2]) of "not": return typeWithSonsResult(tyNot, @[operand]) + of "typeToString": + var prefer = preferTypeName + if traitCall.sons.len >= 2: + let preferStr = traitCall.sons[2].strVal + prefer = parseEnum[TPreferedDesc](preferStr) + result = newStrNode(nkStrLit, operand.typeToString(prefer)) + result.typ = newType(tyString, context) + result.info = traitCall.info of "name", "$": result = newStrNode(nkStrLit, operand.typeToString(preferTypeName)) result.typ = newType(tyString, context) diff --git a/compiler/types.nim b/compiler/types.nim index 1c21fffb16..1473be2610 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -15,8 +15,14 @@ import type TPreferedDesc* = enum - preferName, preferDesc, preferExported, preferModuleInfo, preferGenericArg, - preferTypeName + preferName, # default + preferDesc, # probably should become what preferResolved is + preferExported, + preferModuleInfo, # fully qualified + preferGenericArg, + preferTypeName, + preferResolved, # fully resolved symbols + preferMixed, # show symbol + resolved symbols if it differs, eg: seq[cint{int32}, float] proc typeToString*(typ: PType; prefer: TPreferedDesc = preferName): string template `$`*(typ: PType): string = typeToString(typ) @@ -121,6 +127,7 @@ proc isFloatLit*(t: PType): bool {.inline.} = proc getProcHeader*(conf: ConfigRef; sym: PSym; prefer: TPreferedDesc = preferName; getDeclarationPath = true): string = assert sym != nil + # consider using `skipGenericOwner` to avoid fun2.fun2 when fun2 is generic result = sym.owner.name.s & '.' & sym.name.s if sym.kind in routineKinds: result.add '(' @@ -415,12 +422,12 @@ proc rangeToStr(n: PNode): string = result = valueToString(n.sons[0]) & ".." & valueToString(n.sons[1]) const - typeToStr: array[TTypeKind, string] = ["None", "bool", "Char", "empty", + typeToStr: array[TTypeKind, string] = ["None", "bool", "char", "empty", "Alias", "nil", "untyped", "typed", "typeDesc", "GenericInvocation", "GenericBody", "GenericInst", "GenericParam", "distinct $1", "enum", "ordinal[$1]", "array[$1, $2]", "object", "tuple", "set[$1]", "range[$1]", "ptr ", "ref ", "var ", "seq[$1]", "proc", - "pointer", "OpenArray[$1]", "string", "CString", "Forward", + "pointer", "OpenArray[$1]", "string", "cstring", "Forward", "int", "int8", "int16", "int32", "int64", "float", "float32", "float64", "float128", "uint", "uint8", "uint16", "uint32", "uint64", @@ -431,7 +438,8 @@ const "and", "or", "not", "any", "static", "TypeFromExpr", "FieldAccessor", "void"] -const preferToResolveSymbols = {preferName, preferTypeName, preferModuleInfo, preferGenericArg} +const preferToResolveSymbols = {preferName, preferTypeName, preferModuleInfo, + preferGenericArg, preferResolved, preferMixed} template bindConcreteTypeToUserTypeClass*(tc, concrete: PType) = tc.sons.add concrete @@ -450,208 +458,232 @@ proc addTypeFlags(name: var string, typ: PType) {.inline.} = if tfNotNil in typ.flags: name.add(" not nil") proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = - var t = typ - result = "" - if t == nil: return - if prefer in preferToResolveSymbols and t.sym != nil and - sfAnon notin t.sym.flags and t.kind != tySequence: - if t.kind == tyInt and isIntLit(t): - result = t.sym.name.s & " literal(" & $t.n.intVal & ")" - elif t.kind == tyAlias and t.sons[0].kind != tyAlias: - result = typeToString(t.sons[0]) - elif prefer in {preferName, preferTypeName} or t.sym.owner.isNil: - result = t.sym.name.s - if t.kind == tyGenericParam and t.sonsLen > 0: - result.add ": " - var first = true - for son in t.sons: - if not first: result.add " or " - result.add son.typeToString - first = false + let preferToplevel = prefer + proc getPrefer(prefer: TPreferedDesc): TPreferedDesc = + if preferToplevel in {preferResolved, preferMixed}: + preferToplevel # sticky option else: - result = t.sym.owner.name.s & '.' & t.sym.name.s - result.addTypeFlags(t) - return - case t.kind - of tyInt: - if not isIntLit(t) or prefer == preferExported: - result = typeToStr[t.kind] - else: - if prefer == preferGenericArg: - result = $t.n.intVal + prefer + + proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = + let prefer = getPrefer(prefer) + let t = typ + result = "" + if t == nil: return + if prefer in preferToResolveSymbols and t.sym != nil and + sfAnon notin t.sym.flags and t.kind != tySequence: + if t.kind == tyInt and isIntLit(t): + result = t.sym.name.s & " literal(" & $t.n.intVal & ")" + elif t.kind == tyAlias and t.sons[0].kind != tyAlias: + result = typeToString(t.sons[0]) + elif prefer in {preferResolved, preferMixed}: + case t.kind + of IntegralTypes + {tyFloat..tyFloat128} + {tyString, tyCString}: + result = typeToStr[t.kind] + of tyGenericBody: + result = typeToString(t.lastSon) + of tyCompositeTypeClass: + # avoids showing `A[any]` in `proc fun(a: A)` with `A = object[T]` + result = typeToString(t.lastSon.lastSon) + else: + result = t.sym.name.s + if prefer == preferMixed and result != t.sym.name.s: + result = t.sym.name.s & "{" & result & "}" + elif prefer in {preferName, preferTypeName} or t.sym.owner.isNil: + # note: should probably be: {preferName, preferTypeName, preferGenericArg} + result = t.sym.name.s + if t.kind == tyGenericParam and t.sonsLen > 0: + result.add ": " + var first = true + for son in t.sons: + if not first: result.add " or " + result.add son.typeToString + first = false else: - result = "int literal(" & $t.n.intVal & ")" - of tyGenericInst, tyGenericInvocation: - result = typeToString(t.sons[0]) & '[' - for i in 1 ..< sonsLen(t)-ord(t.kind != tyGenericInvocation): - if i > 1: add(result, ", ") - add(result, typeToString(t.sons[i], preferGenericArg)) - add(result, ']') - of tyGenericBody: - result = typeToString(t.lastSon) & '[' - for i in 0 .. sonsLen(t)-2: - if i > 0: add(result, ", ") - add(result, typeToString(t.sons[i], preferTypeName)) - add(result, ']') - of tyTypeDesc: - if t.sons[0].kind == tyNone: result = "typedesc" - else: result = "type " & typeToString(t.sons[0]) - of tyStatic: - if prefer == preferGenericArg and t.n != nil: - result = t.n.renderTree - else: - result = "static[" & (if t.len > 0: typeToString(t.sons[0]) else: "") & "]" - if t.n != nil: result.add "(" & renderTree(t.n) & ")" - of tyUserTypeClass: - if t.sym != nil and t.sym.owner != nil: - if t.isResolvedUserTypeClass: return typeToString(t.lastSon) - return t.sym.owner.name.s - else: - result = "" - of tyBuiltInTypeClass: - result = case t.base.kind: - of tyVar: "var" - of tyRef: "ref" - of tyPtr: "ptr" - of tySequence: "seq" - of tyArray: "array" - of tySet: "set" - of tyRange: "range" - of tyDistinct: "distinct" - of tyProc: "proc" - of tyObject: "object" - of tyTuple: "tuple" - of tyOpenArray: "openarray" - else: typeToStr[t.base.kind] - of tyInferred: - let concrete = t.previouslyInferred - if concrete != nil: result = typeToString(concrete) - else: result = "inferred[" & typeToString(t.base) & "]" - of tyUserTypeClassInst: - let body = t.base - result = body.sym.name.s & "[" - for i in 1 .. sonsLen(t) - 2: - if i > 1: add(result, ", ") - add(result, typeToString(t.sons[i])) - result.add "]" - of tyAnd: - for i, son in t.sons: - result.add(typeToString(son)) - if i < t.sons.high: - result.add(" and ") - of tyOr: - for i, son in t.sons: - result.add(typeToString(son)) - if i < t.sons.high: - result.add(" or ") - of tyNot: - result = "not " & typeToString(t.sons[0]) - of tyUntyped: - #internalAssert t.len == 0 - result = "untyped" - of tyFromExpr: - if t.n == nil: - result = "unknown" - else: - result = "type(" & renderTree(t.n) & ")" - of tyArray: - if t.sons[0].kind == tyRange: - result = "array[" & rangeToStr(t.sons[0].n) & ", " & - typeToString(t.sons[1]) & ']' - else: - result = "array[" & typeToString(t.sons[0]) & ", " & - typeToString(t.sons[1]) & ']' - of tyUncheckedArray: - result = "UncheckedArray[" & typeToString(t.sons[0]) & ']' - of tySequence: - result = "seq[" & typeToString(t.sons[0]) & ']' - of tyOpt: - result = "opt[" & typeToString(t.sons[0]) & ']' - of tyOrdinal: - result = "ordinal[" & typeToString(t.sons[0]) & ']' - of tySet: - result = "set[" & typeToString(t.sons[0]) & ']' - of tyOpenArray: - result = "openarray[" & typeToString(t.sons[0]) & ']' - of tyDistinct: - result = "distinct " & typeToString(t.sons[0], - if prefer == preferModuleInfo: preferModuleInfo else: preferTypeName) - of tyTuple: - # we iterate over t.sons here, because t.n may be nil - if t.n != nil: - result = "tuple[" - assert(sonsLen(t.n) == sonsLen(t)) - for i in 0 ..< sonsLen(t.n): - assert(t.n.sons[i].kind == nkSym) - add(result, t.n.sons[i].sym.name.s & ": " & typeToString(t.sons[i])) - if i < sonsLen(t.n) - 1: add(result, ", ") + result = t.sym.owner.name.s & '.' & t.sym.name.s + result.addTypeFlags(t) + return + case t.kind + of tyInt: + if not isIntLit(t) or prefer == preferExported: + result = typeToStr[t.kind] + else: + if prefer == preferGenericArg: + result = $t.n.intVal + else: + result = "int literal(" & $t.n.intVal & ")" + of tyGenericInst, tyGenericInvocation: + result = typeToString(t.sons[0]) & '[' + for i in 1 ..< sonsLen(t)-ord(t.kind != tyGenericInvocation): + if i > 1: add(result, ", ") + add(result, typeToString(t.sons[i], preferGenericArg)) add(result, ']') - elif sonsLen(t) == 0: - result = "tuple[]" - else: - if prefer == preferTypeName: result = "(" - else: result = "tuple of (" - for i in 0 ..< sonsLen(t): + of tyGenericBody: + result = typeToString(t.lastSon) & '[' + for i in 0 .. sonsLen(t)-2: + if i > 0: add(result, ", ") + add(result, typeToString(t.sons[i], preferTypeName)) + add(result, ']') + of tyTypeDesc: + if t.sons[0].kind == tyNone: result = "typedesc" + else: result = "type " & typeToString(t.sons[0]) + of tyStatic: + if prefer == preferGenericArg and t.n != nil: + result = t.n.renderTree + else: + result = "static[" & (if t.len > 0: typeToString(t.sons[0]) else: "") & "]" + if t.n != nil: result.add "(" & renderTree(t.n) & ")" + of tyUserTypeClass: + if t.sym != nil and t.sym.owner != nil: + if t.isResolvedUserTypeClass: return typeToString(t.lastSon) + return t.sym.owner.name.s + else: + result = "" + of tyBuiltInTypeClass: + result = case t.base.kind: + of tyVar: "var" + of tyRef: "ref" + of tyPtr: "ptr" + of tySequence: "seq" + of tyArray: "array" + of tySet: "set" + of tyRange: "range" + of tyDistinct: "distinct" + of tyProc: "proc" + of tyObject: "object" + of tyTuple: "tuple" + of tyOpenArray: "openArray" + else: typeToStr[t.base.kind] + of tyInferred: + let concrete = t.previouslyInferred + if concrete != nil: result = typeToString(concrete) + else: result = "inferred[" & typeToString(t.base) & "]" + of tyUserTypeClassInst: + let body = t.base + result = body.sym.name.s & "[" + for i in 1 .. sonsLen(t) - 2: + if i > 1: add(result, ", ") + add(result, typeToString(t.sons[i])) + result.add "]" + of tyAnd: + for i, son in t.sons: + result.add(typeToString(son)) + if i < t.sons.high: + result.add(" and ") + of tyOr: + for i, son in t.sons: + result.add(typeToString(son)) + if i < t.sons.high: + result.add(" or ") + of tyNot: + result = "not " & typeToString(t.sons[0]) + of tyUntyped: + #internalAssert t.len == 0 + result = "untyped" + of tyFromExpr: + if t.n == nil: + result = "unknown" + else: + result = "type(" & renderTree(t.n) & ")" + of tyArray: + if t.sons[0].kind == tyRange: + result = "array[" & rangeToStr(t.sons[0].n) & ", " & + typeToString(t.sons[1]) & ']' + else: + result = "array[" & typeToString(t.sons[0]) & ", " & + typeToString(t.sons[1]) & ']' + of tyUncheckedArray: + result = "UncheckedArray[" & typeToString(t.sons[0]) & ']' + of tySequence: + result = "seq[" & typeToString(t.sons[0]) & ']' + of tyOpt: + result = "opt[" & typeToString(t.sons[0]) & ']' + of tyOrdinal: + result = "ordinal[" & typeToString(t.sons[0]) & ']' + of tySet: + result = "set[" & typeToString(t.sons[0]) & ']' + of tyOpenArray: + result = "openArray[" & typeToString(t.sons[0]) & ']' + of tyDistinct: + result = "distinct " & typeToString(t.sons[0], + if prefer == preferModuleInfo: preferModuleInfo else: preferTypeName) + of tyTuple: + # we iterate over t.sons here, because t.n may be nil + if t.n != nil: + result = "tuple[" + assert(sonsLen(t.n) == sonsLen(t)) + for i in 0 ..< sonsLen(t.n): + assert(t.n.sons[i].kind == nkSym) + add(result, t.n.sons[i].sym.name.s & ": " & typeToString(t.sons[i])) + if i < sonsLen(t.n) - 1: add(result, ", ") + add(result, ']') + elif sonsLen(t) == 0: + result = "tuple[]" + else: + if prefer == preferTypeName: result = "(" + else: result = "tuple of (" + for i in 0 ..< sonsLen(t): + add(result, typeToString(t.sons[i])) + if i < sonsLen(t) - 1: add(result, ", ") + add(result, ')') + of tyPtr, tyRef, tyVar, tyLent: + result = typeToStr[t.kind] + if t.len >= 2: + setLen(result, result.len-1) + result.add '[' + for i in 0 ..< sonsLen(t): + add(result, typeToString(t.sons[i])) + if i < sonsLen(t) - 1: add(result, ", ") + result.add ']' + else: + result.add typeToString(t.sons[0]) + of tyRange: + result = "range " + if t.n != nil and t.n.kind == nkRange: + result.add rangeToStr(t.n) + if prefer != preferExported: + result.add("(" & typeToString(t.sons[0]) & ")") + of tyProc: + result = if tfIterator in t.flags: "iterator " + elif t.owner != nil: + case t.owner.kind + of skTemplate: "template " + of skMacro: "macro " + of skConverter: "converter " + else: "proc " + else: + "proc " + if tfUnresolved in t.flags: result.add "[*missing parameters*]" + result.add "(" + for i in 1 ..< sonsLen(t): + if t.n != nil and i < t.n.len and t.n[i].kind == nkSym: + add(result, t.n[i].sym.name.s) + add(result, ": ") add(result, typeToString(t.sons[i])) if i < sonsLen(t) - 1: add(result, ", ") add(result, ')') - of tyPtr, tyRef, tyVar, tyLent: - result = typeToStr[t.kind] - if t.len >= 2: - setLen(result, result.len-1) - result.add '[' - for i in 0 ..< sonsLen(t): - add(result, typeToString(t.sons[i])) - if i < sonsLen(t) - 1: add(result, ", ") - result.add ']' + if t.len > 0 and t.sons[0] != nil: add(result, ": " & typeToString(t.sons[0])) + var prag = if t.callConv == ccDefault: "" else: CallingConvToStr[t.callConv] + if tfNoSideEffect in t.flags: + addSep(prag) + add(prag, "noSideEffect") + if tfThread in t.flags: + addSep(prag) + add(prag, "gcsafe") + if t.lockLevel.ord != UnspecifiedLockLevel.ord: + addSep(prag) + add(prag, "locks: " & $t.lockLevel) + if len(prag) != 0: add(result, "{." & prag & ".}") + of tyVarargs: + result = typeToStr[t.kind] % typeToString(t.sons[0]) + of tySink: + result = "sink " & typeToString(t.sons[0]) + of tyOwned: + result = "owned " & typeToString(t.sons[0]) else: - result.add typeToString(t.sons[0]) - of tyRange: - result = "range " - if t.n != nil and t.n.kind == nkRange: - result.add rangeToStr(t.n) - if prefer != preferExported: - result.add("(" & typeToString(t.sons[0]) & ")") - of tyProc: - result = if tfIterator in t.flags: "iterator " - elif t.owner != nil: - case t.owner.kind - of skTemplate: "template " - of skMacro: "macro " - of skConverter: "converter " - else: "proc " - else: - "proc " - if tfUnresolved in t.flags: result.add "[*missing parameters*]" - result.add "(" - for i in 1 ..< sonsLen(t): - if t.n != nil and i < t.n.len and t.n[i].kind == nkSym: - add(result, t.n[i].sym.name.s) - add(result, ": ") - add(result, typeToString(t.sons[i])) - if i < sonsLen(t) - 1: add(result, ", ") - add(result, ')') - if t.len > 0 and t.sons[0] != nil: add(result, ": " & typeToString(t.sons[0])) - var prag = if t.callConv == ccDefault: "" else: CallingConvToStr[t.callConv] - if tfNoSideEffect in t.flags: - addSep(prag) - add(prag, "noSideEffect") - if tfThread in t.flags: - addSep(prag) - add(prag, "gcsafe") - if t.lockLevel.ord != UnspecifiedLockLevel.ord: - addSep(prag) - add(prag, "locks: " & $t.lockLevel) - if len(prag) != 0: add(result, "{." & prag & ".}") - of tyVarargs: - result = typeToStr[t.kind] % typeToString(t.sons[0]) - of tySink: - result = "sink " & typeToString(t.sons[0]) - of tyOwned: - result = "owned " & typeToString(t.sons[0]) - else: - result = typeToStr[t.kind] - result.addTypeFlags(t) + result = typeToStr[t.kind] + result.addTypeFlags(t) + result = typeToString(typ, prefer) proc firstOrd*(conf: ConfigRef; t: PType): Int128 = case t.kind diff --git a/tests/errmsgs/tsigmatch.nim b/tests/errmsgs/tsigmatch.nim index 42a98a8913..21e2c217d8 100644 --- a/tests/errmsgs/tsigmatch.nim +++ b/tests/errmsgs/tsigmatch.nim @@ -36,7 +36,7 @@ tsigmatch.nim(143, 13) Error: type mismatch: got but expected one of: proc `@`[T](a: openArray[T]): seq[T] first type mismatch at position: 1 - required type for a: openarray[T] + required type for a: openArray[T] but expression '[int]' is of type: array[0..0, type int] proc `@`[IDX, T](a: sink array[IDX, T]): seq[T] first type mismatch at position: 1 diff --git a/tests/metatype/ttypetraits2.nim b/tests/metatype/ttypetraits2.nim index c5beaa3077..e07b516605 100644 --- a/tests/metatype/ttypetraits2.nim +++ b/tests/metatype/ttypetraits2.nim @@ -14,3 +14,32 @@ block: # isNamedTuple doAssert not Foo3.isNamedTuple doAssert not Foo4.isNamedTuple doAssert not (1,).type.isNamedTuple + +proc typeToString*(t: typedesc, prefer = "preferTypeName"): string {.magic: "TypeTrait".} + ## Returns the name of the given type, with more flexibility than `name`, + ## and avoiding the potential clash with a variable named `name`. + ## prefer = "preferResolved" will resolve type aliases recursively. + # Move to typetraits.nim once api stabilized. + +block: # typeToString + type MyInt = int + type + C[T0, T1] = object + type C2=C # alias => will resolve as C + type C2b=C # alias => will resolve as C (recursively) + type C3[U,V] = C[V,U] + type C4[X] = C[X,X] + template name2(T): string = typeToString(T, "preferResolved") + doAssert MyInt.name2 == "int" + doAssert C3[MyInt, C2b].name2 == "C3[int, C]" + # C3 doesn't get resolved to C, not an alias (nor does C4) + doAssert C2b[MyInt, C4[cstring]].name2 == "C[int, C4[cstring]]" + doAssert C4[MyInt].name2 == "C4[int]" + when BiggestFloat is float and cint is int: + doAssert C2b[cint, BiggestFloat].name2 == "C3[int, C3[float, int32]]" + + template name3(T): string = typeToString(T, "preferMixed") + doAssert MyInt.name3 == "MyInt{int}" + doAssert (tuple[a: MyInt, b: float]).name3 == "tuple[a: MyInt{int}, b: float]" + doAssert (tuple[a: C2b[MyInt, C4[cstring]], b: cint, c: float]).name3 == + "tuple[a: C2b{C}[MyInt{int}, C4[cstring]], b: cint{int32}, c: float]" diff --git a/tests/openarray/t8259.nim b/tests/openarray/t8259.nim index c075769975..283c6cd02d 100644 --- a/tests/openarray/t8259.nim +++ b/tests/openarray/t8259.nim @@ -1,5 +1,5 @@ discard """ - errormsg: "invalid type: 'openarray[int]' for result" + errormsg: "invalid type: 'openArray[int]' for result" line: 6 """ From a055f628f4354f6125e97c42bcf83f8e20149fac Mon Sep 17 00:00:00 2001 From: Andrii Riabushenko Date: Sat, 31 Aug 2019 09:49:47 +0100 Subject: [PATCH 53/61] fixes #12092 --- compiler/semstmts.nim | 4 ++-- tests/destructor/tdestructor_too_late.nim | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 tests/destructor/tdestructor_too_late.nim diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 5a0aac40e3..dff67f175e 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1595,7 +1595,7 @@ proc semOverride(c: PContext, s: PSym, n: PNode) = else: break if obj.kind in {tyObject, tyDistinct, tySequence, tyString}: obj = canonType(c, obj) - if obj.destructor.isNil: + if obj.destructor.isNil and tfCheckedForDestructor notin obj.flags: obj.attachedOps[attachedDestructor] = s else: prevDestructor(c, obj.destructor, obj, n.info) @@ -1660,7 +1660,7 @@ proc semOverride(c: PContext, s: PSym, n: PNode) = obj = canonType(c, obj) #echo "ATTACHING TO ", obj.id, " ", s.name.s, " ", cast[int](obj) let k = if name == "=": attachedAsgn else: attachedSink - if obj.attachedOps[k].isNil: + if obj.attachedOps[k].isNil and tfCheckedForDestructor notin obj.flags: obj.attachedOps[k] = s else: prevDestructor(c, obj.attachedOps[k], obj, n.info) diff --git a/tests/destructor/tdestructor_too_late.nim b/tests/destructor/tdestructor_too_late.nim new file mode 100644 index 0000000000..d279280ba1 --- /dev/null +++ b/tests/destructor/tdestructor_too_late.nim @@ -0,0 +1,14 @@ +discard """ + errmsg: "cannot bind another '=destroy' to: Obj; previous declaration was constructed here implicitly: tdestructor_too_late.nim(7, 16)" +""" +type Obj* = object + v*: int + +proc something(this: sink Obj) = + discard + +proc `=destroy`(this: var Obj) = + echo "igotdestroyed" + this.v = -1 + +var test* = Obj(v: 42) \ No newline at end of file From a388c35741b4e9718ddd1d1f781a56f6bc0373c0 Mon Sep 17 00:00:00 2001 From: Andrii Riabushenko Date: Sat, 31 Aug 2019 09:56:39 +0100 Subject: [PATCH 54/61] support forward declared destructors --- compiler/semstmts.nim | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index dff67f175e..262deba5eb 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1595,7 +1595,9 @@ proc semOverride(c: PContext, s: PSym, n: PNode) = else: break if obj.kind in {tyObject, tyDistinct, tySequence, tyString}: obj = canonType(c, obj) - if obj.destructor.isNil and tfCheckedForDestructor notin obj.flags: + if obj.attachedOps[attachedDestructor] == s: + discard "forward declared destructor" + elif obj.destructor.isNil and tfCheckedForDestructor notin obj.flags: obj.attachedOps[attachedDestructor] = s else: prevDestructor(c, obj.destructor, obj, n.info) @@ -1660,7 +1662,9 @@ proc semOverride(c: PContext, s: PSym, n: PNode) = obj = canonType(c, obj) #echo "ATTACHING TO ", obj.id, " ", s.name.s, " ", cast[int](obj) let k = if name == "=": attachedAsgn else: attachedSink - if obj.attachedOps[k].isNil and tfCheckedForDestructor notin obj.flags: + if obj.attachedOps[k] == s: + discard "forward declared op" + elif obj.attachedOps[k].isNil and tfCheckedForDestructor notin obj.flags: obj.attachedOps[k] = s else: prevDestructor(c, obj.attachedOps[k], obj, n.info) From efe1bed82e8fa9476b4c9ed2a85802f6fb222cc6 Mon Sep 17 00:00:00 2001 From: Albert Safin Date: Sat, 31 Aug 2019 09:46:13 +0000 Subject: [PATCH 55/61] Allow typeof(nil) as a return type --- compiler/ccgtypes.nim | 1 + compiler/jsgen.nim | 2 ++ compiler/types.nim | 2 +- tests/ccgbugs/tnil_type.nim | 5 ++++- 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 1e38267801..5c5999e82f 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -155,6 +155,7 @@ proc mapType(conf: ConfigRef; typ: PType): TCTypeKind = of tyNone, tyTyped: result = ctVoid of tyBool: result = ctBool of tyChar: result = ctChar + of tyNil: result = ctPtr of tySet: result = mapSetType(conf, typ) of tyOpenArray, tyArray, tyVarargs, tyUncheckedArray: result = ctArray of tyObject, tyTuple: result = ctStruct diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 107fa2b28d..1d980ab6f4 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -1600,6 +1600,8 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope = result = putToSeq("{}", indirect) of tyBool: result = putToSeq("false", indirect) + of tyNil: + result = putToSeq("null", indirect) of tyArray: let length = toInt(lengthOrd(p.config, t)) let e = elemType(t) diff --git a/compiler/types.nim b/compiler/types.nim index 1473be2610..3a5f5fa620 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -1310,7 +1310,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind, tyNone, tyForward, tyFromExpr: result = t of tyNil: - if kind != skConst and kind != skParam: result = t + if kind != skConst and kind != skParam and kind != skResult: result = t of tyString, tyBool, tyChar, tyEnum, tyInt..tyUInt64, tyCString, tyPointer: result = nil of tyOrdinal: diff --git a/tests/ccgbugs/tnil_type.nim b/tests/ccgbugs/tnil_type.nim index b57e64513b..12310dae9e 100644 --- a/tests/ccgbugs/tnil_type.nim +++ b/tests/ccgbugs/tnil_type.nim @@ -12,4 +12,7 @@ proc f3(_: typedesc) = discard f3(typeof(nil)) proc f4[T](_: T) = discard -f4(nil) \ No newline at end of file +f4(nil) + +proc f5(): typeof(nil) = nil +discard f5() From a6428657e90d68c3993de4fa6ad05c6006378fc3 Mon Sep 17 00:00:00 2001 From: Albert Safin Date: Sat, 31 Aug 2019 09:52:21 +0000 Subject: [PATCH 56/61] $typeof(nil) is now "typeof(nil)", not "nil" --- compiler/types.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/types.nim b/compiler/types.nim index 3a5f5fa620..29d880ff69 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -423,7 +423,7 @@ proc rangeToStr(n: PNode): string = const typeToStr: array[TTypeKind, string] = ["None", "bool", "char", "empty", - "Alias", "nil", "untyped", "typed", "typeDesc", + "Alias", "typeof(nil)", "untyped", "typed", "typeDesc", "GenericInvocation", "GenericBody", "GenericInst", "GenericParam", "distinct $1", "enum", "ordinal[$1]", "array[$1, $2]", "object", "tuple", "set[$1]", "range[$1]", "ptr ", "ref ", "var ", "seq[$1]", "proc", From 2b565aad89587114148052eabdd430b923c21394 Mon Sep 17 00:00:00 2001 From: cooldome Date: Sat, 31 Aug 2019 18:23:54 +0100 Subject: [PATCH 57/61] Support iterators returning lent T (#11938) * lent iterators * rebase tests * update changelog * fix comments, more tests --- changelog.md | 5 ++++ compiler/lowerings.nim | 4 +-- compiler/semexprs.nim | 8 +++--- compiler/semstmts.nim | 59 +++++++++++++++++++++++++++++----------- compiler/transf.nim | 3 +- lib/system/iterators.nim | 5 ++-- tests/iter/tmoditer.nim | 59 ++++++++++++++++++++++++++++++++++++++++ 7 files changed, 116 insertions(+), 27 deletions(-) diff --git a/changelog.md b/changelog.md index b7c2214c9c..38bb412d09 100644 --- a/changelog.md +++ b/changelog.md @@ -61,6 +61,11 @@ type ## Language additions +- Inline iterators returning `lent T` types are now supported, similarly to iterators returning `var T`: +```nim +iterator myitems[T](x: openarray[T]): lent T +iterator mypairs[T](x: openarray[T]): tuple[idx: int, val: lent T] +``` ## Language changes diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim index 9ff3ece337..fff6c75cab 100644 --- a/compiler/lowerings.nim +++ b/compiler/lowerings.nim @@ -21,8 +21,8 @@ proc newDeref*(n: PNode): PNode {.inline.} = proc newTupleAccess*(g: ModuleGraph; tup: PNode, i: int): PNode = if tup.kind == nkHiddenAddr: - result = newNodeIT(nkHiddenAddr, tup.info, tup.typ.skipTypes(abstractInst+{tyPtr, tyVar})) - result.addSon(newNodeIT(nkBracketExpr, tup.info, tup.typ.skipTypes(abstractInst+{tyPtr, tyVar}).sons[i])) + result = newNodeIT(nkHiddenAddr, tup.info, tup.typ.skipTypes(abstractInst+{tyPtr, tyVar, tyLent})) + result.addSon(newNodeIT(nkBracketExpr, tup.info, tup.typ.skipTypes(abstractInst+{tyPtr, tyVar, tyLent}).sons[i])) addSon(result[0], tup[0]) var lit = newNodeIT(nkIntLit, tup.info, getSysType(g, tup.info, tyInt)) lit.intVal = i diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 765110d561..a3d92da8c7 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1781,21 +1781,21 @@ proc semYieldVarResult(c: PContext, n: PNode, restype: PType) = var t = skipTypes(restype, {tyGenericInst, tyAlias, tySink}) case t.kind of tyVar, tyLent: - if t.kind == tyVar: t.flags.incl tfVarIsPtr # bugfix for #4048, #4910, #6892 + t.flags.incl tfVarIsPtr # bugfix for #4048, #4910, #6892 if n.sons[0].kind in {nkHiddenStdConv, nkHiddenSubConv}: n.sons[0] = n.sons[0].sons[1] n.sons[0] = takeImplicitAddr(c, n.sons[0], t.kind == tyLent) of tyTuple: for i in 0..= high(IX): break inc(i) -iterator mpairs*[IX, T](a:var array[IX, T]):tuple[key:IX,val:var T] {.inline.} = +iterator mpairs*[IX, T](a: var array[IX, T]): tuple[key: IX, val: var T] {.inline.} = ## Iterates over each item of `a`. Yields ``(index, a[index])`` pairs. ## ``a[index]`` can be modified. var i = low(IX) @@ -179,7 +179,6 @@ iterator mpairs*(a: var cstring): tuple[key: int, val: var char] {.inline.} = yield (i, a[i]) inc(i) - iterator items*[T](a: seq[T]): T {.inline.} = ## Iterates over each item of `a`. var i = 0 diff --git a/tests/iter/tmoditer.nim b/tests/iter/tmoditer.nim index 1e6be37e48..34c6321ce3 100644 --- a/tests/iter/tmoditer.nim +++ b/tests/iter/tmoditer.nim @@ -27,3 +27,62 @@ for a in items(arr): echo "" +#-------------------------------------------------------------------- +# Lent iterators +#-------------------------------------------------------------------- +type + NonCopyable = object + x: int + + +proc `=destroy`(o: var NonCopyable) = + discard + +proc `=copy`(dst: var NonCopyable, src: NonCopyable) {.error.} + +proc `=sink`(dst: var NonCopyable, src: NonCopyable) = + dst.x = src.x + +iterator lentItems[T](a: openarray[T]): lent T = + for i in 0..a.high: + yield a[i] + +iterator lentPairs[T](a: array[0..1, T]): tuple[key: int, val: lent T] = + for i in 0..a.high: + yield (i, a[i]) + + +let arr1 = [1, 2, 3] +let arr2 = @["a", "b", "c"] +let arr3 = [NonCopyable(x: 1), NonCopyable(x: 2)] +let arr4 = @[(1, "a"), (2, "b"), (3, "c")] + +var accum: string +for x in lentItems(arr1): + accum &= $x +doAssert(accum == "123") + +accum = "" +for x in lentItems(arr2): + accum &= $x +doAssert(accum == "abc") + +accum = "" +for val in lentItems(arr3): + accum &= $val.x +doAssert(accum == "12") + +accum = "" +for i, val in lentPairs(arr3): + accum &= $i & "-" & $val.x & " " +doAssert(accum == "0-1 1-2 ") + +accum = "" +for i, val in lentItems(arr4): + accum &= $i & "-" & $val & " " +doAssert(accum == "1-a 2-b 3-c ") + +accum = "" +for (i, val) in lentItems(arr4): + accum &= $i & "-" & $val & " " +doAssert(accum == "1-a 2-b 3-c ") From 6e01be34efd60869c7905b1effcc47880cedfb6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arne=20D=C3=B6ring?= Date: Sat, 31 Aug 2019 19:32:59 +0200 Subject: [PATCH 58/61] fixes #11903 (#11908) --- compiler/vm.nim | 4 +++- tests/tools/tnimscriptwithmacro.nims | 22 ++++++++++++++++++++++ tests/vm/tgloballetfrommacro.nim | 13 +++++++++++++ 3 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 tests/tools/tnimscriptwithmacro.nims create mode 100644 tests/vm/tgloballetfrommacro.nim diff --git a/compiler/vm.nim b/compiler/vm.nim index 31dec418fd..072a1826b1 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -2102,8 +2102,9 @@ proc evalMacroCall*(module: PSym; g: ModuleGraph; setupGlobalCtx(module, g) var c = PCtx g.vm + let oldMode = c.mode + c.mode = emStaticStmt c.comesFromHeuristic.line = 0'u16 - c.callsite = nOrig let start = genProc(c, sym) @@ -2142,3 +2143,4 @@ proc evalMacroCall*(module: PSym; g: ModuleGraph; if cyclicTree(result): globalError(c.config, n.info, "macro produced a cyclic tree") dec(g.config.evalMacroCounter) c.callsite = nil + c.mode = oldMode diff --git a/tests/tools/tnimscriptwithmacro.nims b/tests/tools/tnimscriptwithmacro.nims new file mode 100644 index 0000000000..8b97f07696 --- /dev/null +++ b/tests/tools/tnimscriptwithmacro.nims @@ -0,0 +1,22 @@ +discard """ +cmd: "nim e $file" +output: ''' +foobar +nothing +hallo +""" + +# this test ensures that the mode is resetted correctly to repr + +import macros + +macro foobar(): void = + result = newCall(bindSym"echo", newLit("nothing")) + +echo "foobar" + +let x = 123 + +foobar() + +exec "echo hallo" diff --git a/tests/vm/tgloballetfrommacro.nim b/tests/vm/tgloballetfrommacro.nim new file mode 100644 index 0000000000..14dbff1e83 --- /dev/null +++ b/tests/vm/tgloballetfrommacro.nim @@ -0,0 +1,13 @@ +discard """ +errormsg: "cannot evaluate at compile time: BUILTIN_NAMES" +line: 11 +""" + +import sets + +let BUILTIN_NAMES = toSet(["int8", "int16", "int32", "int64"]) + +macro test*(): bool = + echo "int64" notin BUILTIN_NAMES + +echo test() From 35268c500f2143e757a71846b246a1ecb31c72f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oscar=20Nihlg=C3=A5rd?= Date: Sat, 31 Aug 2019 19:34:08 +0200 Subject: [PATCH 59/61] Fix int literals and range interaction (#11197) * Fix int literals and range interaction * Fix test * remove float range fix; update changelog --- changelog.md | 2 ++ compiler/sigmatch.nim | 20 +++++++++++--------- doc/manual.rst | 11 ++++++----- tests/arithm/tarithm.nim | 2 +- tests/range/trange.nim | 11 +++++++++-- 5 files changed, 29 insertions(+), 17 deletions(-) diff --git a/changelog.md b/changelog.md index 38bb412d09..35395afd39 100644 --- a/changelog.md +++ b/changelog.md @@ -34,6 +34,8 @@ type ### Breaking changes in the compiler +- A bug allowing `int` to be implicitly converted to range types of smaller size (e.g `range[0'i8..10'i8]`) has been fixed. + ## Library additions diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index cec3ecb287..d307039439 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -401,18 +401,20 @@ proc handleRange(f, a: PType, min, max: TTypeKind): TTypeRelation = else: result = isNone proc isConvertibleToRange(f, a: PType): bool = - # be less picky for tyRange, as that it is used for array indexing: if f.kind in {tyInt..tyInt64, tyUInt..tyUInt64} and a.kind in {tyInt..tyInt64, tyUInt..tyUInt64}: case f.kind - of tyInt, tyInt64: result = true - of tyInt8: result = a.kind in {tyInt8, tyInt} - of tyInt16: result = a.kind in {tyInt8, tyInt16, tyInt} - of tyInt32: result = a.kind in {tyInt8, tyInt16, tyInt32, tyInt} - of tyUInt, tyUInt64: result = true - of tyUInt8: result = a.kind in {tyUInt8, tyUInt} - of tyUInt16: result = a.kind in {tyUInt8, tyUInt16, tyUInt} - of tyUInt32: result = a.kind in {tyUInt8, tyUInt16, tyUInt32, tyUInt} + of tyInt8: result = isIntLit(a) or a.kind in {tyInt8} + of tyInt16: result = isIntLit(a) or a.kind in {tyInt8, tyInt16} + of tyInt32: result = isIntLit(a) or a.kind in {tyInt8, tyInt16, tyInt32} + # This is wrong, but seems like there's a lot of code that relies on it :( + of tyInt: result = true + of tyInt64: result = isIntLit(a) or a.kind in {tyInt8, tyInt16, tyInt32, tyInt, tyInt64} + of tyUInt8: result = isIntLit(a) or a.kind in {tyUInt8} + of tyUInt16: result = isIntLit(a) or a.kind in {tyUInt8, tyUInt16} + of tyUInt32: result = isIntLit(a) or a.kind in {tyUInt8, tyUInt16, tyUInt32} + of tyUInt: result = isIntLit(a) or a.kind in {tyUInt8, tyUInt16, tyUInt32, tyUInt} + of tyUInt64: result = isIntLit(a) or a.kind in {tyUInt8, tyUInt16, tyUInt32, tyUInt, tyUInt64} else: result = false elif f.kind in {tyFloat..tyFloat128}: # `isIntLit` is correct and should be used above as well, see PR: diff --git a/doc/manual.rst b/doc/manual.rst index 1f8f23310b..a8326d94d5 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -2282,9 +2282,11 @@ algorithm returns true: proc isImplicitlyConvertible(a, b: PType): bool = if isSubtype(a, b) or isCovariant(a, b): return true + if isIntLiteral(a): + return b in {int8, int16, int32, int64, int, uint, uint8, uint16, + uint32, uint64, float32, float64} case a.kind - of int: result = b in {int8, int16, int32, int64, uint, uint8, uint16, - uint32, uint64, float, float32, float64} + of int: result = b in {int32, int64} of int8: result = b in {int16, int32, int64, int} of int16: result = b in {int32, int64, int} of int32: result = b in {int64, int} @@ -2292,9 +2294,8 @@ algorithm returns true: of uint8: result = b in {uint16, uint32, uint64} of uint16: result = b in {uint32, uint64} of uint32: result = b in {uint64} - of float: result = b in {float32, float64} - of float32: result = b in {float64, float} - of float64: result = b in {float32, float} + of float32: result = b in {float64} + of float64: result = b in {float32} of seq: result = b == openArray and typeEquals(a.baseType, b.baseType) of array: diff --git a/tests/arithm/tarithm.nim b/tests/arithm/tarithm.nim index 1150af951b..a79f897a75 100644 --- a/tests/arithm/tarithm.nim +++ b/tests/arithm/tarithm.nim @@ -142,5 +142,5 @@ block tsubrange: var level: n16 = 1 let maxLevel: n16 = 1 - level = min(level + 2, maxLevel) + level = min(level + 2, maxLevel).n16 doAssert level == 1 diff --git a/tests/range/trange.nim b/tests/range/trange.nim index 9df0e6975c..aac9677771 100644 --- a/tests/range/trange.nim +++ b/tests/range/trange.nim @@ -122,10 +122,17 @@ block: block: var x1: range[0'f..1'f] = 1 const x2: range[0'f..1'f] = 1 + var x3: range[0'u8..1'u8] = 1 + const x4: range[0'u8..1'u8] = 1 + var x5: range[0'f32..1'f32] = 1'f64 const x6: range[0'f32..1'f32] = 1'f64 reject: - const x: range[0'f..1'f] = 2'f + var x09: range[0'i8..1'i8] = 1.int reject: - const x: range[0'f..1'f] = 2 + var x10: range[0'i64..1'i64] = 1'u64 + + const x11: range[0'f..1'f] = 2'f + reject: + const x12: range[0'f..1'f] = 2 From ab48d7901e4626120ff430e597cbb32d007e9e0b Mon Sep 17 00:00:00 2001 From: Miran Date: Sun, 1 Sep 2019 00:04:10 +0200 Subject: [PATCH 60/61] hashes: implement murmur3 (#12022) * hashes: implement murmur3 * refactoring; there is only one murmurHash and it works at compile-time via VM hooks * fixes JS tests * makes toOpenArrayByte work with C++ * make it bootstrap in C++ mode for 0.20 --- changelog.md | 2 +- compiler/ccgcalls.nim | 14 +-- compiler/condsyms.nim | 1 + compiler/vmops.nim | 29 +++++ lib/pure/hashes.nim | 196 ++++++++++++++++++++++++++-------- lib/system.nim | 10 ++ tests/parallel/tsendtwice.nim | 11 +- 7 files changed, 204 insertions(+), 59 deletions(-) diff --git a/changelog.md b/changelog.md index 35395afd39..57820c81ff 100644 --- a/changelog.md +++ b/changelog.md @@ -50,7 +50,7 @@ type - Added `os.delEnv` and `nimscript.delEnv`. (#11466) -- Enable Oid usage in hashtables. (#11472) +- Enabled Oid usage in hashtables. (#11472) - Added `unsafeColumnAt` procs, that return unsafe cstring from InstantRow. (#11647) diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim index 4efd33d1ea..53ebc28066 100644 --- a/compiler/ccgcalls.nim +++ b/compiler/ccgcalls.nim @@ -100,21 +100,23 @@ proc openArrayLoc(p: BProc, n: PNode): Rope = if optBoundsCheck in p.options: genBoundsCheck(p, a, b, c) let ty = skipTypes(a.t, abstractVar+{tyPtr}) + let dest = getTypeDesc(p.module, n.typ.sons[0]) case ty.kind of tyArray: let first = toInt64(firstOrd(p.config, ty)) if first == 0: - result = "($1)+($2), ($3)-($2)+1" % [rdLoc(a), rdLoc(b), rdLoc(c)] + result = "($4*)(($1)+($2)), ($3)-($2)+1" % [rdLoc(a), rdLoc(b), rdLoc(c), dest] else: - result = "($1)+(($2)-($4)), ($3)-($2)+1" % [rdLoc(a), rdLoc(b), rdLoc(c), intLiteral(first)] - of tyOpenArray, tyVarargs, tyUncheckedArray: - result = "($1)+($2), ($3)-($2)+1" % [rdLoc(a), rdLoc(b), rdLoc(c)] + result = "($5*)($1)+(($2)-($4)), ($3)-($2)+1" % + [rdLoc(a), rdLoc(b), rdLoc(c), intLiteral(first), dest] + of tyOpenArray, tyVarargs, tyUncheckedArray, tyCString: + result = "($4*)($1)+($2), ($3)-($2)+1" % [rdLoc(a), rdLoc(b), rdLoc(c), dest] of tyString, tySequence: if skipTypes(n.typ, abstractInst).kind == tyVar and not compileToCpp(p.module): - result = "(*$1)$4+($2), ($3)-($2)+1" % [rdLoc(a), rdLoc(b), rdLoc(c), dataField(p)] + result = "($5*)(*$1)$4+($2), ($3)-($2)+1" % [rdLoc(a), rdLoc(b), rdLoc(c), dataField(p), dest] else: - result = "$1$4+($2), ($3)-($2)+1" % [rdLoc(a), rdLoc(b), rdLoc(c), dataField(p)] + result = "($5*)$1$4+($2), ($3)-($2)+1" % [rdLoc(a), rdLoc(b), rdLoc(c), dataField(p), dest] else: internalError(p.config, "openArrayLoc: " & typeToString(a.t)) else: diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index 15a6254723..00ce352f26 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -97,4 +97,5 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimFixedOwned") defineSymbol("nimHasStyleChecks") + defineSymbol("nimToOpenArrayCString") defineSymbol("nimHasUsed") diff --git a/compiler/vmops.nim b/compiler/vmops.nim index 792feb0be0..b0325b5b05 100644 --- a/compiler/vmops.nim +++ b/compiler/vmops.nim @@ -17,6 +17,8 @@ from os import getEnv, existsEnv, dirExists, fileExists, putEnv, walkDir, getApp from md5 import getMD5 from sighashes import symBodyDigest +from hashes import hash + template mathop(op) {.dirty.} = registerCallback(c, "stdlib.math." & astToStr(op), `op Wrapper`) @@ -157,3 +159,30 @@ proc registerAdditionalOps*(c: PCtx) = stackTrace(c, PStackFrame(prc: c.prc.sym, comesFrom: 0, next: nil), c.exceptionInstr, "isExported() requires a symbol. '" & $n & "' is of kind '" & $n.kind & "'", n.info) setResult(a, sfExported in n.sym.flags) + + proc hashVmImpl(a: VmArgs) = + var res = hashes.hash(a.getString(0), a.getInt(1).int, a.getInt(2).int) + if c.config.cmd == cmdCompileToJS: + # emulate JS's terrible integers: + res = cast[int32](res) + setResult(a, res) + + registerCallback c, "stdlib.hashes.hashVmImpl", hashVmImpl + + proc hashVmImplByte(a: VmArgs) = + # nkBracket[...] + let sPos = a.getInt(1).int + let ePos = a.getInt(2).int + let arr = a.getNode(0) + var bytes = newSeq[byte](arr.len) + for i in 0 ..< arr.len: + bytes[i] = byte(arr[i].intVal and 0xff) + + var res = hashes.hash(bytes, sPos, ePos) + if c.config.cmd == cmdCompileToJS: + # emulate JS's terrible integers: + res = cast[int32](res) + setResult(a, res) + + registerCallback c, "stdlib.hashes.hashVmImplByte", hashVmImplByte + registerCallback c, "stdlib.hashes.hashVmImplChar", hashVmImplByte diff --git a/lib/pure/hashes.nim b/lib/pure/hashes.nim index 6af515609b..c1338376e2 100644 --- a/lib/pure/hashes.nim +++ b/lib/pure/hashes.nim @@ -49,9 +49,6 @@ type ## always have a size of a power of two and can use the ``and`` ## operator instead of ``mod`` for truncation of the hash value. -const - IntSize = sizeof(int) - proc `!&`*(h: Hash, val: int): Hash {.inline.} = ## Mixes a hash value `h` with `val` to produce a new hash value. ## @@ -108,13 +105,12 @@ proc hash*(x: pointer): Hash {.inline.} = else: result = cast[Hash](cast[uint](x) shr 3) # skip the alignment -when not defined(booting): - proc hash*[T: proc](x: T): Hash {.inline.} = - ## Efficient hashing of proc vars. Closures are supported too. - when T is "closure": - result = hash(rawProc(x)) !& hash(rawEnv(x)) - else: - result = hash(pointer(x)) +proc hash*[T: proc](x: T): Hash {.inline.} = + ## Efficient hashing of proc vars. Closures are supported too. + when T is "closure": + result = hash(rawProc(x)) !& hash(rawEnv(x)) + else: + result = hash(pointer(x)) proc hash*(x: int): Hash {.inline.} = ## Efficient hashing of integers. @@ -151,27 +147,87 @@ proc hash*(x: float): Hash {.inline.} = proc hash*[A](x: openArray[A]): Hash proc hash*[A](x: set[A]): Hash -template bytewiseHashing(result: Hash, x: typed, start, stop: int) = - for i in start .. stop: - result = result !& hash(x[i]) - result = !$result -template hashImpl(result: Hash, x: typed, start, stop: int) = +when defined(JS): + proc imul(a, b: uint32): uint32 = + # https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/imul + let mask = 0xffff'u32 + var + aHi = (a shr 16) and mask + aLo = a and mask + bHi = (b shr 16) and mask + bLo = b and mask + result = (aLo * bLo) + (aHi * bLo + aLo * bHi) shl 16 +else: + template imul(a, b: uint32): untyped = a * b + +proc rotl32(x: uint32, r: int): uint32 {.inline.} = + (x shl r) or (x shr (32 - r)) + +proc murmurHash(x: openArray[byte]): Hash = + # https://github.com/PeterScott/murmur3/blob/master/murmur3.c + const + c1 = 0xcc9e2d51'u32 + c2 = 0x1b873593'u32 + n1 = 0xe6546b64'u32 + m1 = 0x85ebca6b'u32 + m2 = 0xc2b2ae35'u32 let - elementSize = sizeof(x[start]) - stepSize = IntSize div elementSize - var i = start - while i <= stop+1 - stepSize: - var n = 0 - when nimvm: - # we cannot cast in VM, so we do it manually - for j in countdown(stepSize-1, 0): - n = (n shl (8*elementSize)) or ord(x[i+j]) + size = len(x) + stepSize = 4 # 32-bit + n = size div stepSize + var + h1: uint32 + i = 0 + + # body + while i < n * stepSize: + var k1: uint32 + when defined(js): + var j = stepSize + while j > 0: + dec j + k1 = (k1 shl 8) or (ord(x[i+j])).uint32 else: - n = cast[ptr Hash](unsafeAddr x[i])[] - result = result !& n - i += stepSize - bytewiseHashing(result, x, i, stop) # hash the remaining elements and finish + k1 = cast[ptr uint32](unsafeAddr x[i])[] + inc i, stepSize + + k1 = imul(k1, c1) + k1 = rotl32(k1, 15) + k1 = imul(k1, c2) + + h1 = h1 xor k1 + h1 = rotl32(h1, 13) + h1 = h1*5 + n1 + + # tail + var k1: uint32 + var rem = size mod stepSize + while rem > 0: + dec rem + k1 = (k1 shl 8) or (ord(x[i+rem])).uint32 + k1 = imul(k1, c1) + k1 = rotl32(k1, 15) + k1 = imul(k1, c2) + h1 = h1 xor k1 + + # finalization + h1 = h1 xor size.uint32 + h1 = h1 xor (h1 shr 16) + h1 = imul(h1, m1) + h1 = h1 xor (h1 shr 13) + h1 = imul(h1, m2) + h1 = h1 xor (h1 shr 16) + return cast[Hash](h1) + +proc hashVmImpl(x: string, sPos, ePos: int): Hash = + doAssert false, "implementation override in compiler/vmops.nim" + +proc hashVmImplChar(x: openArray[char], sPos, ePos: int): Hash = + doAssert false, "implementation override in compiler/vmops.nim" + +proc hashVmImplByte(x: openArray[byte], sPos, ePos: int): Hash = + doAssert false, "implementation override in compiler/vmops.nim" proc hash*(x: string): Hash = ## Efficient hashing of strings. @@ -182,7 +238,16 @@ proc hash*(x: string): Hash = runnableExamples: doAssert hash("abracadabra") != hash("AbracadabrA") - hashImpl(result, x, 0, high(x)) + when not defined(nimToOpenArrayCString): + result = 0 + for c in x: + result = result !& ord(c) + result = !$result + else: + when nimvm: + result = hashVmImpl(x, 0, high(x)) + else: + result = murmurHash(toOpenArrayByte(x, 0, high(x))) proc hash*(x: cstring): Hash = ## Efficient hashing of null-terminated strings. @@ -191,7 +256,19 @@ proc hash*(x: cstring): Hash = doAssert hash(cstring"AbracadabrA") == hash("AbracadabrA") doAssert hash(cstring"abracadabra") != hash(cstring"AbracadabrA") - hashImpl(result, x, 0, high(x)) + when not defined(nimToOpenArrayCString): + result = 0 + var i = 0 + while x[i] != '\0': + result = result !& ord(x[i]) + inc i + result = !$result + else: + when not defined(JS) and defined(nimToOpenArrayCString): + murmurHash(toOpenArrayByte(x, 0, x.high)) + else: + let xx = $x + murmurHash(toOpenArrayByte(xx, 0, high(xx))) proc hash*(sBuf: string, sPos, ePos: int): Hash = ## Efficient hashing of a string buffer, from starting @@ -202,7 +279,13 @@ proc hash*(sBuf: string, sPos, ePos: int): Hash = var a = "abracadabra" doAssert hash(a, 0, 3) == hash(a, 7, 10) - hashImpl(result, sBuf, sPos, ePos) + when not defined(nimToOpenArrayCString): + result = 0 + for i in sPos..ePos: + result = result !& ord(sBuf[i]) + result = !$result + else: + murmurHash(toOpenArrayByte(sBuf, sPos, ePos)) proc hashIgnoreStyle*(x: string): Hash = ## Efficient hashing of strings; style is ignored. @@ -300,12 +383,20 @@ proc hash*[T: tuple](x: T): Hash = result = result !& hash(f) result = !$result + proc hash*[A](x: openArray[A]): Hash = ## Efficient hashing of arrays and sequences. - when A is char|SomeInteger: - hashImpl(result, x, 0, x.high) + when A is byte: + result = murmurHash(x) + elif A is char: + when nimvm: + result = hashVmImplChar(x, 0, x.high) + else: + result = murmurHash(toOpenArrayByte(x, 0, x.high)) else: - bytewiseHashing(result, x, 0, x.high) + for a in x: + result = result !& hash(a) + result = !$result proc hash*[A](aBuf: openArray[A], sPos, ePos: int): Hash = ## Efficient hashing of portions of arrays and sequences, from starting @@ -316,10 +407,20 @@ proc hash*[A](aBuf: openArray[A], sPos, ePos: int): Hash = let a = [1, 2, 5, 1, 2, 6] doAssert hash(a, 0, 1) == hash(a, 3, 4) - when A is char|SomeInteger: - hashImpl(result, aBuf, sPos, ePos) + when A is byte: + when nimvm: + result = hashVmImplByte(aBuf, sPos, ePos) + else: + result = murmurHash(toOpenArray(aBuf, sPos, ePos)) + elif A is char: + when nimvm: + result = hashVmImplChar(aBuf, sPos, ePos) + else: + result = murmurHash(toOpenArrayByte(aBuf, sPos, ePos)) else: - bytewiseHashing(result, aBuf, sPos, ePos) + for i in sPos .. ePos: + result = result !& hash(aBuf[i]) + result = !$result proc hash*[A](x: set[A]): Hash = ## Efficient hashing of sets. @@ -334,11 +435,15 @@ when isMainModule: a = "" b = newSeq[char]() c = newSeq[int]() + d = cstring"" + e = "abcd" doAssert hash(a) == 0 doAssert hash(b) == 0 doAssert hash(c) == 0 + doAssert hash(d) == 0 doAssert hashIgnoreCase(a) == 0 doAssert hashIgnoreStyle(a) == 0 + doAssert hash(e, 3, 2) == 0 block sameButDifferent: doAssert hash("aa bb aaaa1234") == hash("aa bb aaaa1234", 0, 13) doAssert hash("aa bb aaaa1234") == hash(cstring"aa bb aaaa1234") @@ -346,14 +451,14 @@ when isMainModule: doAssert hashIgnoreStyle("aa_bb_AAaa1234") == hashIgnoreCase("aaBBAAAa1234") block smallSize: # no multibyte hashing let - xx = @['H','e','l','l','o'] - ii = @[72'i8, 101, 108, 108, 111] - ss = "Hello" + xx = @['H','i'] + ii = @[72'u8, 105] + ss = "Hi" doAssert hash(xx) == hash(ii) doAssert hash(xx) == hash(ss) doAssert hash(xx) == hash(xx, 0, xx.high) doAssert hash(ss) == hash(ss, 0, ss.high) - block largeSize: # longer than 8 characters, should trigger multibyte hashing + block largeSize: # longer than 4 characters let xx = @['H','e','l','l','o'] xxl = @['H','e','l','l','o','w','e','e','n','s'] @@ -362,9 +467,6 @@ when isMainModule: doAssert hash(xxl) == hash(xxl, 0, xxl.high) doAssert hash(ssl) == hash(ssl, 0, ssl.high) doAssert hash(xx) == hash(xxl, 0, 4) - block misc: - let - a = [1'u8, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4] - b = [1'i8, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4] - doAssert hash(a) == hash(b) - doAssert hash(a, 2, 5) == hash(b, 2, 5) + doAssert hash(xx) == hash(ssl, 0, 4) + doAssert hash(xx, 0, 3) == hash(xxl, 0, 3) + doAssert hash(xx, 0, 3) == hash(ssl, 0, 3) diff --git a/lib/system.nim b/lib/system.nim index 16a5e03fe5..edab334127 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -4501,6 +4501,11 @@ when defined(nimconfig): when not defined(js): proc toOpenArray*[T](x: ptr UncheckedArray[T]; first, last: int): openArray[T] {. magic: "Slice".} + when defined(nimToOpenArrayCString): + proc toOpenArray*(x: cstring; first, last: int): openArray[char] {. + magic: "Slice".} + proc toOpenArrayByte*(x: cstring; first, last: int): openArray[byte] {. + magic: "Slice".} proc toOpenArray*[T](x: seq[T]; first, last: int): openArray[T] {. magic: "Slice".} @@ -4510,8 +4515,13 @@ proc toOpenArray*[I, T](x: array[I, T]; first, last: I): openArray[T] {. magic: "Slice".} proc toOpenArray*(x: string; first, last: int): openArray[char] {. magic: "Slice".} + proc toOpenArrayByte*(x: string; first, last: int): openArray[byte] {. magic: "Slice".} +proc toOpenArrayByte*(x: openArray[char]; first, last: int): openArray[byte] {. + magic: "Slice".} +proc toOpenArrayByte*(x: seq[char]; first, last: int): openArray[byte] {. + magic: "Slice".} type ForLoopStmt* {.compilerproc.} = object ## \ diff --git a/tests/parallel/tsendtwice.nim b/tests/parallel/tsendtwice.nim index 2f60b904f3..0b3ce15a5e 100644 --- a/tests/parallel/tsendtwice.nim +++ b/tests/parallel/tsendtwice.nim @@ -1,11 +1,12 @@ discard """ - output: '''ob @[] -ob3 @[] -ob2 @[] -3 + output: '''ob2 @[] ob @[] ob3 @[] -ob2 @[]''' +3 +ob2 @[] +ob @[] +ob3 @[] +''' cmd: "nim c -r --threads:on $file" """ From ad82e65387f39970b0f12cbcb12d8b382236f3da Mon Sep 17 00:00:00 2001 From: Araq Date: Sun, 1 Sep 2019 23:28:26 +0200 Subject: [PATCH 61/61] gc:destructors progress --- compiler/ccgexprs.nim | 2 +- compiler/commands.nim | 49 ++++++++++++++++++------------------ compiler/liftdestructors.nim | 4 +-- compiler/semtypes.nim | 2 +- lib/core/strs.nim | 49 ------------------------------------ lib/system.nim | 2 +- lib/system/gc_ms.nim | 2 +- 7 files changed, 31 insertions(+), 79 deletions(-) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index ea4cd4acaf..0ab592a503 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -2028,7 +2028,7 @@ proc genMove(p: BProc; n: PNode; d: var TLoc) = resetLoc(p, a) proc genDestroy(p: BProc; n: PNode) = - if optNimV2 in p.config.globalOptions: + if p.config.selectedGC == gcDestructors: let arg = n[1].skipAddr let t = arg.typ.skipTypes(abstractInst) case t.kind diff --git a/compiler/commands.nim b/compiler/commands.nim index 5b6f3ac0f9..1123996c13 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -437,30 +437,31 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; processOnOffSwitchG(conf, {optWholeProject}, arg, pass, info) of "gc": expectArg(conf, switch, arg, pass, info) - case arg.normalize - of "boehm": - conf.selectedGC = gcBoehm - defineSymbol(conf.symbols, "boehmgc") - of "refc": - conf.selectedGC = gcRefc - of "v2": - message(conf, info, warnDeprecated, "--gc:v2 is deprecated; using default gc") - of "markandsweep": - conf.selectedGC = gcMarkAndSweep - defineSymbol(conf.symbols, "gcmarkandsweep") - of "destructors": - conf.selectedGC = gcDestructors - defineSymbol(conf.symbols, "gcdestructors") - of "go": - conf.selectedGC = gcGo - defineSymbol(conf.symbols, "gogc") - of "none": - conf.selectedGC = gcNone - defineSymbol(conf.symbols, "nogc") - of "stack", "regions": - conf.selectedGC= gcRegions - defineSymbol(conf.symbols, "gcregions") - else: localError(conf, info, errNoneBoehmRefcExpectedButXFound % arg) + if pass in {passCmd2, passPP}: + case arg.normalize + of "boehm": + conf.selectedGC = gcBoehm + defineSymbol(conf.symbols, "boehmgc") + of "refc": + conf.selectedGC = gcRefc + of "v2": + message(conf, info, warnDeprecated, "--gc:v2 is deprecated; using default gc") + of "markandsweep": + conf.selectedGC = gcMarkAndSweep + defineSymbol(conf.symbols, "gcmarkandsweep") + of "destructors": + conf.selectedGC = gcDestructors + defineSymbol(conf.symbols, "gcdestructors") + of "go": + conf.selectedGC = gcGo + defineSymbol(conf.symbols, "gogc") + of "none": + conf.selectedGC = gcNone + defineSymbol(conf.symbols, "nogc") + of "stack", "regions": + conf.selectedGC= gcRegions + defineSymbol(conf.symbols, "gcregions") + else: localError(conf, info, errNoneBoehmRefcExpectedButXFound % arg) of "warnings", "w": if processOnOffSwitchOrList(conf, {optWarns}, arg, pass, info): listWarnings(conf) of "warning": processSpecificNote(arg, wWarning, pass, info, switch, conf) diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim index 71f0baecf1..3574adca08 100644 --- a/compiler/liftdestructors.nim +++ b/compiler/liftdestructors.nim @@ -129,7 +129,7 @@ proc newDeepCopyCall(op: PSym; x, y: PNode): PNode = result = newAsgnStmt(x, newOpCall(op, y)) proc useNoGc(c: TLiftCtx; t: PType): bool {.inline.} = - result = optNimV2 in c.g.config.globalOptions and + result = c.g.config.selectedGC == gcDestructors and ({tfHasGCedMem, tfHasOwned} * t.flags != {} or t.isGCedMem) proc instantiateGeneric(c: var TLiftCtx; op: PSym; t, typeInst: PType): PSym = @@ -563,7 +563,7 @@ proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp; typ.attachedOps[kind] = result var tk: TTypeKind - if optNimV2 in g.config.globalOptions: + if g.config.selectedGC == gcDestructors: tk = skipTypes(typ, {tyOrdinal, tyRange, tyInferred, tyGenericInst, tyStatic, tyAlias, tySink}).kind else: tk = tyNone # no special casing for strings and seqs diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 9fec57b154..41d7f0d195 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -1653,7 +1653,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = of mSet: result = semSet(c, n, prev) of mOrdinal: result = semOrdinal(c, n, prev) of mSeq: - if c.config.selectedGC == gcDestructors and optNimV2 notin c.config.globalOptions: + if false: # c.config.selectedGC == gcDestructors and optNimV2 notin c.config.globalOptions: let s = c.graph.sysTypes[tySequence] assert s != nil assert prev == nil diff --git a/lib/core/strs.nim b/lib/core/strs.nim index 92c39331fa..3b7a46ff16 100644 --- a/lib/core/strs.nim +++ b/lib/core/strs.nim @@ -9,20 +9,6 @@ ## Default new string implementation used by Nim's core. -when false: - # these are to be implemented or changed in the code generator. - - #proc rawNewStringNoInit(space: int): NimString {.compilerproc.} - # seems to be unused. - proc copyDeepString(src: NimString): NimString {.inline.} - # ----------------- sequences ---------------------------------------------- - - proc incrSeqV3(s: PGenericSeq, typ: PNimType): PGenericSeq {.compilerproc.} - proc setLengthSeqV2(s: PGenericSeq, typ: PNimType, newLen: int): PGenericSeq {. - compilerRtl.} - proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} - proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} - import allocators type @@ -45,41 +31,6 @@ template frees(s) = if not isLiteral(s): s.p.allocator.dealloc(s.p.allocator, s.p, contentSize(s.p.cap)) -when not defined(nimV2): - proc `=destroy`(s: var string) = - var a = cast[ptr NimStringV2](addr s) - frees(a) - a.len = 0 - a.p = nil - - proc `=sink`(x: var string, y: string) = - var a = cast[ptr NimStringV2](addr x) - var b = cast[ptr NimStringV2](unsafeAddr y) - # we hope this is optimized away for not yet alive objects: - if unlikely(a.p == b.p): return - frees(a) - a.len = b.len - a.p = b.p - - proc `=`(x: var string, y: string) = - var a = cast[ptr NimStringV2](addr x) - var b = cast[ptr NimStringV2](unsafeAddr y) - if unlikely(a.p == b.p): return - frees(a) - a.len = b.len - if isLiteral(b): - # we can shallow copy literals: - a.p = b.p - else: - let allocator = if a.p != nil and a.p.allocator != nil: a.p.allocator else: getLocalAllocator() - # we have to allocate the 'cap' here, consider - # 'let y = newStringOfCap(); var x = y' - # on the other hand... These get turned into moves now. - a.p = cast[ptr NimStrPayload](allocator.alloc(allocator, contentSize(b.len))) - a.p.allocator = allocator - a.p.cap = b.len - copyMem(unsafeAddr a.p.data[0], unsafeAddr b.p.data[0], b.len+1) - proc resize(old: int): int {.inline.} = if old <= 0: result = 4 elif old < 65536: result = old * 2 diff --git a/lib/system.nim b/lib/system.nim index edab334127..6b1421160c 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -2093,7 +2093,7 @@ when not defined(JS) and not defined(nimscript) and hostOS != "standalone": when not defined(JS) and not defined(nimscript) and hasAlloc and not defined(gcDestructors): proc addChar(s: NimString, c: char): NimString {.compilerproc, benign.} -when not defined(gcDestructors): +when not defined(gcDestructors) or defined(nimscript): proc add*[T](x: var seq[T], y: T) {.magic: "AppendSeqElem", noSideEffect.} ## Generic proc for adding a data item `y` to a container `x`. ## diff --git a/lib/system/gc_ms.nim b/lib/system/gc_ms.nim index 87d8034857..3ce4289303 100644 --- a/lib/system/gc_ms.nim +++ b/lib/system/gc_ms.nim @@ -511,7 +511,7 @@ when not defined(useNimRtl): gch.tracing = true proc GC_fullCollect() = - var oldThreshold = gch.cycleThreshold + let oldThreshold = gch.cycleThreshold gch.cycleThreshold = 0 # forces cycle collection collectCT(gch, 0) gch.cycleThreshold = oldThreshold