From a9f21cffdf7304272b5bbabeabec4a3e659819fa Mon Sep 17 00:00:00 2001 From: PMunch Date: Fri, 16 Mar 2018 15:57:40 +0100 Subject: Add CommentStmt to astGenRepr (#7313) * Added codeRepr and dumpCode to the macros module. This allows those writing macros to write examples, get the code to generate the AST for that example, and then modify that code to be dynamic with the macro function. --- lib/core/macros.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib/core') diff --git a/lib/core/macros.nim b/lib/core/macros.nim index ed9c304fe..d09f5f933 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -678,7 +678,7 @@ proc astGenRepr*(n: NimNode): string {.compileTime, benign.} = ## See also `repr`, `treeRepr`, and `lispRepr`. const - NodeKinds = {nnkEmpty, nnkNilLit, nnkIdent, nnkSym, nnkNone} + NodeKinds = {nnkEmpty, nnkNilLit, nnkIdent, nnkSym, nnkNone, nnkCommentStmt} LitKinds = {nnkCharLit..nnkInt64Lit, nnkFloatLit..nnkFloat64Lit, nnkStrLit..nnkTripleStrLit} proc escape(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect.} = @@ -723,7 +723,7 @@ proc astGenRepr*(n: NimNode): string {.compileTime, benign.} = of nnkCharLit: res.add("'" & $chr(n.intVal) & "'") of nnkIntLit..nnkInt64Lit: res.add($n.intVal) of nnkFloatLit..nnkFloat64Lit: res.add($n.floatVal) - of nnkStrLit..nnkTripleStrLit: res.add($n.strVal.escape()) + of nnkStrLit..nnkTripleStrLit, nnkCommentStmt: res.add($n.strVal.escape()) of nnkIdent: res.add(($n.ident).escape()) of nnkSym: res.add(($n.symbol).escape()) of nnkNone: assert false -- cgit 1.4.1-2-gfad0 From d8373622165f9dbdf12eef8c81204f35a376ff76 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 3 Apr 2018 10:25:20 +0200 Subject: C codegen: preparations for different seq and string implementations --- compiler/ccgexprs.nim | 30 +++++------------- compiler/ccgliterals.nim | 81 ++++++++++++++++++++++++++++++++++++++++++++++++ compiler/ccgtypes.nim | 13 ++++---- compiler/cgen.nim | 17 +++++++--- compiler/cgendata.nim | 3 +- compiler/pragmas.nim | 2 +- lib/core/seqs.nim | 6 ++-- lib/core/strs.nim | 7 ++--- 8 files changed, 116 insertions(+), 43 deletions(-) create mode 100644 compiler/ccgliterals.nim (limited to 'lib/core') diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 471f36d89..6ee7d698e 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -30,12 +30,6 @@ proc intLiteral(i: BiggestInt): Rope = else: result = ~"(IL64(-9223372036854775807) - IL64(1))" -proc getStrLit(m: BModule, s: string): Rope = - discard cgsym(m, "TGenericSeq") - result = getTempName(m) - addf(m.s[cfsData], "STRING_LITERAL($1, $2, $3);$n", - [result, makeCString(s), rope(len(s))]) - proc genLiteral(p: BProc, n: PNode, ty: PType): Rope = if ty == nil: internalError(n.info, "genLiteral: ty is nil") case n.kind @@ -67,19 +61,9 @@ proc genLiteral(p: BProc, n: PNode, ty: PType): Rope = of nkStrLit..nkTripleStrLit: case skipTypes(ty, abstractVarRange).kind of tyNil: - result = ropecg(p.module, "((#NimStringDesc*) NIM_NIL)", []) + result = genNilStringLiteral(p.module, n.info) of tyString: - if n.strVal.isNil: - result = ropecg(p.module, "((#NimStringDesc*) NIM_NIL)", []) - else: - let id = nodeTableTestOrSet(p.module.dataCache, n, p.module.labels) - if id == p.module.labels: - # string literal not found in the cache: - result = ropecg(p.module, "((#NimStringDesc*) &$1)", - [getStrLit(p.module, n.strVal)]) - else: - result = ropecg(p.module, "((#NimStringDesc*) &$1$2)", - [p.module.tmpBase, rope(id)]) + result = genStringLiteral(p.module, n) else: if n.strVal.isNil: result = rope("NIM_NIL") else: result = makeCString(n.strVal) @@ -823,16 +807,16 @@ proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym) = genInExprAux(p, it, u, v, test) let id = nodeTableTestOrSet(p.module.dataCache, newStrNode(nkStrLit, field.name.s), p.module.labels) - let strLit = if id == p.module.labels: getStrLit(p.module, field.name.s) + let strLit = if id == p.module.labels: genStringLiteralDataOnly(p.module, field.name.s, e.info) else: p.module.tmpBase & rope(id) if op.magic == mNot: linefmt(p, cpsStmts, - "if ($1) #raiseFieldError(((#NimStringDesc*) &$2));$n", - rdLoc(test), strLit) + "if ($1) #raiseFieldError($2);$n", + rdLoc(test), genStringLiteralFromData(p.module, strLit, e.info)) else: linefmt(p, cpsStmts, - "if (!($1)) #raiseFieldError(((#NimStringDesc*) &$2));$n", - rdLoc(test), strLit) + "if (!($1)) #raiseFieldError($2);$n", + rdLoc(test), genStringLiteralFromData(p.module, strLit, e.info)) proc genCheckedRecordField(p: BProc, e: PNode, d: var TLoc) = if optFieldCheck in p.options: diff --git a/compiler/ccgliterals.nim b/compiler/ccgliterals.nim new file mode 100644 index 000000000..3011f0a94 --- /dev/null +++ b/compiler/ccgliterals.nim @@ -0,0 +1,81 @@ +# +# +# The Nim Compiler +# (c) Copyright 2018 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## This include file contains the logic to produce constant string +## and seq literals. The code here is responsible that +## ``const x = ["a", "b"]`` works without hidden runtime creation code. +## The price is that seqs and strings are not purely a library +## implementation. + +template detectVersion(field, corename) = + if m.g.field == 0: + let core = getCompilerProc(corename) + if core == nil or core.kind != skConst: + m.g.field = 1 + else: + m.g.field = int ast.getInt(core.ast) + result = m.g.field + +proc detectStrVersion(m: BModule): int = + detectVersion(strVersion, "nimStrVersion") + +proc detectSeqVersion(m: BModule): int = + detectVersion(seqVersion, "nimSeqVersion") + +# ----- Version 1: GC'ed strings and seqs -------------------------------- + +proc genStringLiteralDataOnlyV1(m: BModule, s: string): Rope = + discard cgsym(m, "TGenericSeq") + result = getTempName(m) + addf(m.s[cfsData], "STRING_LITERAL($1, $2, $3);$n", + [result, makeCString(s), rope(len(s))]) + +proc genStringLiteralV1(m: BModule; n: PNode): Rope = + if s.isNil: + result = ropecg(m, "((#NimStringDesc*) NIM_NIL)", []) + else: + let id = nodeTableTestOrSet(m.dataCache, n, m.labels) + if id == m.labels: + # string literal not found in the cache: + result = ropecg(m, "((#NimStringDesc*) &$1)", + [genStringLiteralDataOnlyV1(m, n.strVal)]) + else: + result = ropecg(m, "((#NimStringDesc*) &$1$2)", + [m.tmpBase, rope(id)]) + +# ------ Version 2: destructor based strings and seqs ----------------------- + +proc genStringLiteralDataOnlyV2(m: BModule, s: string): Rope = + discard "to implement" + +proc genStringLiteralV2(m: BModule; n: PNode): Rope = + discard "to implement" + +# ------ Version selector --------------------------------------------------- + +proc genStringLiteralDataOnly(m: BModule; s: string; info: TLineInfo): Rope = + case detectStrVersion(m) + of 0, 1: result = genStringLiteralDataOnlyV1(m, s) + of 2: result = genStringLiteralDataOnlyV2(m, s) + else: + localError(info, "cannot determine how to produce code for string literal") + +proc genStringLiteralFromData(m: BModule; data: Rope; info: TLineInfo): Rope = + result = ropecg(m, "((#NimStringDesc*) &$1)", + [data]) + +proc genNilStringLiteral(m: BModule; info: TLineInfo): Rope = + result = ropecg(m, "((#NimStringDesc*) NIM_NIL)", []) + +proc genStringLiteral(m: BModule; n: PNode): Rope = + case detectStrVersion(m) + of 0, 1: result = genStringLiteralV1(m, n) + of 2: result = genStringLiteralV2(m, n) + else: + localError(n.info, "cannot determine how to produce code for string literal") diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index ed44c577d..2a4a10555 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -267,10 +267,6 @@ proc addAbiCheck(m: BModule, t: PType, name: Rope) = if isDefined("checkabi"): addf(m.s[cfsTypeInfo], "NIM_CHECK_SIZE($1, $2);$n", [name, rope(getSize(t))]) -proc getTempName(m: BModule): Rope = - result = m.tmpBase & rope(m.labels) - inc m.labels - proc ccgIntroducedPtr(s: PSym): bool = var pt = skipTypes(s.typ, typedescInst) assert skResult != s.kind @@ -316,8 +312,13 @@ proc getSimpleTypeDesc(m: BModule, typ: PType): Rope = of tyPointer: result = typeNameOrLiteral(m, typ, "void*") of tyString: - discard cgsym(m, "NimStringDesc") - result = typeNameOrLiteral(m, typ, "NimStringDesc*") + case detectStrVersion(m) + of 2: + discard cgsym(m, "string") + result = typeNameOrLiteral(m, typ, "NimStringV2") + else: + discard cgsym(m, "NimStringDesc") + result = typeNameOrLiteral(m, typ, "NimStringDesc*") of tyCString: result = typeNameOrLiteral(m, typ, "NCSTRING") of tyBool: result = typeNameOrLiteral(m, typ, "NIM_BOOL") of tyChar: result = typeNameOrLiteral(m, typ, "NIM_CHAR") diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 01610010b..05f222520 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -238,7 +238,12 @@ proc genProc(m: BModule, prc: PSym) template compileToCpp(m: BModule): untyped = gCmd == cmdCompileToCpp or sfCompileToCpp in m.module.flags -include "ccgtypes.nim" +proc getTempName(m: BModule): Rope = + result = m.tmpBase & rope(m.labels) + inc m.labels + +include ccgliterals +include ccgtypes # ------------------------------ Manager of temporaries ------------------ @@ -551,11 +556,13 @@ proc loadDynamicLib(m: BModule, lib: PLib) = for i in countup(0, high(s)): inc(m.labels) if i > 0: add(loadlib, "||") - appcg(m, loadlib, "($1 = #nimLoadLibrary((#NimStringDesc*) &$2))$n", - [tmp, getStrLit(m, s[i])]) + let n = newStrNode(nkStrLit, s[i]) + n.info = lib.path.info + appcg(m, loadlib, "($1 = #nimLoadLibrary($2))$n", + [tmp, genStringLiteral(m, n)]) appcg(m, m.s[cfsDynLibInit], - "if (!($1)) #nimLoadLibraryError((#NimStringDesc*) &$2);$n", - [loadlib, getStrLit(m, lib.path.strVal)]) + "if (!($1)) #nimLoadLibraryError($2);$n", + [loadlib, genStringLiteral(m, lib.path)]) else: var p = newProc(nil, m) p.options = p.options - {optStackTrace, optEndb} diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim index efa346934..f8167acdc 100644 --- a/compiler/cgendata.nim +++ b/compiler/cgendata.nim @@ -70,7 +70,7 @@ type threadVarAccessed*: bool # true if the proc already accessed some threadvar 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[n: 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 @@ -116,6 +116,7 @@ type breakpoints*: Rope # later the breakpoints are inserted into the main proc typeInfoMarker*: TypeCache config*: ConfigRef + strVersion*, seqVersion*: int # version of the string/seq implementation to use TCGen = object of TPassContext # represents a C source file s*: TCFileSections # sections of the C file diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 1295ee18d..78ded578f 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -65,7 +65,7 @@ const wGensym, wInject, wCodegenDecl, wGuard, wGoto, wExportNims, wUsed} constPragmas* = {wImportc, wExportc, wHeader, wDeprecated, wMagic, wNodecl, wExtern, wImportCpp, wImportObjC, wError, wGensym, wInject, wExportNims, - wIntDefine, wStrDefine, wUsed} + wIntDefine, wStrDefine, wUsed, wCompilerProc, wCore} letPragmas* = varPragmas procTypePragmas* = {FirstCallConv..LastCallConv, wVarargs, wNosideeffect, wThread, wRaises, wLocks, wTags, wGcSafe} diff --git a/lib/core/seqs.nim b/lib/core/seqs.nim index c32cf3690..02c192851 100644 --- a/lib/core/seqs.nim +++ b/lib/core/seqs.nim @@ -15,11 +15,11 @@ type len, cap: int data: ptr UncheckedArray[T] +const nimSeqVersion {.core.} = 2 + template frees(s) = dealloc(s.data, s.cap * sizeof(T)) # XXX make code memory safe for overflows in '*' -proc nimSeqLiteral[T](x: openArray[T]): seq[T] {.core.} = - seq[T](len: x.len, cap: x.len, data: x) when defined(nimHasTrace): proc `=trace`[T](s: seq[T]; a: Allocator) = @@ -120,7 +120,7 @@ proc `$`*[T](x: seq[T]): string = result = "@[" var firstElement = true for i in 0.. 0: result.data = alloc0(len+1) -converter toCString(x: string): cstring {.core.} = +converter toCString(x: string): cstring {.core, inline.} = if x.len == 0: cstring"" else: cast[cstring](x.data) proc newStringOfCap*(cap: int): string = -- cgit 1.4.1-2-gfad0 From 5d46e1eaa2861b54b290b65f43056295aba953f8 Mon Sep 17 00:00:00 2001 From: Arne Döring Date: Wed, 4 Apr 2018 15:37:07 +0200 Subject: Fixes #7473 (#7475) * removed code duplication * changelog entry, adopt tests for changes --- changelog.md | 3 +++ lib/core/macros.nim | 44 ++++++++-------------------------------- tests/parser/tpostexprblocks.nim | 9 ++++---- tests/vm/tnimnode.nim | 12 +++++------ 4 files changed, 21 insertions(+), 47 deletions(-) (limited to 'lib/core') diff --git a/changelog.md b/changelog.md index b8d28b633..4891786b7 100644 --- a/changelog.md +++ b/changelog.md @@ -18,6 +18,9 @@ ### Library changes +- ``macros.astGenRepr``, ``macros.lispRepr`` and ``macros.treeRepr`` + now escapes the content of string literals consistently. + ### Language additions ### Language changes diff --git a/lib/core/macros.nim b/lib/core/macros.nim index d09f5f933..5a4c4c1e9 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -620,7 +620,7 @@ proc treeRepr*(n: NimNode): string {.compileTime, benign.} = of nnkNilLit: res.add(" nil") of nnkCharLit..nnkInt64Lit: res.add(" " & $n.intVal) of nnkFloatLit..nnkFloat64Lit: res.add(" " & $n.floatVal) - of nnkStrLit..nnkTripleStrLit: res.add(" " & $n.strVal) + of nnkStrLit..nnkTripleStrLit: res.add(" " & $n.strVal.newLit.repr) of nnkIdent: res.add(" ident\"" & $n.ident & '"') of nnkSym: res.add(" \"" & $n.symbol & '"') of nnkNone: assert false @@ -645,7 +645,7 @@ proc lispRepr*(n: NimNode): string {.compileTime, benign.} = of nnkNilLit: add(result, "nil") of nnkCharLit..nnkInt64Lit: add(result, $n.intVal) of nnkFloatLit..nnkFloat64Lit: add(result, $n.floatVal) - of nnkStrLit..nnkTripleStrLit: add(result, $n.strVal) + of nnkStrLit..nnkTripleStrLit, nnkCommentStmt: add(result, n.strVal.newLit.repr) of nnkIdent: add(result, "ident\"" & $n.ident & '"') of nnkSym: add(result, $n.symbol) of nnkNone: assert false @@ -681,33 +681,6 @@ proc astGenRepr*(n: NimNode): string {.compileTime, benign.} = NodeKinds = {nnkEmpty, nnkNilLit, nnkIdent, nnkSym, nnkNone, nnkCommentStmt} LitKinds = {nnkCharLit..nnkInt64Lit, nnkFloatLit..nnkFloat64Lit, nnkStrLit..nnkTripleStrLit} - proc escape(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect.} = - ## Functions copied from strutils - proc toHex(x: BiggestInt, len: Positive): string {.noSideEffect, rtl.} = - const - HexChars = "0123456789ABCDEF" - var - t = x - result = newString(len) - for j in countdown(len-1, 0): - result[j] = HexChars[int(t and 0xF)] - t = t shr 4 - # handle negative overflow - if t == 0 and x < 0: t = -1 - - result = newStringOfCap(s.len + s.len shr 2) - result.add(prefix) - for c in items(s): - case c - of '\0'..'\31', '\128'..'\255': - add(result, "\\x") - add(result, toHex(ord(c), 2)) - of '\\': add(result, "\\\\") - of '\'': add(result, "\\'") - of '\"': add(result, "\\\"") - else: add(result, c) - add(result, suffix) - proc traverse(res: var string, level: int, n: NimNode) {.benign.} = for i in 0..level-1: res.add " " if n.kind in NodeKinds: @@ -723,9 +696,9 @@ proc astGenRepr*(n: NimNode): string {.compileTime, benign.} = of nnkCharLit: res.add("'" & $chr(n.intVal) & "'") of nnkIntLit..nnkInt64Lit: res.add($n.intVal) of nnkFloatLit..nnkFloat64Lit: res.add($n.floatVal) - of nnkStrLit..nnkTripleStrLit, nnkCommentStmt: res.add($n.strVal.escape()) - of nnkIdent: res.add(($n.ident).escape()) - of nnkSym: res.add(($n.symbol).escape()) + of nnkStrLit..nnkTripleStrLit, nnkCommentStmt: res.add($n.strVal.newLit.repr) + of nnkIdent: res.add(($n.ident).newLit.repr()) + of nnkSym: res.add(($n.symbol).newLit.repr()) of nnkNone: assert false else: res.add(".newTree(") @@ -774,7 +747,6 @@ macro dumpTreeImm*(s: untyped): untyped {.deprecated.} = echo s.treeRepr macro dumpLispImm*(s: untyped): untyped {.deprecated.} = echo s.lispRepr ## Deprecated. - proc newEmptyNode*(): NimNode {.compileTime, noSideEffect.} = ## Create a new empty node result = newNimNode(nnkEmpty) @@ -1227,7 +1199,7 @@ proc customPragmaNode(n: NimNode): NimNode = let recList = typDef[2][2] for identDefs in recList: for i in 0 .. identDefs.len - 3: - if identDefs[i].kind == nnkPragmaExpr and + if identDefs[i].kind == nnkPragmaExpr and identDefs[i][0].kind == nnkIdent and $identDefs[i][0] == $n[1]: return identDefs[i][1] @@ -1237,7 +1209,7 @@ macro hasCustomPragma*(n: typed, cp: typed{nkSym}): untyped = ## ## .. code-block:: nim ## template myAttr() {.pragma.} - ## type + ## type ## MyObj = object ## myField {.myAttr.}: int ## var o: MyObj @@ -1255,7 +1227,7 @@ macro getCustomPragmaVal*(n: typed, cp: typed{nkSym}): untyped = ## ## .. code-block:: nim ## template serializationKey(key: string) {.pragma.} - ## type + ## type ## MyObj = object ## myField {.serializationKey: "mf".}: int ## var o: MyObj diff --git a/tests/parser/tpostexprblocks.nim b/tests/parser/tpostexprblocks.nim index 3b9c956c2..e97b9c7ee 100644 --- a/tests/parser/tpostexprblocks.nim +++ b/tests/parser/tpostexprblocks.nim @@ -22,26 +22,26 @@ StmtList Empty Call Ident ident"foo070" - StrLit test + StrLit "test" StmtList DiscardStmt Empty Call Ident ident"foo080" - StrLit test + StrLit "test" StmtList DiscardStmt Empty Command Ident ident"foo090" - StrLit test + StrLit "test" StmtList DiscardStmt Empty Command Ident ident"foo100" Call - StrLit test + StrLit "test" StmtList DiscardStmt Empty @@ -540,4 +540,3 @@ dumpTree: foo380.add((quote do: discard )[0]) - diff --git a/tests/vm/tnimnode.nim b/tests/vm/tnimnode.nim index 0614b9807..2727df290 100644 --- a/tests/vm/tnimnode.nim +++ b/tests/vm/tnimnode.nim @@ -26,12 +26,12 @@ proc checkNode(arg: NimNode; name: string): void {. compileTime .} = seqAppend.add(arg) # bit this creates a copy arg.add newCall(ident"echo", newLit("Hello World")) - assertEq arg.lispRepr , """StmtList(DiscardStmt(Empty()), Call(Ident(ident"echo"), StrLit(Hello World)))""" - assertEq node.lispRepr , """StmtList(DiscardStmt(Empty()), Call(Ident(ident"echo"), StrLit(Hello World)))""" - assertEq nodeArray[0].lispRepr , """StmtList(DiscardStmt(Empty()), Call(Ident(ident"echo"), StrLit(Hello World)))""" - assertEq nodeSeq[0].lispRepr , """StmtList(DiscardStmt(Empty()), Call(Ident(ident"echo"), StrLit(Hello World)))""" - assertEq seqAppend[0].lispRepr , """StmtList(DiscardStmt(Empty()), Call(Ident(ident"echo"), StrLit(Hello World)))""" - assertEq seqAppend[1].lispRepr , """StmtList(DiscardStmt(Empty()), Call(Ident(ident"echo"), StrLit(Hello World)))""" + assertEq arg.lispRepr , """StmtList(DiscardStmt(Empty()), Call(Ident(ident"echo"), StrLit("Hello World")))""" + assertEq node.lispRepr , """StmtList(DiscardStmt(Empty()), Call(Ident(ident"echo"), StrLit("Hello World")))""" + assertEq nodeArray[0].lispRepr , """StmtList(DiscardStmt(Empty()), Call(Ident(ident"echo"), StrLit("Hello World")))""" + assertEq nodeSeq[0].lispRepr , """StmtList(DiscardStmt(Empty()), Call(Ident(ident"echo"), StrLit("Hello World")))""" + assertEq seqAppend[0].lispRepr , """StmtList(DiscardStmt(Empty()), Call(Ident(ident"echo"), StrLit("Hello World")))""" + assertEq seqAppend[1].lispRepr , """StmtList(DiscardStmt(Empty()), Call(Ident(ident"echo"), StrLit("Hello World")))""" echo "OK" -- cgit 1.4.1-2-gfad0 From 6baca58693672f8fc9485272b3c350d0b0b77163 Mon Sep 17 00:00:00 2001 From: Arne Döring Date: Wed, 11 Apr 2018 16:48:01 +0200 Subject: Get symbol kind (#7491) --- changelog.md | 2 + compiler/ast.nim | 5 +- compiler/condsyms.nim | 1 + compiler/guards.nim | 6 +- compiler/vm.nim | 35 ++++---- compiler/vmdef.nim | 2 +- compiler/vmgen.nim | 2 +- doc/astspec.txt | 178 +++++++++++++++++++-------------------- lib/core/macros.nim | 162 +++++++++++++++++++++-------------- lib/pure/htmlgen.nim | 6 +- tests/macros/tgensym.nim | 2 + tests/parser/tpostexprblocks.nim | 178 +++++++++++++++++++-------------------- tests/vm/tnimnode.nim | 12 +-- 13 files changed, 319 insertions(+), 272 deletions(-) (limited to 'lib/core') diff --git a/changelog.md b/changelog.md index 3bf9b8cb8..d3ac27d6d 100644 --- a/changelog.md +++ b/changelog.md @@ -26,6 +26,8 @@ - ``macros.astGenRepr``, ``macros.lispRepr`` and ``macros.treeRepr`` now escapes the content of string literals consistently. +- ``macros.NimSym`` and ``macros.NimIdent`` is now deprecated in favor + of the more general ``NimNode``. ### Language additions diff --git a/compiler/ast.nim b/compiler/ast.nim index 55032234f..8fbce84f0 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -632,10 +632,11 @@ type mCpuEndian, mHostOS, mHostCPU, mBuildOS, mBuildCPU, mAppType, mNaN, mInf, mNegInf, mCompileOption, mCompileOptionArg, - mNLen, mNChild, mNSetChild, mNAdd, mNAddMultiple, mNDel, mNKind, + mNLen, mNChild, mNSetChild, mNAdd, mNAddMultiple, mNDel, + mNKind, mNSymKind mNIntVal, mNFloatVal, mNSymbol, mNIdent, mNGetType, mNStrVal, mNSetIntVal, mNSetFloatVal, mNSetSymbol, mNSetIdent, mNSetType, mNSetStrVal, mNLineInfo, - mNNewNimNode, mNCopyNimNode, mNCopyNimTree, mStrToIdent, mIdentToStr, + mNNewNimNode, mNCopyNimNode, mNCopyNimTree, mStrToIdent, mNBindSym, mLocals, mNCallSite, mEqIdent, mEqNimrodNode, mSameNodeType, mGetImpl, mNHint, mNWarning, mNError, diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index 0be2899be..85ebdd7bf 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -113,3 +113,4 @@ proc initDefines*() = defineSymbol("nimHasRunnableExamples") defineSymbol("nimNewDot") defineSymbol("nimHasNilChecks") + defineSymbol("nimSymKind") diff --git a/compiler/guards.nim b/compiler/guards.nim index a5e6058c9..94af5202d 100644 --- a/compiler/guards.nim +++ b/compiler/guards.nim @@ -768,8 +768,10 @@ macro `=~`(x: PNode, pat: untyped): bool = var conds = newTree(nnkBracket) m(x, pat, conds) - when declared(macros.toNimIdent): - result = nestList(toNimIdent"and", conds) + when compiles(nestList(ident"and", conds)): + result = nestList(ident"and", conds) + #elif declared(macros.toNimIdent): + # result = nestList(toNimIdent"and", conds) else: result = nestList(!"and", conds) diff --git a/compiler/vm.nim b/compiler/vm.nim index 6ea6f1492..33c17eff4 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -892,7 +892,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = regs[ra].node = if a.sym.ast.isNil: newNode(nkNilLit) else: copyTree(a.sym.ast) else: - stackTrace(c, tos, pc, errFieldXNotFound, "symbol") + stackTrace(c, tos, pc, errGenerated, "node is not a symbol") of opcEcho: let rb = instr.regB if rb == 1: @@ -1233,6 +1233,14 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = decodeB(rkInt) regs[ra].intVal = ord(regs[rb].node.kind) c.comesFromHeuristic = regs[rb].node.info + of opcNSymKind: + decodeB(rkInt) + let a = regs[rb].node + if a.kind == nkSym: + regs[ra].intVal = ord(a.sym.kind) + else: + stackTrace(c, tos, pc, errGenerated, "node is not a symbol") + c.comesFromHeuristic = regs[rb].node.info of opcNIntVal: decodeB(rkInt) let a = regs[rb].node @@ -1295,9 +1303,17 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = decodeB(rkNode) createStr regs[ra] let a = regs[rb].node - if a.kind in {nkStrLit..nkTripleStrLit}: regs[ra].node.strVal = a.strVal - elif a.kind == nkCommentStmt: regs[ra].node.strVal = a.comment - else: stackTrace(c, tos, pc, errFieldXNotFound, "strVal") + case a.kind + of {nkStrLit..nkTripleStrLit}: + regs[ra].node.strVal = a.strVal + of nkCommentStmt: + regs[ra].node.strVal = a.comment + of nkIdent: + regs[ra].node.strVal = a.ident.s + of nkSym: + regs[ra].node.strVal = a.sym.name.s + else: + stackTrace(c, tos, pc, errFieldXNotFound, "strVal") of opcSlurp: decodeB(rkNode) createStr regs[ra] @@ -1387,17 +1403,6 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = else: regs[ra].node = newNodeI(nkIdent, c.debug[pc]) regs[ra].node.ident = getIdent(regs[rb].node.strVal) - of opcIdentToStr: - decodeB(rkNode) - let a = regs[rb].node - createStr regs[ra] - regs[ra].node.info = c.debug[pc] - if a.kind == nkSym: - regs[ra].node.strVal = a.sym.name.s - elif a.kind == nkIdent: - regs[ra].node.strVal = a.ident.s - else: - stackTrace(c, tos, pc, errFieldXNotFound, "ident") of opcSetType: if regs[ra].kind != rkNode: internalError(c.debug[pc], "cannot set type") diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim index dd4bc5060..66bc8dfd2 100644 --- a/compiler/vmdef.nim +++ b/compiler/vmdef.nim @@ -79,6 +79,7 @@ type opcNAdd, opcNAddMultiple, opcNKind, + opcNSymKind, opcNIntVal, opcNFloatVal, opcNSymbol, @@ -101,7 +102,6 @@ type opcNGetLine, opcNGetColumn, opcNGetFile, opcEqIdent, opcStrToIdent, - opcIdentToStr, opcGetImpl, opcEcho, diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index b8f1ef711..a661f59eb 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -1080,6 +1080,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = of mNAdd: genBinaryABC(c, n, dest, opcNAdd) of mNAddMultiple: genBinaryABC(c, n, dest, opcNAddMultiple) of mNKind: genUnaryABC(c, n, dest, opcNKind) + of mNSymKind: genUnaryABC(c, n, dest, opcNSymKind) of mNIntVal: genUnaryABC(c, n, dest, opcNIntVal) of mNFloatVal: genUnaryABC(c, n, dest, opcNFloatVal) of mNSymbol: genUnaryABC(c, n, dest, opcNSymbol) @@ -1125,7 +1126,6 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = else: localError(n.info, "invalid bindSym usage") of mStrToIdent: genUnaryABC(c, n, dest, opcStrToIdent) - of mIdentToStr: genUnaryABC(c, n, dest, opcIdentToStr) of mEqIdent: genBinaryABC(c, n, dest, opcEqIdent) of mEqNimrodNode: genBinaryABC(c, n, dest, opcEqNimrodNode) of mSameNodeType: genBinaryABC(c, n, dest, opcSameNodeType) diff --git a/doc/astspec.txt b/doc/astspec.txt index 6d755c2e2..73058cd93 100644 --- a/doc/astspec.txt +++ b/doc/astspec.txt @@ -28,12 +28,8 @@ contains: intVal: BiggestInt ## the int literal of nnkFloatLit..nnkFloat64Lit: floatVal: BiggestFloat ## the float literal - of nnkStrLit..nnkTripleStrLit: + of nnkStrLit..nnkTripleStrLit, nnkCommentStmt, nnkIdent, nnkSym: strVal: string ## the string literal - of nnkIdent: - ident: NimIdent ## the identifier - of nnkSym: - symbol: NimSym ## the symbol (after symbol lookup phase) else: sons: seq[NimNode] ## the node's sons (or children) @@ -74,8 +70,8 @@ Nim expression Corresponding AST ``"""abc"""`` ``nnkTripleStrLit(strVal = "abc")`` ``' '`` ``nnkCharLit(intVal = 32)`` ``nil`` ``nnkNilLit()`` -``myIdentifier`` ``nnkIdent(ident = !"myIdentifier")`` -``myIdentifier`` after lookup pass: ``nnkSym(symbol = ...)`` +``myIdentifier`` ``nnkIdent(strVal = "myIdentifier")`` +``myIdentifier`` after lookup pass: ``nnkSym(strVal = "myIdentifier", ...)`` ----------------- --------------------------------------------- Identifiers are ``nnkIdent`` nodes. After the name lookup pass these nodes @@ -97,7 +93,7 @@ AST: .. code-block:: nim nnkCommand( - nnkIdent(!"echo"), + nnkIdent("echo"), nnkStrLit("abc"), nnkStrLit("xyz") ) @@ -115,7 +111,7 @@ AST: .. code-block:: nim nnkCall( - nnkIdent(!"echo"), + nnkIdent("echo"), nnkStrLit("abc"), nnkStrLit("xyz") ) @@ -133,7 +129,7 @@ AST: .. code-block:: nim nnkInfix( - nnkIdent(!"&"), + nnkIdent("&"), nnkStrLit("abc"), nnkStrLit("xyz") ) @@ -150,10 +146,10 @@ AST: .. code-block:: nim nnkInfix( - nnkIdent(!"+"), + nnkIdent("+"), nnkIntLit(5), nnkInfix( - nnkIdent(!"*"), + nnkIdent("*"), nnkIntLit(3), nnkIntLit(4) ) @@ -174,7 +170,7 @@ AST: .. code-block:: nim nnkCall( nnkAccQuoted( - nnkIdent(!"+") + nnkIdent("+") ), nnkIntLit(3), nnkIntLit(4) @@ -192,7 +188,7 @@ AST: .. code-block:: nim nnkPrefix( - nnkIdent(!"?"), + nnkIdent("?"), nnkStrLit("abc") ) @@ -212,8 +208,8 @@ AST: .. code-block:: nim nnkPostfix( - nnkIdent(!"*"), - nnkIdent(!"identifier") + nnkIdent("*"), + nnkIdent("identifier") ) @@ -229,10 +225,10 @@ AST: .. code-block:: nim nnkCall( - nnkIdent(!"writeLine"), + nnkIdent("writeLine"), nnkExprEqExpr( - nnkIdent(!"file"), - nnkIdent(!"stdout") + nnkIdent("file"), + nnkIdent("stdout") ), nnkStrLit("hallo") ) @@ -253,7 +249,7 @@ AST: .. code-block:: nim nnkCallStrLit( - nnkIdent(!"echo"), + nnkIdent("echo"), nnkRStrLit("hello") ) @@ -268,7 +264,7 @@ Concrete syntax: AST: .. code-block:: nim - nnkDerefExpr(nnkIdent(!"x")) + nnkDerefExpr(nnkIdent("x")) Addr operator @@ -282,7 +278,7 @@ Concrete syntax: AST: .. code-block:: nim - nnkAddr(nnkIdent(!"x")) + nnkAddr(nnkIdent("x")) Cast operator @@ -296,7 +292,7 @@ Concrete syntax: AST: .. code-block:: nim - nnkCast(nnkIdent(!"T"), nnkIdent(!"x")) + nnkCast(nnkIdent("T"), nnkIdent("x")) Object access operator ``.`` @@ -310,7 +306,7 @@ Concrete syntax: AST: .. code-block:: nim - nnkDotExpr(nnkIdent(!"x"), nnkIdent(!"y")) + nnkDotExpr(nnkIdent("x"), nnkIdent("y")) If you use Nim's flexible calling syntax (as in ``x.len()``), the result is the same as above but wrapped in an ``nnkCall``. @@ -327,7 +323,7 @@ Concrete syntax: AST: .. code-block:: nim - nnkBracketExpr(nnkIdent(!"x"), nnkIdent(!"y")) + nnkBracketExpr(nnkIdent("x"), nnkIdent("y")) Parentheses @@ -373,8 +369,8 @@ AST: .. code-block:: nim nnkTableConstr( - nnkExprColonExpr(nnkIdent(!"a"), nnkIntLit(3)), - nnkExprColonExpr(nnkIdent(!"b"), nnkIntLit(5)) + nnkExprColonExpr(nnkIdent("a"), nnkIntLit(3)), + nnkExprColonExpr(nnkIdent("b"), nnkIntLit(5)) ) @@ -410,7 +406,7 @@ AST: .. code-block:: nim nnkInfix( - nnkIdent(!".."), + nnkIdent(".."), nnkIntLit(1), nnkIntLit(3) ) @@ -461,7 +457,7 @@ Documentation Comments ---------------------- Double-hash (``##``) comments in the code actually have their own format, -using ``strVal`` to get and set the comment text. Single-hash (``#``) +using ``strVal`` to get and set the comment text. Single-hash (``#``) comments are ignored. Concrete syntax: @@ -497,7 +493,7 @@ AST: .. code-block:: nim nnkPragma( nnkExprColonExpr( - nnkIdent(!"emit"), + nnkIdent("emit"), nnkStrLit("#include ") # the "argument" ) ) @@ -515,10 +511,10 @@ AST: .. code-block:: nim nnkPragma( nnkExprColonExpr( - nnkIdent(!"pragma"), # this is always first when declaring a new pragma - nnkIdent(!"cdeclRename") # the name of the pragma + nnkIdent("pragma"), # this is always first when declaring a new pragma + nnkIdent("cdeclRename") # the name of the pragma ), - nnkIdent(!"cdecl") + nnkIdent("cdecl") ) Statements @@ -570,7 +566,7 @@ Concrete syntax: AST: .. code-block:: nim - nnkAsgn(nnkIdent(!"x"), nnkIntLit(42)) + nnkAsgn(nnkIdent("x"), nnkIntLit(42)) This is not the syntax for assignment when combined with ``var``, ``let``, or ``const``. @@ -736,7 +732,7 @@ Concrete syntax: AST: .. code-block:: nim - nnkBreakStmt(nnkIdent(!"otherLocation")) + nnkBreakStmt(nnkIdent("otherLocation")) If ``break`` is used without a jump-to location, ``nnkEmpty`` replaces ``nnkIdent``. @@ -751,7 +747,7 @@ Concrete syntax: AST: .. code-block:: nim - nnkBlockStmt(nnkIdent(!"name"), nnkStmtList(...)) + nnkBlockStmt(nnkIdent("name"), nnkStmtList(...)) A ``block`` doesn't need an name, in which case ``nnkEmpty`` is used. @@ -787,7 +783,7 @@ Concrete syntax: AST: .. code-block:: nim - nnkImportStmt(nnkIdent(!"math")) + nnkImportStmt(nnkIdent("math")) With ``except``, we get ``nnkImportExceptStmt``. @@ -799,7 +795,7 @@ Concrete syntax: AST: .. code-block:: nim - nnkImportExceptStmt(nnkIdent(!"math"),nnkIdent(!"pow")) + nnkImportExceptStmt(nnkIdent("math"),nnkIdent("pow")) Note that ``import math as m`` does not use a different node; rather, we use ``nnkImportStmt`` with ``as`` as an infix operator. @@ -814,9 +810,9 @@ AST: .. code-block:: nim nnkImportStmt( nnkInfix( - nnkIdent(!"as"), - nnkIdent(!"strutils"), - nnkIdent(!"su") + nnkIdent("as"), + nnkIdent("strutils"), + nnkIdent("su") ) ) @@ -833,7 +829,7 @@ Concrete syntax: AST: .. code-block:: nim - nnkFromStmt(nnkIdent(!"math"), nnkIdent(!"pow")) + nnkFromStmt(nnkIdent("math"), nnkIdent("pow")) Using ``from math as m import pow`` works identically to the ``as`` modifier with the ``import`` statement, but wrapped in ``nnkFromStmt``. @@ -852,7 +848,7 @@ Concrete syntax: AST: .. code-block:: nim - nnkExportStmt(nnkIdent(!"unsigned")) + nnkExportStmt(nnkIdent("unsigned")) Similar to the ``import`` statement, the AST is different for ``export ... except``. @@ -865,7 +861,7 @@ Concrete syntax: AST: .. code-block:: nim - nnkExportExceptStmt(nnkIdent(!"math"),nnkIdent(!"pow")) + nnkExportExceptStmt(nnkIdent("math"),nnkIdent("pow")) Include statement ----------------- @@ -880,7 +876,7 @@ Concrete syntax: AST: .. code-block:: nim - nnkIncludeStmt(nnkIdent(!"blocks")) + nnkIncludeStmt(nnkIdent("blocks")) Var section ----------- @@ -895,7 +891,7 @@ AST: .. code-block:: nim nnkVarSection( nnkIdentDefs( - nnkIdent(!"a"), + nnkIdent("a"), nnkEmpty(), # or nnkIdent(...) if the variable declares the type nnkIntLit(3), ) @@ -925,7 +921,7 @@ AST: .. code-block:: nim nnkLetSection( nnkIdentDefs( - nnkIdent(!"a"), + nnkIdent("a"), nnkEmpty(), # or nnkIdent(...) for the type nnkIntLit(3), ) @@ -944,7 +940,7 @@ AST: .. code-block:: nim nnkConstSection( nnkConstDef( # not nnkConstDefs! - nnkIdent(!"a"), + nnkIdent("a"), nnkEmpty(), # or nnkIdent(...) if the variable declares the type nnkIntLit(3), # required in a const declaration! ) @@ -966,9 +962,9 @@ AST: .. code-block:: nim nnkTypeSection( nnkTypeDef( - nnkIdent(!"A"), + nnkIdent("A"), nnkEmpty(), - nnkIdent(!"int") + nnkIdent("int") ) ) @@ -985,10 +981,10 @@ AST: .. code-block:: nim # ... nnkTypeDef( - nnkIdent(!"MyInt"), + nnkIdent("MyInt"), nnkEmpty(), nnkDistinctTy( - nnkIdent(!"int") + nnkIdent("int") ) ) @@ -1004,10 +1000,10 @@ AST: .. code-block:: nim nnkTypeSection( nnkTypeDef( - nnkIdent(!"A"), + nnkIdent("A"), nnkGenericParams( nnkIdentDefs( - nnkIdent(!"T"), + nnkIdent("T"), nnkEmpty(), # if the type is declared with options, like # ``[T: SomeInteger]``, they are given here nnkEmpty(), @@ -1031,12 +1027,12 @@ AST: .. code-block:: nim # ... nnkTypeDef( - nnkIdent(!"IO"), + nnkIdent("IO"), nnkEmpty(), nnkObjectTy( nnkEmpty(), # no pragmas here nnkOfInherit( - nnkIdent(!"RootObj") # inherits from RootObj + nnkIdent("RootObj") # inherits from RootObj ) nnkEmpty() ) @@ -1062,43 +1058,43 @@ AST: # ... nnkObjectTy( nnkPragma( - nnkIdent(!"inheritable") + nnkIdent("inheritable") ), nnkEmpty(), nnkRecList( # list of object parameters nnkIdentDefs( - nnkIdent(!"name"), - nnkIdent(!"string"), + nnkIdent("name"), + nnkIdent("string"), nnkEmpty() ), nnkRecCase( # case statement within object (not nnkCaseStmt) nnkIdentDefs( - nnkIdent(!"isFat"), - nnkIdent(!"bool"), + nnkIdent("isFat"), + nnkIdent("bool"), nnkEmpty() ), nnkOfBranch( - nnkIdent(!"true"), + nnkIdent("true"), nnkRecList( # again, a list of object parameters nnkIdentDefs( - nnkIdent(!"m"), + nnkIdent("m"), nnkBracketExpr( - nnkIdent(!"array"), + nnkIdent("array"), nnkIntLit(100000), - nnkIdent(!"T") + nnkIdent("T") ), nnkEmpty() ) ), nnkOfBranch( - nnkIdent(!"false"), + nnkIdent("false"), nnkRecList( nnkIdentDefs( - nnkIdent(!"m"), + nnkIdent("m"), nnkBracketExpr( - nnkIdent(!"array"), + nnkIdent("array"), nnkIntLit(10), - nnkIdent(!"T") + nnkIdent("T") ), nnkEmpty() ) @@ -1123,7 +1119,7 @@ AST: # ... nnkEnumTy( nnkEmpty(), - nnkIdent(!"First") # you need at least one nnkIdent or the compiler complains + nnkIdent("First") # you need at least one nnkIdent or the compiler complains ) The usage of ``concept`` (experimental) is similar to objects. @@ -1158,9 +1154,9 @@ AST: .. code-block:: nim # ... within nnkGenericParams nnkIdentDefs( - nnkIdent(!"T"), + nnkIdent("T"), nnkStaticTy( - nnkIdent(!"int") + nnkIdent("int") ), nnkEmpty() ) @@ -1180,7 +1176,7 @@ Nim type Corresponding AST ``distinct`` ``nnkDistinctTy`` ``enum`` ``nnkEnumTy`` ``concept`` ``nnkTypeClassTy``\* -``array`` ``nnkBracketExpr(nnkIdent(!"array"),...``\* +``array`` ``nnkBracketExpr(nnkIdent("array"),...``\* ``proc`` ``nnkProcTy`` ``iterator`` ``nnkIteratorTy`` ``object`` ``nnkObjectTy`` @@ -1200,7 +1196,7 @@ AST: .. code-block:: nim # ... nnkTypeDef( - nnkIdent(!"MyProc"), + nnkIdent("MyProc"), nnkGenericParams( # here, not with the proc # ... ) @@ -1225,7 +1221,7 @@ Concrete syntax: AST: .. code-block:: nim - nnkMixinStmt(nnkIdent(!"x")) + nnkMixinStmt(nnkIdent("x")) Bind statement -------------- @@ -1238,7 +1234,7 @@ Concrete syntax: AST: .. code-block:: nim - nnkBindStmt(nnkIdent(!"x")) + nnkBindStmt(nnkIdent("x")) Procedure declaration --------------------- @@ -1255,26 +1251,26 @@ AST: .. code-block:: nim nnkProcDef( - nnkPostfix(nnkIdent(!"*"), nnkIdent(!"hello")), # the exported proc name + nnkPostfix(nnkIdent("*"), nnkIdent("hello")), # the exported proc name nnkEmpty(), # patterns for term rewriting in templates and macros (not procs) nnkGenericParams( # generic type parameters, like with type declaration nnkIdentDefs( - nnkIdent(!"T"), nnkIdent(!"SomeInteger") + nnkIdent("T"), nnkIdent("SomeInteger") ) ), nnkFormalParams( - nnkIdent(!"int"), # the first FormalParam is the return type. nnkEmpty() if there is none + nnkIdent("int"), # the first FormalParam is the return type. nnkEmpty() if there is none nnkIdentDefs( - nnkIdent(!"x"), - nnkIdent(!"int"), # type type (required for procs, not for templates) + nnkIdent("x"), + nnkIdent("int"), # type type (required for procs, not for templates) nnkIntLit(3) # a default value ), nnkIdentDefs( - nnkIdent(!"y"), - nnkIdent(!"float32"), + nnkIdent("y"), + nnkIdent("float32"), nnkEmpty() ) - nnkPragma(nnkIdent(!"inline")), + nnkPragma(nnkIdent("inline")), nnkEmpty(), # reserved slot for future use nnkStmtList(nnkDiscardStmt(nnkEmpty())) # the meat of the proc ) @@ -1296,9 +1292,9 @@ AST: nnkFormalParams( nnkEmpty(), # no return here nnkIdentDefs( - nnkIdent(!"a"), # the first parameter - nnkIdent(!"b"), # directly to the second parameter - nnkIdent(!"int"), # their shared type identifier + nnkIdent("a"), # the first parameter + nnkIdent("b"), # directly to the second parameter + nnkIdent("int"), # their shared type identifier nnkEmpty(), # default value would go here ) ), @@ -1318,7 +1314,7 @@ AST: # ... nnkFormalParams( nnkVarTy( - nnkIdent(!"int") + nnkIdent("int") ) ) @@ -1337,7 +1333,7 @@ AST: .. code-block:: nim nnkIteratorDef( - nnkIdent(!"nonsense"), + nnkIdent("nonsense"), nnkEmpty(), ... ) @@ -1356,7 +1352,7 @@ AST: .. code-block:: nim nnkConverterDef( - nnkIdent(!"toBool"), + nnkIdent("toBool"), # ... ) @@ -1378,7 +1374,7 @@ AST: .. code-block:: nim nnkTemplateDef( - nnkIdent(!"optOpt"), + nnkIdent("optOpt"), nnkStmtList( # instead of nnkEmpty() expr1 ), diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 5a4c4c1e9..9edaab237 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -118,7 +118,7 @@ type ## use ``ident"abc"``. NimSymObj = object # hidden - NimSym* = ref NimSymObj + NimSym* {.deprecated.} = ref NimSymObj ## represents a Nim *symbol* in the compiler; a *symbol* is a looked-up ## *ident*. @@ -134,25 +134,23 @@ const proc `!`*(s: string): NimIdent {.magic: "StrToIdent", noSideEffect, deprecated.} ## constructs an identifier from the string `s` - ## **Deprecated since version 0.18.0**: Use ``toNimIdent`` instead. + ## **Deprecated since version 0.18.0**: Use ``ident`` or ``newIdentNode`` instead. -proc toNimIdent*(s: string): NimIdent {.magic: "StrToIdent", noSideEffect.} +proc toNimIdent*(s: string): NimIdent {.magic: "StrToIdent", noSideEffect, deprecated.} ## constructs an identifier from the string `s` + ## **Deprecated since version 0.18.1**; Use ``ident`` or ``newIdentNode`` instead. -proc `$`*(i: NimIdent): string {.magic: "IdentToStr", noSideEffect.} - ## converts a Nim identifier to a string - -proc `$`*(s: NimSym): string {.magic: "IdentToStr", noSideEffect.} - ## converts a Nim symbol to a string - -proc `==`*(a, b: NimIdent): bool {.magic: "EqIdent", noSideEffect.} +proc `==`*(a, b: NimIdent): bool {.magic: "EqIdent", noSideEffect, deprecated.} ## compares two Nim identifiers + ## **Deprecated since version 0.18.1**; Use ``==`` on ``NimNode`` instead. proc `==`*(a, b: NimNode): bool {.magic: "EqNimrodNode", noSideEffect.} ## compares two Nim nodes -proc `==`*(a, b: NimSym): bool {.magic: "EqNimrodNode", noSideEffect.} +proc `==`*(a, b: NimSym): bool {.magic: "EqNimrodNode", noSideEffect, deprecated.} ## compares two Nim symbols + ## **Deprecated since version 0.18.1**; Use ```==`(NimNode,NimNode)`` instead. + proc sameType*(a, b: NimNode): bool {.magic: "SameNodeType", noSideEffect.} = ## compares two Nim nodes' types. Return true if the types are the same, @@ -195,8 +193,47 @@ proc kind*(n: NimNode): NimNodeKind {.magic: "NKind", noSideEffect.} proc intVal*(n: NimNode): BiggestInt {.magic: "NIntVal", noSideEffect.} proc floatVal*(n: NimNode): BiggestFloat {.magic: "NFloatVal", noSideEffect.} -proc symbol*(n: NimNode): NimSym {.magic: "NSymbol", noSideEffect.} -proc ident*(n: NimNode): NimIdent {.magic: "NIdent", noSideEffect.} + +proc ident*(n: NimNode): NimIdent {.magic: "NIdent", noSideEffect, deprecated.} = + ## **Deprecated since version 0.18.1**; All functionality is defined on ``NimNode``. + +proc symbol*(n: NimNode): NimSym {.magic: "NSymbol", noSideEffect, deprecated.} + ## **Deprecated since version 0.18.1**; All functionality is defined on ``NimNode``. + +proc getImpl*(s: NimSym): NimNode {.magic: "GetImpl", noSideEffect, deprecated: "use `getImpl: NimNode -> NimNode` instead".} + +when defined(nimSymKind): + proc symKind*(symbol: NimNode): NimSymKind {.magic: "NSymKind", noSideEffect.} + proc getImpl*(symbol: NimNode): NimNode {.magic: "GetImpl", noSideEffect.} + proc strVal*(n: NimNode): string {.magic: "NStrVal", noSideEffect.} + ## retrieve the implementation of `symbol`. `symbol` can be a + ## routine or a const. + + proc `$`*(i: NimIdent): string {.magic: "NStrVal", noSideEffect, deprecated.} + ## converts a Nim identifier to a string + ## **Deprecated since version 0.18.1**; Use ``strVal`` instead. + + proc `$`*(s: NimSym): string {.magic: "NStrVal", noSideEffect, deprecated.} + ## converts a Nim symbol to a string + ## **Deprecated since version 0.18.1**; Use ``strVal`` instead. + +else: # bootstrapping substitute + proc getImpl*(symbol: NimNode): NimNode = + symbol.symbol.getImpl + + proc strValOld(n: NimNode): string {.magic: "NStrVal", noSideEffect.} + + proc `$`*(s: NimSym): string {.magic: "IdentToStr", noSideEffect.} + + proc `$`*(i: NimIdent): string {.magic: "IdentToStr", noSideEffect.} + + proc strVal*(n: NimNode): string = + if n.kind == nnkIdent: + $n.ident + elif n.kind == nnkSym: + $n.symbol + else: + n.strValOld proc getType*(n: NimNode): NimNode {.magic: "NGetType", noSideEffect.} ## with 'getType' you can access the node's `type`:idx:. A Nim type is @@ -228,12 +265,15 @@ proc getTypeImpl*(n: NimNode): NimNode {.magic: "NGetType", noSideEffect.} proc getTypeImpl*(n: typedesc): NimNode {.magic: "NGetType", noSideEffect.} ## Like getType except it includes generic parameters for the implementation -proc strVal*(n: NimNode): string {.magic: "NStrVal", noSideEffect.} - proc `intVal=`*(n: NimNode, val: BiggestInt) {.magic: "NSetIntVal", noSideEffect.} proc `floatVal=`*(n: NimNode, val: BiggestFloat) {.magic: "NSetFloatVal", noSideEffect.} -proc `symbol=`*(n: NimNode, val: NimSym) {.magic: "NSetSymbol", noSideEffect.} -proc `ident=`*(n: NimNode, val: NimIdent) {.magic: "NSetIdent", noSideEffect.} + +proc `symbol=`*(n: NimNode, val: NimSym) {.magic: "NSetSymbol", noSideEffect, deprecated.} + ## **Deprecated since version 0.18.1**; Generate a new ``NimNode`` with ``genSym`` instead. + +proc `ident=`*(n: NimNode, val: NimIdent) {.magic: "NSetIdent", noSideEffect, deprecated.} + ## **Deprecated since version 0.18.1**; Generate a new ``NimNode`` with ``ident(string)`` instead. + #proc `typ=`*(n: NimNode, typ: typedesc) {.magic: "NSetType".} # this is not sound! Unfortunately forbidding 'typ=' is not enough, as you # can easily do: @@ -255,11 +295,6 @@ proc newNimNode*(kind: NimNodeKind, proc copyNimNode*(n: NimNode): NimNode {.magic: "NCopyNimNode", noSideEffect.} proc copyNimTree*(n: NimNode): NimNode {.magic: "NCopyNimTree", noSideEffect.} -proc getImpl*(s: NimSym): NimNode {.magic: "GetImpl", noSideEffect.} = - ## retrieve the implementation of a symbol `s`. `s` can be a routine or a - ## const. - discard - proc error*(msg: string, n: NimNode = nil) {.magic: "NError", benign.} ## writes an error message at compile time @@ -294,11 +329,9 @@ proc newIdentNode*(i: NimIdent): NimNode {.compileTime.} = result = newNimNode(nnkIdent) result.ident = i -proc newIdentNode*(i: string): NimNode {.compileTime.} = - ## creates an identifier node from `i` - result = newNimNode(nnkIdent) - result.ident = toNimIdent i - +proc newIdentNode*(i: string): NimNode {.magic: "StrToIdent", noSideEffect.} + ## creates an identifier node from `i`. It is simply an alias for + ## ``ident(string)``. Use that, it's shorter. type BindSymRule* = enum ## specifies how ``bindSym`` behaves @@ -464,9 +497,11 @@ proc newCall*(theProc: NimNode, result.add(args) proc newCall*(theProc: NimIdent, - args: varargs[NimNode]): NimNode {.compileTime.} = + args: varargs[NimNode]): NimNode {.compileTime, deprecated.} = ## produces a new call node. `theProc` is the proc that is called with ## the arguments ``args[0..]``. + ## **Deprecated since version 0.18.1**; Use ``newCall(string, ...)``, + ## or ``newCall(NimNode, ...)`` instead. result = newNimNode(nnkCall) result.add(newIdentNode(theProc)) result.add(args) @@ -594,17 +629,30 @@ proc newLit*(s: string): NimNode {.compileTime.} = result = newNimNode(nnkStrLit) result.strVal = s -proc nestList*(theProc: NimIdent, - x: NimNode): NimNode {.compileTime.} = - ## nests the list `x` into a tree of call expressions: - ## ``[a, b, c]`` is transformed into ``theProc(a, theProc(c, d))``. +proc nestList*(op: NimNode; pack: NimNode): NimNode {.compileTime.} = + ## nests the list `pack` into a tree of call expressions: + ## ``[a, b, c]`` is transformed into ``op(a, op(c, d))``. + ## This is also known as fold expression. + if pack.len < 1: + error("`nestList` expects a node with at least 1 child") + result = pack[^1] + for i in countdown(pack.len - 2, 0): + result = newCall(op, pack[i], result) + +proc nestList*(op: NimNode; pack: NimNode; init: NimNode): NimNode {.compileTime.} = + ## nests the list `pack` into a tree of call expressions: + ## ``[a, b, c]`` is transformed into ``op(a, op(c, d))``. + ## This is also known as fold expression. + result = init + for i in countdown(pack.len - 1, 0): + result = newCall(op, pack[i], result) + +proc nestList*(theProc: NimIdent, x: NimNode): NimNode {.compileTime, deprecated.} = + ## **Deprecated since version 0.18.1**; Use one of ``nestList(NimNode, ...)`` instead. var L = x.len result = newCall(theProc, x[L-2], x[L-1]) for i in countdown(L-3, 0): - # XXX the 'copyNimTree' here is necessary due to a bug in the evaluation - # engine that would otherwise create an endless loop here. :-( - # This could easily user code and so should be fixed in evals.nim somehow. - result = newCall(theProc, x[i], copyNimTree(result)) + result = newCall(theProc, x[i], result) proc treeRepr*(n: NimNode): string {.compileTime, benign.} = ## Convert the AST `n` to a human-readable tree-like string. @@ -620,9 +668,8 @@ proc treeRepr*(n: NimNode): string {.compileTime, benign.} = of nnkNilLit: res.add(" nil") of nnkCharLit..nnkInt64Lit: res.add(" " & $n.intVal) of nnkFloatLit..nnkFloat64Lit: res.add(" " & $n.floatVal) - of nnkStrLit..nnkTripleStrLit: res.add(" " & $n.strVal.newLit.repr) - of nnkIdent: res.add(" ident\"" & $n.ident & '"') - of nnkSym: res.add(" \"" & $n.symbol & '"') + of nnkStrLit..nnkTripleStrLit, nnkIdent, nnkSym: + res.add(" " & $n.strVal.newLit.repr) of nnkNone: assert false else: for j in 0..n.len-1: @@ -645,9 +692,8 @@ proc lispRepr*(n: NimNode): string {.compileTime, benign.} = of nnkNilLit: add(result, "nil") of nnkCharLit..nnkInt64Lit: add(result, $n.intVal) of nnkFloatLit..nnkFloat64Lit: add(result, $n.floatVal) - of nnkStrLit..nnkTripleStrLit, nnkCommentStmt: add(result, n.strVal.newLit.repr) - of nnkIdent: add(result, "ident\"" & $n.ident & '"') - of nnkSym: add(result, $n.symbol) + of nnkStrLit..nnkTripleStrLit, nnkCommentStmt, nnkident, nnkSym: + add(result, n.strVal.newLit.repr) of nnkNone: assert false else: if n.len > 0: @@ -696,9 +742,8 @@ proc astGenRepr*(n: NimNode): string {.compileTime, benign.} = of nnkCharLit: res.add("'" & $chr(n.intVal) & "'") of nnkIntLit..nnkInt64Lit: res.add($n.intVal) of nnkFloatLit..nnkFloat64Lit: res.add($n.floatVal) - of nnkStrLit..nnkTripleStrLit, nnkCommentStmt: res.add($n.strVal.newLit.repr) - of nnkIdent: res.add(($n.ident).newLit.repr()) - of nnkSym: res.add(($n.symbol).newLit.repr()) + of nnkStrLit..nnkTripleStrLit, nnkCommentStmt, nnkIdent, nnkSym: + res.add(n.strVal.newLit.repr) of nnkNone: assert false else: res.add(".newTree(") @@ -742,10 +787,10 @@ macro dumpAstGen*(s: untyped): untyped = echo s.astGenRepr ## See `dumpTree`. macro dumpTreeImm*(s: untyped): untyped {.deprecated.} = echo s.treeRepr - ## Deprecated. + ## Deprecated. Use `dumpTree` instead. macro dumpLispImm*(s: untyped): untyped {.deprecated.} = echo s.lispRepr - ## Deprecated. + ## Deprecated. Use `dumpLisp` instead. proc newEmptyNode*(): NimNode {.compileTime, noSideEffect.} = ## Create a new empty node @@ -991,28 +1036,21 @@ proc `body=`*(someProc: NimNode, val: NimNode) {.compileTime.} = proc basename*(a: NimNode): NimNode {.compiletime, benign.} - proc `$`*(node: NimNode): string {.compileTime.} = ## Get the string of an identifier node case node.kind - of nnkIdent: - result = $node.ident of nnkPostfix: - result = $node.basename.ident & "*" - of nnkStrLit..nnkTripleStrLit: + result = node.basename.strVal & "*" + of nnkStrLit..nnkTripleStrLit, nnkCommentStmt, nnkSym, nnkIdent: result = node.strVal - of nnkSym: - result = $node.symbol of nnkOpenSymChoice, nnkClosedSymChoice: result = $node[0] of nnkAccQuoted: result = $node[0] - of nnkCommentStmt: - result = node.strVal else: badNodeKind node.kind, "$" -proc ident*(name: string): NimNode {.compileTime,inline.} = newIdentNode(name) +proc ident*(name: string): NimNode {.magic: "StrToIdent", noSideEffect.} ## Create a new ident node from a string iterator items*(n: NimNode): NimNode {.inline.} = @@ -1129,10 +1167,8 @@ proc eqIdent*(node: NimNode; s: string): bool {.compileTime.} = ## is the same as ``s``. Note that this is the preferred way to check! Most ## other ways like ``node.ident`` are much more error-prone, unfortunately. case node.kind - of nnkIdent: - result = node.ident == toNimIdent s - of nnkSym: - result = eqIdent($node.symbol, s) + of nnkSym, nnkIdent: + result = eqIdent(node.strVal, s) of nnkOpenSymChoice, nnkClosedSymChoice: result = eqIdent($node[0], s) else: @@ -1189,11 +1225,11 @@ macro expandMacros*(body: typed): untyped = proc customPragmaNode(n: NimNode): NimNode = expectKind(n, {nnkSym, nnkDotExpr}) if n.kind == nnkSym: - let sym = n.symbol.getImpl() + let sym = n.getImpl() sym.expectRoutine() result = sym.pragma elif n.kind == nnkDotExpr: - let typDef = getImpl(getTypeInst(n[0]).symbol) + let typDef = getImpl(getTypeInst(n[0])) typDef.expectKind(nnkTypeDef) typDef[2].expectKind(nnkObjectTy) let recList = typDef[2][2] diff --git a/lib/pure/htmlgen.nim b/lib/pure/htmlgen.nim index c0934a45b..e7562029a 100644 --- a/lib/pure/htmlgen.nim +++ b/lib/pure/htmlgen.nim @@ -92,8 +92,10 @@ proc xmlCheckedTag*(e: NimNode, tag: string, optAttr = "", reqAttr = "", result.add(newStrLitNode("")) - result = nestList(!"&", result) - + when compiles(nestList(ident"&", result)): + result = nestList(ident"&", result) + else: + result = nestList(!"&", result) macro a*(e: varargs[untyped]): untyped = ## generates the HTML ``a`` element. diff --git a/tests/macros/tgensym.nim b/tests/macros/tgensym.nim index 955168939..1237b8bf7 100644 --- a/tests/macros/tgensym.nim +++ b/tests/macros/tgensym.nim @@ -28,6 +28,7 @@ macro async2(prc: untyped): untyped = # -> iterator nameIter(): FutureBase {.closure.} = # Changing this line to: newIdentNode($prc[0].ident & "Iter") # will make it work. var iteratorNameSym = genSym(nskIterator, $prc[0].ident & "Iter") + assert iteratorNameSym.symKind == nskIterator #var iteratorNameSym = newIdentNode($prc[0].ident & "Iter") var procBody = prc[6].convertReturns(retFutureSym) @@ -42,6 +43,7 @@ macro async2(prc: untyped): untyped = var varNameIter = newVarStmt(varNameIterSym, iteratorNameSym) outerProcBody.add varNameIter var varFirstSym = genSym(nskVar, "first") + assert varFirstSym.symKind == nskVar var varFirst = newVarStmt(varFirstSym, newCall(varNameIterSym)) outerProcBody.add varFirst diff --git a/tests/parser/tpostexprblocks.nim b/tests/parser/tpostexprblocks.nim index e97b9c7ee..c27bbf321 100644 --- a/tests/parser/tpostexprblocks.nim +++ b/tests/parser/tpostexprblocks.nim @@ -1,82 +1,82 @@ discard """ nimout: ''' StmtList - Ident ident"foo010" + Ident "foo010" Call - Ident ident"foo020" + Ident "foo020" Call - Ident ident"foo030" - Ident ident"x" + Ident "foo030" + Ident "x" Command - Ident ident"foo040" - Ident ident"x" + Ident "foo040" + Ident "x" Call - Ident ident"foo050" + Ident "foo050" StmtList DiscardStmt Empty Call - Ident ident"foo060" + Ident "foo060" StmtList DiscardStmt Empty Call - Ident ident"foo070" + Ident "foo070" StrLit "test" StmtList DiscardStmt Empty Call - Ident ident"foo080" + Ident "foo080" StrLit "test" StmtList DiscardStmt Empty Command - Ident ident"foo090" + Ident "foo090" StrLit "test" StmtList DiscardStmt Empty Command - Ident ident"foo100" + Ident "foo100" Call StrLit "test" StmtList DiscardStmt Empty Command - Ident ident"foo101" + Ident "foo101" Call IntLit 10 StmtList DiscardStmt Empty Command - Ident ident"foo110" + Ident "foo110" IntLit 1 Par Infix - Ident ident"+" + Ident "+" IntLit 2 IntLit 3 StmtList DiscardStmt Empty Command - Ident ident"foo120" + Ident "foo120" IntLit 1 Call Par Infix - Ident ident"+" + Ident "+" IntLit 2 IntLit 3 StmtList DiscardStmt Empty Call - Ident ident"foo130" + Ident "foo130" Do Empty Empty @@ -84,7 +84,7 @@ StmtList FormalParams Empty IdentDefs - Ident ident"x" + Ident "x" Empty Empty Empty @@ -93,7 +93,7 @@ StmtList DiscardStmt Empty Call - Ident ident"foo140" + Ident "foo140" Do Empty Empty @@ -101,8 +101,8 @@ StmtList FormalParams Empty IdentDefs - Ident ident"x" - Ident ident"int" + Ident "x" + Ident "int" Empty Empty Empty @@ -110,16 +110,16 @@ StmtList DiscardStmt Empty Call - Ident ident"foo150" + Ident "foo150" Do Empty Empty Empty FormalParams - Ident ident"int" + Ident "int" IdentDefs - Ident ident"x" - Ident ident"int" + Ident "x" + Ident "int" Empty Empty Empty @@ -127,9 +127,9 @@ StmtList DiscardStmt Empty Command - Ident ident"foo160" + Ident "foo160" Call - Ident ident"x" + Ident "x" Do Empty Empty @@ -137,7 +137,7 @@ StmtList FormalParams Empty IdentDefs - Ident ident"y" + Ident "y" Empty Empty Empty @@ -146,7 +146,7 @@ StmtList DiscardStmt Empty Call - Ident ident"foo170" + Ident "foo170" StmtList DiscardStmt Empty @@ -155,7 +155,7 @@ StmtList DiscardStmt Empty Call - Ident ident"foo180" + Ident "foo180" StmtList DiscardStmt Empty @@ -167,9 +167,9 @@ StmtList DiscardStmt Empty Command - Ident ident"foo190" + Ident "foo190" Call - Ident ident"x" + Ident "x" Do Empty Empty @@ -177,7 +177,7 @@ StmtList FormalParams Empty IdentDefs - Ident ident"y" + Ident "y" Empty Empty Empty @@ -190,9 +190,9 @@ StmtList Empty Empty FormalParams - Ident ident"int" + Ident "int" IdentDefs - Ident ident"z" + Ident "z" Empty Empty Empty @@ -205,10 +205,10 @@ StmtList Empty Empty FormalParams - Ident ident"int" + Ident "int" IdentDefs - Ident ident"w" - Ident ident"int" + Ident "w" + Ident "int" Empty Empty Empty @@ -223,10 +223,10 @@ StmtList DiscardStmt Empty Call - Ident ident"foo200" - Ident ident"x" + Ident "foo200" + Ident "x" Call - Ident ident"bar" + Ident "bar" StmtList DiscardStmt Empty @@ -236,53 +236,53 @@ StmtList Empty VarSection IdentDefs - Ident ident"a" + Ident "a" Empty - Ident ident"foo210" + Ident "foo210" VarSection IdentDefs - Ident ident"a" + Ident "a" Empty Call - Ident ident"foo220" + Ident "foo220" VarSection IdentDefs - Ident ident"a" + Ident "a" Empty Call - Ident ident"foo230" - Ident ident"x" + Ident "foo230" + Ident "x" VarSection IdentDefs - Ident ident"a" + Ident "a" Empty Command - Ident ident"foo240" - Ident ident"x" + Ident "foo240" + Ident "x" VarSection IdentDefs - Ident ident"a" + Ident "a" Empty Call - Ident ident"foo250" + Ident "foo250" StmtList DiscardStmt Empty VarSection IdentDefs - Ident ident"a" + Ident "a" Empty Call - Ident ident"foo260" + Ident "foo260" StmtList DiscardStmt Empty VarSection IdentDefs - Ident ident"a" + Ident "a" Empty Call - Ident ident"foo270" + Ident "foo270" StmtList DiscardStmt Empty @@ -292,12 +292,12 @@ StmtList Empty VarSection IdentDefs - Ident ident"a" + Ident "a" Empty Command - Ident ident"foo280" + Ident "foo280" Call - Ident ident"x" + Ident "x" Do Empty Empty @@ -305,7 +305,7 @@ StmtList FormalParams Empty IdentDefs - Ident ident"y" + Ident "y" Empty Empty Empty @@ -318,40 +318,40 @@ StmtList DiscardStmt Empty Asgn - Ident ident"a" - Ident ident"foo290" + Ident "a" + Ident "foo290" Asgn - Ident ident"a" + Ident "a" Call - Ident ident"foo300" + Ident "foo300" Asgn - Ident ident"a" + Ident "a" Call - Ident ident"foo310" - Ident ident"x" + Ident "foo310" + Ident "x" Asgn - Ident ident"a" + Ident "a" Command - Ident ident"foo320" - Ident ident"x" + Ident "foo320" + Ident "x" Asgn - Ident ident"a" + Ident "a" Call - Ident ident"foo330" + Ident "foo330" StmtList DiscardStmt Empty Asgn - Ident ident"a" + Ident "a" Call - Ident ident"foo340" + Ident "foo340" StmtList DiscardStmt Empty Asgn - Ident ident"a" + Ident "a" Call - Ident ident"foo350" + Ident "foo350" StmtList DiscardStmt Empty @@ -360,13 +360,13 @@ StmtList DiscardStmt Empty Asgn - Ident ident"a" + Ident "a" Command - Ident ident"foo360" + Ident "foo360" Call DotExpr - Ident ident"x" - Ident ident"bar" + Ident "x" + Ident "bar" Do Empty Empty @@ -374,7 +374,7 @@ StmtList FormalParams Empty IdentDefs - Ident ident"y" + Ident "y" Empty Empty Empty @@ -388,20 +388,20 @@ StmtList Empty Command DotExpr - Ident ident"foo370" - Ident ident"add" + Ident "foo370" + Ident "add" Call - Ident ident"quote" + Ident "quote" StmtList DiscardStmt Empty Call DotExpr - Ident ident"foo380" - Ident ident"add" + Ident "foo380" + Ident "add" BracketExpr Call - Ident ident"quote" + Ident "quote" StmtList DiscardStmt Empty diff --git a/tests/vm/tnimnode.nim b/tests/vm/tnimnode.nim index 2727df290..188bac9bf 100644 --- a/tests/vm/tnimnode.nim +++ b/tests/vm/tnimnode.nim @@ -26,12 +26,12 @@ proc checkNode(arg: NimNode; name: string): void {. compileTime .} = seqAppend.add(arg) # bit this creates a copy arg.add newCall(ident"echo", newLit("Hello World")) - assertEq arg.lispRepr , """StmtList(DiscardStmt(Empty()), Call(Ident(ident"echo"), StrLit("Hello World")))""" - assertEq node.lispRepr , """StmtList(DiscardStmt(Empty()), Call(Ident(ident"echo"), StrLit("Hello World")))""" - assertEq nodeArray[0].lispRepr , """StmtList(DiscardStmt(Empty()), Call(Ident(ident"echo"), StrLit("Hello World")))""" - assertEq nodeSeq[0].lispRepr , """StmtList(DiscardStmt(Empty()), Call(Ident(ident"echo"), StrLit("Hello World")))""" - assertEq seqAppend[0].lispRepr , """StmtList(DiscardStmt(Empty()), Call(Ident(ident"echo"), StrLit("Hello World")))""" - assertEq seqAppend[1].lispRepr , """StmtList(DiscardStmt(Empty()), Call(Ident(ident"echo"), StrLit("Hello World")))""" + assertEq arg.lispRepr , """StmtList(DiscardStmt(Empty()), Call(Ident("echo"), StrLit("Hello World")))""" + assertEq node.lispRepr , """StmtList(DiscardStmt(Empty()), Call(Ident("echo"), StrLit("Hello World")))""" + assertEq nodeArray[0].lispRepr , """StmtList(DiscardStmt(Empty()), Call(Ident("echo"), StrLit("Hello World")))""" + assertEq nodeSeq[0].lispRepr , """StmtList(DiscardStmt(Empty()), Call(Ident("echo"), StrLit("Hello World")))""" + assertEq seqAppend[0].lispRepr , """StmtList(DiscardStmt(Empty()), Call(Ident("echo"), StrLit("Hello World")))""" + assertEq seqAppend[1].lispRepr , """StmtList(DiscardStmt(Empty()), Call(Ident("echo"), StrLit("Hello World")))""" echo "OK" -- cgit 1.4.1-2-gfad0 From c3cc52087fb46e7898e9099945667204e40befbc Mon Sep 17 00:00:00 2001 From: genotrance Date: Thu, 12 Apr 2018 06:59:14 -0500 Subject: Added a few useful os calls to VM (#7440) --- changelog.md | 1 + compiler/vmops.nim | 48 +++++++++++++++++++++++++++++------------------- lib/core/macros.nim | 4 ++++ tests/vm/tvmmisc.nim | 24 +++++++++++++++++++++--- 4 files changed, 55 insertions(+), 22 deletions(-) (limited to 'lib/core') diff --git a/changelog.md b/changelog.md index d3ac27d6d..689c6d253 100644 --- a/changelog.md +++ b/changelog.md @@ -54,3 +54,4 @@ - The VM's instruction count limit was raised to 1 billion instructions in order to support more complex computations at compile-time. +- Added ``macros.getProjectPath`` and ``ospaths.putEnv`` procs to VM. diff --git a/compiler/vmops.nim b/compiler/vmops.nim index 2a00f207a..f7debe2df 100644 --- a/compiler/vmops.nim +++ b/compiler/vmops.nim @@ -13,7 +13,9 @@ from math import sqrt, ln, log10, log2, exp, round, arccos, arcsin, arctan, arctan2, cos, cosh, hypot, sinh, sin, tan, tanh, pow, trunc, floor, ceil, fmod -from os import getEnv, existsEnv, dirExists, fileExists, walkDir +from os import getEnv, existsEnv, dirExists, fileExists, putEnv, walkDir + +from options import gProjectPath template mathop(op) {.dirty.} = registerCallback(c, "stdlib.math." & astToStr(op), `op Wrapper`) @@ -27,6 +29,9 @@ template ospathsop(op) {.dirty.} = template systemop(op) {.dirty.} = registerCallback(c, "stdlib.system." & astToStr(op), `op Wrapper`) +template macrosop(op) {.dirty.} = + registerCallback(c, "stdlib.macros." & astToStr(op), `op Wrapper`) + template wrap1f_math(op) {.dirty.} = proc `op Wrapper`(a: VmArgs) {.nimcall.} = setResult(a, op(getFloat(a, 0))) @@ -37,30 +42,30 @@ template wrap2f_math(op) {.dirty.} = setResult(a, op(getFloat(a, 0), getFloat(a, 1))) mathop op -template wrap1s_os(op) {.dirty.} = +template wrap0(op, modop) {.dirty.} = proc `op Wrapper`(a: VmArgs) {.nimcall.} = - setResult(a, op(getString(a, 0))) - osop op + setResult(a, op()) + modop op -template wrap1s_ospaths(op) {.dirty.} = +template wrap1s(op, modop) {.dirty.} = proc `op Wrapper`(a: VmArgs) {.nimcall.} = setResult(a, op(getString(a, 0))) - ospathsop op + modop op -template wrap2s_ospaths(op) {.dirty.} = +template wrap2s(op, modop) {.dirty.} = proc `op Wrapper`(a: VmArgs) {.nimcall.} = setResult(a, op(getString(a, 0), getString(a, 1))) - ospathsop op + modop op -template wrap1s_system(op) {.dirty.} = +template wrap1svoid(op, modop) {.dirty.} = proc `op Wrapper`(a: VmArgs) {.nimcall.} = - setResult(a, op(getString(a, 0))) - systemop op + op(getString(a, 0)) + modop op -template wrap2svoid_system(op) {.dirty.} = +template wrap2svoid(op, modop) {.dirty.} = proc `op Wrapper`(a: VmArgs) {.nimcall.} = op(getString(a, 0), getString(a, 1)) - systemop op + modop op proc getCurrentExceptionMsgWrapper(a: VmArgs) {.nimcall.} = setResult(a, if a.currentException.isNil: "" @@ -77,6 +82,9 @@ proc gorgeExWrapper(a: VmArgs) {.nimcall.} = a.currentLineInfo) setResult a, newTree(nkPar, newStrNode(nkStrLit, s), newIntNode(nkIntLit, e)) +proc getProjectPathWrapper(a: VmArgs) {.nimcall.} = + setResult a, gProjectPath + proc registerAdditionalOps*(c: PCtx) = wrap1f_math(sqrt) wrap1f_math(ln) @@ -101,13 +109,15 @@ proc registerAdditionalOps*(c: PCtx) = wrap1f_math(ceil) wrap2f_math(fmod) - wrap2s_ospaths(getEnv) - wrap1s_ospaths(existsEnv) - wrap1s_os(dirExists) - wrap1s_os(fileExists) - wrap2svoid_system(writeFile) - wrap1s_system(readFile) + wrap2s(getEnv, ospathsop) + wrap1s(existsEnv, ospathsop) + wrap2svoid(putEnv, ospathsop) + wrap1s(dirExists, osop) + wrap1s(fileExists, osop) + wrap2svoid(writeFile, systemop) + wrap1s(readFile, systemop) systemop getCurrentExceptionMsg registerCallback c, "stdlib.*.staticWalkDir", proc (a: VmArgs) {.nimcall.} = setResult(a, staticWalkDirImpl(getString(a, 0), getBool(a, 1))) systemop gorgeEx + macrosop getProjectPath diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 9edaab237..3614cf71a 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -1293,3 +1293,7 @@ macro unpackVarargs*(callee: untyped; args: varargs[untyped]): untyped = result = newCall(callee) for i in 0 ..< args.len: result.add args[i] + +proc getProjectPath*(): string = discard + ## Returns the path to the currently compiling project + diff --git a/tests/vm/tvmmisc.nim b/tests/vm/tvmmisc.nim index 42a58fa8f..472660bc2 100644 --- a/tests/vm/tvmmisc.nim +++ b/tests/vm/tvmmisc.nim @@ -1,5 +1,8 @@ # bug #4462 import macros +import os +import ospaths +import strutils block: proc foo(t: typedesc) {.compileTime.} = @@ -18,7 +21,7 @@ block: # #6379 static: import algorithm - + var numArray = [1, 2, 3, 4, -1] numArray.sort(cmp) assert numArray == [-1, 1, 2, 3, 4] @@ -57,10 +60,25 @@ block: result = @[0] result.setLen(2) var tmp: int - - for i in 0 .. <2: + + for i in 0 ..< 2: inc tmp result[i] = tmp const fact1000 = abc() assert fact1000 == @[1, 2] + +# Tests for VM ops +block: + static: + assert "vm" in getProjectPath() + + let b = getEnv("UNSETENVVAR") + assert b == "" + assert existsEnv("UNSERENVVAR") == false + putEnv("UNSETENVVAR", "VALUE") + assert getEnv("UNSETENVVAR") == "VALUE" + assert existsEnv("UNSETENVVAR") == true + + assert fileExists("MISSINGFILE") == false + assert dirExists("MISSINGDIR") == false -- cgit 1.4.1-2-gfad0 From 19a1cc914fb8a744243a9730f120f6e932134106 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 12 Apr 2018 20:00:01 +0200 Subject: deprecate macros.callsite; fixes #7369 --- changelog.md | 2 ++ lib/core/macros.nim | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'lib/core') diff --git a/changelog.md b/changelog.md index bf9020800..ce2fc08f3 100644 --- a/changelog.md +++ b/changelog.md @@ -3,6 +3,8 @@ ### Changes affecting backwards compatibility - The stdlib module ``future`` has been renamed to ``sugar``. +- ``macros.callsite`` is now deprecated. Since the introduction of ``varargs`` + parameters this became unnecessary. #### Breaking changes in the standard library diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 3614cf71a..4e935c5e8 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -361,8 +361,10 @@ proc genSym*(kind: NimSymKind = nskLet; ident = ""): NimNode {. ## generates a fresh symbol that is guaranteed to be unique. The symbol ## needs to occur in a declaration context. -proc callsite*(): NimNode {.magic: "NCallSite", benign.} +proc callsite*(): NimNode {.magic: "NCallSite", benign, + deprecated: "use varargs[untyped] in the macro prototype instead".} ## returns the AST of the invocation expression that invoked this macro. + ## **Deprecated since version 0.18.1**. proc toStrLit*(n: NimNode): NimNode {.compileTime.} = ## converts the AST `n` to the concrete Nim code and wraps that -- cgit 1.4.1-2-gfad0 From 55fe3d87a708935570ccb911e2046b99b442da0f Mon Sep 17 00:00:00 2001 From: Arne Döring Date: Fri, 13 Apr 2018 14:05:07 +0200 Subject: added comment to macros (#7598) * added comment to macros * Update macros.nim --- lib/core/macros.nim | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'lib/core') diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 4e935c5e8..3bdd29b0a 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -14,6 +14,12 @@ include "system/inclrtl" ## .. include:: ../../doc/astspec.txt +# If you look for the implementation of the magic symbols, copy the +# magic string and open the file "../../compiler/vm.nim" and search +# for the magic string with the prefix "opc". For example the +# implementation of ``{.magic: "FooBar".}`` will be right under +# ``of opcFooBar:``. + type NimNodeKind* = enum nnkNone, nnkEmpty, nnkIdent, nnkSym, -- cgit 1.4.1-2-gfad0 From 47335aab4148b2cd5b28cd3012d6d6e0a0c82db7 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 13 Apr 2018 17:06:46 +0200 Subject: introduce nkTupleConstr AST node for unary tuple construction; breaking change --- changelog.md | 4 ++++ compiler/ast.nim | 3 ++- compiler/ccgexprs.nim | 4 ++-- compiler/dfa.nim | 2 +- compiler/evalffi.nim | 6 +++--- compiler/jsgen.nim | 8 ++++---- compiler/parser.nim | 6 ++++++ compiler/pragmas.nim | 4 ++-- compiler/renderer.nim | 8 ++++++++ compiler/sem.nim | 2 +- compiler/semexprs.nim | 20 +++++++++++--------- compiler/semfold.nim | 6 +++--- compiler/semmacrosanity.nim | 2 +- compiler/semmagic.nim | 2 +- compiler/semstmts.nim | 4 ++-- compiler/semtypes.nim | 5 +++-- compiler/transf.nim | 6 +++--- compiler/trees.nim | 2 +- compiler/vm.nim | 18 +++++++++--------- compiler/vmdeps.nim | 2 +- compiler/vmgen.nim | 6 +++--- compiler/vmmarshal.nim | 2 +- compiler/vmops.nim | 4 ++-- compiler/writetracking.nim | 4 ++-- lib/core/macros.nim | 3 ++- tests/tuples/tanontuples.nim | 14 +++++++++++++- 26 files changed, 91 insertions(+), 56 deletions(-) (limited to 'lib/core') diff --git a/changelog.md b/changelog.md index ce2fc08f3..364047d05 100644 --- a/changelog.md +++ b/changelog.md @@ -5,6 +5,10 @@ - The stdlib module ``future`` has been renamed to ``sugar``. - ``macros.callsite`` is now deprecated. Since the introduction of ``varargs`` parameters this became unnecessary. +- Anonymous tuples with a single element can now be written as ``(1,)`` with a + trailing comma. The underlying AST is ``nnkTupleConst(newLit 1)`` for this + example. ``nnkTupleConstr`` is a new node kind your macros need to be able + to deal with! #### Breaking changes in the standard library diff --git a/compiler/ast.nim b/compiler/ast.nim index aa7250513..da7e828f2 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -221,7 +221,8 @@ type nkGotoState, # used for the state machine (for iterators) nkState, # give a label to a code section (for iterators) nkBreakState, # special break statement for easier code generation - nkFuncDef # a func + nkFuncDef, # a func + nkTupleConstr # a tuple constructor TNodeKinds* = set[TNodeKind] diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 5888f6430..7e3c2632a 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -2226,7 +2226,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = genSeqConstr(p, n, d) else: genArrayConstr(p, n, d) - of nkPar: + of nkPar, nkTupleConstr: if isDeepConstExpr(n) and n.len != 0: exprComplexConst(p, n, d) else: @@ -2458,7 +2458,7 @@ proc genConstExpr(p: BProc, n: PNode): Rope = var cs: TBitSet toBitSet(n, cs) result = genRawSetData(cs, int(getSize(n.typ))) - of nkBracket, nkPar, nkClosure: + of nkBracket, nkPar, nkTupleConstr, nkClosure: var t = skipTypes(n.typ, abstractInst) if t.kind == tySequence: result = genConstSeq(p, n, n.typ) diff --git a/compiler/dfa.nim b/compiler/dfa.nim index b648995f4..bc9d13870 100644 --- a/compiler/dfa.nim +++ b/compiler/dfa.nim @@ -323,7 +323,7 @@ proc gen(c: var Con; n: PNode) = of nkBreakStmt: genBreak(c, n) of nkTryStmt: genTry(c, n) of nkStmtList, nkStmtListExpr, nkChckRangeF, nkChckRange64, nkChckRange, - nkBracket, nkCurly, nkPar, nkClosure, nkObjConstr: + nkBracket, nkCurly, nkPar, nkTupleConstr, nkClosure, nkObjConstr: for x in n: gen(c, x) of nkPragmaBlock: gen(c, n.lastSon) of nkDiscardStmt: gen(c, n.sons[0]) diff --git a/compiler/evalffi.nim b/compiler/evalffi.nim index 5bf8f358a..0e3d0609d 100644 --- a/compiler/evalffi.nim +++ b/compiler/evalffi.nim @@ -151,7 +151,7 @@ proc getField(n: PNode; position: int): PSym = else: discard proc packObject(x: PNode, typ: PType, res: pointer) = - internalAssert x.kind in {nkObjConstr, nkPar} + internalAssert x.kind in {nkObjConstr, nkPar, nkTupleConstr} # compute the field's offsets: discard typ.getSize for i in countup(ord(x.kind == nkObjConstr), sonsLen(x) - 1): @@ -260,14 +260,14 @@ proc unpackObject(x: pointer, typ: PType, n: PNode): PNode = # iterate over any actual field of 'n' ... if n is nil we need to create # the nkPar node: if n.isNil: - result = newNode(nkPar) + result = newNode(nkTupleConstr) result.typ = typ if typ.n.isNil: internalError("cannot unpack unnamed tuple") unpackObjectAdd(x, typ.n, result) else: result = n - if result.kind notin {nkObjConstr, nkPar}: + if result.kind notin {nkObjConstr, nkPar, nkTupleConstr}: globalError(n.info, "cannot map value from FFI") if typ.n.isNil: globalError(n.info, "cannot unpack unnamed tuple") diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index dc74fa933..357708bb9 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -307,7 +307,7 @@ proc useMagic(p: PProc, name: string) = proc isSimpleExpr(p: PProc; n: PNode): bool = # calls all the way down --> can stay expression based - if n.kind in nkCallKinds+{nkBracketExpr, nkDotExpr, nkPar} or + if n.kind in nkCallKinds+{nkBracketExpr, nkDotExpr, nkPar, nkTupleConstr} or (p.target == targetJS and n.kind in {nkObjConstr, nkBracket, nkCurly}): for c in n: if not p.isSimpleExpr(c): return false @@ -894,7 +894,7 @@ proc countJsParams(typ: PType): int = const nodeKindsNeedNoCopy = {nkCharLit..nkInt64Lit, nkStrLit..nkTripleStrLit, - nkFloatLit..nkFloat64Lit, nkCurly, nkPar, nkObjConstr, nkStringToCString, + nkFloatLit..nkFloat64Lit, nkCurly, nkPar, nkTupleConstr, nkObjConstr, nkStringToCString, nkCStringToString, nkCall, nkPrefix, nkPostfix, nkInfix, nkCommand, nkHiddenCallConv, nkCallStrLit} @@ -1714,7 +1714,7 @@ proc genToArray(p: PProc; n: PNode; r: var TCompRes) = if x.kind == nkBracket: for i in countup(0, x.len - 1): let it = x[i] - if it.kind == nkPar and it.len == 2: + if it.kind in {nkPar, nkTupleConstr} and it.len == 2: if i > 0: r.res.add(", ") gen(p, it[0], a) gen(p, it[1], b) @@ -2309,7 +2309,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = of nkClosure: gen(p, n[0], r) of nkCurly: genSetConstr(p, n, r) of nkBracket: genArrayConstr(p, n, r) - of nkPar: genTupleConstr(p, n, r) + of nkPar, nkTupleConstr: genTupleConstr(p, n, r) of nkObjConstr: genObjConstr(p, n, r) of nkHiddenStdConv, nkHiddenSubConv, nkConv: genConv(p, n, r) of nkAddr, nkHiddenAddr: diff --git a/compiler/parser.nim b/compiler/parser.nim index c14330ec2..c8edd5136 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -399,6 +399,9 @@ proc exprColonEqExprListAux(p: var TParser, endTok: TTokType, result: PNode) = addSon(result, a) if p.tok.tokType != tkComma: break getTok(p) + # (1,) produces a tuple expression + if endTok == tkParRi and p.tok.tokType == tkParRi: + result.kind = nkTupleConstr skipComment(p, a) optPar(p) eat(p, endTok) @@ -566,6 +569,9 @@ proc parsePar(p: var TParser): PNode = if p.tok.tokType == tkComma: getTok(p) skipComment(p, a) + # (1,) produces a tuple expression: + if p.tok.tokType == tkParRi: + result.kind = nkTupleConstr # progress guaranteed while p.tok.tokType != tkParRi and p.tok.tokType != tkEof: var a = exprColonEqExpr(p) diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index d5fed7640..bb7801f6f 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -423,7 +423,7 @@ proc processCompile(c: PContext, n: PNode) = result = "" let it = if n.kind in nkPragmaCallKinds and n.len == 2: n.sons[1] else: n - if it.kind == nkPar and it.len == 2: + if it.kind in {nkPar, nkTupleConstr} and it.len == 2: let s = getStrLit(c, it, 0) let dest = getStrLit(c, it, 1) var found = parentDir(n.info.toFullPath) / s @@ -530,7 +530,7 @@ proc pragmaLine(c: PContext, n: PNode) = if n.kind in nkPragmaCallKinds and n.len == 2: n.sons[1] = c.semConstExpr(c, n.sons[1]) let a = n.sons[1] - if a.kind == nkPar: + if a.kind in {nkPar, nkTupleConstr}: # unpack the tuple var x = a.sons[0] var y = a.sons[1] diff --git a/compiler/renderer.nim b/compiler/renderer.nim index 0b1b0479f..7d513afb1 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -437,6 +437,9 @@ proc lsub(g: TSrcGen; n: PNode): int = of nkCommand: result = lsub(g, n.sons[0]) + lcomma(g, n, 1) + 1 of nkExprEqExpr, nkAsgn, nkFastAsgn: result = lsons(g, n) + 3 of nkPar, nkCurly, nkBracket, nkClosure: result = lcomma(g, n) + 2 + of nkTupleConstr: + # assume the trailing comma: + result = lcomma(g, n) + 3 of nkArgList: result = lcomma(g, n) of nkTableConstr: result = if n.len > 0: lcomma(g, n) + 2 else: len("{:}") @@ -1007,6 +1010,11 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = put(g, tkParLe, "(") gcomma(g, n, c) put(g, tkParRi, ")") + of nkTupleConstr: + put(g, tkParLe, "(") + gcomma(g, n, c) + if n.len == 1: put(g, tkComma, ",") + put(g, tkParRi, ")") of nkCurly: put(g, tkCurlyLe, "{") gcomma(g, n, c) diff --git a/compiler/sem.nim b/compiler/sem.nim index 937f1637a..4fef1bc60 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -85,7 +85,7 @@ proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode = result.typ = formal else: let x = result.skipConv - if x.kind == nkPar and formal.kind != tyExpr: + if x.kind in {nkPar, nkTupleConstr} and formal.kind != tyExpr: changeType(x, formal, check=true) else: result = skipHiddenSubConv(result) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index a523bfc9e..4256e0aa6 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -215,7 +215,7 @@ proc semConv(c: PContext, n: PNode): PNode = # handle SomeProcType(SomeGenericProc) if op.kind == nkSym and op.sym.isGenericRoutine: result.sons[1] = fitNode(c, result.typ, result.sons[1], result.info) - elif op.kind == nkPar and targetType.kind == tyTuple: + elif op.kind in {nkPar, nkTupleConstr} and targetType.kind == tyTuple: op = fitNode(c, targetType, op, result.info) of convNotNeedeed: message(n.info, hintConvFromXtoItselfNotNeeded, result.typ.typeToString) @@ -364,7 +364,7 @@ proc changeType(n: PNode, newType: PType, check: bool) = of nkCurly, nkBracket: for i in countup(0, sonsLen(n) - 1): changeType(n.sons[i], elemType(newType), check) - of nkPar: + of nkPar, nkTupleConstr: let tup = newType.skipTypes({tyGenericInst, tyAlias, tySink}) if tup.kind != tyTuple: if tup.kind == tyObject: return @@ -1402,7 +1402,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode = result = buildOverloadedSubscripts(n.sons[0], getIdent"{}=") add(result, n[1]) return semExprNoType(c, result) - of nkPar: + of nkPar, nkTupleConstr: if a.len >= 2: # unfortunately we need to rewrite ``(x, y) = foo()`` already here so # that overloading of the assignment operator still works. Usually we @@ -1521,10 +1521,10 @@ proc semYieldVarResult(c: PContext, n: PNode, restype: PType) = var e = skipTypes(t.sons[i], {tyGenericInst, tyAlias, tySink}) if e.kind in {tyVar, tyLent}: if e.kind == tyVar: e.flags.incl tfVarIsPtr # bugfix for #4048, #4910, #6892 - if n.sons[0].kind == nkPar: + if n.sons[0].kind in {nkPar, nkTupleConstr}: n.sons[0].sons[i] = takeImplicitAddr(c, n.sons[0].sons[i], e.kind == tyLent) elif n.sons[0].kind in {nkHiddenStdConv, nkHiddenSubConv} and - n.sons[0].sons[1].kind == nkPar: + n.sons[0].sons[1].kind in {nkPar, nkTupleConstr}: var a = n.sons[0].sons[1] a.sons[i] = takeImplicitAddr(c, a.sons[i], false) else: @@ -2047,12 +2047,12 @@ proc semTableConstr(c: PContext, n: PNode): PNode = var x = n.sons[i] if x.kind == nkExprColonExpr and sonsLen(x) == 2: for j in countup(lastKey, i-1): - var pair = newNodeI(nkPar, x.info) + var pair = newNodeI(nkTupleConstr, x.info) pair.add(n.sons[j]) pair.add(x[1]) result.add(pair) - var pair = newNodeI(nkPar, x.info) + var pair = newNodeI(nkTupleConstr, x.info) pair.add(x[0]) pair.add(x[1]) result.add(pair) @@ -2072,6 +2072,7 @@ proc checkPar(n: PNode): TParKind = result = paTuplePositions # () elif length == 1: if n.sons[0].kind == nkExprColonExpr: result = paTupleFields + elif n.kind == nkTupleConstr: result = paTuplePositions else: result = paSingle # (expr) else: if n.sons[0].kind == nkExprColonExpr: result = paTupleFields @@ -2088,7 +2089,7 @@ proc checkPar(n: PNode): TParKind = return paNone proc semTupleFieldsConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = - result = newNodeI(nkPar, n.info) + result = newNodeI(nkTupleConstr, n.info) var typ = newTypeS(tyTuple, c) typ.n = newNodeI(nkRecList, n.info) # nkIdentDefs var ids = initIntSet() @@ -2113,6 +2114,7 @@ proc semTupleFieldsConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = proc semTuplePositionsConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = result = n # we don't modify n, but compute the type: + result.kind = nkTupleConstr var typ = newTypeS(tyTuple, c) # leave typ.n nil! for i in countup(0, sonsLen(n) - 1): n.sons[i] = semExprWithType(c, n.sons[i], flags*{efAllowDestructor}) @@ -2344,7 +2346,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = invalidPragma(n) result = semExpr(c, n[0], flags) - of nkPar: + of nkPar, nkTupleConstr: case checkPar(n) of paNone: result = errorNode(c, n) of paTuplePositions: diff --git a/compiler/semfold.nim b/compiler/semfold.nim index 62bab4edb..096fc19e0 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -427,7 +427,7 @@ proc foldArrayAccess(m: PSym, n: PNode): PNode = var idx = getOrdValue(y) case x.kind - of nkPar: + of nkPar, nkTupleConstr: if idx >= 0 and idx < sonsLen(x): result = x.sons[int(idx)] if result.kind == nkExprColonExpr: result = result.sons[1] @@ -450,7 +450,7 @@ proc foldArrayAccess(m: PSym, n: PNode): PNode = proc foldFieldAccess(m: PSym, n: PNode): PNode = # a real field access; proc calls have already been transformed var x = getConstExpr(m, n.sons[0]) - if x == nil or x.kind notin {nkObjConstr, nkPar}: return + if x == nil or x.kind notin {nkObjConstr, nkPar, nkTupleConstr}: return var field = n.sons[1].sym for i in countup(ord(x.kind == nkObjConstr), sonsLen(x) - 1): @@ -624,7 +624,7 @@ proc getConstExpr(m: PSym, n: PNode): PNode = # if a == nil: return nil # result.sons[i].sons[1] = a # incl(result.flags, nfAllConst) - of nkPar: + of nkPar, nkTupleConstr: # tuple constructor result = copyTree(n) if (sonsLen(n) > 0) and (n.sons[0].kind == nkExprColonExpr): diff --git a/compiler/semmacrosanity.nim b/compiler/semmacrosanity.nim index fe9bb6c8d..f6df67441 100644 --- a/compiler/semmacrosanity.nim +++ b/compiler/semmacrosanity.nim @@ -50,7 +50,7 @@ proc annotateType*(n: PNode, t: PType) = else: internalAssert(n.sons[i].kind == nkExprColonExpr) annotateType(n.sons[i].sons[1], field.typ) - of nkPar: + of nkPar, nkTupleConstr: if x.kind == tyTuple: n.typ = t for i in 0 ..< n.len: diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index 9031e4640..3f0df0065 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -73,7 +73,7 @@ proc expectIntLit(c: PContext, n: PNode): int = else: localError(n.info, errIntLiteralExpected) proc semInstantiationInfo(c: PContext, n: PNode): PNode = - result = newNodeIT(nkPar, n.info, n.typ) + result = newNodeIT(nkTupleConstr, n.info, n.typ) let idx = expectIntLit(c, n.sons[1]) let useFullPaths = expectIntLit(c, n.sons[2]) let info = getInfoContext(idx) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index c53ff9803..3de26344c 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -552,7 +552,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = b.sons[length-2] = a.sons[length-2] # keep type desc for doc generator b.sons[length-1] = def addToVarSection(c, result, n, b) - elif tup.kind == tyTuple and def.kind == nkPar and + elif tup.kind == tyTuple and def.kind in {nkPar, nkTupleConstr} and a.kind == nkIdentDefs and a.len > 3: message(a.info, warnEachIdentIsTuple) @@ -592,7 +592,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = addSon(b, copyTree(def)) addToVarSection(c, result, n, b) else: - if def.kind == nkPar: v.ast = def[j] + if def.kind in {nkPar, nkTupleConstr}: v.ast = def[j] setVarType(v, tup.sons[j]) b.sons[j] = newSymNode(v) checkNilable(v) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index a23ee01e1..1fc263617 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -392,8 +392,8 @@ proc semAnonTuple(c: PContext, n: PNode, prev: PType): PType = if sonsLen(n) == 0: localError(n.info, errTypeExpected) result = newOrPrevType(tyTuple, prev, c) - for i in countup(0, sonsLen(n) - 1): - addSonSkipIntLit(result, semTypeNode(c, n.sons[i], nil)) + for it in n: + addSonSkipIntLit(result, semTypeNode(c, it, nil)) proc semTuple(c: PContext, n: PNode, prev: PType): PType = var typ: PType @@ -1341,6 +1341,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = if sonsLen(n) == 1: result = semTypeNode(c, n.sons[0], prev) else: result = semAnonTuple(c, n, prev) + of nkTupleConstr: result = semAnonTuple(c, n, prev) of nkCallKinds: let x = n[0] let ident = case x.kind diff --git a/compiler/transf.nim b/compiler/transf.nim index e6dc69b38..f30f8583a 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -334,7 +334,7 @@ proc transformYield(c: PTransf, n: PNode): PTransNode = if skipTypes(e.typ, {tyGenericInst, tyAlias, tySink}).kind == tyTuple and c.transCon.forStmt.len != 3: e = skipConv(e) - if e.kind == nkPar: + if e.kind in {nkPar, nkTupleConstr}: for i in countup(0, sonsLen(e) - 1): var v = e.sons[i] if v.kind == nkExprColonExpr: v = v.sons[1] @@ -500,7 +500,7 @@ proc putArgInto(arg: PNode, formal: PType): TPutArgInto = case arg.kind of nkEmpty..nkNilLit: result = paDirectMapping - of nkPar, nkCurly, nkBracket: + of nkPar, nkTupleConstr, nkCurly, nkBracket: result = paFastAsgn for i in countup(0, sonsLen(arg) - 1): if putArgInto(arg.sons[i], formal) != paDirectMapping: return @@ -745,7 +745,7 @@ proc transformExceptBranch(c: PTransf, n: PNode): PTransNode = proc dontInlineConstant(orig, cnst: PNode): bool {.inline.} = # symbols that expand to a complex constant (array, etc.) should not be # inlined, unless it's the empty array: - result = orig.kind == nkSym and cnst.kind in {nkCurly, nkPar, nkBracket} and + result = orig.kind == nkSym and cnst.kind in {nkCurly, nkPar, nkTupleConstr, nkBracket} and cnst.len != 0 proc commonOptimizations*(c: PSym, n: PNode): PNode = diff --git a/compiler/trees.nim b/compiler/trees.nim index f69108942..fb523de9d 100644 --- a/compiler/trees.nim +++ b/compiler/trees.nim @@ -97,7 +97,7 @@ proc isDeepConstExpr*(n: PNode): bool = result = true of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv: result = isDeepConstExpr(n.sons[1]) - of nkCurly, nkBracket, nkPar, nkObjConstr, nkClosure, nkRange: + of nkCurly, nkBracket, nkPar, nkTupleConstr, nkObjConstr, nkClosure, nkRange: for i in ord(n.kind == nkObjConstr) ..< n.len: if not isDeepConstExpr(n.sons[i]): return false if n.typ.isNil: result = true diff --git a/compiler/vm.nim b/compiler/vm.nim index 33c17eff4..7e2a171a2 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -418,14 +418,14 @@ proc setLenSeq(c: PCtx; node: PNode; newLen: int; info: TLineInfo) = let typ = node.typ.skipTypes(abstractInst+{tyRange}-{tyTypeDesc}) let typeEntry = typ.sons[0].skipTypes(abstractInst+{tyRange}-{tyTypeDesc}) let typeKind = case typeEntry.kind - of tyUInt..tyUInt64: nkUIntLit - of tyRange, tyEnum, tyBool, tyChar, tyInt..tyInt64: nkIntLit - of tyFloat..tyFloat128: nkFloatLit - of tyString: nkStrLit - of tyObject: nkObjConstr - of tySequence: nkNilLit - of tyProc, tyTuple: nkPar - else: nkEmpty + of tyUInt..tyUInt64: nkUIntLit + of tyRange, tyEnum, tyBool, tyChar, tyInt..tyInt64: nkIntLit + of tyFloat..tyFloat128: nkFloatLit + of tyString: nkStrLit + of tyObject: nkObjConstr + of tySequence: nkNilLit + of tyProc, tyTuple: nkTupleConstr + else: nkEmpty let oldLen = node.len setLen(node.sons, newLen) @@ -939,7 +939,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = let rb = instr.regB let rc = instr.regC let bb = regs[rb].node - let isClosure = bb.kind == nkPar + let isClosure = bb.kind == nkTupleConstr let prc = if not isClosure: bb.sym else: bb.sons[0].sym if prc.offset < -1: # it's a callback: diff --git a/compiler/vmdeps.nim b/compiler/vmdeps.nim index bb6c47324..071cc7706 100644 --- a/compiler/vmdeps.nim +++ b/compiler/vmdeps.nim @@ -186,7 +186,7 @@ proc mapTypeToAstX(t: PType; info: TLineInfo; if inst: # only named tuples have a node, unnamed tuples don't if t.n.isNil: - result = newNodeX(nkPar) + result = newNodeX(nkTupleConstr) for subType in t.sons: result.add mapTypeToAst(subType, info) else: diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index a8ecfd4ae..c3eaf8946 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -1560,7 +1560,7 @@ proc getNullValue(typ: PType, info: TLineInfo): PNode = if t.callConv != ccClosure: result = newNodeIT(nkNilLit, info, t) else: - result = newNodeIT(nkPar, info, t) + result = newNodeIT(nkTupleConstr, info, t) result.add(newNodeIT(nkNilLit, info, t)) result.add(newNodeIT(nkNilLit, info, t)) of tyObject: @@ -1577,7 +1577,7 @@ proc getNullValue(typ: PType, info: TLineInfo): PNode = for i in countup(0, int(lengthOrd(t)) - 1): addSon(result, getNullValue(elemType(t), info)) of tyTuple: - result = newNodeIT(nkPar, info, t) + result = newNodeIT(nkTupleConstr, info, t) for i in countup(0, sonsLen(t) - 1): addSon(result, getNullValue(t.sons[i], info)) of tySet: @@ -1884,7 +1884,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = of nkBracket: genArrayConstr(c, n, dest) of nkCurly: genSetConstr(c, n, dest) of nkObjConstr: genObjConstr(c, n, dest) - of nkPar, nkClosure: genTupleConstr(c, n, dest) + of nkPar, nkClosure, nkTupleConstr: genTupleConstr(c, n, dest) of nkCast: if allowCast in c.features: genConv(c, n, n.sons[1], dest, opcCast) diff --git a/compiler/vmmarshal.nim b/compiler/vmmarshal.nim index 5f725994e..d76909443 100644 --- a/compiler/vmmarshal.nim +++ b/compiler/vmmarshal.nim @@ -190,7 +190,7 @@ proc loadAny(p: var JsonParser, t: PType, of tyTuple: if p.kind != jsonObjectStart: raiseParseErr(p, "'{' expected for an object") next(p) - result = newNode(nkPar) + result = newNode(nkTupleConstr) var i = 0 while p.kind != jsonObjectEnd and p.kind != jsonEof: if p.kind != jsonString: diff --git a/compiler/vmops.nim b/compiler/vmops.nim index f7debe2df..7f8bf06c1 100644 --- a/compiler/vmops.nim +++ b/compiler/vmops.nim @@ -74,13 +74,13 @@ proc getCurrentExceptionMsgWrapper(a: VmArgs) {.nimcall.} = proc staticWalkDirImpl(path: string, relative: bool): PNode = result = newNode(nkBracket) for k, f in walkDir(path, relative): - result.add newTree(nkPar, newIntNode(nkIntLit, k.ord), + result.add newTree(nkTupleConstr, newIntNode(nkIntLit, k.ord), newStrNode(nkStrLit, f)) proc gorgeExWrapper(a: VmArgs) {.nimcall.} = let (s, e) = opGorge(getString(a, 0), getString(a, 1), getString(a, 2), a.currentLineInfo) - setResult a, newTree(nkPar, newStrNode(nkStrLit, s), newIntNode(nkIntLit, e)) + setResult a, newTree(nkTupleConstr, newStrNode(nkStrLit, s), newIntNode(nkIntLit, e)) proc getProjectPathWrapper(a: VmArgs) {.nimcall.} = setResult a, gProjectPath diff --git a/compiler/writetracking.nim b/compiler/writetracking.nim index 577db613d..e03d6fb59 100644 --- a/compiler/writetracking.nim +++ b/compiler/writetracking.nim @@ -120,7 +120,7 @@ proc returnsNewExpr*(n: PNode): NewLocation = nkStmtList, nkStmtListExpr, nkBlockStmt, nkBlockExpr, nkOfBranch, nkElifBranch, nkElse, nkExceptBranch, nkFinally, nkCast: result = returnsNewExpr(n.lastSon) - of nkCurly, nkBracket, nkPar, nkObjConstr, nkClosure, + of nkCurly, nkBracket, nkPar, nkTupleConstr, nkObjConstr, nkClosure, nkIfExpr, nkIfStmt, nkWhenStmt, nkCaseStmt, nkTryStmt: result = newLit for i in ord(n.kind == nkObjConstr) ..< n.len: @@ -179,7 +179,7 @@ proc deps(w: var W; n: PNode) = for child in n: let last = lastSon(child) if last.kind == nkEmpty: continue - if child.kind == nkVarTuple and last.kind == nkPar: + if child.kind == nkVarTuple and last.kind in {nkPar, nkTupleConstr}: internalAssert child.len-2 == last.len for i in 0 .. child.len-3: deps(w, child.sons[i], last.sons[i], {}) diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 3bdd29b0a..a4c819a34 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -82,7 +82,8 @@ type nnkGotoState, nnkState, nnkBreakState, - nnkFuncDef + nnkFuncDef, + nnkTupleConstr NimNodeKinds* = set[NimNodeKind] NimTypeKind* = enum # some types are no longer used, see ast.nim diff --git a/tests/tuples/tanontuples.nim b/tests/tuples/tanontuples.nim index 49803e5ac..f514670d3 100644 --- a/tests/tuples/tanontuples.nim +++ b/tests/tuples/tanontuples.nim @@ -1,7 +1,10 @@ discard """ - output: '''61, 125''' + output: '''61, 125 +(Field0: 0) (Field0: 13)''' """ +import macros + proc `^` (a, b: int): int = result = 1 for i in 1..b: result = result * a @@ -12,3 +15,12 @@ var n = (56, 3) m = (n[0] + m[1], m[1] ^ n[1]) echo m[0], ", ", m[1] + +# also test we can produce unary anon tuples in a macro: +macro mm(): untyped = + result = newTree(nnkTupleConstr, newLit(13)) + +proc nowTuple(): (int,) = + result = (0,) + +echo nowTuple(), " ", mm() -- cgit 1.4.1-2-gfad0 From 3d1d163efff446bd420a150114b7914c7b34798e Mon Sep 17 00:00:00 2001 From: RSDuck Date: Sat, 14 Apr 2018 08:33:36 +0200 Subject: fixes #7451 (#7575) --- changelog.md | 3 ++ compiler/semstmts.nim | 21 ++++++-- lib/core/macros.nim | 85 ++++++++++++++++++++++++-------- tests/pragmas/tcustom_pragma.nim | 101 ++++++++++++++++++++++++++++++++++----- 4 files changed, 174 insertions(+), 36 deletions(-) (limited to 'lib/core') diff --git a/changelog.md b/changelog.md index 60e993359..7569bf133 100644 --- a/changelog.md +++ b/changelog.md @@ -41,6 +41,9 @@ now escapes the content of string literals consistently. - ``macros.NimSym`` and ``macros.NimIdent`` is now deprecated in favor of the more general ``NimNode``. +- ``macros.getImpl`` now includes the pragmas of types, instead of omitting them. +- ``macros.hasCustomPragma`` and ``macros.getCustomPragmaVal`` now + also support ``ref`` and ``ptr`` types, pragmas on types and variant fields. ### Language additions diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 3de26344c..85fe3d793 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -765,6 +765,15 @@ proc addGenericParamListToScope(c: PContext, n: PNode) = if a.kind == nkSym: addDecl(c, a.sym) else: illFormedAst(a) +proc typeSectionTypeName(n: PNode): PNode = + if n.kind == nkPragmaExpr: + if n.len == 0: illFormedAst(n) + result = n.sons[0] + else: + result = n + if result.kind != nkSym: illFormedAst(n) + + proc typeSectionLeftSidePass(c: PContext, n: PNode) = # process the symbols on the left side for the whole type section, before # we even look at the type definitions on the right @@ -825,7 +834,10 @@ proc typeSectionLeftSidePass(c: PContext, n: PNode) = # add it here, so that recursive types are possible: if sfGenSym notin s.flags: addInterfaceDecl(c, s) - a.sons[0] = newSymNode(s) + if name.kind == nkPragmaExpr: + a.sons[0].sons[0] = newSymNode(s) + else: + a.sons[0] = newSymNode(s) proc checkCovariantParamsUsages(genericType: PType) = var body = genericType[^1] @@ -914,8 +926,7 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) = if a.kind == nkCommentStmt: continue if (a.kind != nkTypeDef): illFormedAst(a) checkSonsLen(a, 3) - let name = a.sons[0] - if (name.kind != nkSym): illFormedAst(a) + let name = typeSectionTypeName(a.sons[0]) var s = name.sym if s.magic == mNone and a.sons[2].kind == nkEmpty: localError(a.info, errImplOfXexpected, s.name.s) @@ -1021,8 +1032,8 @@ proc typeSectionFinalPass(c: PContext, n: PNode) = for i in countup(0, sonsLen(n) - 1): var a = n.sons[i] if a.kind == nkCommentStmt: continue - if a.sons[0].kind != nkSym: illFormedAst(a) - var s = a.sons[0].sym + let name = typeSectionTypeName(a.sons[0]) + var s = name.sym # compute the type's size and check for illegal recursions: if a.sons[1].kind == nkEmpty: var x = a[2] diff --git a/lib/core/macros.nim b/lib/core/macros.nim index a4c819a34..e71b7cdc8 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -1232,33 +1232,73 @@ macro expandMacros*(body: typed): untyped = echo result.toStrLit proc customPragmaNode(n: NimNode): NimNode = - expectKind(n, {nnkSym, nnkDotExpr}) - if n.kind == nnkSym: - let sym = n.getImpl() - sym.expectRoutine() - result = sym.pragma - elif n.kind == nnkDotExpr: - let typDef = getImpl(getTypeInst(n[0])) - typDef.expectKind(nnkTypeDef) - typDef[2].expectKind(nnkObjectTy) - let recList = typDef[2][2] - for identDefs in recList: - for i in 0 .. identDefs.len - 3: - if identDefs[i].kind == nnkPragmaExpr and - identDefs[i][0].kind == nnkIdent and $identDefs[i][0] == $n[1]: - return identDefs[i][1] + expectKind(n, {nnkSym, nnkDotExpr, nnkBracketExpr, nnkTypeOfExpr, nnkCheckedFieldExpr}) + let + typ = n.getTypeInst() + + if typ.typeKind == ntyTypeDesc: + return typ[1].getImpl()[0][1] + + if n.kind == nnkSym: # either an variable or a proc + let impl = n.getImpl() + if impl.kind in RoutineNodes: + return impl.pragma + else: + return typ.getImpl()[0][1] + + if n.kind in {nnkDotExpr, nnkCheckedFieldExpr}: + let name = (if n.kind == nnkCheckedFieldExpr: n[0][1] else: n[1]) + var typDef = getImpl(getTypeInst(if n.kind == nnkCheckedFieldExpr or n[0].kind == nnkHiddenDeref: n[0][0] else: n[0])) + while typDef != nil: + typDef.expectKind(nnkTypeDef) + typDef[2].expectKind({nnkRefTy, nnkPtrTy, nnkObjectTy}) + let isRef = typDef[2].kind in {nnkRefTy, nnkPtrTy} + if isRef and typDef[2][0].kind in {nnkSym, nnkBracketExpr}: # defines ref type for another object(e.g. X = ref X) + typDef = getImpl(typDef[2][0]) + else: # object definition, maybe an object directly defined as a ref type + let + obj = (if isRef: typDef[2][0] else: typDef[2]) + var identDefsStack = newSeq[NimNode](obj[2].len) + for i in 0.. 0: + var identDefs = identDefsStack.pop() + if identDefs.kind == nnkRecCase: + identDefsStack.add(identDefs[0]) + for i in 1.. 0 and p[0].kind == nnkSym and p[0] == cp: return p[1] - return newEmptyNode() + + error(n.repr & " doesn't have a pragma named " & cp.repr()) # returning an empty node results in most cases in a cryptic error, when not defined(booting): diff --git a/tests/pragmas/tcustom_pragma.nim b/tests/pragmas/tcustom_pragma.nim index 415ae6a32..28a8713ce 100644 --- a/tests/pragmas/tcustom_pragma.nim +++ b/tests/pragmas/tcustom_pragma.nim @@ -1,12 +1,12 @@ import macros - + block: template myAttr() {.pragma.} proc myProc():int {.myAttr.} = 2 - const myAttrIdx = myProc.hasCustomPragma(myAttr) - static: - assert(myAttrIdx) + const hasMyAttr = myProc.hasCustomPragma(myAttr) + static: + assert(hasMyAttr) block: template myAttr(a: string) {.pragma.} @@ -14,14 +14,14 @@ block: type MyObj = object myField1, myField2 {.myAttr: "hi".}: int var o: MyObj - static: + static: assert o.myField2.hasCustomPragma(myAttr) assert(not o.myField1.hasCustomPragma(myAttr)) -import custom_pragma +import custom_pragma block: # A bit more advanced case - type - Subfield = object + type + Subfield {.defaultValue: "catman".} = object c {.serializationKey: "cc".}: float MySerializable = object @@ -29,10 +29,9 @@ block: # A bit more advanced case b {.custom_pragma.defaultValue"hello".} : int field: Subfield d {.alternativeKey("df", 5).}: float - e {.alternativeKey(V = 5).}: seq[bool] - + e {.alternativeKey(V = 5).}: seq[bool] - proc myproc(x: int, s: string) {.alternativeKey(V = 5), serializationKey"myprocSS".} = + proc myproc(x: int, s: string) {.alternativeKey(V = 5), serializationKey"myprocSS".} = echo x, s @@ -51,3 +50,83 @@ block: # A bit more advanced case static: assert(procSerKey == "myprocSS") static: assert(hasCustomPragma(myproc, alternativeKey)) + + # pragma on an object + static: + assert Subfield.hasCustomPragma(defaultValue) + assert(Subfield.getCustomPragmaVal(defaultValue) == "catman") + + assert hasCustomPragma(type(s.field), defaultValue) + +block: # ref types + type + Node = object of RootObj + left {.serializationKey:"l".}, right {.serializationKey:"r".}: NodeRef + NodeRef = ref Node + NodePtr = ptr Node + + SpecialNodeRef = ref object of NodeRef + data {.defaultValue"none".}: string + + MyFile {.defaultValue: "closed".} = ref object + path {.defaultValue: "invalid".}: string + + var s = NodeRef() + + const + leftSerKey = getCustomPragmaVal(s.left, serializationKey) + rightSerKey = getCustomPragmaVal(s.right, serializationKey) + static: + assert leftSerKey == "l" + assert rightSerKey == "r" + + var specS = SpecialNodeRef() + + const + dataDefVal = hasCustomPragma(specS.data, defaultValue) + specLeftSerKey = hasCustomPragma(specS.left, serializationKey) + static: + assert dataDefVal == true + assert specLeftSerKey == true + + var ptrS = NodePtr(nil) + const + ptrRightSerKey = getCustomPragmaVal(s.right, serializationKey) + static: + assert ptrRightSerKey == "r" + + var f = MyFile() + const + fileDefVal = f.getCustomPragmaVal(defaultValue) + filePathDefVal = f.path.getCustomPragmaVal(defaultValue) + static: + assert fileDefVal == "closed" + assert filePathDefVal == "invalid" + +block: + type + VariantKind = enum + variInt, + variFloat + variString + variNestedCase + Variant = object + case kind: VariantKind + of variInt: integer {.serializationKey: "int".}: BiggestInt + of variFloat: floatp: BiggestFloat + of variString: str {.serializationKey: "string".}: string + of variNestedCase: + case nestedKind: VariantKind + of variInt..variNestedCase: nestedItem {.defaultValue: "Nimmers of the world, unite!".}: int + + let vari = Variant(kind: variInt) + + const + hasIntSerKey = vari.integer.hasCustomPragma(serializationKey) + strSerKey = vari.str.getCustomPragmaVal(serializationKey) + nestedItemDefVal = vari.nestedItem.getCustomPragmaVal(defaultValue) + + static: + assert hasIntSerKey + assert strSerKey == "string" + assert nestedItemDefVal == "Nimmers of the world, unite!" \ No newline at end of file -- cgit 1.4.1-2-gfad0 From 2478be3f21c244ff67943a963d9c4bc0337cb437 Mon Sep 17 00:00:00 2001 From: jcosborn Date: Sun, 15 Apr 2018 06:51:16 -0500 Subject: add example to docs for getTypeInst and getTypeImpl (#7206) * add example to docs for getTypeInst and getTypeImpl * made examples use runnableExamples * changed assert to doAssert --- lib/core/macros.nim | 54 ++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 45 insertions(+), 9 deletions(-) (limited to 'lib/core') diff --git a/lib/core/macros.nim b/lib/core/macros.nim index e71b7cdc8..e76e9241f 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -258,19 +258,55 @@ proc getType*(n: typedesc): NimNode {.magic: "NGetType", noSideEffect.} proc typeKind*(n: NimNode): NimTypeKind {.magic: "NGetType", noSideEffect.} ## Returns the type kind of the node 'n' that should represent a type, that - ## means the node should have been obtained via `getType`. - -proc getTypeInst*(n: NimNode): NimNode {.magic: "NGetType", noSideEffect.} - ## Like getType except it includes generic parameters for a specific instance + ## means the node should have been obtained via ``getType``. + +proc getTypeInst*(n: NimNode): NimNode {.magic: "NGetType", noSideEffect.} = + ## Returns the `type`:idx: of a node in a form matching the way the + ## type instance was declared in the code. + runnableExamples: + type + Vec[N: static[int], T] = object + arr: array[N, T] + Vec4[T] = Vec[4, T] + Vec4f = Vec4[float32] + var a: Vec4f + var b: Vec4[float32] + var c: Vec[4, float32] + macro dumpTypeInst(x: typed): untyped = + newLit(x.getTypeInst.repr) + doAssert(dumpTypeInst(a) == "Vec4f") + doAssert(dumpTypeInst(b) == "Vec4[float32]") + doAssert(dumpTypeInst(c) == "Vec[4, float32]") proc getTypeInst*(n: typedesc): NimNode {.magic: "NGetType", noSideEffect.} - ## Like getType except it includes generic parameters for a specific instance - -proc getTypeImpl*(n: NimNode): NimNode {.magic: "NGetType", noSideEffect.} - ## Like getType except it includes generic parameters for the implementation + ## Version of ``getTypeInst`` which takes a ``typedesc``. + +proc getTypeImpl*(n: NimNode): NimNode {.magic: "NGetType", noSideEffect.} = + ## Returns the `type`:idx: of a node in a form matching the implementation + ## of the type. Any intermediate aliases are expanded to arrive at the final + ## type implementation. You can instead use ``getImpl`` on a symbol if you + ## want to find the intermediate aliases. + runnableExamples: + type + Vec[N: static[int], T] = object + arr: array[N, T] + Vec4[T] = Vec[4, T] + Vec4f = Vec4[float32] + var a: Vec4f + var b: Vec4[float32] + var c: Vec[4, float32] + macro dumpTypeImpl(x: typed): untyped = + newLit(x.getTypeImpl.repr) + let t = """ +object + arr: array[0 .. 3, float32] +""" + doAssert(dumpTypeImpl(a) == t) + doAssert(dumpTypeImpl(b) == t) + doAssert(dumpTypeImpl(c) == t) proc getTypeImpl*(n: typedesc): NimNode {.magic: "NGetType", noSideEffect.} - ## Like getType except it includes generic parameters for the implementation + ## Version of ``getTypeImpl`` which takes a ``typedesc``. proc `intVal=`*(n: NimNode, val: BiggestInt) {.magic: "NSetIntVal", noSideEffect.} proc `floatVal=`*(n: NimNode, val: BiggestFloat) {.magic: "NSetFloatVal", noSideEffect.} -- cgit 1.4.1-2-gfad0 From ed5b7cbac004551d77db889eca044fdda6f75790 Mon Sep 17 00:00:00 2001 From: Arne Döring Date: Sun, 15 Apr 2018 23:38:43 +0200 Subject: move eqIdent to vm.nim (#7585) * Strutils comment changes. * fix typo --- compiler/condsyms.nim | 1 + compiler/idents.nim | 2 +- compiler/vm.nim | 32 +++++++++++++++++-- lib/core/macros.nim | 83 +++++++++++++++++++++++++++++------------------- lib/pure/cstrutils.nim | 6 ++-- lib/pure/strutils.nim | 11 ++++--- tests/macros/tmacro1.nim | 42 ++++++++++++++++++++++++ 7 files changed, 134 insertions(+), 43 deletions(-) (limited to 'lib/core') diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index 85ebdd7bf..028aedb5b 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -114,3 +114,4 @@ proc initDefines*() = defineSymbol("nimNewDot") defineSymbol("nimHasNilChecks") defineSymbol("nimSymKind") + defineSymbol("nimVmEqIdent") diff --git a/compiler/idents.nim b/compiler/idents.nim index 2cce4710e..34cf5bf1e 100644 --- a/compiler/idents.nim +++ b/compiler/idents.nim @@ -37,7 +37,7 @@ proc resetIdentCache*() = for i in low(legacy.buckets)..high(legacy.buckets): legacy.buckets[i] = nil -proc cmpIgnoreStyle(a, b: cstring, blen: int): int = +proc cmpIgnoreStyle*(a, b: cstring, blen: int): int = if a[0] != b[0]: return 1 var i = 0 var j = 0 diff --git a/compiler/vm.nim b/compiler/vm.nim index 7e2a171a2..09226988a 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -1392,10 +1392,36 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = regs[ra].node.typ = n.typ of opcEqIdent: decodeBC(rkInt) - if regs[rb].node.kind == nkIdent and regs[rc].node.kind == nkIdent: - regs[ra].intVal = ord(regs[rb].node.ident.id == regs[rc].node.ident.id) + # aliases for shorter and easier to understand code below + let aNode = regs[rb].node + let bNode = regs[rc].node + # these are cstring to prevent string copy, and cmpIgnoreStyle from + # takes cstring arguments + var aStrVal: cstring + var bStrVal: cstring + # extract strVal from argument ``a`` + case aNode.kind + of {nkStrLit..nkTripleStrLit}: + aStrVal = aNode.strVal.cstring + of nkIdent: + aStrVal = aNode.ident.s.cstring + of nkSym: + aStrVal = aNode.sym.name.s.cstring else: - regs[ra].intVal = 0 + stackTrace(c, tos, pc, errFieldXNotFound, "strVal") + # extract strVal from argument ``b`` + case bNode.kind + of {nkStrLit..nkTripleStrLit}: + bStrVal = bNode.strVal.cstring + of nkIdent: + bStrVal = bNode.ident.s.cstring + of nkSym: + bStrVal = bNode.sym.name.s.cstring + else: + stackTrace(c, tos, pc, errFieldXNotFound, "strVal") + # set result + regs[ra].intVal = + ord(idents.cmpIgnoreStyle(aStrVal,bStrVal,high(int)) == 0) of opcStrToIdent: decodeB(rkNode) if regs[rb].node.kind notin {nkStrLit..nkTripleStrLit}: diff --git a/lib/core/macros.nim b/lib/core/macros.nim index e76e9241f..7217475c6 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -1186,38 +1186,57 @@ proc copy*(node: NimNode): NimNode {.compileTime.} = ## An alias for copyNimTree(). return node.copyNimTree() -proc cmpIgnoreStyle(a, b: cstring): int {.noSideEffect.} = - proc toLower(c: char): char {.inline.} = - if c in {'A'..'Z'}: result = chr(ord(c) + (ord('a') - ord('A'))) - else: result = c - var i = 0 - var j = 0 - # first char is case sensitive - if a[0] != b[0]: return 1 - while true: - while a[i] == '_': inc(i) - while b[j] == '_': inc(j) # BUGFIX: typo - var aa = toLower(a[i]) - var bb = toLower(b[j]) - result = ord(aa) - ord(bb) - if result != 0 or aa == '\0': break - inc(i) - inc(j) - -proc eqIdent*(a, b: string): bool = cmpIgnoreStyle(a, b) == 0 - ## Check if two idents are identical. - -proc eqIdent*(node: NimNode; s: string): bool {.compileTime.} = - ## Check if node is some identifier node (``nnkIdent``, ``nnkSym``, etc.) - ## is the same as ``s``. Note that this is the preferred way to check! Most - ## other ways like ``node.ident`` are much more error-prone, unfortunately. - case node.kind - of nnkSym, nnkIdent: - result = eqIdent(node.strVal, s) - of nnkOpenSymChoice, nnkClosedSymChoice: - result = eqIdent($node[0], s) - else: - result = false +when defined(nimVmEqIdent): + proc eqIdent*(a: string; b: string): bool {.magic: "EqIdent", noSideEffect.} + ## Style insensitive comparison. + + proc eqIdent*(a: NimNode; b: string): bool {.magic: "EqIdent", noSideEffect.} + ## Style insensitive comparison. + ## ``a`` can be an identifier or a symbol. + + proc eqIdent*(a: string; b: NimNode): bool {.magic: "EqIdent", noSideEffect.} + ## Style insensitive comparison. + ## ``b`` can be an identifier or a symbol. + + proc eqIdent*(a: NimNode; b: NimNode): bool {.magic: "EqIdent", noSideEffect.} + ## Style insensitive comparison. + ## ``a`` and ``b`` can be an identifier or a symbol. + +else: + # this procedure is optimized for native code, it should not be compiled to nimVM bytecode. + proc cmpIgnoreStyle(a, b: cstring): int {.noSideEffect.} = + proc toLower(c: char): char {.inline.} = + if c in {'A'..'Z'}: result = chr(ord(c) + (ord('a') - ord('A'))) + else: result = c + var i = 0 + var j = 0 + # first char is case sensitive + if a[0] != b[0]: return 1 + while true: + while a[i] == '_': inc(i) + while b[j] == '_': inc(j) # BUGFIX: typo + var aa = toLower(a[i]) + var bb = toLower(b[j]) + result = ord(aa) - ord(bb) + if result != 0 or aa == '\0': break + inc(i) + inc(j) + + + proc eqIdent*(a, b: string): bool = cmpIgnoreStyle(a, b) == 0 + ## Check if two idents are identical. + + proc eqIdent*(node: NimNode; s: string): bool {.compileTime.} = + ## Check if node is some identifier node (``nnkIdent``, ``nnkSym``, etc.) + ## is the same as ``s``. Note that this is the preferred way to check! Most + ## other ways like ``node.ident`` are much more error-prone, unfortunately. + case node.kind + of nnkSym, nnkIdent: + result = eqIdent(node.strVal, s) + of nnkOpenSymChoice, nnkClosedSymChoice: + result = eqIdent($node[0], s) + else: + result = false proc hasArgOfName*(params: NimNode; name: string): bool {.compiletime.}= ## Search nnkFormalParams for an argument. diff --git a/lib/pure/cstrutils.nim b/lib/pure/cstrutils.nim index 437140892..fe9ceb68b 100644 --- a/lib/pure/cstrutils.nim +++ b/lib/pure/cstrutils.nim @@ -45,8 +45,10 @@ proc endsWith*(s, suffix: cstring): bool {.noSideEffect, proc cmpIgnoreStyle*(a, b: cstring): int {.noSideEffect, rtl, extern: "csuCmpIgnoreStyle".} = - ## Compares two strings normalized (i.e. case and - ## underscores do not matter). Returns: + ## Semantically the same as ``cmp(normalize($a), normalize($b))``. It + ## is just optimized to not allocate temporary strings. This should + ## NOT be used to compare Nim identifier names. use `macros.eqIdent` + ## for that. Returns: ## ## | 0 iff a == b ## | < 0 iff a < b diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index 7b8a9a0d0..d772b066c 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -385,8 +385,8 @@ proc normalize*(s: string): string {.noSideEffect, procvar, rtl, extern: "nsuNormalize".} = ## Normalizes the string `s`. ## - ## That means to convert it to lower case and remove any '_'. This is needed - ## for Nim identifiers for example. + ## That means to convert it to lower case and remove any '_'. This + ## should NOT be used to normalize Nim identifier names. result = newString(s.len) var j = 0 for i in 0..len(s) - 1: @@ -418,8 +418,10 @@ proc cmpIgnoreCase*(a, b: string): int {.noSideEffect, proc cmpIgnoreStyle*(a, b: string): int {.noSideEffect, rtl, extern: "nsuCmpIgnoreStyle", procvar.} = - ## Compares two strings normalized (i.e. case and - ## underscores do not matter). Returns: + ## Semantically the same as ``cmp(normalize(a), normalize(b))``. It + ## is just optimized to not allocate temporary strings. This should + ## NOT be used to compare Nim identifier names. use `macros.eqIdent` + ## for that. Returns: ## ## | 0 iff a == b ## | < 0 iff a < b @@ -436,7 +438,6 @@ proc cmpIgnoreStyle*(a, b: string): int {.noSideEffect, inc(i) inc(j) - proc strip*(s: string, leading = true, trailing = true, chars: set[char] = Whitespace): string {.noSideEffect, rtl, extern: "nsuStrip".} = diff --git a/tests/macros/tmacro1.nim b/tests/macros/tmacro1.nim index ac6bd02a5..0b83345a1 100644 --- a/tests/macros/tmacro1.nim +++ b/tests/macros/tmacro1.nim @@ -18,5 +18,47 @@ macro test*(a: untyped): untyped = t.b = true t.z = 4.5 + test: "hi" + +import strutils + +template assertNot(arg: untyped): untyped = + assert(not(arg)) + +static: + ## test eqIdent + let a = "abc_def" + let b = "abcDef" + let c = "AbcDef" + + assert eqIdent( a , b ) + assert eqIdent(newIdentNode(a), b ) + assert eqIdent( a , newIdentNode(b)) + assert eqIdent(newIdentNode(a), newIdentNode(b)) + + assert eqIdent( a , b ) + assert eqIdent(genSym(nskLet, a), b ) + assert eqIdent( a , genSym(nskLet, b)) + assert eqIdent(genSym(nskLet, a), genSym(nskLet, b)) + + assert eqIdent(newIdentNode( a), newIdentNode( b)) + assert eqIdent(genSym(nskLet, a), newIdentNode( b)) + assert eqIdent(newIdentNode( a), genSym(nskLet, b)) + assert eqIdent(genSym(nskLet, a), genSym(nskLet, b)) + + assertNot eqIdent( c , b ) + assertNot eqIdent(newIdentNode(c), b ) + assertNot eqIdent( c , newIdentNode(b)) + assertNot eqIdent(newIdentNode(c), newIdentNode(b)) + + assertNot eqIdent( c , b ) + assertNot eqIdent(genSym(nskLet, c), b ) + assertNot eqIdent( c , genSym(nskLet, b)) + assertNot eqIdent(genSym(nskLet, c), genSym(nskLet, b)) + + assertNot eqIdent(newIdentNode( c), newIdentNode( b)) + assertNot eqIdent(genSym(nskLet, c), newIdentNode( b)) + assertNot eqIdent(newIdentNode( c), genSym(nskLet, b)) + assertNot eqIdent(genSym(nskLet, c), genSym(nskLet, b)) -- cgit 1.4.1-2-gfad0 From 04df7f147c6e08b28113173328d7e03fce381af9 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Mon, 16 Apr 2018 00:08:52 -0700 Subject: update macros.nim: followup on pull #7598 (#7619) @Araq @krux02 https://github.com/nim-lang/Nim/pull/7598 seemed inaccurate eg for NLineInfo there's `mNLineInfo` but no opcNLineInfo --- lib/core/macros.nim | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'lib/core') diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 7217475c6..993756b28 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -14,11 +14,8 @@ include "system/inclrtl" ## .. include:: ../../doc/astspec.txt -# If you look for the implementation of the magic symbols, copy the -# magic string and open the file "../../compiler/vm.nim" and search -# for the magic string with the prefix "opc". For example the -# implementation of ``{.magic: "FooBar".}`` will be right under -# ``of opcFooBar:``. +# If you look for the implementation of the magic symbol +# ``{.magic: "Foo".}``, search for `mFoo` and `opcFoo`. type NimNodeKind* = enum -- cgit 1.4.1-2-gfad0 From 412cd61dabe160708c502e37a48b9c3c7c2c6017 Mon Sep 17 00:00:00 2001 From: Dmitry Atamanov Date: Mon, 16 Apr 2018 20:37:08 +0300 Subject: Fixes #7595 (#7623) * Fixes #7595 * Add brackets * Fix for treeRepr and lispRepr too --- lib/core/macros.nim | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'lib/core') diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 993756b28..b050eb6c8 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -706,8 +706,7 @@ proc treeRepr*(n: NimNode): string {.compileTime, benign.} = res.add(($n.kind).substr(3)) case n.kind - of nnkEmpty: discard # same as nil node in this representation - of nnkNilLit: res.add(" nil") + of nnkEmpty, nnkNilLit: discard # same as nil node in this representation of nnkCharLit..nnkInt64Lit: res.add(" " & $n.intVal) of nnkFloatLit..nnkFloat64Lit: res.add(" " & $n.floatVal) of nnkStrLit..nnkTripleStrLit, nnkIdent, nnkSym: @@ -730,8 +729,7 @@ proc lispRepr*(n: NimNode): string {.compileTime, benign.} = add(result, "(") case n.kind - of nnkEmpty: discard # same as nil node in this representation - of nnkNilLit: add(result, "nil") + of nnkEmpty, nnkNilLit: discard # same as nil node in this representation of nnkCharLit..nnkInt64Lit: add(result, $n.intVal) of nnkFloatLit..nnkFloat64Lit: add(result, $n.floatVal) of nnkStrLit..nnkTripleStrLit, nnkCommentStmt, nnkident, nnkSym: @@ -766,7 +764,7 @@ proc astGenRepr*(n: NimNode): string {.compileTime, benign.} = ## See also `repr`, `treeRepr`, and `lispRepr`. const - NodeKinds = {nnkEmpty, nnkNilLit, nnkIdent, nnkSym, nnkNone, nnkCommentStmt} + NodeKinds = {nnkEmpty, nnkIdent, nnkSym, nnkNone, nnkCommentStmt} LitKinds = {nnkCharLit..nnkInt64Lit, nnkFloatLit..nnkFloat64Lit, nnkStrLit..nnkTripleStrLit} proc traverse(res: var string, level: int, n: NimNode) {.benign.} = @@ -775,12 +773,13 @@ proc astGenRepr*(n: NimNode): string {.compileTime, benign.} = res.add("new" & ($n.kind).substr(3) & "Node(") elif n.kind in LitKinds: res.add("newLit(") + elif n.kind == nnkNilLit: + res.add("newNilLit()") else: res.add($n.kind) case n.kind - of nnkEmpty: discard - of nnkNilLit: res.add("nil") + of nnkEmpty, nnkNilLit: discard of nnkCharLit: res.add("'" & $chr(n.intVal) & "'") of nnkIntLit..nnkInt64Lit: res.add($n.intVal) of nnkFloatLit..nnkFloat64Lit: res.add($n.floatVal) -- cgit 1.4.1-2-gfad0 From 34029263725ef931863ea73966dc08588758dada Mon Sep 17 00:00:00 2001 From: RSDuck Date: Thu, 19 Apr 2018 11:07:45 +0200 Subject: hasCustomPragma/getCustomPragmaVal: small fix (#7650) * fix hasCustomPragma/getCustomPragmaVal for types without pragma * fix pragma on pointer test * removed trailing spaces --- lib/core/macros.nim | 6 +++++- tests/pragmas/tcustom_pragma.nim | 10 +++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) (limited to 'lib/core') diff --git a/lib/core/macros.nim b/lib/core/macros.nim index b050eb6c8..1dc067e1a 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -1288,7 +1288,11 @@ proc customPragmaNode(n: NimNode): NimNode = typ = n.getTypeInst() if typ.typeKind == ntyTypeDesc: - return typ[1].getImpl()[0][1] + let impl = typ[1].getImpl() + if impl[0].kind == nnkPragmaExpr: + return impl[0][1] + else: + return impl[0] # handle types which don't have macro at all if n.kind == nnkSym: # either an variable or a proc let impl = n.getImpl() diff --git a/tests/pragmas/tcustom_pragma.nim b/tests/pragmas/tcustom_pragma.nim index 28a8713ce..33a4a7e65 100644 --- a/tests/pragmas/tcustom_pragma.nim +++ b/tests/pragmas/tcustom_pragma.nim @@ -51,6 +51,9 @@ block: # A bit more advanced case static: assert(hasCustomPragma(myproc, alternativeKey)) + const hasFieldCustomPragma = s.field.hasCustomPragma(defaultValue) + static: assert(hasFieldCustomPragma == false) + # pragma on an object static: assert Subfield.hasCustomPragma(defaultValue) @@ -71,6 +74,8 @@ block: # ref types MyFile {.defaultValue: "closed".} = ref object path {.defaultValue: "invalid".}: string + TypeWithoutPragma = object + var s = NodeRef() const @@ -91,7 +96,7 @@ block: # ref types var ptrS = NodePtr(nil) const - ptrRightSerKey = getCustomPragmaVal(s.right, serializationKey) + ptrRightSerKey = getCustomPragmaVal(ptrS.right, serializationKey) static: assert ptrRightSerKey == "r" @@ -103,6 +108,9 @@ block: # ref types assert fileDefVal == "closed" assert filePathDefVal == "invalid" + static: + assert TypeWithoutPragma.hasCustomPragma(defaultValue) == false + block: type VariantKind = enum -- cgit 1.4.1-2-gfad0 From bdcb72959729a5c4b195f9bf6b2ac8783c27f38e Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Wed, 18 Apr 2018 19:40:08 +0300 Subject: Better support for treating templates and macros as symbols. This allows you to pass a template or a macro to another macro which can then inspect the implementation of the former template/macro using `getImpl`. Since templates can be freely redefined, this allows you to treat their symbols as compile-time variables that have lexical scope. A motivating PoC example for a logging library taking advantage of this will be provided in the next commit. Implementation details: * The name of a template or a macro will be consider a symbol if the template/macro requires parameters * For parameterless templates/macros, you can use `bindSym`, which was extended to also work outside of compile-time procs. --- compiler/ast.nim | 13 +++ compiler/evaltempl.nim | 2 +- compiler/semexprs.nim | 8 +- compiler/semmagic.nim | 4 + compiler/sigmatch.nim | 8 +- lib/core/macros.nim | 2 +- tests/macros/ttemplatesymbols.nim | 173 ++++++++++++++++++++++++++++++++++++++ 7 files changed, 202 insertions(+), 8 deletions(-) create mode 100644 tests/macros/ttemplatesymbols.nim (limited to 'lib/core') diff --git a/compiler/ast.nim b/compiler/ast.nim index b8202abe6..350fa5f6a 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -1615,6 +1615,19 @@ proc originatingModule*(s: PSym): PSym = proc isRoutine*(s: PSym): bool {.inline.} = result = s.kind in skProcKinds +proc isCompileTimeProc*(s: PSym): bool {.inline.} = + result = s.kind == skMacro or + s.kind == skProc and sfCompileTime in s.flags + +proc requiredParams*(s: PSym): int = + # Returns the number of required params (without default values) + # XXX: Perhaps we can store this in the `offset` field of the + # symbol instead? + for i in 1 ..< s.typ.len: + if s.typ.n[i].sym.ast != nil: + return i - 1 + return s.typ.len - 1 + proc hasPattern*(s: PSym): bool {.inline.} = result = isRoutine(s) and s.ast.sons[patternPos].kind != nkEmpty diff --git a/compiler/evaltempl.nim b/compiler/evaltempl.nim index fbb7eb2e6..502430815 100644 --- a/compiler/evaltempl.nim +++ b/compiler/evaltempl.nim @@ -63,7 +63,7 @@ proc evalTemplateArgs(n: PNode, s: PSym; fromHlo: bool): PNode = # if the template has zero arguments, it can be called without ``()`` # `n` is then a nkSym or something similar var totalParams = case n.kind - of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit: n.len-1 + of nkCallKinds: n.len-1 else: 0 var diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 4a3672aa0..29f377a19 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -960,18 +960,20 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode = else: result = newSymNode(s, n.info) of skMacro: - if efNoEvaluateGeneric in flags and s.ast[genericParamsPos].len > 0: + if efNoEvaluateGeneric in flags and s.ast[genericParamsPos].len > 0 or + (n.kind notin nkCallKinds and s.requiredParams > 0): markUsed(n.info, s, c.graph.usageSym) styleCheckUse(n.info, s) - result = newSymNode(s, n.info) + result = symChoice(c, n, s, scClosed) else: result = semMacroExpr(c, n, n, s, flags) of skTemplate: if efNoEvaluateGeneric in flags and s.ast[genericParamsPos].len > 0 or + (n.kind notin nkCallKinds and s.requiredParams > 0) or sfCustomPragma in sym.flags: markUsed(n.info, s, c.graph.usageSym) styleCheckUse(n.info, s) - result = newSymNode(s, n.info) + result = symChoice(c, n, s, scClosed) else: result = semTemplateExpr(c, n, s, flags) of skParam: diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index 3f0df0065..bf3c55120 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -199,6 +199,10 @@ proc semBindSym(c: PContext, n: PNode): PNode = if s != nil: # we need to mark all symbols: var sc = symChoice(c, id, s, TSymChoiceRule(isMixin.intVal)) + if not getCurrOwner(c).isCompileTimeProc: + # inside regular code, bindSym resolves to the sym-choice + # nodes (see tinspectsymbol) + return sc result.add(sc) else: errorUndeclaredIdentifier(c, n.sons[1].info, sl.strVal) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 4263ef581..7e566afad 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -2032,8 +2032,9 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType, y.calleeSym = m.calleeSym z.calleeSym = m.calleeSym var best = -1 - for i in countup(0, sonsLen(arg) - 1): - if arg.sons[i].sym.kind in {skProc, skFunc, skMethod, skConverter, skIterator}: + for i in 0 ..< arg.len: + if arg.sons[i].sym.kind in {skProc, skFunc, skMethod, skConverter, + skIterator, skMacro, skTemplate}: copyCandidate(z, m) z.callee = arg.sons[i].typ if tfUnresolved in z.callee.flags: continue @@ -2062,6 +2063,7 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType, x = z elif cmp == 0: y = z # z is as good as x + if x.state == csEmpty: result = nil elif y.state == csMatch and cmpCandidates(x, y) == 0: @@ -2070,7 +2072,7 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType, # ambiguous: more than one symbol fits! # See tsymchoice_for_expr as an example. 'f.kind == tyExpr' should match # anyway: - if f.kind == tyExpr: result = arg + if f.kind in {tyExpr, tyStmt}: result = arg else: result = nil else: # only one valid interpretation found: diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 1dc067e1a..fa5cd67df 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -385,7 +385,7 @@ type {.deprecated: [TBindSymRule: BindSymRule].} -proc bindSym*(ident: string, rule: BindSymRule = brClosed): NimNode {. +proc bindSym*(ident: static[string], rule: BindSymRule = brClosed): NimNode {. magic: "NBindSym", noSideEffect.} ## creates a node that binds `ident` to a symbol node. The bound symbol ## may be an overloaded symbol. diff --git a/tests/macros/ttemplatesymbols.nim b/tests/macros/ttemplatesymbols.nim new file mode 100644 index 000000000..8d9c9ec02 --- /dev/null +++ b/tests/macros/ttemplatesymbols.nim @@ -0,0 +1,173 @@ +import + macros, algorithm, strutils + +proc normalProc(x: int) = + echo x + +template templateWithtouParams = + echo 10 + +proc overloadedProc(x: int) = + echo x + +proc overloadedProc(x: string) = + echo x + +proc overloadedProc[T](x: T) = + echo x + +template normalTemplate(x: int) = + echo x + +template overloadedTemplate(x: int) = + echo x + +template overloadedTemplate(x: string) = + echo x + +macro normalMacro(x: int): untyped = + discard + +macro macroWithoutParams: untyped = + discard + +macro inspectSymbol(sym: typed, expected: static[string]): untyped = + if sym.kind == nnkSym: + echo "Symbol node:" + let res = sym.getImpl.repr & "\n" + echo res + # echo "|", res, "|" + # echo "|", expected, "|" + if expected.len > 0: assert res == expected + elif sym.kind in {nnkClosedSymChoice, nnkOpenSymChoice}: + echo "Begin sym choice:" + var results = newSeq[string](0) + for innerSym in sym: + results.add innerSym.getImpl.repr + sort(results, cmp[string]) + let res = results.join("\n") & "\n" + echo res + if expected.len > 0: assert res == expected + echo "End symchoice." + else: + echo "Non-symbol node: ", sym.kind + if expected.len > 0: assert $sym.kind == expected + +macro inspectUntyped(sym: untyped, expected: static[string]): untyped = + let res = sym.repr + echo "Untyped node: ", res + assert res == expected + +inspectSymbol templateWithtouParams, "nnkCommand" + # this template is expanded, because bindSym was not used + # the end result is the template body (nnkCommand) + +inspectSymbol bindSym("templateWithtouParams"), """ +template templateWithtouParams() = + echo 10 + +""" + +inspectSymbol macroWithoutParams, "nnkEmpty" + # Just like the template above, the macro was expanded + +inspectSymbol bindSym("macroWithoutParams"), """ +macro macroWithoutParams(): untyped = + discard + +""" + +inspectSymbol normalMacro, """ +macro normalMacro(x: int): untyped = + discard + +""" + # Since the normalMacro has params, it's automatically + # treated as a symbol here (no need for `bindSym`) + +inspectSymbol bindSym("normalMacro"), """ +macro normalMacro(x: int): untyped = + discard + +""" + +inspectSymbol normalTemplate, """ +template normalTemplate(x: int) = + echo x + +""" + +inspectSymbol bindSym("normalTemplate"), """ +template normalTemplate(x: int) = + echo x + +""" + +inspectSymbol overloadedTemplate, """ +template overloadedTemplate(x: int) = + echo x + +template overloadedTemplate(x: string) = + echo x + +""" + +inspectSymbol bindSym("overloadedTemplate"), """ +template overloadedTemplate(x: int) = + echo x + +template overloadedTemplate(x: string) = + echo x + +""" + +inspectUntyped bindSym("overloadedTemplate"), """bindSym("overloadedTemplate")""" + # binSym is active only in the presense of `typed` params. + # `untyped` params still get the raw AST + +inspectSymbol normalProc, """ +proc normalProc(x: int) = + echo [x] + +""" + +inspectSymbol bindSym("normalProc"), """ +proc normalProc(x: int) = + echo [x] + +""" + +inspectSymbol overloadedProc, """ +proc overloadedProc(x: int) = + echo [x] + +proc overloadedProc(x: string) = + echo [x] + +proc overloadedProc[T](x: T) = + echo x + +""" + # XXX: There seems to be a repr rendering problem above. + # Notice that `echo [x]` + +inspectSymbol overloadedProc[float], """ +proc overloadedProc(x: T) = + echo [x] + +""" + # As expected, when we select a specific generic, the + # AST is no longer a symChoice + +inspectSymbol bindSym("overloadedProc"), """ +proc overloadedProc(x: int) = + echo [x] + +proc overloadedProc(x: string) = + echo [x] + +proc overloadedProc[T](x: T) = + echo x + +""" + -- cgit 1.4.1-2-gfad0 From 6d19d1eeb21487b62883852dc543cc891c129f62 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 3 Jun 2018 20:15:12 +0200 Subject: macros.nim: remove deprecated symbols --- lib/core/macros.nim | 3 --- 1 file changed, 3 deletions(-) (limited to 'lib/core') diff --git a/lib/core/macros.nim b/lib/core/macros.nim index fa5cd67df..1f251b73e 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -126,9 +126,6 @@ type ## represents a Nim *symbol* in the compiler; a *symbol* is a looked-up ## *ident*. -{.deprecated: [TNimrodNodeKind: NimNodeKind, TNimNodeKinds: NimNodeKinds, - TNimrodTypeKind: NimTypeKind, TNimrodSymKind: NimSymKind, - TNimrodIdent: NimIdent, PNimrodSymbol: NimSym].} const nnkLiterals* = {nnkCharLit..nnkNilLit} -- cgit 1.4.1-2-gfad0 From b5194f592c033c02e9eaf93fde6ee0de2b7623e2 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 3 Jun 2018 20:18:43 +0200 Subject: WIP: an API for VM replay global state support --- compiler/ast.nim | 10 +++-- compiler/macrocacheimpl.nim | 49 ++++++++++++++++++++++++ compiler/rodimpl.nim | 27 +++++--------- compiler/vm.nim | 91 ++++++++++++++++++++++++++++++++++++++++++++- compiler/vmdef.nim | 14 ++++++- compiler/vmgen.nim | 38 ++++++++++++++----- doc/intern.txt | 47 ++++++++++++++++++++++- lib/core/macrocache.nim | 47 +++++++++++++++++++++++ 8 files changed, 288 insertions(+), 35 deletions(-) create mode 100644 compiler/macrocacheimpl.nim create mode 100644 lib/core/macrocache.nim (limited to 'lib/core') diff --git a/compiler/ast.nim b/compiler/ast.nim index 7758bffd3..40c1b064d 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -634,14 +634,18 @@ type mNaN, mInf, mNegInf, mCompileOption, mCompileOptionArg, mNLen, mNChild, mNSetChild, mNAdd, mNAddMultiple, mNDel, - mNKind, mNSymKind + mNKind, mNSymKind, + + mNccValue, mNccInc, mNcsAdd, mNcsIncl, mNcsLen, mNcsAt, + mNctPut, mNctLen, mNctGet, mNctHasNext, mNctNext, + mNIntVal, mNFloatVal, mNSymbol, mNIdent, mNGetType, mNStrVal, mNSetIntVal, mNSetFloatVal, mNSetSymbol, mNSetIdent, mNSetType, mNSetStrVal, mNLineInfo, mNNewNimNode, mNCopyNimNode, mNCopyNimTree, mStrToIdent, mNBindSym, mLocals, mNCallSite, - mEqIdent, mEqNimrodNode, mSameNodeType, mGetImpl, + mEqIdent, mEqNimrodNode, mSameNodeType, mGetImpl, mNGenSym, mNHint, mNWarning, mNError, - mInstantiationInfo, mGetTypeInfo, mNGenSym, + mInstantiationInfo, mGetTypeInfo, mNimvm, mIntDefine, mStrDefine, mRunnableExamples, mException, mBuiltinType diff --git a/compiler/macrocacheimpl.nim b/compiler/macrocacheimpl.nim new file mode 100644 index 000000000..5c27f9265 --- /dev/null +++ b/compiler/macrocacheimpl.nim @@ -0,0 +1,49 @@ +# +# +# The Nim Compiler +# (c) Copyright 2018 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## This module implements helpers for the macro cache. + +import lineinfos, ast, modulegraphs, vmdef, magicsys + +proc genCall3(g: ModuleGraph; m: TMagic; s: string; a, b, c: PNode): PNode = + newTree(nkStaticStmt, newTree(nkCall, createMagic(g, s, m).newSymNode, a, b, c)) + +proc genCall2(g: ModuleGraph; m: TMagic; s: string; a, b: PNode): PNode = + newTree(nkStaticStmt, newTree(nkCall, createMagic(g, s, m).newSymNode, a, b)) + +template nodeFrom(s: string): PNode = + var res = newStrNode(s, info) + res.typ = getSysType(g, info, tyString) + res + +template nodeFrom(i: BiggestInt): PNode = + var res = newIntNode(i, info) + res.typ = getSysType(g, info, tyInt) + res + +template nodeFrom(n: PNode): PNode = copyTree(n) + +template record(call) = + g.recordStmt(g, c.module, call) + +proc recordInc*(c: PCtx; info: TLineInfo; key: string; by: BiggestInt) = + let g = c.graph + record genCall2(mNccInc, "inc", nodeFrom key, nodeFrom by) + +proc recordPut*(c: PCtx; info: TLineInfo; key: string; k: string; val: PNode) = + let g = c.graph + record genCall3(mNctPut, "[]=", nodeFrom key, nodeFrom k, nodeFrom val) + +proc recordAdd*(c: PCtx; info: TLineInfo; key: string; val: PNode) = + let g = c.graph + record genCall2(mNcsAdd, "add", nodeFrom key, nodeFrom val) + +proc recordIncl*(c: PCtx; info: TLineInfo; key: string; val: PNode) = + let g = c.graph + record genCall2(mNcsIncl, "incl", nodeFrom key, nodeFrom val) diff --git a/compiler/rodimpl.nim b/compiler/rodimpl.nim index e6c6b6374..e2160aa84 100644 --- a/compiler/rodimpl.nim +++ b/compiler/rodimpl.nim @@ -10,16 +10,12 @@ ## This module implements the new compilation cache. import strutils, os, intsets, tables, ropes, db_sqlite, msgs, options, types, - renderer, rodutils, idents, astalgo, btrees, magicsys, cgmeth, extccomp + renderer, rodutils, idents, astalgo, btrees, magicsys, cgmeth, extccomp, vm ## Todo: -## - Implement the 'import' replay logic so that the codegen runs over -## dependent modules. ## - Make conditional symbols and the configuration part of a module's -## dependencies. -## - Test multi methods. -## - Implement the limited VM support based on replays. -## - Depencency computation should use *signature* hashes in order to +## dependencies. Also include the rodfile "version". +## - Dependency computation should use *signature* hashes in order to ## avoid recompiling dependent modules. template db(): DbConn = g.incr.db @@ -157,8 +153,6 @@ proc encodeLoc(g: ModuleGraph; loc: TLoc, result: var string) = if loc.lode != nil: add(result, '^') encodeNode(g, unknownLineInfo(), loc.lode, result) - #encodeVInt(cast[int32](loc.t.id), result) - #pushType(w, loc.t) if loc.r != nil: add(result, '!') encodeStr($loc.r, result) @@ -765,11 +759,9 @@ proc loadModuleSymTab(g; module: PSym) = proc replay(g: ModuleGraph; module: PSym; n: PNode) = case n.kind of nkStaticStmt: - #evalStaticStmt() - discard "XXX to implement" - of nkVarSection, nkLetSection: - #setupCompileTimeVar() - discard "XXX to implement" + evalStaticStmt(module, g, n[0], module) + #of nkVarSection, nkLetSection: + # nkVarSections are already covered by the vmgen which produces nkStaticStmt of nkMethodDef: methodDef(g, n[namePos].sym, fromCache=true) of nkCommentStmt: @@ -798,10 +790,9 @@ proc replay(g: ModuleGraph; module: PSym; n: PNode) = internalAssert g.config, false of nkImportStmt: for x in n: - if x.kind == nkStrLit: - # XXX check that importModuleCallback implements the right logic - let imported = g.importModuleCallback(g, module, fileInfoIdx(g.config, n[0].strVal)) - internalAssert g.config, imported.id < 0 + internalAssert g.config, x.kind == nkStrLit + let imported = g.importModuleCallback(g, module, fileInfoIdx(g.config, n[0].strVal)) + internalAssert g.config, imported.id < 0 of nkStmtList, nkStmtListExpr: for x in n: replay(g, module, x) else: discard "nothing to do for this node" diff --git a/compiler/vm.nim b/compiler/vm.nim index 2ba5e7ebf..019aa08e8 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -19,7 +19,7 @@ import ast except getstr import strutils, astalgo, msgs, vmdef, vmgen, nimsets, types, passes, parser, vmdeps, idents, trees, renderer, options, transf, parseutils, - vmmarshal, gorgeimpl, lineinfos + vmmarshal, gorgeimpl, lineinfos, tables, btrees, macrocacheimpl from semfold import leValueConv, ordinalValToString from evaltempl import evalTemplate @@ -1572,6 +1572,95 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = var sym = newSym(k.TSymKind, getIdent(c.cache, name), c.module.owner, c.debug[pc]) incl(sym.flags, sfGenSym) regs[ra].node = newSymNode(sym) + + of opcNccValue: + decodeB(rkInt) + let destKey = regs[rb].node.strVal + regs[ra].intVal = getOrDefault(c.cacheCounters, destKey) + of opcNccInc: + let rb = instr.regB + let destKey = regs[ra].node.strVal + let by = regs[rb].intVal + let v = getOrDefault(c.cacheCounters, destKey) + c.cacheCounters[destKey] = v+by + recordInc(c, c.debug[pc], destKey, by) + of opcNcsAdd: + let destKey = regs[ra].node.strVal + let val = regs[instr.regB].node + if not contains(c.cacheSeqs, destKey): + c.cacheSeqs[destKey] = newTree(nkStmtList, val) + # newNodeI(nkStmtList, c.debug[pc]) + else: + c.cacheSeqs[destKey].add val + recordAdd(c, c.debug[pc], destKey, val) + of opcNcsIncl: + let destKey = regs[ra].node.strVal + let val = regs[instr.regB].node + if not contains(c.cacheSeqs, destKey): + c.cacheSeqs[destKey] = newTree(nkStmtList, val) + else: + block search: + for existing in c.cacheSeqs[destKey]: + if exprStructuralEquivalent(existing, val, strictSymEquality=true): + break search + c.cacheSeqs[destKey].add val + recordIncl(c, c.debug[pc], destKey, val) + of opcNcsLen: + decodeB(rkInt) + let destKey = regs[rb].node.strVal + regs[ra].intVal = + if contains(c.cacheSeqs, destKey): c.cacheSeqs[destKey].len else: 0 + of opcNcsAt: + decodeBC(rkNode) + let idx = regs[rc].intVal + let destKey = regs[rb].node.strVal + if contains(c.cacheSeqs, destKey) and idx <% c.cacheSeqs[destKey].len: + regs[ra].node = c.cacheSeqs[destKey][idx.int] + else: + stackTrace(c, tos, pc, errIndexOutOfBounds) + of opcNctPut: + let destKey = regs[ra].node.strVal + let key = regs[instr.regB].node.strVal + let val = regs[instr.regC].node + if not contains(c.cacheTables, destKey): + c.cacheTables[destKey] = initBTree[string, PNode]() + if not contains(c.cacheTables[destKey], key): + c.cacheTables[destKey].add(key, val) + recordPut(c, c.debug[pc], destKey, key, val) + else: + stackTrace(c, tos, pc, "key already exists: " & key) + of opcNctLen: + decodeB(rkInt) + let destKey = regs[rb].node.strVal + regs[ra].intVal = + if contains(c.cacheTables, destKey): c.cacheTables[destKey].len else: 0 + of opcNctGet: + decodeBC(rkNode) + let destKey = regs[rb].node.strVal + let key = regs[rc].node.strVal + if contains(c.cacheTables, destKey): + if contains(c.cacheTables[destKey], key): + regs[ra].node = getOrDefault(c.cacheTables[destKey], key) + else: + stackTrace(c, tos, pc, "key does not exist: " & key) + else: + stackTrace(c, tos, pc, "key does not exist: " & destKey) + of opcNctHasNext: + decodeBC(rkInt) + let destKey = regs[rb].node.strVal + regs[ra].intVal = + btrees.hasNext(c.cacheTables[destKey], regs[rc].intVal.int) else: 0 + of opcNctNext: + decodeBC(rkNode) + let destKey = regs[rb].node.strVal + let index = regs[rc].intVal + if contains(c.cacheTables, destKey): + let (k, v, nextIndex) = btrees.next(c.cacheTables[destKey], index.int) + regs[ra].node = newTree(nkTupleConstr, newStrNode(k, c.debug[pc]), v, + newIntNode(nkIntLit, nextIndex)) + else: + stackTrace(c, tos, pc, "key does not exist: " & destKey) + of opcTypeTrait: # XXX only supports 'name' for now; we can use regC to encode the # type trait operation diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim index 9c31dda24..1ef984466 100644 --- a/compiler/vmdef.nim +++ b/compiler/vmdef.nim @@ -10,7 +10,8 @@ ## This module contains the type definitions for the new evaluation engine. ## An instruction is 1-3 int32s in memory, it is a register based VM. -import ast, passes, msgs, idents, intsets, options, modulegraphs, lineinfos +import ast, passes, msgs, idents, intsets, options, modulegraphs, lineinfos, + tables, btrees const byteExcess* = 128 # we use excess-K for immediates @@ -91,6 +92,9 @@ type opcNSetFloatVal, opcNSetSymbol, opcNSetIdent, opcNSetType, opcNSetStrVal, opcNNewNimNode, opcNCopyNimNode, opcNCopyNimTree, opcNDel, opcGenSym, + opcNccValue, opcNccInc, opcNcsAdd, opcNcsIncl, opcNcsLen, opcNcsAt, + opcNctPut, opcNctLen, opcNctGet, opcNctHasNext, opcNctNext, + opcSlurp, opcGorge, opcParseExprToAst, @@ -209,6 +213,9 @@ type config*: ConfigRef graph*: ModuleGraph oldErrorCount*: int + cacheSeqs*: Table[string, PNode] + cacheCounters*: Table[string, BiggestInt] + cacheTables*: Table[string, BTree[string, PNode]] TPosition* = distinct int @@ -219,7 +226,10 @@ proc newCtx*(module: PSym; cache: IdentCache; g: ModuleGraph): PCtx = globals: newNode(nkStmtListExpr), constants: newNode(nkStmtList), types: @[], prc: PProc(blocks: @[]), module: module, loopIterations: MaxLoopIterations, comesFromHeuristic: unknownLineInfo(), callbacks: @[], errorFlag: "", - cache: cache, config: g.config, graph: g) + cache: cache, config: g.config, graph: g, + cacheSeqs: initTable[string, PNode]() + cacheCounters: initTable[string, BiggestInt]() + cacheTables: initTable[string, BTree[string, PNode]]()) proc refresh*(c: PCtx, module: PSym) = c.module = module diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index c641cc844..9444e41d8 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -804,6 +804,17 @@ proc genIntCast(c: PCtx; n: PNode; dest: var TDest) = else: globalError(c.config, n.info, "VM is only allowed to 'cast' between integers of same size") +proc genVoidABC(c: PCtx, n: PNode, dest: TRegister, opcode: TOpcode) + unused(c, n, dest) + var + tmp1 = c.genx(n.sons[1]) + tmp2 = c.genx(n.sons[2]) + tmp3 = c.genx(n.sons[3]) + c.gABC(n, opcode, tmp1, tmp2, tmp3) + c.freeTemp(tmp1) + c.freeTemp(tmp2) + c.freeTemp(tmp3) + proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = case m of mAnd: c.genAndOr(n, opcFJmp, dest) @@ -1067,20 +1078,25 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = of mNLen: genUnaryABI(c, n, dest, opcLenSeq, nimNodeFlag) of mGetImpl: genUnaryABC(c, n, dest, opcGetImpl) of mNChild: genBinaryABC(c, n, dest, opcNChild) - of mNSetChild, mNDel: - unused(c, n, dest) - var - tmp1 = c.genx(n.sons[1]) - tmp2 = c.genx(n.sons[2]) - tmp3 = c.genx(n.sons[3]) - c.gABC(n, if m == mNSetChild: opcNSetChild else: opcNDel, tmp1, tmp2, tmp3) - c.freeTemp(tmp1) - c.freeTemp(tmp2) - c.freeTemp(tmp3) + of mNSetChild: genVoidABC(c, n, dest, opcNSetChild) + of mNDel: genVoidABC(c, n, dest, opcNDel) of mNAdd: genBinaryABC(c, n, dest, opcNAdd) of mNAddMultiple: genBinaryABC(c, n, dest, opcNAddMultiple) of mNKind: genUnaryABC(c, n, dest, opcNKind) of mNSymKind: genUnaryABC(c, n, dest, opcNSymKind) + + of mNccValue: genUnaryABC(c, n, dest, opcNccValue) + of mNccInc: genBinaryABC(c, n, dest, opcNccInc) + of mNcsAdd: genBinaryABC(c, n, dest, opcNcsAdd + of mNcsIncl: genBinaryABC(c, n, dest, opcNcsIncl + of mNcsLen: genUnaryABC(c, n, dest, opcNcsLen) + of mNcsAt: genBinaryABC(c, n, dest, opcNcsAt) + of mNctPut: genVoidABC(c, n, dest, opcNctPut) + of mNctLen: genUnaryABC(c, n, dest, opcNctLen) + of mNctGet: genBinaryABC(c, n, dest, opcNctGet) + of mNctHasNext: genBinaryABC(c, n, dest, opcNctHasNext) + of mNctNext: genBinaryABC(c, n, dest, opcNctNext) + of mNIntVal: genUnaryABC(c, n, dest, opcNIntVal) of mNFloatVal: genUnaryABC(c, n, dest, opcNFloatVal) of mNSymbol: genUnaryABC(c, n, dest, opcNSymbol) @@ -1795,6 +1811,8 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = if s.magic != mNone: genMagic(c, n, dest, s.magic) elif matches(s, "stdlib", "marshal", "to"): + # XXX marshal load&store should not be opcodes, but use the + # general callback mechanisms. genMarshalLoad(c, n, dest) elif matches(s, "stdlib", "marshal", "$$"): genMarshalStore(c, n, dest) diff --git a/doc/intern.txt b/doc/intern.txt index a4545583e..b253cac42 100644 --- a/doc/intern.txt +++ b/doc/intern.txt @@ -268,7 +268,52 @@ severe way. Plenty of different solutions have been proposed: Since we adopt the "replay the top level statements" idea, the natural solution to this problem is to emit pseudo top level statements that -reflect the mutations done to the global variable. +reflect the mutations done to the global variable. However, this is +MUCH harder than it sounds, for example ``squeaknim`` uses this +snippet: + +.. code-block:: nim + apicall.add(") module: '" & dllName & "'>\C" & + "\t^self externalCallFailed\C!\C\C") + stCode.add(st & "\C\t\"Generated by NimSqueak\"\C\t" & apicall) + +We can "replay" ``stCode.add`` only if the values of ``st`` +and ``apicall`` are known. And even then a hash table's ``add`` with its +hashing mechanism is too hard to replay. + +In practice, things are worse still, consider ``someGlobal[i][j].add arg``. +We only know the root is ``someGlobal`` but the concrete path to the data +is unknown as is the value that is added. We could compute a "diff" between +the global states and use that to compute a symbol patchset, but this is +quite some work, expensive to do at runtime (it would need to run after +every module has been compiled) and also would break for hash tables. + +We need an API that hides the complex aliasing problems by not relying +on Nim's global variables. The obvious solution is to use string keys +instead of global variables: + +.. code-block:: nim + + proc cachePut*(key: string; value: string) + proc cacheGet*(key: string): string + +However, the values being strings/json is quite problematic: Many +lookup tables that are built at compiletime embed *proc vars* and +types which have no obvious string representation... Seems like +AST diffing is still the best idea as it will not require to use +an alien API and works with some existing Nimble packages, at least. + +On the other hand, in Nim's future I would like to replace the VM +by native code. A diff algorithm wouldn't work for that. +Instead the native code would work with an API like ``put``, ``get``: + +.. code-block:: nim + + proc cachePut*(key: string; value: NimNode) + proc cacheGet*(key: string): NimNode + +The API should embrace the AST diffing notion: See the +module ``macrocache`` for the final details. diff --git a/lib/core/macrocache.nim b/lib/core/macrocache.nim new file mode 100644 index 000000000..bd48b5bd4 --- /dev/null +++ b/lib/core/macrocache.nim @@ -0,0 +1,47 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2018 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## This module provides an API for macros that need to collect compile +## time information across module boundaries in global variables. +## Starting with version 0.19 of Nim this is not directly supported anymore +## as it breaks incremental compilations. +## Instead the API here needs to be used. See XXX (wikipedia page) for a +## theoretical foundation behind this. + +type + CacheSeq* = distinct string + CacheTable* = distinct string + CacheCounter* = distinct string + +proc value*(c: CacheCounter): int {.magic: "NccValue".} +proc inc*(c: CacheCounter; by = 1) {.magic: "NccInc".} + +proc add*(s: CacheSeq; value: NimNode) {.magic: "NcsAdd".} +proc incl*(s: CacheSeq; value: NimNode) {.magic: "NcsIncl".} +proc len*(s: CacheSeq): int {.magic: "NcsLen".} +proc `[]`*(s: CacheSeq; i: int): NimNode {.magic: "NcsAt".} + +iterator items*(s: CacheSeq): NimNode = + for i in 0 ..< len(s): yield s[i] + +proc `[]=`*(t: CacheTable; key: string, value: NimNode) {.magic: "NctPut".} + ## 'key' has to be unique! + +proc len*(t: CacheTable): int {.magic: "NctLen".} +proc `[]`*(t: CacheTable; key: string): NimNode {.magic: "NctGet".} + +proc hasNext(t: CacheTable; iter: int): bool {.magic: "NctHasNext".} +proc next(t: CacheTable; iter: int): (string, NimNode, int) {.magic: "NctNext".} + +iterator pairs*(t: CacheTable): (string, NimNode) = + var h = 0 + while hasNext(t, h): + let (a, b, h2) = next(t, h) + yield (a, b) + h = h2 -- cgit 1.4.1-2-gfad0 From dec97924a4bb6a719380475de46485ce71eeb56c Mon Sep 17 00:00:00 2001 From: cooldome Date: Mon, 9 Jul 2018 12:11:03 +0200 Subject: Custom pragmas in proc types (#8205) --- compiler/ast.nim | 3 ++- compiler/sempass2.nim | 24 ++++++++++++++---------- compiler/vmdeps.nim | 3 ++- lib/core/macros.nim | 4 +++- tests/pragmas/tcustom_pragma.nim | 10 +++++++++- 5 files changed, 30 insertions(+), 14 deletions(-) (limited to 'lib/core') diff --git a/compiler/ast.nim b/compiler/ast.nim index 6302c21b9..dbf1151a9 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -322,7 +322,8 @@ const usesEffects* = 1 # read effects at position 1 writeEffects* = 2 # write effects at position 2 tagEffects* = 3 # user defined tag ('gc', 'time' etc.) - effectListLen* = 4 # list of effects list + pragmasEffects* = 4 # not an effect, but a slot for pragmas in proc type + effectListLen* = 5 # list of effects list type TTypeKind* = enum # order is important! diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index 4d3ee0408..e7a76ec22 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -552,6 +552,10 @@ proc isOwnedProcVar(n: PNode; owner: PSym): bool = # XXX prove the soundness of this effect system rule result = n.kind == nkSym and n.sym.kind == skParam and owner == n.sym.owner +proc isNoEffectList(n: PNode): bool {.inline.} = + assert n.kind == nkEffectList + n.len == 0 or (n[tagEffects] == nil and n[exceptionEffects] == nil) + proc trackOperand(tracked: PEffects, n: PNode, paramType: PType) = let a = skipConvAndClosure(n) let op = a.typ @@ -561,7 +565,7 @@ proc trackOperand(tracked: PEffects, n: PNode, paramType: PType) = let s = n.skipConv if s.kind == nkSym and s.sym.kind in routineKinds: propagateEffects(tracked, n, s.sym) - elif effectList.len == 0: + elif isNoEffectList(effectList): if isForwardedProc(n): # we have no explicit effects but it's a forward declaration and so it's # stated there are no additional effects, so simply propagate them: @@ -723,7 +727,7 @@ proc track(tracked: PEffects, n: PNode) = var effectList = op.n.sons[0] if a.kind == nkSym and a.sym.kind == skMethod: propagateEffects(tracked, n, a.sym) - elif effectList.len == 0: + elif isNoEffectList(effectList): if isForwardedProc(a): propagateEffects(tracked, n, a.sym) elif isIndirectCall(a, tracked.owner): @@ -897,19 +901,18 @@ proc checkMethodEffects*(g: ModuleGraph; disp, branch: PSym) = [$branch.typ.lockLevel, $disp.typ.lockLevel]) proc setEffectsForProcType*(g: ModuleGraph; t: PType, n: PNode) = - var effects = t.n.sons[0] + var effects = t.n[0] if t.kind != tyProc or effects.kind != nkEffectList: return - - let - raisesSpec = effectSpec(n, wRaises) - tagsSpec = effectSpec(n, wTags) - if not isNil(raisesSpec) or not isNil(tagsSpec): + if n.kind != nkEmpty: internalAssert g.config, effects.len == 0 newSeq(effects.sons, effectListLen) + let raisesSpec = effectSpec(n, wRaises) if not isNil(raisesSpec): - effects.sons[exceptionEffects] = raisesSpec + effects[exceptionEffects] = raisesSpec + let tagsSpec = effectSpec(n, wTags) if not isNil(tagsSpec): - effects.sons[tagEffects] = tagsSpec + effects[tagEffects] = tagsSpec + effects[pragmasEffects] = n proc initEffects(g: ModuleGraph; effects: PNode; s: PSym; t: var TEffects) = newSeq(effects.sons, effectListLen) @@ -917,6 +920,7 @@ proc initEffects(g: ModuleGraph; effects: PNode; s: PSym; t: var TEffects) = effects.sons[tagEffects] = newNodeI(nkArgList, s.info) effects.sons[usesEffects] = g.emptyNode effects.sons[writeEffects] = g.emptyNode + effects.sons[pragmasEffects] = g.emptyNode t.exc = effects.sons[exceptionEffects] t.tags = effects.sons[tagEffects] diff --git a/compiler/vmdeps.nim b/compiler/vmdeps.nim index 865ecd36e..bf2418eaf 100644 --- a/compiler/vmdeps.nim +++ b/compiler/vmdeps.nim @@ -229,7 +229,8 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo; for i in 1.. 0: t.n[0][pragmasEffects].copyTree + else: newNodeI(nkEmpty, info) else: result = mapTypeToBracket("proc", mNone, t, info) of tyOpenArray: result = mapTypeToBracket("openArray", mOpenArray, t, info) diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 1f251b73e..8a1be3720 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -1284,7 +1284,9 @@ proc customPragmaNode(n: NimNode): NimNode = let typ = n.getTypeInst() - if typ.typeKind == ntyTypeDesc: + if typ.kind == nnkBracketExpr and typ.len > 1 and typ[1].kind == nnkProcTy: + return typ[1][1] + elif typ.typeKind == ntyTypeDesc: let impl = typ[1].getImpl() if impl[0].kind == nnkPragmaExpr: return impl[0][1] diff --git a/tests/pragmas/tcustom_pragma.nim b/tests/pragmas/tcustom_pragma.nim index cd1edccf6..a5f86b54d 100644 --- a/tests/pragmas/tcustom_pragma.nim +++ b/tests/pragmas/tcustom_pragma.nim @@ -145,4 +145,12 @@ block: type Annotated {.simpleAttr.} = object proc generic_proc[T]() = - assert Annotated.hasCustomPragma(simpleAttr) \ No newline at end of file + assert Annotated.hasCustomPragma(simpleAttr) + + +#-------------------------------------------------------------------------- +# Pragma on proc type + +let a: proc(x: int) {.defaultValue(5).} = nil +static: + doAssert hasCustomPragma(a.type, defaultValue) -- cgit 1.4.1-2-gfad0 From 5b59852406e3d4a494186dddf9a5702062901668 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 11 Jul 2018 16:39:16 +0200 Subject: system.substr is not implemented with compilerProcs anymore --- compiler/ccgexprs.nim | 5 +- compiler/condsyms.nim | 1 + compiler/semtypes.nim | 4 ++ compiler/types.nim | 2 + lib/core/allocators.nim | 40 ++++++++++----- lib/core/strs.nim | 132 ++++++++++++++++++++++++++++++++++-------------- lib/system.nim | 34 ++++++++----- lib/system/sysstr.nim | 10 ++-- 8 files changed, 159 insertions(+), 69 deletions(-) (limited to 'lib/core') diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index b8a7f3b08..736701780 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -1902,8 +1902,9 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mIncl, mExcl, mCard, mLtSet, mLeSet, mEqSet, mMulSet, mPlusSet, mMinusSet, mInSet: genSetOp(p, e, d, op) - of mNewString, mNewStringOfCap, mCopyStr, mCopyStrLast, mExit, - mParseBiggestFloat: + of mCopyStr, mCopyStrLast: + genCall(p, e, d) + of mNewString, mNewStringOfCap, mExit, mParseBiggestFloat: var opr = e.sons[0].sym if lfNoDecl notin opr.loc.flags: discard cgsym(p.module, $opr.loc.r) diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index 8b2d36722..fcc7998df 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -72,3 +72,4 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimNoZeroTerminator") defineSymbol("nimNotNil") defineSymbol("nimVmExportFixed") + defineSymbol("nimNewRuntime") diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 17420111f..e888b7f7c 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -1671,6 +1671,8 @@ proc processMagicType(c: PContext, m: PSym) = of mString: setMagicType(c.config, m, tyString, c.config.target.ptrSize) rawAddSon(m.typ, getSysType(c.graph, m.info, tyChar)) + if c.config.selectedGc == gcDestructors: + incl m.typ.flags, tfHasAsgn of mCstring: setMagicType(c.config, m, tyCString, c.config.target.ptrSize) rawAddSon(m.typ, getSysType(c.graph, m.info, tyChar)) @@ -1710,6 +1712,8 @@ proc processMagicType(c: PContext, m: PSym) = setMagicType(c.config, m, tySet, 0) of mSeq: setMagicType(c.config, m, tySequence, 0) + if c.config.selectedGc == gcDestructors: + incl m.typ.flags, tfHasAsgn of mOpt: setMagicType(c.config, m, tyOpt, 0) of mOrdinal: diff --git a/compiler/types.nim b/compiler/types.nim index 4d3f99c31..66807cf97 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -278,6 +278,8 @@ proc analyseObjectWithTypeField(t: PType): TTypeFieldResult = proc isGCRef(t: PType): bool = result = t.kind in GcTypeKinds or (t.kind == tyProc and t.callConv == ccClosure) + if result and t.kind in {tyString, tySequence} and tfHasAsgn in t.flags: + result = false proc containsGarbageCollectedRef*(typ: PType): bool = # returns true if typ contains a reference, sequence or string (all the diff --git a/lib/core/allocators.nim b/lib/core/allocators.nim index 62f5e9756..74a8d3753 100644 --- a/lib/core/allocators.nim +++ b/lib/core/allocators.nim @@ -8,28 +8,40 @@ # type + AllocatorFlag* {.pure.} = enum ## flags describing the properties of the allocator + ThreadLocal ## the allocator is thread local only. + ZerosMem ## the allocator always zeros the memory on an allocation Allocator* = ptr object {.inheritable.} alloc*: proc (a: Allocator; size: int; alignment: int = 8): pointer {.nimcall.} dealloc*: proc (a: Allocator; p: pointer; size: int) {.nimcall.} realloc*: proc (a: Allocator; p: pointer; oldSize, newSize: int): pointer {.nimcall.} + flags*: set[AllocatorFlag] var - currentAllocator {.threadvar.}: Allocator + localAllocator {.threadvar.}: Allocator + sharedAllocator: Allocator -proc getCurrentAllocator*(): Allocator = - result = currentAllocator +proc getLocalAllocator*(): Allocator = + result = localAllocator -proc setCurrentAllocator*(a: Allocator) = - currentAllocator = a +proc setLocalAllocator*(a: Allocator) = + localAllocator = a -proc alloc*(size: int; alignment: int = 8): pointer = - let a = getCurrentAllocator() - result = a.alloc(a, size, alignment) +proc getSharedAllocator*(): Allocator = + result = sharedAllocator -proc dealloc*(p: pointer; size: int) = - let a = getCurrentAllocator() - a.dealloc(a, p, size) +proc setSharedAllocator*(a: Allocator) = + sharedAllocator = a -proc realloc*(p: pointer; oldSize, newSize: int): pointer = - let a = getCurrentAllocator() - result = a.realloc(a, p, oldSize, newSize) +when false: + proc alloc*(size: int; alignment: int = 8): pointer = + let a = getCurrentAllocator() + result = a.alloc(a, size, alignment) + + proc dealloc*(p: pointer; size: int) = + let a = getCurrentAllocator() + a.dealloc(a, p, size) + + proc realloc*(p: pointer; oldSize, newSize: int): pointer = + let a = getCurrentAllocator() + result = a.realloc(a, p, oldSize, newSize) diff --git a/lib/core/strs.nim b/lib/core/strs.nim index ff38aef1d..1ef4f09de 100644 --- a/lib/core/strs.nim +++ b/lib/core/strs.nim @@ -7,54 +7,114 @@ # distribution, for details about the copyright. # -## Default string implementation used by Nim's core. +## 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 rawNewString(space: int): NimString {.compilerProc.} + proc mnewString(len: int): NimString {.compilerProc.} + proc copyStrLast(s: NimString, start, last: int): NimString {.compilerProc.} + proc nimToCStringConv(s: NimString): cstring {.compilerProc, inline.} + proc copyStr(s: NimString, start: int): NimString {.compilerProc.} + proc toNimStr(str: cstring, len: int): NimString {.compilerProc.} + proc cstrToNimstr(str: cstring): NimString {.compilerRtl.} + proc copyString(src: NimString): NimString {.compilerRtl.} + proc copyStringRC1(src: NimString): NimString {.compilerRtl.} + proc copyDeepString(src: NimString): NimString {.inline.} + proc addChar(s: NimString, c: char): NimString + proc resizeString(dest: NimString, addlen: int): NimString {.compilerRtl.} + proc appendString(dest, src: NimString) {.compilerproc, inline.} + proc appendChar(dest: NimString, c: char) {.compilerproc, inline.} + proc setLengthStr(s: NimString, newLen: int): NimString {.compilerRtl.} + # ----------------- 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 - string {.core, exportc: "NimStringV2".} = object - len, cap: int - data: ptr UncheckedArray[char] + StrContent = object + cap: int + region: Allocator + data: UncheckedArray[char] + + NimString {.core.} = object + len: int + p: ptr StrContent ## invariant. Never nil const nimStrVersion {.core.} = 2 -template frees(s) = dealloc(s.data, s.cap + 1) +template isLiteral(s): bool = s.len == 0 or s.p.region == nil + +template contentSize(cap): int = cap + 1 + sizeof(int) + sizeof(Allocator) + +template frees(s) = + if not isLiteral(s): + s.p.region.dealloc(s.p, contentSize(s.p.cap)) + +proc `=destroy`(s: var NimString) = + frees(s) + s.len = 0 -proc `=destroy`(s: var string) = - if s.data != nil: - frees(s) - s.data = nil - s.len = 0 - s.cap = 0 +template lose(a) = + frees(a) -proc `=sink`(a: var string, b: string) = +proc `=sink`(a: var NimString, b: NimString) = # we hope this is optimized away for not yet alive objects: - if a.data != nil and a.data != b.data: - frees(a) + if unlikely(a.p == b.p): return + lose(a) a.len = b.len - a.cap = b.cap - a.data = b.data + a.p = b.p -proc `=`(a: var string; b: string) = - if a.data != nil and a.data != b.data: - frees(a) - a.data = nil +proc `=`(a: var NimString; b: NimString) = + if unlikely(a.p == b.p): return + lose(a) a.len = b.len - a.cap = b.cap - if b.data != nil: - a.data = cast[type(a.data)](alloc(a.cap + 1)) - copyMem(a.data, b.data, a.cap+1) - -proc resize(s: var string) = - let old = s.cap - if old == 0: s.cap = 8 - else: s.cap = (s.cap * 3) shr 1 - s.data = cast[type(s.data)](realloc(s.data, old + 1, s.cap + 1)) - -proc add*(s: var string; c: char) = - if s.len >= s.cap: resize(s) - s.data[s.len] = c - s.data[s.len+1] = '\0' + if isLiteral(b): + # we can shallow copy literals: + a.p = b.p + else: + let region = if a.p.region != nil: a.p.region 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 StrContent](region.alloc(contentSize(b.len))) + a.p.region = region + 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 + else: result = old * 3 div 2 # for large arrays * 3/2 is better + +proc prepareAdd(s: var NimString; addlen: int) = + if isLiteral(s): + let oldP = s.p + # can't mutate a literal, so we need a fresh copy here: + let region = getLocalAllocator() + s.p = cast[ptr StrContent](region.alloc(contentSize(s.len + addlen))) + s.p.region = region + s.p.cap = s.len + addlen + if s.len > 0: + # we are about to append, so there is no need to copy the \0 terminator: + copyMem(unsafeAddr s.p.data[0], unsafeAddr oldP.data[0], s.len) + elif s.len + addlen > s.p.cap: + let cap = max(s.len + addlen, resize(s.p.cap)) + s.p = s.p.region.realloc(s.p, oldSize = contentSize(s.p.cap), newSize = contentSize(cap)) + s.p.cap = cap + +proc nimAddCharV1(s: var NimString; c: char) {.compilerRtl.} = + prepareAdd(s, 1) + s.p.data[s.len] = c + s.p.data[s.len+1] = '\0' inc s.len proc ensure(s: var string; newLen: int) = @@ -71,8 +131,6 @@ proc add*(s: var string; y: string) = copyMem(addr s.data[len], y.data, y.data.len + 1) s.len = newLen -proc len*(s: string): int {.inline.} = s.len - proc newString*(len: int): string = result.len = len result.cap = len diff --git a/lib/system.nim b/lib/system.nim index d8ecfd55f..620dff724 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -413,7 +413,7 @@ include "system/inclrtl" const NoFakeVars* = defined(nimscript) ## true if the backend doesn't support \ ## "fake variables" like 'var EBADF {.importc.}: cint'. -when not defined(JS): +when not defined(JS) and not defined(gcDestructors): type TGenericSeq {.compilerproc, pure, inheritable.} = object len, reserved: int @@ -1702,17 +1702,6 @@ proc addQuitProc*(QuitProc: proc() {.noconv.}) {. # In case of an unhandled exeption the exit handlers should # not be called explicitly! The user may decide to do this manually though. -proc substr*(s: string, first = 0): string {. - magic: "CopyStr", importc: "copyStr", noSideEffect.} -proc substr*(s: string, first, last: int): string {. - magic: "CopyStrLast", importc: "copyStrLast", noSideEffect.} - ## copies a slice of `s` into a new string and returns this new - ## string. The bounds `first` and `last` denote the indices of - ## the first and last characters that shall be copied. If ``last`` - ## is omitted, it is treated as ``high(s)``. If ``last >= s.len``, ``s.len`` - ## is used instead: This means ``substr`` can also be used to `cut`:idx: - ## or `limit`:idx: a string's length. - when not defined(nimscript) and not defined(JS): proc zeroMem*(p: pointer, size: Natural) {.inline, benign.} ## overwrites the contents of the memory at ``p`` with the value 0. @@ -3262,7 +3251,11 @@ when not defined(JS): #and not defined(nimscript): when hasAlloc: include "system/mmdisp" {.pop.} {.push stack_trace: off, profiler:off.} - when hasAlloc: include "system/sysstr" + when hasAlloc: + when defined(gcDestructors): + include "core/strs" + else: + include "system/sysstr" {.pop.} when hasAlloc: include "system/strmantle" @@ -4171,6 +4164,21 @@ when not defined(js): proc toOpenArrayByte*(x: string; first, last: int): openarray[byte] {. magic: "Slice".} +proc substr*(s: string, first, last: int): string = + let L = max(min(last, high(s)) - first + 1, 0) + result = newString(L) + for i in 0 .. L-1: + result[i] = s[i+first] + +proc substr*(s: string, first = 0): string = + ## copies a slice of `s` into a new string and returns this new + ## string. The bounds `first` and `last` denote the indices of + ## the first and last characters that shall be copied. If ``last`` + ## is omitted, it is treated as ``high(s)``. If ``last >= s.len``, ``s.len`` + ## is used instead: This means ``substr`` can also be used to `cut`:idx: + ## or `limit`:idx: a string's length. + result = substr(s, first, high(s)) + type ForLoopStmt* {.compilerProc.} = object ## special type that marks a macro ## as a `for-loop macro`:idx: diff --git a/lib/system/sysstr.nim b/lib/system/sysstr.nim index 24ba02af6..9a73d3f9d 100644 --- a/lib/system/sysstr.nim +++ b/lib/system/sysstr.nim @@ -63,6 +63,8 @@ proc mnewString(len: int): NimString {.compilerProc.} = result.len = len proc copyStrLast(s: NimString, start, last: int): NimString {.compilerProc.} = + # This is not used by most recent versions of the compiler anymore, but + # required for bootstrapping purposes. let start = max(start, 0) let len = min(last, s.len-1) - start + 1 if len > 0: @@ -73,13 +75,15 @@ proc copyStrLast(s: NimString, start, last: int): NimString {.compilerProc.} = else: result = rawNewString(len) +proc copyStr(s: NimString, start: int): NimString {.compilerProc.} = + # This is not used by most recent versions of the compiler anymore, but + # required for bootstrapping purposes. + result = copyStrLast(s, start, s.len-1) + proc nimToCStringConv(s: NimString): cstring {.compilerProc, inline.} = if s == nil or s.len == 0: result = cstring"" else: result = cstring(addr s.data) -proc copyStr(s: NimString, start: int): NimString {.compilerProc.} = - result = copyStrLast(s, start, s.len-1) - proc toNimStr(str: cstring, len: int): NimString {.compilerProc.} = result = rawNewStringNoInit(len) result.len = len -- cgit 1.4.1-2-gfad0 From 74bf316619129b3c07c35c796cbc8744cbca87aa Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 13 Jul 2018 21:15:47 +0200 Subject: more progress on destructor based strings --- compiler/ccgcalls.nim | 10 ++- compiler/ccgexprs.nim | 74 +++++++++++++++------- compiler/ccgtrav.nim | 7 ++- compiler/ccgtypes.nim | 2 +- compiler/cgen.nim | 16 +++-- compiler/transf.nim | 8 +-- lib/core/strs.nim | 146 ++++++++++++++++++++----------------------- lib/system.nim | 163 +++++++++++++++++++++++++----------------------- lib/system/assign.nim | 5 -- lib/system/cgprocs.nim | 1 - lib/system/gc_ms.nim | 96 ++++++++++++++-------------- lib/system/hti.nim | 2 +- lib/system/jssys.nim | 4 +- lib/system/mmdisp.nim | 2 +- lib/system/sysio.nim | 2 +- lib/system/widestrs.nim | 3 +- 16 files changed, 288 insertions(+), 253 deletions(-) (limited to 'lib/core') diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim index 9712d5dce..ab15b9f2f 100644 --- a/compiler/ccgcalls.nim +++ b/compiler/ccgcalls.nim @@ -124,15 +124,19 @@ proc openArrayLoc(p: BProc, n: PNode): Rope = of tyString, tySequence: if skipTypes(n.typ, abstractInst).kind == tyVar and not compileToCpp(p.module): - result = "(*$1)$3, (*$1 ? (*$1)->$2 : 0)" % [a.rdLoc, lenField(p), dataField(p)] + var t: TLoc + t.r = "(*$1)" % [a.rdLoc] + result = "(*$1)$3, $2" % [a.rdLoc, lenExpr(p, t), dataField(p)] else: - result = "$1$3, ($1 ? $1->$2 : 0)" % [a.rdLoc, lenField(p), dataField(p)] + result = "$1$3, $2" % [a.rdLoc, lenExpr(p, a), dataField(p)] of tyArray: result = "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, a.t))] of tyPtr, tyRef: case lastSon(a.t).kind of tyString, tySequence: - result = "(*$1)$3, (*$1 ? (*$1)->$2 : 0)" % [a.rdLoc, lenField(p), dataField(p)] + var t: TLoc + t.r = "(*$1)" % [a.rdLoc] + result = "(*$1)$3, (*$1 ? (*$1)->$2 : 0)" % [a.rdLoc, lenExpr(p, t), dataField(p)] of tyArray: result = "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, lastSon(a.t)))] else: diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 736701780..689e2483e 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -254,7 +254,12 @@ proc genGenericAsgn(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = # tfShallow flag for the built-in string type too! So we check only # here for this flag, where it is reasonably safe to do so # (for objects, etc.): - if needToCopy notin flags or + if p.config.selectedGC == gcDestructors: + useStringh(p.module) + linefmt(p, cpsStmts, + "memcpy((void*)$1, (NIM_CONST void*)$2, sizeof($3));$n", + addrLoc(p.config, dest), addrLoc(p.config, src), rdLoc(dest)) + elif needToCopy notin flags or tfShallow in skipTypes(dest.t, abstractVarRange).flags: if dest.storage == OnStack or not usesWriteBarrier(p.config): useStringh(p.module) @@ -280,14 +285,18 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = of tyRef: genRefAssign(p, dest, src, flags) of tySequence: - if (needToCopy notin flags and src.storage != OnStatic) or canMove(src.lode): + if p.config.selectedGC == gcDestructors: + genGenericAsgn(p, dest, src, flags) + elif (needToCopy notin flags and src.storage != OnStatic) or canMove(src.lode): genRefAssign(p, dest, src, flags) else: linefmt(p, cpsStmts, "#genericSeqAssign($1, $2, $3);$n", addrLoc(p.config, dest), rdLoc(src), genTypeInfo(p.module, dest.t, dest.lode.info)) of tyString: - if (needToCopy notin flags and src.storage != OnStatic) or canMove(src.lode): + if p.config.selectedGC == gcDestructors: + genGenericAsgn(p, dest, src, flags) + elif (needToCopy notin flags and src.storage != OnStatic) or canMove(src.lode): genRefAssign(p, dest, src, flags) else: if dest.storage == OnStack or not usesWriteBarrier(p.config): @@ -457,6 +466,13 @@ proc binaryStmt(p: BProc, e: PNode, d: var TLoc, frmt: string) = initLocExpr(p, e.sons[2], b) lineCg(p, cpsStmts, frmt, rdLoc(a), rdLoc(b)) +proc binaryStmtAddr(p: BProc, e: PNode, d: var TLoc, frmt: string) = + var a, b: TLoc + if d.k != locNone: internalError(p.config, e.info, "binaryStmtAddr") + initLocExpr(p, e.sons[1], a) + initLocExpr(p, e.sons[2], b) + lineCg(p, cpsStmts, frmt, addrLoc(p.config, a), rdLoc(b)) + proc unaryStmt(p: BProc, e: PNode, d: var TLoc, frmt: string) = var a: TLoc if d.k != locNone: internalError(p.config, e.info, "unaryStmt") @@ -893,8 +909,8 @@ proc genBoundsCheck(p: BProc; arr, a, b: TLoc) = of tySequence, tyString: linefmt(p, cpsStmts, "if ($2-$1 != -1 && " & - "(!$3 || (NU)($1) >= (NU)($3->$4) || (NU)($2) >= (NU)($3->$4))) #raiseIndexError();$n", - rdLoc(a), rdLoc(b), rdLoc(arr), lenField(p)) + "((NU)($1) >= (NU)$3 || (NU)($2) >= (NU)$3)) #raiseIndexError();$n", + rdLoc(a), rdLoc(b), lenExpr(p, arr)) else: discard proc genOpenArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) = @@ -918,12 +934,12 @@ proc genSeqElem(p: BProc, n, x, y: PNode, d: var TLoc) = if optBoundsCheck in p.options: if ty.kind == tyString and (not defined(nimNoZeroTerminator) or optLaxStrings in p.options): linefmt(p, cpsStmts, - "if (!$2 || (NU)($1) > (NU)($2->$3)) #raiseIndexError();$n", - rdLoc(b), rdLoc(a), lenField(p)) + "if ((NU)($1) > (NU)$2) #raiseIndexError();$n", + rdLoc(b), lenExpr(p, a)) else: linefmt(p, cpsStmts, - "if (!$2 || (NU)($1) >= (NU)($2->$3)) #raiseIndexError();$n", - rdLoc(b), rdLoc(a), lenField(p)) + "if ((NU)($1) >= (NU)$2) #raiseIndexError();$n", + rdLoc(b), lenExpr(p, a)) if d.k == locNone: d.storage = OnHeap if skipTypes(a.t, abstractVar).kind in {tyRef, tyPtr}: a.r = ropecg(p.module, "(*$1)", a.r) @@ -1013,6 +1029,12 @@ proc genEcho(p: BProc, n: PNode) = proc gcUsage(conf: ConfigRef; n: PNode) = if conf.selectedGC == gcNone: message(conf, n.info, warnGcMem, n.renderTree) +proc strLoc(p: BProc; d: TLoc): Rope = + if p.config.selectedGc == gcDestructors: + result = addrLoc(p.config, d) + else: + result = rdLoc(d) + proc genStrConcat(p: BProc, e: PNode, d: var TLoc) = # # s = 'Hello ' & name & ', how do you feel?' & 'z' @@ -1040,13 +1062,14 @@ proc genStrConcat(p: BProc, e: PNode, d: var TLoc) = initLocExpr(p, e.sons[i + 1], a) if skipTypes(e.sons[i + 1].typ, abstractVarRange).kind == tyChar: inc(L) - add(appends, ropecg(p.module, "#appendChar($1, $2);$n", tmp.r, rdLoc(a))) + add(appends, ropecg(p.module, "#appendChar($1, $2);$n", strLoc(p, tmp), rdLoc(a))) else: if e.sons[i + 1].kind in {nkStrLit..nkTripleStrLit}: inc(L, len(e.sons[i + 1].strVal)) else: - addf(lens, "($1 ? $1->$2 : 0) + ", [rdLoc(a), lenField(p)]) - add(appends, ropecg(p.module, "#appendString($1, $2);$n", tmp.r, rdLoc(a))) + add(lens, lenExpr(p, a)) + add(lens, " + ") + add(appends, ropecg(p.module, "#appendString($1, $2);$n", strLoc(p, tmp), rdLoc(a))) linefmt(p, cpsStmts, "$1 = #rawNewString($2$3);$n", tmp.r, lens, rope(L)) add(p.s(cpsStmts), appends) if d.k == locNone: @@ -1079,16 +1102,21 @@ proc genStrAppend(p: BProc, e: PNode, d: var TLoc) = if skipTypes(e.sons[i + 2].typ, abstractVarRange).kind == tyChar: inc(L) add(appends, ropecg(p.module, "#appendChar($1, $2);$n", - rdLoc(dest), rdLoc(a))) + strLoc(p, dest), rdLoc(a))) else: if e.sons[i + 2].kind in {nkStrLit..nkTripleStrLit}: inc(L, len(e.sons[i + 2].strVal)) else: - addf(lens, "($1 ? $1->$2 : 0) + ", [rdLoc(a), lenField(p)]) + add(lens, lenExpr(p, a)) + add(lens, " + ") add(appends, ropecg(p.module, "#appendString($1, $2);$n", - rdLoc(dest), rdLoc(a))) - linefmt(p, cpsStmts, "$1 = #resizeString($1, $2$3);$n", - rdLoc(dest), lens, rope(L)) + strLoc(p, dest), rdLoc(a))) + if p.config.selectedGC == gcDestructors: + linefmt(p, cpsStmts, "#prepareAdd($1, $2$3);$n", + addrLoc(p.config, dest), lens, rope(L)) + else: + linefmt(p, cpsStmts, "$1 = #resizeString($1, $2$3);$n", + rdLoc(dest), lens, rope(L)) add(p.s(cpsStmts), appends) gcUsage(p.config, e) @@ -1440,7 +1468,7 @@ proc genRepr(p: BProc, e: PNode, d: var TLoc) = putIntoDest(p, b, e, "$1, $1Len_0" % [rdLoc(a)], a.storage) of tyString, tySequence: putIntoDest(p, b, e, - "$1$3, ($1 ? $1->$2 : 0)" % [rdLoc(a), lenField(p), dataField(p)], a.storage) + "$1$3, $2" % [rdLoc(a), lenExpr(p, a), dataField(p)], a.storage) of tyArray: putIntoDest(p, b, e, "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, a.t))], a.storage) @@ -1787,11 +1815,11 @@ proc genStrEquals(p: BProc, e: PNode, d: var TLoc) = if a.kind in {nkStrLit..nkTripleStrLit} and a.strVal == "": initLocExpr(p, e.sons[2], x) putIntoDest(p, d, e, - ropecg(p.module, "(!($1) || ($1)->$2 == 0)", rdLoc(x), lenField(p))) + ropecg(p.module, "($1 == 0)", lenExpr(p, x))) elif b.kind in {nkStrLit..nkTripleStrLit} and b.strVal == "": initLocExpr(p, e.sons[1], x) putIntoDest(p, d, e, - ropecg(p.module, "(!($1) || ($1)->$2 == 0)", rdLoc(x), lenField(p))) + ropecg(p.module, "($1 == 0)", lenExpr(p, x))) else: binaryExpr(p, e, d, "#eqStrings($1, $2)") @@ -1851,7 +1879,11 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = getTypeDesc(p.module, ranged), res]) of mConStrStr: genStrConcat(p, e, d) - of mAppendStrCh: binaryStmt(p, e, d, "$1 = #addChar($1, $2);$n") + of mAppendStrCh: + if p.config.selectedGC == gcDestructors: + binaryStmtAddr(p, e, d, "#nimAddCharV1($1, $2);$n") + else: + binaryStmt(p, e, d, "$1 = #addChar($1, $2);$n") of mAppendStrStr: genStrAppend(p, e, d) of mAppendSeqElem: genSeqElemAppend(p, e, d) of mEqStr: genStrEquals(p, e, d) diff --git a/compiler/ccgtrav.nim b/compiler/ccgtrav.nim index e74d5a953..5a6dc8a6e 100644 --- a/compiler/ccgtrav.nim +++ b/compiler/ccgtrav.nim @@ -107,8 +107,11 @@ proc genTraverseProcSeq(c: TTraversalClosure, accessor: Rope, typ: PType) = var i: TLoc getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo(), tyInt), i) let oldCode = p.s(cpsStmts) - lineF(p, cpsStmts, "for ($1 = 0; $1 < $2->$3; $1++) {$n", - [i.r, accessor, lenField(c.p)]) + var a: TLoc + a.r = accessor + + lineF(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n", + [i.r, lenExpr(c.p, a)]) let oldLen = p.s(cpsStmts).len genTraverseProc(c, "$1$3[$2]" % [accessor, i.r, dataField(c.p)], typ.sons[0]) if p.s(cpsStmts).len == oldLen: diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 66ad34c32..756711642 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -282,7 +282,7 @@ proc getSimpleTypeDesc(m: BModule, typ: PType): Rope = of tyString: case detectStrVersion(m) of 2: - discard cgsym(m, "string") + discard cgsym(m, "NimStringV2") result = typeNameOrLiteral(m, typ, "NimStringV2") else: discard cgsym(m, "NimStringDesc") diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 32891c62c..0ca7533d4 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -235,9 +235,20 @@ proc getTempName(m: BModule): Rope = result = m.tmpBase & rope(m.labels) inc m.labels +proc rdLoc(a: TLoc): Rope = + # 'read' location (deref if indirect) + result = a.r + if lfIndirect in a.flags: result = "(*$1)" % [result] + proc lenField(p: BProc): Rope = result = rope(if p.module.compileToCpp: "len" else: "Sup.len") +proc lenExpr(p: BProc; a: TLoc): Rope = + if p.config.selectedGc == gcDestructors: + result = rdLoc(a) & ".len" + else: + result = "($1 ? $1->$2 : 0)" % [rdLoc(a), lenField(p)] + proc dataField(p: BProc): Rope = result = rope"->data" @@ -246,11 +257,6 @@ include ccgtypes # ------------------------------ Manager of temporaries ------------------ -proc rdLoc(a: TLoc): Rope = - # 'read' location (deref if indirect) - result = a.r - if lfIndirect in a.flags: result = "(*$1)" % [result] - proc addrLoc(conf: ConfigRef; a: TLoc): Rope = result = a.r if lfIndirect notin a.flags and mapType(conf, a.t) != ctArray: diff --git a/compiler/transf.nim b/compiler/transf.nim index dae8d1ee6..acfccb5ff 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -1049,8 +1049,8 @@ proc transformStmt*(g: ModuleGraph; module: PSym, n: PNode): PNode = when useEffectSystem: trackTopLevelStmt(g, module, result) #if n.info ?? "temp.nim": # echo renderTree(result, {renderIds}) - if c.needsDestroyPass: - result = injectDestructorCalls(g, module, result) + #if c.needsDestroyPass: + # result = injectDestructorCalls(g, module, result) incl(result.flags, nfTransf) proc transformExpr*(g: ModuleGraph; module: PSym, n: PNode): PNode = @@ -1062,6 +1062,6 @@ proc transformExpr*(g: ModuleGraph; module: PSym, n: PNode): PNode = liftDefer(c, result) # expressions are not to be injected with destructor calls as that # the list of top level statements needs to be collected before. - if c.needsDestroyPass: - result = injectDestructorCalls(g, module, result) + #if c.needsDestroyPass: + # result = injectDestructorCalls(g, module, result) incl(result.flags, nfTransf) diff --git a/lib/core/strs.nim b/lib/core/strs.nim index 1ef4f09de..c07c4605e 100644 --- a/lib/core/strs.nim +++ b/lib/core/strs.nim @@ -14,28 +14,15 @@ when false: #proc rawNewStringNoInit(space: int): NimString {.compilerProc.} # seems to be unused. - proc rawNewString(space: int): NimString {.compilerProc.} - proc mnewString(len: int): NimString {.compilerProc.} - proc copyStrLast(s: NimString, start, last: int): NimString {.compilerProc.} - proc nimToCStringConv(s: NimString): cstring {.compilerProc, inline.} - proc copyStr(s: NimString, start: int): NimString {.compilerProc.} - proc toNimStr(str: cstring, len: int): NimString {.compilerProc.} - proc cstrToNimstr(str: cstring): NimString {.compilerRtl.} - proc copyString(src: NimString): NimString {.compilerRtl.} - proc copyStringRC1(src: NimString): NimString {.compilerRtl.} proc copyDeepString(src: NimString): NimString {.inline.} - proc addChar(s: NimString, c: char): NimString - proc resizeString(dest: NimString, addlen: int): NimString {.compilerRtl.} - proc appendString(dest, src: NimString) {.compilerproc, inline.} - proc appendChar(dest: NimString, c: char) {.compilerproc, inline.} proc setLengthStr(s: NimString, newLen: int): NimString {.compilerRtl.} # ----------------- sequences ---------------------------------------------- - proc incrSeqV3(s: PGenericSeq, typ: PNimType): PGenericSeq {.compilerProc.} = + 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.} = + proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} + proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} import allocators @@ -45,35 +32,36 @@ type region: Allocator data: UncheckedArray[char] - NimString {.core.} = object + NimStringV2 {.core.} = object len: int - p: ptr StrContent ## invariant. Never nil + p: ptr StrContent ## can be nil if len == 0. const nimStrVersion {.core.} = 2 -template isLiteral(s): bool = s.len == 0 or s.p.region == nil +template isLiteral(s): bool = s.p == nil or s.p.region == nil template contentSize(cap): int = cap + 1 + sizeof(int) + sizeof(Allocator) template frees(s) = if not isLiteral(s): - s.p.region.dealloc(s.p, contentSize(s.p.cap)) + s.p.region.dealloc(s.p.region, s.p, contentSize(s.p.cap)) -proc `=destroy`(s: var NimString) = +proc `=destroy`(s: var NimStringV2) = frees(s) s.len = 0 + s.p = nil template lose(a) = frees(a) -proc `=sink`(a: var NimString, b: NimString) = +proc `=sink`(a: var NimStringV2, b: NimStringV2) = # we hope this is optimized away for not yet alive objects: if unlikely(a.p == b.p): return lose(a) a.len = b.len a.p = b.p -proc `=`(a: var NimString; b: NimString) = +proc `=`(a: var NimStringV2; b: NimStringV2) = if unlikely(a.p == b.p): return lose(a) a.len = b.len @@ -85,7 +73,7 @@ proc `=`(a: var NimString; b: NimString) = # 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 StrContent](region.alloc(contentSize(b.len))) + a.p = cast[ptr StrContent](region.alloc(region, contentSize(b.len))) a.p.region = region a.p.cap = b.len copyMem(unsafeAddr a.p.data[0], unsafeAddr b.p.data[0], b.len+1) @@ -95,12 +83,12 @@ proc resize(old: int): int {.inline.} = elif old < 65536: result = old * 2 else: result = old * 3 div 2 # for large arrays * 3/2 is better -proc prepareAdd(s: var NimString; addlen: int) = +proc prepareAdd(s: var NimStringV2; addlen: int) {.compilerRtl.} = if isLiteral(s): let oldP = s.p # can't mutate a literal, so we need a fresh copy here: let region = getLocalAllocator() - s.p = cast[ptr StrContent](region.alloc(contentSize(s.len + addlen))) + s.p = cast[ptr StrContent](region.alloc(region, contentSize(s.len + addlen))) s.p.region = region s.p.cap = s.len + addlen if s.len > 0: @@ -108,61 +96,65 @@ proc prepareAdd(s: var NimString; addlen: int) = copyMem(unsafeAddr s.p.data[0], unsafeAddr oldP.data[0], s.len) elif s.len + addlen > s.p.cap: let cap = max(s.len + addlen, resize(s.p.cap)) - s.p = s.p.region.realloc(s.p, oldSize = contentSize(s.p.cap), newSize = contentSize(cap)) + s.p = cast[ptr StrContent](s.p.region.realloc(s.p.region, s.p, + oldSize = contentSize(s.p.cap), + newSize = contentSize(cap))) s.p.cap = cap -proc nimAddCharV1(s: var NimString; c: char) {.compilerRtl.} = +proc nimAddCharV1(s: var NimStringV2; c: char) {.compilerRtl.} = prepareAdd(s, 1) s.p.data[s.len] = c s.p.data[s.len+1] = '\0' inc s.len -proc ensure(s: var string; newLen: int) = - let old = s.cap - if newLen >= old: - s.cap = max((old * 3) shr 1, newLen) - if s.cap > 0: - s.data = cast[type(s.data)](realloc(s.data, old + 1, s.cap + 1)) - -proc add*(s: var string; y: string) = - if y.len != 0: - let newLen = s.len + y.len - ensure(s, newLen) - copyMem(addr s.data[len], y.data, y.data.len + 1) - s.len = newLen - -proc newString*(len: int): string = - result.len = len - result.cap = len - if len > 0: - result.data = alloc0(len+1) - -converter toCString(x: string): cstring {.core, inline.} = - if x.len == 0: cstring"" else: cast[cstring](x.data) - -proc newStringOfCap*(cap: int): string = - result.len = 0 - result.cap = cap - if cap > 0: - result.data = alloc(cap+1) - -proc `&`*(a, b: string): string = - let sum = a.len + b.len - result = newStringOfCap(sum) - result.len = sum - copyMem(addr result.data[0], a.data, a.len) - copyMem(addr result.data[a.len], b.data, b.len) - if sum > 0: - result.data[sum] = '\0' - -proc concat(x: openArray[string]): string {.core.} = - ## used be the code generator to optimize 'x & y & z ...' - var sum = 0 - for i in 0 ..< x.len: inc(sum, x[i].len) - result = newStringOfCap(sum) - sum = 0 - for i in 0 ..< x.len: - let L = x[i].len - copyMem(addr result.data[sum], x[i].data, L) - inc(sum, L) - +proc toNimStr(str: cstring, len: int): NimStringV2 {.compilerProc.} = + if len <= 0: + result = NimStringV2(len: 0, p: nil) + else: + let region = getLocalAllocator() + var p = cast[ptr StrContent](region.alloc(region, contentSize(len))) + p.region = region + p.cap = len + if len > 0: + # we are about to append, so there is no need to copy the \0 terminator: + copyMem(unsafeAddr p.data[0], str, len) + result = NimStringV2(len: 0, p: p) + +proc cstrToNimstr(str: cstring): NimStringV2 {.compilerRtl.} = + if str == nil: toNimStr(str, 0) + else: toNimStr(str, str.len) + +proc nimToCStringConv(s: NimStringV2): cstring {.compilerProc, inline.} = + if s.len == 0: result = cstring"" + else: result = cstring(unsafeAddr s.p.data) + +proc appendString(dest: var NimStringV2; src: NimStringV2) {.compilerproc, inline.} = + if src.len > 0: + # also copy the \0 terminator: + copyMem(unsafeAddr dest.p.data[dest.len], unsafeAddr src.p.data[0], src.len+1) + +proc appendChar(dest: var NimStringV2; c: char) {.compilerproc, inline.} = + dest.p.data[dest.len] = c + dest.p.data[dest.len+1] = '\0' + inc dest.len + +proc rawNewString(space: int): NimStringV2 {.compilerProc.} = + # this is also 'system.newStringOfCap'. + if space <= 0: + result = NimStringV2(len: 0, p: nil) + else: + let region = getLocalAllocator() + var p = cast[ptr StrContent](region.alloc(region, contentSize(space))) + p.region = region + p.cap = space + result = NimStringV2(len: 0, p: p) + +proc mnewString(len: int): NimStringV2 {.compilerProc.} = + if len <= 0: + result = NimStringV2(len: 0, p: nil) + else: + let region = getLocalAllocator() + var p = cast[ptr StrContent](region.alloc(region, contentSize(len))) + p.region = region + p.cap = len + result = NimStringV2(len: len, p: p) diff --git a/lib/system.nim b/lib/system.nim index 620dff724..1defe20ee 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -211,6 +211,7 @@ proc new*(T: typedesc): auto = new(r) return r +const ThisIsSystem = true proc internalNew*[T](a: var ref T) {.magic: "New", noSideEffect.} ## leaked implementation detail. Do not use. @@ -426,8 +427,9 @@ when not defined(JS) and not defined(gcDestructors): NimString = ptr NimStringDesc when not defined(JS) and not defined(nimscript): - template space(s: PGenericSeq): int {.dirty.} = - s.reserved and not (seqShallowFlag or strlitFlag) + when not defined(gcDestructors): + template space(s: PGenericSeq): int {.dirty.} = + s.reserved and not (seqShallowFlag or strlitFlag) include "system/hti" type @@ -730,7 +732,8 @@ proc newSeqOfCap*[T](cap: Natural): seq[T] {. ## ``cap``. discard -when not defined(JS): +when not defined(JS) and not defined(gcDestructors): + # XXX enable this for --gc:destructors proc newSeqUninitialized*[T: SomeNumber](len: Natural): seq[T] = ## creates a new sequence of type ``seq[T]`` with length ``len``. ## @@ -1502,11 +1505,11 @@ const hasAlloc = (hostOS != "standalone" or not defined(nogc)) and not defined(n when not defined(JS) and not defined(nimscript) and hostOS != "standalone": include "system/cgprocs" -when not defined(JS) and not defined(nimscript) and hasAlloc: +when not defined(JS) and not defined(nimscript) and hasAlloc and not defined(gcDestructors): proc addChar(s: NimString, c: char): NimString {.compilerProc, benign.} -proc add *[T](x: var seq[T], y: T) {.magic: "AppendSeqElem", noSideEffect.} -proc add *[T](x: var seq[T], y: openArray[T]) {.noSideEffect.} = +proc add*[T](x: var seq[T], y: T) {.magic: "AppendSeqElem", noSideEffect.} +proc add*[T](x: var seq[T], y: openArray[T]) {.noSideEffect.} = ## Generic proc for adding a data item `y` to a container `x`. ## For containers that have an order, `add` means *append*. New generic ## containers should also call their adding proc `add` for consistency. @@ -2829,6 +2832,58 @@ else: if x < 0: -x else: x {.pop.} +when not defined(JS): + proc likely_proc(val: bool): bool {.importc: "likely", nodecl, nosideeffect.} + proc unlikely_proc(val: bool): bool {.importc: "unlikely", nodecl, nosideeffect.} + +template likely*(val: bool): bool = + ## Hints the optimizer that `val` is likely going to be true. + ## + ## You can use this template to decorate a branch condition. On certain + ## platforms this can help the processor predict better which branch is + ## going to be run. Example: + ## + ## .. code-block:: nim + ## for value in inputValues: + ## if likely(value <= 100): + ## process(value) + ## else: + ## echo "Value too big!" + ## + ## On backends without branch prediction (JS and the nimscript VM), this + ## template will not affect code execution. + when nimvm: + val + else: + when defined(JS): + val + else: + likely_proc(val) + +template unlikely*(val: bool): bool = + ## Hints the optimizer that `val` is likely going to be false. + ## + ## You can use this proc to decorate a branch condition. On certain + ## platforms this can help the processor predict better which branch is + ## going to be run. Example: + ## + ## .. code-block:: nim + ## for value in inputValues: + ## if unlikely(value > 100): + ## echo "Value too big!" + ## else: + ## process(value) + ## + ## On backends without branch prediction (JS and the nimscript VM), this + ## template will not affect code execution. + when nimvm: + val + else: + when defined(JS): + val + else: + unlikely_proc(val) + type FileSeekPos* = enum ## Position relative to which seek should happen # The values are ordered so that they match with stdio @@ -2862,10 +2917,11 @@ when not defined(JS): #and not defined(nimscript): when declared(nimGC_setStackBottom): nimGC_setStackBottom(locals) - {.push profiler: off.} - var - strDesc = TNimType(size: sizeof(string), kind: tyString, flags: {ntfAcyclic}) - {.pop.} + when not defined(gcDestructors): + {.push profiler: off.} + var + strDesc = TNimType(size: sizeof(string), kind: tyString, flags: {ntfAcyclic}) + {.pop.} # ----------------- IO Part ------------------------------------------------ @@ -3302,8 +3358,9 @@ when not defined(JS): #and not defined(nimscript): while f.readLine(res): yield res when not defined(nimscript) and hasAlloc: - include "system/assign" - include "system/repr" + when not defined(gcDestructors): + include "system/assign" + include "system/repr" when hostOS != "standalone" and not defined(nimscript): proc getCurrentException*(): ref Exception {.compilerRtl, inl, benign.} = @@ -3410,58 +3467,6 @@ proc quit*(errormsg: string, errorcode = QuitFailure) {.noReturn.} = {.pop.} # checks {.pop.} # hints -when not defined(JS): - proc likely_proc(val: bool): bool {.importc: "likely", nodecl, nosideeffect.} - proc unlikely_proc(val: bool): bool {.importc: "unlikely", nodecl, nosideeffect.} - -template likely*(val: bool): bool = - ## Hints the optimizer that `val` is likely going to be true. - ## - ## You can use this template to decorate a branch condition. On certain - ## platforms this can help the processor predict better which branch is - ## going to be run. Example: - ## - ## .. code-block:: nim - ## for value in inputValues: - ## if likely(value <= 100): - ## process(value) - ## else: - ## echo "Value too big!" - ## - ## On backends without branch prediction (JS and the nimscript VM), this - ## template will not affect code execution. - when nimvm: - val - else: - when defined(JS): - val - else: - likely_proc(val) - -template unlikely*(val: bool): bool = - ## Hints the optimizer that `val` is likely going to be false. - ## - ## You can use this proc to decorate a branch condition. On certain - ## platforms this can help the processor predict better which branch is - ## going to be run. Example: - ## - ## .. code-block:: nim - ## for value in inputValues: - ## if unlikely(value > 100): - ## echo "Value too big!" - ## else: - ## process(value) - ## - ## On backends without branch prediction (JS and the nimscript VM), this - ## template will not affect code execution. - when nimvm: - val - else: - when defined(JS): - val - else: - unlikely_proc(val) - proc `/`*(x, y: int): float {.inline, noSideEffect.} = ## integer division that results in a float. result = toFloat(x) / toFloat(y) @@ -4090,6 +4095,21 @@ template once*(body: untyped): untyped = {.pop.} #{.push warning[GcMem]: off, warning[Uninit]: off.} +proc substr*(s: string, first, last: int): string = + let L = max(min(last, high(s)) - first + 1, 0) + result = newString(L) + for i in 0 .. L-1: + result[i] = s[i+first] + +proc substr*(s: string, first = 0): string = + ## copies a slice of `s` into a new string and returns this new + ## string. The bounds `first` and `last` denote the indices of + ## the first and last characters that shall be copied. If ``last`` + ## is omitted, it is treated as ``high(s)``. If ``last >= s.len``, ``s.len`` + ## is used instead: This means ``substr`` can also be used to `cut`:idx: + ## or `limit`:idx: a string's length. + result = substr(s, first, high(s)) + when defined(nimconfig): include "system/nimscript" @@ -4164,21 +4184,6 @@ when not defined(js): proc toOpenArrayByte*(x: string; first, last: int): openarray[byte] {. magic: "Slice".} -proc substr*(s: string, first, last: int): string = - let L = max(min(last, high(s)) - first + 1, 0) - result = newString(L) - for i in 0 .. L-1: - result[i] = s[i+first] - -proc substr*(s: string, first = 0): string = - ## copies a slice of `s` into a new string and returns this new - ## string. The bounds `first` and `last` denote the indices of - ## the first and last characters that shall be copied. If ``last`` - ## is omitted, it is treated as ``high(s)``. If ``last >= s.len``, ``s.len`` - ## is used instead: This means ``substr`` can also be used to `cut`:idx: - ## or `limit`:idx: a string's length. - result = substr(s, first, high(s)) - type ForLoopStmt* {.compilerProc.} = object ## special type that marks a macro ## as a `for-loop macro`:idx: diff --git a/lib/system/assign.nim b/lib/system/assign.nim index 16b56aba7..2b74e6682 100644 --- a/lib/system/assign.nim +++ b/lib/system/assign.nim @@ -202,11 +202,6 @@ proc objectInit(dest: pointer, typ: PNimType) = # ---------------------- assign zero ----------------------------------------- -proc nimDestroyRange[T](r: T) {.compilerProc.} = - # internal proc used for destroying sequences and arrays - mixin `=destroy` - for i in countup(0, r.len - 1): `=destroy`(r[i]) - proc genericReset(dest: pointer, mt: PNimType) {.compilerProc, benign.} proc genericResetAux(dest: pointer, n: ptr TNimNode) = var d = cast[ByteAddress](dest) diff --git a/lib/system/cgprocs.nim b/lib/system/cgprocs.nim index 660c68116..72219c2b7 100644 --- a/lib/system/cgprocs.nim +++ b/lib/system/cgprocs.nim @@ -12,7 +12,6 @@ type LibHandle = pointer # private type ProcAddr = pointer # library loading and loading of procs: -{.deprecated: [TLibHandle: LibHandle, TProcAddr: ProcAddr].} proc nimLoadLibrary(path: string): LibHandle {.compilerproc.} proc nimUnloadLibrary(lib: LibHandle) {.compilerproc.} diff --git a/lib/system/gc_ms.nim b/lib/system/gc_ms.nim index 75f9c6749..96221b175 100644 --- a/lib/system/gc_ms.nim +++ b/lib/system/gc_ms.nim @@ -264,12 +264,13 @@ proc forAllChildren(cell: PCell, op: WalkOp) = of tyRef, tyOptAsRef: # common case forAllChildrenAux(cellToUsr(cell), cell.typ.base, op) of tySequence: - var d = cast[ByteAddress](cellToUsr(cell)) - var s = cast[PGenericSeq](d) - if s != nil: - for i in 0..s.len-1: - forAllChildrenAux(cast[pointer](d +% i *% cell.typ.base.size +% - GenericSeqSize), cell.typ.base, op) + when not defined(gcDestructors): + var d = cast[ByteAddress](cellToUsr(cell)) + var s = cast[PGenericSeq](d) + if s != nil: + for i in 0..s.len-1: + forAllChildrenAux(cast[pointer](d +% i *% cell.typ.base.size +% + GenericSeqSize), cell.typ.base, op) else: discard proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap): pointer = @@ -310,53 +311,54 @@ proc newObjNoInit(typ: PNimType, size: int): pointer {.compilerRtl.} = result = rawNewObj(typ, size, gch) when defined(memProfiler): nimProfile(size) -proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} = - # `newObj` already uses locks, so no need for them here. - let size = addInt(mulInt(len, typ.base.size), GenericSeqSize) - result = newObj(typ, size) - cast[PGenericSeq](result).len = len - cast[PGenericSeq](result).reserved = len - when defined(memProfiler): nimProfile(size) - proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} = result = rawNewObj(typ, size, gch) zeroMem(result, size) when defined(memProfiler): nimProfile(size) -proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} = - let size = addInt(mulInt(len, typ.base.size), GenericSeqSize) - result = newObj(typ, size) - cast[PGenericSeq](result).len = len - cast[PGenericSeq](result).reserved = len - when defined(memProfiler): nimProfile(size) - -proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer = - acquire(gch) - collectCT(gch, newsize + sizeof(Cell)) - var ol = usrToCell(old) - sysAssert(ol.typ != nil, "growObj: 1") - gcAssert(ol.typ.kind in {tyString, tySequence}, "growObj: 2") - - var res = cast[PCell](rawAlloc(gch.region, newsize + sizeof(Cell))) - var elemSize = 1 - if ol.typ.kind != tyString: elemSize = ol.typ.base.size - incTypeSize ol.typ, newsize - - var oldsize = cast[PGenericSeq](old).len*elemSize + GenericSeqSize - copyMem(res, ol, oldsize + sizeof(Cell)) - zeroMem(cast[pointer](cast[ByteAddress](res)+% oldsize +% sizeof(Cell)), - newsize-oldsize) - sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "growObj: 3") - when withBitvectors: incl(gch.allocated, res) - when useCellIds: - inc gch.idGenerator - res.id = gch.idGenerator - release(gch) - result = cellToUsr(res) - when defined(memProfiler): nimProfile(newsize-oldsize) +when not defined(gcDestructors): + proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} = + # `newObj` already uses locks, so no need for them here. + let size = addInt(mulInt(len, typ.base.size), GenericSeqSize) + result = newObj(typ, size) + cast[PGenericSeq](result).len = len + cast[PGenericSeq](result).reserved = len + when defined(memProfiler): nimProfile(size) + + proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} = + let size = addInt(mulInt(len, typ.base.size), GenericSeqSize) + result = newObj(typ, size) + cast[PGenericSeq](result).len = len + cast[PGenericSeq](result).reserved = len + when defined(memProfiler): nimProfile(size) + + proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer = + acquire(gch) + collectCT(gch, newsize + sizeof(Cell)) + var ol = usrToCell(old) + sysAssert(ol.typ != nil, "growObj: 1") + gcAssert(ol.typ.kind in {tyString, tySequence}, "growObj: 2") + + var res = cast[PCell](rawAlloc(gch.region, newsize + sizeof(Cell))) + var elemSize = 1 + if ol.typ.kind != tyString: elemSize = ol.typ.base.size + incTypeSize ol.typ, newsize + + var oldsize = cast[PGenericSeq](old).len*elemSize + GenericSeqSize + copyMem(res, ol, oldsize + sizeof(Cell)) + zeroMem(cast[pointer](cast[ByteAddress](res)+% oldsize +% sizeof(Cell)), + newsize-oldsize) + sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "growObj: 3") + when withBitvectors: incl(gch.allocated, res) + when useCellIds: + inc gch.idGenerator + res.id = gch.idGenerator + release(gch) + result = cellToUsr(res) + when defined(memProfiler): nimProfile(newsize-oldsize) -proc growObj(old: pointer, newsize: int): pointer {.rtl.} = - result = growObj(old, newsize, gch) + proc growObj(old: pointer, newsize: int): pointer {.rtl.} = + result = growObj(old, newsize, gch) {.push profiler:off.} diff --git a/lib/system/hti.nim b/lib/system/hti.nim index 45b1d1cd3..bb3769ac4 100644 --- a/lib/system/hti.nim +++ b/lib/system/hti.nim @@ -7,7 +7,7 @@ # distribution, for details about the copyright. # -when declared(NimString): +when declared(ThisIsSystem): # we are in system module: {.pragma: codegenType, compilerproc.} else: diff --git a/lib/system/jssys.nim b/lib/system/jssys.nim index e12bab184..5fff869f0 100644 --- a/lib/system/jssys.nim +++ b/lib/system/jssys.nim @@ -31,8 +31,6 @@ type JSRef = ref RootObj # Fake type. -{.deprecated: [TSafePoint: SafePoint, TCallFrame: CallFrame].} - var framePtr {.importc, nodecl, volatile.}: PCallFrame excHandler {.importc, nodecl, volatile.}: int = 0 @@ -506,7 +504,7 @@ proc chckNilDisp(p: pointer) {.compilerproc.} = if p == nil: sysFatal(NilAccessError, "cannot dispatch; dispatcher is nil") -type NimString = string # hack for hti.nim +const ThisIsSystem = true # for hti.nim include "system/hti" proc isFatPointer(ti: PNimType): bool = diff --git a/lib/system/mmdisp.nim b/lib/system/mmdisp.nim index ff2da7aba..e7e14b948 100644 --- a/lib/system/mmdisp.nim +++ b/lib/system/mmdisp.nim @@ -554,7 +554,7 @@ else: else: include "system/gc" -when not declared(nimNewSeqOfCap): +when not declared(nimNewSeqOfCap) and not defined(gcDestructors): proc nimNewSeqOfCap(typ: PNimType, cap: int): pointer {.compilerproc.} = when defined(gcRegions): let s = mulInt(cap, typ.base.size) # newStr already adds GenericSeqSize diff --git a/lib/system/sysio.nim b/lib/system/sysio.nim index f3a576be0..f6e691c0d 100644 --- a/lib/system/sysio.nim +++ b/lib/system/sysio.nim @@ -148,7 +148,7 @@ proc readLine(f: File, line: var TaintedString): bool = if line.string.isNil: line = TaintedString(newStringOfCap(80)) else: - when not defined(nimscript): + when not defined(nimscript) and not defined(gcDestructors): sp = cint(cast[PGenericSeq](line.string).space) line.string.setLen(sp) while true: diff --git a/lib/system/widestrs.nim b/lib/system/widestrs.nim index a8b28c279..85e5e1462 100644 --- a/lib/system/widestrs.nim +++ b/lib/system/widestrs.nim @@ -10,13 +10,12 @@ # Nim support for C/C++'s `wide strings`:idx:. This is part of the system # module! Do not import it directly! -when not declared(NimString): +when not declared(ThisIsSystem): {.error: "You must not import this module explicitly".} type Utf16Char* = distinct int16 WideCString* = ref UncheckedArray[Utf16Char] -{.deprecated: [TUtf16Char: Utf16Char].} proc len*(w: WideCString): int = ## returns the length of a widestring. This traverses the whole string to -- cgit 1.4.1-2-gfad0 From 32afdc09c6e2e6b32566df9e70cb71ae43eb9355 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 17 Jul 2018 13:19:42 +0200 Subject: WIP: strings/seqs based on destructors --- compiler/ast.nim | 1 + compiler/ccgexprs.nim | 20 +++- compiler/ccgliterals.nim | 8 +- compiler/ccgtypes.nim | 39 +++++-- compiler/cgen.nim | 5 +- compiler/destroyer.nim | 25 +++-- compiler/semstmts.nim | 5 +- compiler/types.nim | 13 ++- lib/core/seqs.nim | 236 +++++++++++++++++++++------------------ lib/core/strs.nim | 25 +++-- lib/system.nim | 12 ++ lib/system/jssys.nim | 1 - tests/destructor/tcustomseqs.nim | 7 +- 13 files changed, 248 insertions(+), 149 deletions(-) (limited to 'lib/core') diff --git a/compiler/ast.nim b/compiler/ast.nim index 6302c21b9..7cc785ad7 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -626,6 +626,7 @@ type mIsPartOf, mAstToStr, mParallel, mSwap, mIsNil, mArrToSeq, mCopyStr, mCopyStrLast, mNewString, mNewStringOfCap, mParseBiggestFloat, + mMove, mWasMoved, mReset, mArray, mOpenArray, mRange, mSet, mSeq, mOpt, mVarargs, mRef, mPtr, mVar, mDistinct, mVoid, mTuple, diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 689e2483e..0d87cc19d 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -1224,8 +1224,14 @@ proc genNewSeq(p: BProc, e: PNode) = var a, b: TLoc initLocExpr(p, e.sons[1], a) initLocExpr(p, e.sons[2], b) - genNewSeqAux(p, a, b.rdLoc) - gcUsage(p.config, e) + if p.config.selectedGC == gcDestructors: + let seqtype = skipTypes(e.sons[1].typ, abstractVarRange) + linefmt(p, cpsStmts, "$1.len = $2; $1.p = ($4*) #newSeqPayload($2, sizeof($3));$n", + a.rdLoc, b.rdLoc, getTypeDesc(p.module, seqtype.lastSon), + getSeqPayloadType(p.module, seqtype)) + else: + genNewSeqAux(p, a, b.rdLoc) + gcUsage(p.config, e) proc genNewSeqOfCap(p: BProc; e: PNode; d: var TLoc) = let seqtype = skipTypes(e.typ, abstractVarRange) @@ -1543,6 +1549,9 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) = else: internalError(p.config, e.info, "genArrayLen()") proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc) = + if p.config.selectedGc == gcDestructors: + genCall(p, e, d) + return var a, b: TLoc assert(d.k == locNone) var x = e.sons[1] @@ -1561,8 +1570,11 @@ proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc) = gcUsage(p.config, e) proc genSetLengthStr(p: BProc, e: PNode, d: var TLoc) = - binaryStmt(p, e, d, "$1 = #setLengthStr($1, $2);$n") - gcUsage(p.config, e) + if p.config.selectedGc == gcDestructors: + binaryStmtAddr(p, e, d, "#setLengthStrV2($1, $2);$n") + else: + binaryStmt(p, e, d, "$1 = #setLengthStr($1, $2);$n") + gcUsage(p.config, e) proc genSwap(p: BProc, e: PNode, d: var TLoc) = # swap(a, b) --> diff --git a/compiler/ccgliterals.nim b/compiler/ccgliterals.nim index cfe71375e..a06e4e5d0 100644 --- a/compiler/ccgliterals.nim +++ b/compiler/ccgliterals.nim @@ -53,16 +53,20 @@ proc genStringLiteralV1(m: BModule; n: PNode): Rope = proc genStringLiteralDataOnlyV2(m: BModule, s: string): Rope = result = getTempName(m) - addf(m.s[cfsData], " static const NIM_CHAR $1[$2] = $3;$n", + addf(m.s[cfsData], "static const struct {$n" & + " NI cap; void* allocator; NIM_CHAR[$2] data;$n" & + "} $1 = { $2, NIM_NIL, $3 };$n", [result, rope(len(s)+1), makeCString(s)]) proc genStringLiteralV2(m: BModule; n: PNode): Rope = let id = nodeTableTestOrSet(m.dataCache, n, m.labels) if id == m.labels: + discard cgsym(m, "NimStrPayload") + discard cgsym(m, "NimStringV2") # string literal not found in the cache: let pureLit = genStringLiteralDataOnlyV2(m, n.strVal) result = getTempName(m) - addf(m.s[cfsData], "static const #NimStringV2 $1 = {$2, $2, $3};$n", + addf(m.s[cfsData], "static const NimStringV2 $1 = {$2, (NimStrPayload*)&$3};$n", [result, rope(len(n.strVal)+1), pureLit]) else: result = m.tmpBase & rope(id) diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 756711642..ea06544bb 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -324,6 +324,10 @@ proc getForwardStructFormat(m: BModule): string = if m.compileToCpp: result = "$1 $2;$n" else: result = "typedef $1 $2 $2;$n" +proc seqStar(m: BModule): string = + if m.config.selectedGC == gcDestructors: result = "" + else: result = "*" + proc getTypeForward(m: BModule, typ: PType; sig: SigHash): Rope = result = cacheGetType(m.forwTypeCache, sig) if result != nil: return @@ -355,7 +359,7 @@ proc getTypeDescWeak(m: BModule; t: PType; check: var IntSet): Rope = result = getTypeForward(m, t, hashType(t)) pushType(m, t) of tySequence: - result = getTypeForward(m, t, hashType(t)) & "*" + result = getTypeForward(m, t, hashType(t)) & seqStar(m) pushType(m, t) else: result = getTypeDescAux(m, t, check) @@ -487,7 +491,7 @@ proc genRecordFieldsAux(m: BModule, n: PNode, if fieldType.kind == tyArray and tfUncheckedArray in fieldType.flags: addf(result, "$1 $2[SEQ_DECL_SIZE];$n", [getTypeDescAux(m, fieldType.elemType, check), sname]) - elif fieldType.kind in {tySequence, tyOpt}: + elif fieldType.kind == tySequence: # we need to use a weak dependency here for trecursive_table. addf(result, "$1 $2;$n", [getTypeDescWeak(m, field.loc.t, check), sname]) elif field.bitsize != 0: @@ -601,6 +605,14 @@ proc resolveStarsInCppType(typ: PType, idx, stars: int): PType = result = if result.kind == tyGenericInst: result.sons[1] else: result.elemType +proc getSeqPayloadType(m: BModule; t: PType): Rope = + var check = initIntSet() + result = getTypeForward(m, t, hashType(t)) + # XXX remove this duplication: + appcg(m, m.s[cfsSeqTypes], + "struct $2_Content { NI cap; void* allocator; $1 data[SEQ_DECL_SIZE];$n } ", + [getTypeDescAux(m, t.sons[0], check), result]) + proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope = # returns only the type's name var t = origTyp.skipTypes(irrelevantForBackend) @@ -641,7 +653,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope = of tySequence: # no restriction! We have a forward declaration for structs let name = getTypeForward(m, et, hashType et) - result = name & "*" & star + result = name & seqStar(m) & star m.typeCache[sig] = result pushType(m, et) else: @@ -705,20 +717,29 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope = [structOrUnion(t), result]) m.forwTypeCache[sig] = result assert(cacheGetType(m.typeCache, sig) == nil) - m.typeCache[sig] = result & "*" + m.typeCache[sig] = result & seqStar(m) if not isImportedType(t): if skipTypes(t.sons[0], typedescInst).kind != tyEmpty: const cppSeq = "struct $2 : #TGenericSeq {$n" cSeq = "struct $2 {$n" & " #TGenericSeq Sup;$n" - appcg(m, m.s[cfsSeqTypes], - (if m.compileToCpp: cppSeq else: cSeq) & - " $1 data[SEQ_DECL_SIZE];$n" & - "};$n", [getTypeDescAux(m, t.sons[0], check), result]) + if m.config.selectedGC == gcDestructors: + appcg(m, m.s[cfsSeqTypes], + "struct $2_Content { NI cap; void* allocator; $1 data[SEQ_DECL_SIZE];$n } " & + "struct $2 {$n" & + " NI len; $2_Content* p;$n" & + "};$n", [getTypeDescAux(m, t.sons[0], check), result]) + else: + appcg(m, m.s[cfsSeqTypes], + (if m.compileToCpp: cppSeq else: cSeq) & + " $1 data[SEQ_DECL_SIZE];$n" & + "};$n", [getTypeDescAux(m, t.sons[0], check), result]) + elif m.config.selectedGC == gcDestructors: + internalError(m.config, "cannot map the empty seq type to a C type") else: result = rope("TGenericSeq") - add(result, "*") + add(result, seqStar(m)) of tyArray: var n: BiggestInt = lengthOrd(m.config, t) if n <= 0: n = 1 # make an array of at least one element diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 0ca7533d4..180aa4731 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -250,7 +250,10 @@ proc lenExpr(p: BProc; a: TLoc): Rope = result = "($1 ? $1->$2 : 0)" % [rdLoc(a), lenField(p)] proc dataField(p: BProc): Rope = - result = rope"->data" + if p.config.selectedGc == gcDestructors: + result = rope".p->data" + else: + result = rope"->data" include ccgliterals include ccgtypes diff --git a/compiler/destroyer.nim b/compiler/destroyer.nim index 0395728c2..c2a74edb1 100644 --- a/compiler/destroyer.nim +++ b/compiler/destroyer.nim @@ -100,12 +100,12 @@ Rule Pattern Transformed into finally: `=destroy`(x) 1.2 var x: sink T; stmts var x: sink T; stmts; ensureEmpty(x) 2 x = f() `=sink`(x, f()) -3 x = lastReadOf z `=sink`(x, z) +3 x = lastReadOf z `=sink`(x, z); wasMoved(z) 4.1 y = sinkParam `=sink`(y, sinkParam) 4.2 x = y `=`(x, y) # a copy 5.1 f_sink(g()) f_sink(g()) 5.2 f_sink(y) f_sink(copy y); # copy unless we can see it's the last read -5.3 f_sink(move y) f_sink(y); reset(y) # explicit moves empties 'y' +5.3 f_sink(move y) f_sink(y); wasMoved(y) # explicit moves empties 'y' 5.4 f_noSink(g()) var tmp = bitwiseCopy(g()); f(tmp); `=destroy`(tmp) Remarks: Rule 1.2 is not yet implemented because ``sink`` is currently @@ -282,6 +282,11 @@ proc destructiveMoveSink(n: PNode; c: var Con): PNode = newIntTypeNode(nkIntLit, 0, getSysType(c.graph, n.info, tyBool))) result.add n +proc genMagicCall(n: PNode; c: var Con; magicname: string; m: TMagic): PNode = + result = newNodeI(nkCall, n.info) + result.add(newSymNode(createMagic(c.graph, magicname, m))) + result.add n + proc moveOrCopy(dest, ri: PNode; c: var Con): PNode = if ri.kind in constrExprs: result = genSink(c, ri.typ, dest) @@ -290,8 +295,10 @@ proc moveOrCopy(dest, ri: PNode; c: var Con): PNode = recurse(ri, ri2) result.add ri2 elif ri.kind == nkSym and isHarmlessVar(ri.sym, c): - result = genSink(c, ri.typ, dest) - result.add p(ri, c) + # Rule 3: `=sink`(x, z); wasMoved(z) + var snk = genSink(c, ri.typ, dest) + snk.add p(ri, c) + result = newTree(nkStmtList, snk, genMagicCall(ri, c, "wasMoved", mWasMoved)) elif ri.kind == nkSym and isSinkParam(ri.sym): result = genSink(c, ri.typ, dest) result.add destructiveMoveSink(ri, c) @@ -313,11 +320,9 @@ proc passCopyToSink(n: PNode; c: var Con): PNode = result.add newTree(nkAsgn, tmp, p(n, c)) result.add tmp -proc genReset(n: PNode; c: var Con): PNode = - result = newNodeI(nkCall, n.info) - result.add(newSymNode(createMagic(c.graph, "reset", mReset))) - # The mReset builtin does not take the address: - result.add n +proc genWasMoved(n: PNode; c: var Con): PNode = + # The mWasMoved builtin does not take the address. + result = genMagicCall(n, c, "wasMoved", mWasMoved) proc destructiveMoveVar(n: PNode; c: var Con): PNode = # generate: (let tmp = v; reset(v); tmp) @@ -334,7 +339,7 @@ proc destructiveMoveVar(n: PNode; c: var Con): PNode = add(v, vpart) result.add v - result.add genReset(n, c) + result.add genWasMoved(n, c) result.add tempAsNode proc p(n: PNode; c: var Con): PNode = diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 6ef03456e..781813795 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1553,7 +1553,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, if proto.typ.callConv != s.typ.callConv or proto.typ.flags < s.typ.flags: localError(c.config, n.sons[pragmasPos].info, errPragmaOnlyInHeaderOfProcX % ("'" & proto.name.s & "' from " & c.config$proto.info)) - if sfForward notin proto.flags: + if sfForward notin proto.flags and proto.magic == mNone: wrongRedefinition(c, n.info, proto.name.s) excl(proto.flags, sfForward) closeScope(c) # close scope with wrong parameter symbols @@ -1619,7 +1619,8 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, openScope(c) n.sons[bodyPos] = semGenericStmt(c, n.sons[bodyPos]) closeScope(c) - fixupInstantiatedSymbols(c, s) + if s.magic == mNone: + fixupInstantiatedSymbols(c, s) if s.kind == skMethod: semMethodPrototype(c, s, n) if sfImportc in s.flags: # so we just ignore the body after semantic checking for importc: diff --git a/compiler/types.nim b/compiler/types.nim index 66807cf97..76d233e8a 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -1335,14 +1335,23 @@ proc computeSizeAux(conf: ConfigRef; typ: PType, a: var BiggestInt): BiggestInt if typ.callConv == ccClosure: result = 2 * conf.target.ptrSize else: result = conf.target.ptrSize a = conf.target.ptrSize - of tyString, tyNil: + of tyString: + if tfHasAsgn in typ.flags: + result = conf.target.ptrSize * 2 + else: + result = conf.target.ptrSize + of tyNil: result = conf.target.ptrSize a = result of tyCString, tySequence, tyPtr, tyRef, tyVar, tyLent, tyOpenArray: let base = typ.lastSon if base == typ or (base.kind == tyTuple and base.size==szIllegalRecursion): result = szIllegalRecursion - else: result = conf.target.ptrSize + else: + if typ.kind == tySequence and tfHasAsgn in typ.flags: + result = conf.target.ptrSize * 2 + else: + result = conf.target.ptrSize a = result of tyArray: let elemSize = computeSizeAux(conf, typ.sons[1], a) diff --git a/lib/core/seqs.nim b/lib/core/seqs.nim index 02c192851..4327d2352 100644 --- a/lib/core/seqs.nim +++ b/lib/core/seqs.nim @@ -7,133 +7,157 @@ # distribution, for details about the copyright. # -import allocators, typetraits + +import typetraits +# strs already imported allocators for us. ## Default seq implementation used by Nim's core. type - seq*[T] = object - len, cap: int - data: ptr UncheckedArray[T] + NimSeqPayload {.core.}[T] = object + cap: int + region: Allocator + data: UncheckedArray[T] + + NimSeqV2*[T] = object + len: int + p: ptr NimSeqPayload[T] const nimSeqVersion {.core.} = 2 -template frees(s) = dealloc(s.data, s.cap * sizeof(T)) +template payloadSize(cap): int = cap * sizeof(T) + sizeof(int) + sizeof(Allocator) # XXX make code memory safe for overflows in '*' -when defined(nimHasTrace): - proc `=trace`[T](s: seq[T]; a: Allocator) = - for i in 0 ..< s.len: `=trace`(s.data[i], a) +when false: + # this is currently not part of Nim's type bound operators and so it's + # built into the tracing proc generation just like before. + proc `=trace`[T](s: NimSeqV2[T]) = + for i in 0 ..< s.len: `=trace`(s.data[i]) -proc `=destroy`[T](x: var seq[T]) = - if x.data != nil: +proc `=destroy`[T](x: var NimSeqV2[T]) = + var p = x.p + if p != nil: when not supportsCopyMem(T): - for i in 0.. 0: + copyMem(unsafeAddr a.p.data[0], unsafeAddr b.p.data[0], a.len * sizeof(T)) else: for i in 0..= x.cap: resize(x) - result = addr(x.data[x.len]) - inc x.len - -template add*[T](x: var seq[T]; y: T) = - reserveSlot(x)[] = y - -proc shrink*[T](x: var seq[T]; newLen: int) = - assert newLen <= x.len - assert newLen >= 0 + a.p = b.p + +when false: + 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.} + + +type + PayloadBase = object + cap: int + region: Allocator + +proc newSeqPayload(cap, elemSize: int): pointer {.compilerRtl.} = + # we have to use type erasure here as Nim does not support generic + # compilerProcs. Oh well, this will all be inlined anyway. + if cap <= 0: + let region = getLocalAllocator() + var p = cast[ptr PayloadBase](region.alloc(region, cap * elemSize + sizeof(int) + sizeof(Allocator))) + p.region = region + p.cap = cap + result = p + else: + result = nil + +proc prepareSeqAdd(len: int; p: pointer; addlen, elemSize: int): pointer {.compilerRtl.} = + if len+addlen <= len: + result = p + elif p == nil: + result = newSeqPayload(len+addlen, elemSize) + else: + # Note: this means we cannot support things that have internal pointers as + # they get reallocated here. This needs to be documented clearly. + var p = cast[ptr PayloadBase](p) + let region = if p.region == nil: getLocalAllocator() else: p.region + let cap = max(resize(p.cap), len+addlen) + var q = cast[ptr PayloadBase](region.realloc(region, p, + sizeof(int) + sizeof(Allocator) + elemSize * p.cap, + sizeof(int) + sizeof(Allocator) + elemSize * cap)) + q.region = region + q.cap = cap + result = q + +proc shrink*[T](x: var seq[T]; newLen: Natural) = + sysAssert newLen <= x.len, "invalid newLen parameter for 'shrink'" when not supportsCopyMem(T): for i in countdown(x.len - 1, newLen - 1): - `=destroy`(x.data[i]) - x.len = newLen - -proc grow*[T](x: var seq[T]; newLen: int; value: T) = - if newLen <= x.len: return - assert newLen >= 0 - if x.cap == 0: x.cap = newLen - else: x.cap = max(newLen, (x.cap * 3) shr 1) - x.data = cast[type(x.data)](realloc(x.data, x.cap * sizeof(T))) - for i in x.len..= x.cap: resize(x) + result = addr(x.data[x.len]) + inc x.len + + template add*[T](x: var NimSeqV2[T]; y: T) = + reserveSlot(x)[] = y + + template `[]`*[T](x: NimSeqV2[T]; i: Natural): T = + assert i < x.len + x.data[i] + + template `[]=`*[T](x: NimSeqV2[T]; i: Natural; y: T) = + assert i < x.len + x.data[i] = y + + proc `@`*[T](elems: openArray[T]): NimSeqV2[T] = + result.cap = elems.len + result.len = elems.len + result.data = cast[type(result.data)](alloc(result.cap * sizeof(T))) + when supportsCopyMem(T): + copyMem(result.data, unsafeAddr(elems[0]), result.cap * sizeof(T)) else: - result.addQuoted(value) - - result.add("]") + for i in 0.. 0: @@ -96,7 +95,7 @@ proc prepareAdd(s: var NimStringV2; addlen: int) {.compilerRtl.} = copyMem(unsafeAddr s.p.data[0], unsafeAddr oldP.data[0], s.len) elif s.len + addlen > s.p.cap: let cap = max(s.len + addlen, resize(s.p.cap)) - s.p = cast[ptr StrContent](s.p.region.realloc(s.p.region, s.p, + s.p = cast[ptr NimStrPayload](s.p.region.realloc(s.p.region, s.p, oldSize = contentSize(s.p.cap), newSize = contentSize(cap))) s.p.cap = cap @@ -112,7 +111,7 @@ proc toNimStr(str: cstring, len: int): NimStringV2 {.compilerProc.} = result = NimStringV2(len: 0, p: nil) else: let region = getLocalAllocator() - var p = cast[ptr StrContent](region.alloc(region, contentSize(len))) + var p = cast[ptr NimStrPayload](region.alloc(region, contentSize(len))) p.region = region p.cap = len if len > 0: @@ -144,7 +143,7 @@ proc rawNewString(space: int): NimStringV2 {.compilerProc.} = result = NimStringV2(len: 0, p: nil) else: let region = getLocalAllocator() - var p = cast[ptr StrContent](region.alloc(region, contentSize(space))) + var p = cast[ptr NimStrPayload](region.alloc(region, contentSize(space))) p.region = region p.cap = space result = NimStringV2(len: 0, p: p) @@ -154,7 +153,15 @@ proc mnewString(len: int): NimStringV2 {.compilerProc.} = result = NimStringV2(len: 0, p: nil) else: let region = getLocalAllocator() - var p = cast[ptr StrContent](region.alloc(region, contentSize(len))) + var p = cast[ptr NimStrPayload](region.alloc(region, contentSize(len))) p.region = region p.cap = len result = NimStringV2(len: len, p: p) + +proc setLengthStrV2(s: var NimStringV2, newLen: int) {.compilerRtl.} = + if newLen > s.len: + prepareAdd(s, newLen - s.len) + else: + s.len = newLen + # this also only works because the destructor + # looks at s.p and not s.len diff --git a/lib/system.nim b/lib/system.nim index 1defe20ee..2f6f45948 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -230,6 +230,17 @@ proc reset*[T](obj: var T) {.magic: "Reset", noSideEffect.} ## resets an object `obj` to its initial (binary zero) value. This needs to ## be called before any possible `object branch transition`:idx:. +when defined(nimNewRuntime): + proc wasMoved*[T](obj: var T) {.magic: "WasMoved", noSideEffect.} = + ## resets an object `obj` to its initial (binary zero) value to signify + ## it was "moved" and to signify its destructor should do nothing and + ## ideally be optimized away. + discard + + proc move*[T](x: var T): T {.magic: "Move", noSideEffect.} = + result = x + wasMoved(x) + type range*{.magic: "Range".}[T] ## Generic type to construct range types. array*{.magic: "Array".}[I, T] ## Generic type to construct @@ -3310,6 +3321,7 @@ when not defined(JS): #and not defined(nimscript): when hasAlloc: when defined(gcDestructors): include "core/strs" + include "core/seqs" else: include "system/sysstr" {.pop.} diff --git a/lib/system/jssys.nim b/lib/system/jssys.nim index 5fff869f0..e500444ea 100644 --- a/lib/system/jssys.nim +++ b/lib/system/jssys.nim @@ -504,7 +504,6 @@ proc chckNilDisp(p: pointer) {.compilerproc.} = if p == nil: sysFatal(NilAccessError, "cannot dispatch; dispatcher is nil") -const ThisIsSystem = true # for hti.nim include "system/hti" proc isFatPointer(ti: PNimType): bool = diff --git a/tests/destructor/tcustomseqs.nim b/tests/destructor/tcustomseqs.nim index 97d7c07b6..83df0053f 100644 --- a/tests/destructor/tcustomseqs.nim +++ b/tests/destructor/tcustomseqs.nim @@ -43,9 +43,10 @@ proc `=destroy`*[T](x: var myseq[T]) = proc `=`*[T](a: var myseq[T]; b: myseq[T]) = if a.data == b.data: return if a.data != nil: - dealloc(a.data) - inc deallocCount - a.data = nil + `=destroy`(a) + #dealloc(a.data) + #inc deallocCount + #a.data = nil a.len = b.len a.cap = b.cap if b.data != nil: -- cgit 1.4.1-2-gfad0 From 9c3336dcffcb10f662f5c358f63d073db1454116 Mon Sep 17 00:00:00 2001 From: andri lim Date: Sat, 21 Jul 2018 00:48:12 +0700 Subject: fixes #8371, macros.hasCustomPragma doesn't crash anymore (#8378) * fixes #8371, macros.hasCustomPragma doesn't crash anymore * fix macros.hasCustomPragma --- lib/core/macros.nim | 4 +++- tests/pragmas/tcustom_pragma.nim | 20 ++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) (limited to 'lib/core') diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 8a1be3720..345c53b08 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -1284,7 +1284,7 @@ proc customPragmaNode(n: NimNode): NimNode = let typ = n.getTypeInst() - if typ.kind == nnkBracketExpr and typ.len > 1 and typ[1].kind == nnkProcTy: + if typ.kind == nnkBracketExpr and typ.len > 1 and typ[1].kind == nnkProcTy: return typ[1][1] elif typ.typeKind == ntyTypeDesc: let impl = typ[1].getImpl() @@ -1319,6 +1319,8 @@ proc customPragmaNode(n: NimNode): NimNode = if identDefs.kind == nnkRecCase: identDefsStack.add(identDefs[0]) for i in 1.. Date: Fri, 27 Jul 2018 18:19:18 +0200 Subject: allocators: add deallocAll proc pointer --- lib/core/allocators.nim | 1 + 1 file changed, 1 insertion(+) (limited to 'lib/core') diff --git a/lib/core/allocators.nim b/lib/core/allocators.nim index 74a8d3753..f652f0d85 100644 --- a/lib/core/allocators.nim +++ b/lib/core/allocators.nim @@ -15,6 +15,7 @@ type alloc*: proc (a: Allocator; size: int; alignment: int = 8): pointer {.nimcall.} dealloc*: proc (a: Allocator; p: pointer; size: int) {.nimcall.} realloc*: proc (a: Allocator; p: pointer; oldSize, newSize: int): pointer {.nimcall.} + deallocAll*: proc (a: Allocator) {.nimcall.} flags*: set[AllocatorFlag] var -- cgit 1.4.1-2-gfad0 From ef4b755183f6564cc0f35cdf01794626c4e5fe2f Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 27 Jul 2018 18:20:13 +0200 Subject: allows a destructor to be attached to a tyString/tySequence --- compiler/ast.nim | 6 +++--- compiler/semstmts.nim | 10 +++++----- lib/core/seqs.nim | 12 +++++++++--- lib/core/strs.nim | 17 +++++++++++------ 4 files changed, 28 insertions(+), 17 deletions(-) (limited to 'lib/core') diff --git a/compiler/ast.nim b/compiler/ast.nim index 3870c82ee..a61ac055e 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -1088,9 +1088,9 @@ proc newSym*(symKind: TSymKind, name: PIdent, owner: PSym, result.id = getID() when debugIds: registerId(result) - #if result.id == 93289: + #if result.id == 77131: # writeStacktrace() - # MessageOut(name.s & " has id: " & toString(result.id)) + # echo name.s proc isMetaType*(t: PType): bool = return t.kind in tyMetaTypes or @@ -1272,7 +1272,7 @@ proc newType*(kind: TTypeKind, owner: PSym): PType = when debugIds: registerId(result) when false: - if result.id == 205734: + if result.id == 76426: echo "KNID ", kind writeStackTrace() diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 781813795..f7d8b6b7b 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1017,8 +1017,8 @@ proc checkForMetaFields(c: PContext; n: PNode) = case t.kind of tySequence, tySet, tyArray, tyOpenArray, tyVar, tyLent, tyPtr, tyRef, tyProc, tyGenericInvocation, tyGenericInst, tyAlias, tySink: - let start = int ord(t.kind in {tyGenericInvocation, tyGenericInst}) - for i in start ..< t.sons.len: + let start = ord(t.kind in {tyGenericInvocation, tyGenericInst}) + for i in start ..< t.len: checkMeta(t.sons[i]) else: checkMeta(t) @@ -1337,7 +1337,7 @@ proc semOverride(c: PContext, s: PSym, n: PNode) = if obj.kind in {tyGenericBody, tyGenericInst}: obj = obj.lastSon elif obj.kind == tyGenericInvocation: obj = obj.sons[0] else: break - if obj.kind in {tyObject, tyDistinct}: + if obj.kind in {tyObject, tyDistinct, tySequence, tyString}: if obj.destructor.isNil: obj.destructor = s else: @@ -1359,7 +1359,7 @@ proc semOverride(c: PContext, s: PSym, n: PNode) = if t.kind == tyGenericBody: t = t.lastSon elif t.kind == tyGenericInvocation: t = t.sons[0] else: break - if t.kind in {tyObject, tyDistinct, tyEnum}: + if t.kind in {tyObject, tyDistinct, tyEnum, tySequence, tyString}: if t.deepCopy.isNil: t.deepCopy = s else: localError(c.config, n.info, errGenerated, @@ -1388,7 +1388,7 @@ proc semOverride(c: PContext, s: PSym, n: PNode) = elif objB.kind in {tyGenericInvocation, tyGenericInst}: objB = objB.sons[0] else: break - if obj.kind in {tyObject, tyDistinct} and sameType(obj, objB): + if obj.kind in {tyObject, tyDistinct, tySequence, tyString} and sameType(obj, objB): let opr = if s.name.s == "=": addr(obj.assignment) else: addr(obj.sink) if opr[].isNil: opr[] = s diff --git a/lib/core/seqs.nim b/lib/core/seqs.nim index 4327d2352..4dcf6cbbb 100644 --- a/lib/core/seqs.nim +++ b/lib/core/seqs.nim @@ -34,7 +34,8 @@ when false: proc `=trace`[T](s: NimSeqV2[T]) = for i in 0 ..< s.len: `=trace`(s.data[i]) -proc `=destroy`[T](x: var NimSeqV2[T]) = +proc `=destroy`[T](s: var seq[T]) = + var x = cast[ptr NimSeqV2[T]](addr s) var p = x.p if p != nil: when not supportsCopyMem(T): @@ -43,7 +44,10 @@ proc `=destroy`[T](x: var NimSeqV2[T]) = x.p = nil x.len = 0 -proc `=`[T](a: var NimSeqV2[T]; b: NimSeqV2[T]) = +proc `=`[T](x: var seq[T]; y: seq[T]) = + var a = cast[ptr NimSeqV2[T]](addr x) + var b = cast[ptr NimSeqV2[T]](unsafeAddr y) + if a.p == b.p: return `=destroy`(a) a.len = b.len @@ -56,7 +60,9 @@ proc `=`[T](a: var NimSeqV2[T]; b: NimSeqV2[T]) = for i in 0.. Date: Tue, 31 Jul 2018 14:02:04 -0700 Subject: `lineInfoObj` (and `check`, `expect`) now return absolute paths (#8466) --- changelog.md | 3 ++- compiler/vm.nim | 2 +- lib/core/macros.nim | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) (limited to 'lib/core') diff --git a/changelog.md b/changelog.md index e0027d504..3dfe63f3b 100644 --- a/changelog.md +++ b/changelog.md @@ -25,7 +25,6 @@ - The dot style for import paths (e.g ``import path.to.module`` instead of ``import path/to/module``) has been deprecated. - #### Breaking changes in the standard library - ``re.split`` for empty regular expressions now yields every character in @@ -61,6 +60,8 @@ 1-based coordinates on POSIX for correct behaviour; the Windows behaviour was always correct). +- ``lineInfoObj`` now returns absolute path instead of project path. + It's used by ``lineInfo``, ``check``, ``expect``, ``require``, etc. #### Breaking changes in the compiler diff --git a/compiler/vm.nim b/compiler/vm.nim index d7e1b5da3..f8b1cee73 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -1409,7 +1409,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcNGetFile: decodeB(rkNode) let n = regs[rb].node - regs[ra].node = newStrNode(nkStrLit, toFilename(c.config, n.info)) + regs[ra].node = newStrNode(nkStrLit, toFullPath(c.config, n.info)) regs[ra].node.info = n.info regs[ra].node.typ = n.typ of opcNGetLine: diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 345c53b08..17178a634 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -425,6 +425,7 @@ proc getColumn(arg: NimNode): int {.magic: "NLineInfo", noSideEffect.} proc getFile(arg: NimNode): string {.magic: "NLineInfo", noSideEffect.} proc lineInfoObj*(n: NimNode): LineInfo {.compileTime.} = + ## returns ``LineInfo`` of ``n``, using absolute path for ``filename`` result.filename = n.getFile result.line = n.getLine result.column = n.getColumn -- cgit 1.4.1-2-gfad0 From 78c0ac54070e860ec0ed8ac0b58658f7ad52227a Mon Sep 17 00:00:00 2001 From: andri lim Date: Thu, 2 Aug 2018 17:56:44 +0700 Subject: fixes #7827, bindSym enhancement (#8499) * bindSym power up, working prototype * update bindSym doc * add bindSym test * fix some typo * fix bindSym doc * get rid of specialops field from vm * add experimental: dynamicBindSym --- changelog.md | 5 ++++ compiler/options.nim | 3 ++- compiler/semmagic.nim | 67 ++++++++++++++++++++++++++++++++++++++++++++++- compiler/semtypes.nim | 1 + compiler/vm.nim | 18 ++++++++++++- compiler/vmdef.nim | 5 ++-- compiler/vmgen.nim | 45 ++++++++++++++++++++++++++----- lib/core/macros.nim | 10 ++++++- tests/macros/tbindsym.nim | 42 +++++++++++++++++++++++++++++ 9 files changed, 183 insertions(+), 13 deletions(-) (limited to 'lib/core') diff --git a/changelog.md b/changelog.md index 3dfe63f3b..045286ef9 100644 --- a/changelog.md +++ b/changelog.md @@ -196,4 +196,9 @@ - Nintendo Switch was added as a new platform target. See [the compiler user guide](https://nim-lang.org/docs/nimc.html) for more info. +- macros.bindSym now capable to accepts not only literal string or string constant expression. + bindSym enhancement make it also can accepts computed string or ident node inside macros / + compile time functions / static blocks. Only in templates / regular code it retains it's old behavior. + This new feature can be accessed via {.experimental: "dynamicBindSym".} pragma/switch + ### Bugfixes diff --git a/compiler/options.nim b/compiler/options.nim index fcbb98fe6..598adf27d 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -116,7 +116,8 @@ type callOperator, parallel, destructor, - notnil + notnil, + dynamicBindSym SymbolFilesOption* = enum disabledSf, writeOnlySf, readOnlySf, v2Sf diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index b5875c67a..8bfa5545e 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -207,6 +207,67 @@ proc semBindSym(c: PContext, n: PNode): PNode = else: errorUndeclaredIdentifier(c, n.sons[1].info, sl.strVal) +proc opBindSym(c: PContext, scope: PScope, n: PNode, isMixin: int, info: PNode): PNode = + if n.kind notin {nkStrLit, nkRStrLit, nkTripleStrLit, nkIdent}: + localError(c.config, info.info, errStringOrIdentNodeExpected) + return errorNode(c, n) + + if isMixin < 0 or isMixin > high(TSymChoiceRule).int: + localError(c.config, info.info, errConstExprExpected) + return errorNode(c, n) + + let id = if n.kind == nkIdent: n + else: newIdentNode(getIdent(c.cache, n.strVal), info.info) + + let tmpScope = c.currentScope + c.currentScope = scope + let s = qualifiedLookUp(c, id, {checkUndeclared}) + if s != nil: + # we need to mark all symbols: + result = symChoice(c, id, s, TSymChoiceRule(isMixin)) + else: + errorUndeclaredIdentifier(c, info.info, if n.kind == nkIdent: n.ident.s + else: n.strVal) + c.currentScope = tmpScope + +proc semDynamicBindSym(c: PContext, n: PNode): PNode = + # inside regular code, bindSym resolves to the sym-choice + # nodes (see tinspectsymbol) + if not (c.inStaticContext > 0 or getCurrOwner(c).isCompileTimeProc): + return semBindSym(c, n) + + if c.graph.vm.isNil: + setupGlobalCtx(c.module, c.graph) + + let + vm = PCtx c.graph.vm + # cache the current scope to + # prevent it lost into oblivion + scope = c.currentScope + + # cannot use this + # vm.config.features.incl dynamicBindSym + + proc bindSymWrapper(a: VmArgs) = + # capture PContext and currentScope + # param description: + # 0. ident, a string literal / computed string / or ident node + # 1. bindSym rule + # 2. info node + a.setResult opBindSym(c, scope, a.getNode(0), a.getInt(1).int, a.getNode(2)) + + let + # altough we use VM callback here, it is not + # executed like 'normal' VM callback + idx = vm.registerCallback("bindSymImpl", bindSymWrapper) + # dummy node to carry idx information to VM + idxNode = newIntTypeNode(nkIntLit, idx, c.graph.getSysType(TLineInfo(), tyInt)) + + result = copyNode(n) + for x in n: result.add x + result.add n # info node + result.add idxNode + proc semShallowCopy(c: PContext, n: PNode, flags: TExprFlags): PNode proc semOf(c: PContext, n: PNode): PNode = @@ -270,7 +331,11 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode, of mOf: result = semOf(c, n) of mHigh, mLow: result = semLowHigh(c, n, n[0].sym.magic) of mShallowCopy: result = semShallowCopy(c, n, flags) - of mNBindSym: result = semBindSym(c, n) + of mNBindSym: + if dynamicBindSym notin c.features: + result = semBindSym(c, n) + else: + result = semDynamicBindSym(c, n) of mProcCall: result = n result.typ = n[1].typ diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 24896e944..972c8c709 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -11,6 +11,7 @@ # included from sem.nim const + errStringOrIdentNodeExpected = "string or ident node expected" errStringLiteralExpected = "string literal expected" errIntLiteralExpected = "integer literal expected" errWrongNumberOfVariables = "wrong number of variables" diff --git a/compiler/vm.nim b/compiler/vm.nim index f8b1cee73..a6ec4788b 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -1222,9 +1222,25 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = node.typ.callConv == ccClosure and node.sons[0].kind == nkNilLit and node.sons[1].kind == nkNilLit)) of opcNBindSym: + # cannot use this simple check + # if dynamicBindSym notin c.config.features: + + # bindSym with static input decodeBx(rkNode) regs[ra].node = copyTree(c.constants.sons[rbx]) regs[ra].node.flags.incl nfIsRef + of opcNDynBindSym: + # experimental bindSym + let + rb = instr.regB + rc = instr.regC + idx = int(regs[rb+rc-1].intVal) + callback = c.callbacks[idx].value + args = VmArgs(ra: ra, rb: rb, rc: rc, slots: cast[pointer](regs), + currentException: c.currentExceptionB, + currentLineInfo: c.debug[pc]) + callback(args) + regs[ra].node.flags.incl nfIsRef of opcNChild: decodeBC(rkNode) let idx = regs[rc].intVal.int @@ -1773,7 +1789,7 @@ proc getGlobalValue*(c: PCtx; s: PSym): PNode = include vmops -proc setupGlobalCtx(module: PSym; graph: ModuleGraph) = +proc setupGlobalCtx*(module: PSym; graph: ModuleGraph) = if graph.vm.isNil: graph.vm = newCtx(module, graph.cache, graph) registerAdditionalOps(PCtx graph.vm) diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim index f7466b392..866b79568 100644 --- a/compiler/vmdef.nim +++ b/compiler/vmdef.nim @@ -136,7 +136,7 @@ type opcLdGlobalAddr, # dest = addr(globals[Bx]) opcLdImmInt, # dest = immediate value - opcNBindSym, + opcNBindSym, opcNDynBindSym, opcSetType, # dest.typ = types[Bx] opcTypeTrait, opcMarshalLoad, opcMarshalStore, @@ -229,7 +229,8 @@ proc refresh*(c: PCtx, module: PSym) = c.prc = PProc(blocks: @[]) c.loopIterations = MaxLoopIterations -proc registerCallback*(c: PCtx; name: string; callback: VmCallback) = +proc registerCallback*(c: PCtx; name: string; callback: VmCallback): int {.discardable.} = + result = c.callbacks.len c.callbacks.add((name, callback)) const diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 31e04a6c0..3b5ea4beb 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -817,6 +817,43 @@ proc genVoidABC(c: PCtx, n: PNode, dest: TDest, opcode: TOpcode) = c.freeTemp(tmp2) c.freeTemp(tmp3) +proc genBindSym(c: PCtx; n: PNode; dest: var TDest) = + # nah, cannot use c.config.features because sempass context + # can have local experimental switch + # if dynamicBindSym notin c.config.features: + if n.len == 2: # hmm, reliable? + # bindSym with static input + if n[1].kind in {nkClosedSymChoice, nkOpenSymChoice, nkSym}: + let idx = c.genLiteral(n[1]) + if dest < 0: dest = c.getTemp(n.typ) + c.gABx(n, opcNBindSym, dest, idx) + else: + localError(c.config, n.info, "invalid bindSym usage") + else: + # experimental bindSym + if dest < 0: dest = c.getTemp(n.typ) + let x = c.getTempRange(n.len, slotTempUnknown) + + # callee symbol + var tmp0 = TDest(x) + c.genLit(n.sons[0], tmp0) + + # original parameters + for i in 1.. Date: Sun, 5 Aug 2018 09:38:14 +0200 Subject: WIP: nothing works --- compiler/semtypes.nim | 40 +++++++++++++++++++++++++++++++++------- lib/core/strs.nim | 2 +- lib/system/excpt.nim | 9 +++++---- 3 files changed, 39 insertions(+), 12 deletions(-) (limited to 'lib/core') diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index c3784f7b6..29ed1b870 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -144,9 +144,7 @@ proc semSet(c: PContext, n: PNode, prev: PType): PType = localError(c.config, n.info, errXExpectsOneTypeParam % "set") addSonSkipIntLit(result, errorType(c)) -proc semContainer(c: PContext, n: PNode, kind: TTypeKind, kindStr: string, - prev: PType): PType = - result = newOrPrevType(kind, prev, c) +proc semContainerArg(c: PContext; n: PNode, kindStr: string; result: PType) = if sonsLen(n) == 2: var base = semTypeNode(c, n.sons[1], nil) if base.kind == tyVoid: @@ -156,6 +154,11 @@ proc semContainer(c: PContext, n: PNode, kind: TTypeKind, kindStr: string, localError(c.config, n.info, errXExpectsOneTypeParam % kindStr) addSonSkipIntLit(result, errorType(c)) +proc semContainer(c: PContext, n: PNode, kind: TTypeKind, kindStr: string, + prev: PType): PType = + result = newOrPrevType(kind, prev, c) + semContainerArg(c, n, kindStr, result) + proc semVarargs(c: PContext, n: PNode, prev: PType): PType = result = newOrPrevType(tyVarargs, prev, c) if sonsLen(n) == 2 or sonsLen(n) == 3: @@ -1505,9 +1508,29 @@ 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: - result = semContainer(c, n, tySequence, "seq", prev) + let s = c.graph.sysTypes[tySequence] + assert s != nil + assert prev == nil + result = copyType(s, s.owner, keepId=false) + # XXX figure out why this has children already... + result.sons.setLen 0 + result.n = nil if c.config.selectedGc == gcDestructors: - incl result.flags, tfHasAsgn + result.flags = {tfHasAsgn} + else: + result.flags = {} + semContainerArg(c, n, "seq", result) + when false: + debugT = true + echo "Start!" + #debug result + assert(not containsGenericType(result)) + debugT = false + echo "End!" + when false: + result = semContainer(c, n, tySequence, "seq", prev) + if c.config.selectedGc == gcDestructors: + incl result.flags, tfHasAsgn of mOpt: result = semContainer(c, n, tyOpt, "opt", prev) of mVarargs: result = semVarargs(c, n, prev) of mTypeDesc, mTypeTy: @@ -1681,8 +1704,9 @@ proc processMagicType(c: PContext, m: PSym) = of mString: setMagicType(c.config, m, tyString, c.config.target.ptrSize) rawAddSon(m.typ, getSysType(c.graph, m.info, tyChar)) - if c.config.selectedGc == gcDestructors: - incl m.typ.flags, tfHasAsgn + when false: + if c.config.selectedGc == gcDestructors: + incl m.typ.flags, tfHasAsgn of mCstring: setMagicType(c.config, m, tyCString, c.config.target.ptrSize) rawAddSon(m.typ, getSysType(c.graph, m.info, tyChar)) @@ -1724,6 +1748,8 @@ proc processMagicType(c: PContext, m: PSym) = setMagicType(c.config, m, tySequence, 0) if c.config.selectedGc == gcDestructors: incl m.typ.flags, tfHasAsgn + assert c.graph.sysTypes[tySequence] == nil + c.graph.sysTypes[tySequence] = m.typ of mOpt: setMagicType(c.config, m, tyOpt, 0) of mOrdinal: diff --git a/lib/core/strs.nim b/lib/core/strs.nim index 562beaa91..186add52a 100644 --- a/lib/core/strs.nim +++ b/lib/core/strs.nim @@ -46,7 +46,7 @@ template frees(s) = s.p.region.dealloc(s.p.region, s.p, contentSize(s.p.cap)) proc `=destroy`(s: var string) = - var a = cast[ptr NimStringV2[T]](addr s) + var a = cast[ptr NimStringV2](addr s) frees(a) a.len = 0 a.p = nil diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim index dabfe010e..1e590965a 100644 --- a/lib/system/excpt.nim +++ b/lib/system/excpt.nim @@ -428,10 +428,11 @@ proc getStackTrace(e: ref Exception): string = else: result = "" -proc getStackTraceEntries*(e: ref Exception): seq[StackTraceEntry] = - ## Returns the attached stack trace to the exception ``e`` as - ## a ``seq``. This is not yet available for the JS backend. - shallowCopy(result, e.trace) +when not defined(gcDestructors): + proc getStackTraceEntries*(e: ref Exception): seq[StackTraceEntry] = + ## Returns the attached stack trace to the exception ``e`` as + ## a ``seq``. This is not yet available for the JS backend. + shallowCopy(result, e.trace) when defined(nimRequiresNimFrame): proc stackOverflow() {.noinline.} = -- cgit 1.4.1-2-gfad0 From 6e3d1dced55229309a85849a45c877a798080453 Mon Sep 17 00:00:00 2001 From: andri lim Date: Mon, 6 Aug 2018 04:38:21 +0700 Subject: fixes #5617, 'copyLineInfo' addition (#8523) --- changelog.md | 1 + compiler/vm.nim | 27 +++++++++++++-------------- compiler/vmdef.nim | 2 +- compiler/vmgen.nim | 16 ++++++++-------- lib/core/macros.nim | 3 +++ tests/macros/tlineinfo.nim | 14 ++++++++++++++ 6 files changed, 40 insertions(+), 23 deletions(-) create mode 100644 tests/macros/tlineinfo.nim (limited to 'lib/core') diff --git a/changelog.md b/changelog.md index 045286ef9..4c19abfc0 100644 --- a/changelog.md +++ b/changelog.md @@ -91,6 +91,7 @@ - ``parseOct`` and ``parseBin`` in parseutils now also support the ``maxLen`` argument similar to ``parseHexInt`` - Added the proc ``flush`` for memory mapped files. - Added the ``MemMapFileStream``. +- Added ``macros.copyLineInfo`` to copy lineInfo from other node ### Library changes diff --git a/compiler/vm.nim b/compiler/vm.nim index a6ec4788b..d4b041dea 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -1422,24 +1422,23 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = ensureKind(rkNode) if c.callsite != nil: regs[ra].node = c.callsite else: stackTrace(c, tos, pc, errFieldXNotFound & "callsite") - of opcNGetFile: - decodeB(rkNode) - let n = regs[rb].node - regs[ra].node = newStrNode(nkStrLit, toFullPath(c.config, n.info)) - regs[ra].node.info = n.info - regs[ra].node.typ = n.typ - of opcNGetLine: - decodeB(rkNode) + of opcNGetLineInfo: + decodeBImm(rkNode) let n = regs[rb].node - regs[ra].node = newIntNode(nkIntLit, n.info.line.int) + case imm + of 0: # getFile + regs[ra].node = newStrNode(nkStrLit, toFullPath(c.config, n.info)) + of 1: # getLine + regs[ra].node = newIntNode(nkIntLit, n.info.line.int) + of 2: # getColumn + regs[ra].node = newIntNode(nkIntLit, n.info.col) + else: + internalAssert c.config, false regs[ra].node.info = n.info regs[ra].node.typ = n.typ - of opcNGetColumn: + of opcNSetLineInfo: decodeB(rkNode) - let n = regs[rb].node - regs[ra].node = newIntNode(nkIntLit, n.info.col) - regs[ra].node.info = n.info - regs[ra].node.typ = n.typ + regs[ra].node.info = regs[rb].node.info of opcEqIdent: decodeBC(rkInt) # aliases for shorter and easier to understand code below diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim index 866b79568..a4489513a 100644 --- a/compiler/vmdef.nim +++ b/compiler/vmdef.nim @@ -102,7 +102,7 @@ type opcNError, opcNWarning, opcNHint, - opcNGetLine, opcNGetColumn, opcNGetFile, + opcNGetLineInfo, opcNSetLineInfo, opcEqIdent, opcStrToIdent, opcGetImpl, diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 3b5ea4beb..965c75485 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -1181,14 +1181,14 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = of mSameNodeType: genBinaryABC(c, n, dest, opcSameNodeType) of mNLineInfo: case n[0].sym.name.s - of "getFile": - genUnaryABC(c, n, dest, opcNGetFile) - of "getLine": - genUnaryABC(c, n, dest, opcNGetLine) - of "getColumn": - genUnaryABC(c, n, dest, opcNGetColumn) - else: - internalAssert c.config, false + of "getFile": genUnaryABI(c, n, dest, opcNGetLineInfo, 0) + of "getLine": genUnaryABI(c, n, dest, opcNGetLineInfo, 1) + of "getColumn": genUnaryABI(c, n, dest, opcNGetLineInfo, 2) + of "copyLineInfo": + internalAssert c.config, n.len == 3 + unused(c, n, dest) + genBinaryStmt(c, n, opcNSetLineInfo) + else: internalAssert c.config, false of mNHint: unused(c, n, dest) genUnaryStmt(c, n, opcNHint) diff --git a/lib/core/macros.nim b/lib/core/macros.nim index d4e8ada0a..90fea440e 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -432,6 +432,9 @@ proc getLine(arg: NimNode): int {.magic: "NLineInfo", noSideEffect.} proc getColumn(arg: NimNode): int {.magic: "NLineInfo", noSideEffect.} proc getFile(arg: NimNode): string {.magic: "NLineInfo", noSideEffect.} +proc copyLineInfo*(arg: NimNode, info: NimNode) {.magic: "NLineInfo", noSideEffect.} + ## copy lineinfo from info node + proc lineInfoObj*(n: NimNode): LineInfo {.compileTime.} = ## returns ``LineInfo`` of ``n``, using absolute path for ``filename`` result.filename = n.getFile diff --git a/tests/macros/tlineinfo.nim b/tests/macros/tlineinfo.nim new file mode 100644 index 000000000..2ab0e1ee8 --- /dev/null +++ b/tests/macros/tlineinfo.nim @@ -0,0 +1,14 @@ +# issue #5617, feature request +# Ability to set a NimNode's lineinfo +import macros + +type + Test = object + +macro mixer(n: typed): untyped = + let x = newIdentNode("echo") + x.copyLineInfo(n) + result = newLit(x.lineInfo == n.lineInfo) + +var z = mixer(Test) +doAssert z -- cgit 1.4.1-2-gfad0 From 3a626179ee041ee3b4e2068d9baf7799bad8ca35 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sat, 25 Aug 2018 12:48:37 -0700 Subject: doAssert, assert now print full path of failing line on error (#8555) --- lib/core/macros.nim | 4 ++- lib/system.nim | 28 ++++++++-------- lib/system/helpers.nim | 11 +++++++ tests/assert/testhelper.nim | 12 +++++++ tests/assert/tfailedassert.nim | 69 +++++++++++++++++++++++++++++++++------- tests/assert/tfaileddoassert.nim | 15 +++++++-- 6 files changed, 108 insertions(+), 31 deletions(-) create mode 100644 lib/system/helpers.nim create mode 100644 tests/assert/testhelper.nim (limited to 'lib/core') diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 90fea440e..bac9ce7a2 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -8,6 +8,7 @@ # include "system/inclrtl" +include "system/helpers" ## This module contains the interface to the compiler's abstract syntax ## tree (`AST`:idx:). Macros operate on this tree. @@ -422,7 +423,8 @@ type line*,column*: int proc `$`*(arg: Lineinfo): string = - result = arg.filename & "(" & $arg.line & ", " & $arg.column & ")" + # BUG: without `result = `, gives compile error + result = lineInfoToString(arg.filename, arg.line, arg.column) #proc lineinfo*(n: NimNode): LineInfo {.magic: "NLineInfo", noSideEffect.} ## returns the position the node appears in the original source file diff --git a/lib/system.nim b/lib/system.nim index dcfb04c5a..8b98706ab 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -3758,6 +3758,16 @@ proc failedAssertImpl*(msg: string) {.raises: [], tags: [].} = tags: [].} Hide(raiseAssert)(msg) +include "system/helpers" # for `lineInfoToString` + +template assertImpl(cond: bool, msg = "", enabled: static[bool]) = + const loc = $instantiationInfo(-1, true) + bind instantiationInfo + mixin failedAssertImpl + when enabled: + if not cond: + failedAssertImpl(loc & " `" & astToStr(cond) & "` " & msg) + template assert*(cond: bool, msg = "") = ## Raises ``AssertionError`` with `msg` if `cond` is false. Note ## that ``AssertionError`` is hidden from the effect system, so it doesn't @@ -3767,23 +3777,11 @@ template assert*(cond: bool, msg = "") = ## The compiler may not generate any code at all for ``assert`` if it is ## advised to do so through the ``-d:release`` or ``--assertions:off`` ## `command line switches `_. - # NOTE: `true` is correct here; --excessiveStackTrace:on will control whether - # or not to output full paths. - bind instantiationInfo - mixin failedAssertImpl - {.line: instantiationInfo(fullPaths = true).}: - when compileOption("assertions"): - if not cond: failedAssertImpl(astToStr(cond) & ' ' & msg) + assertImpl(cond, msg, compileOption("assertions")) template doAssert*(cond: bool, msg = "") = - ## same as `assert` but is always turned on and not affected by the - ## ``--assertions`` command line switch. - # NOTE: `true` is correct here; --excessiveStackTrace:on will control whether - # or not to output full paths. - bind instantiationInfo - mixin failedAssertImpl - {.line: instantiationInfo(fullPaths = true).}: - if not cond: failedAssertImpl(astToStr(cond) & ' ' & msg) + ## same as ``assert`` but is always turned on regardless of ``--assertions`` + assertImpl(cond, msg, true) iterator items*[T](a: seq[T]): T {.inline.} = ## iterates over each item of `a`. diff --git a/lib/system/helpers.nim b/lib/system/helpers.nim new file mode 100644 index 000000000..fb1218684 --- /dev/null +++ b/lib/system/helpers.nim @@ -0,0 +1,11 @@ +## helpers used system.nim and other modules, avoids code duplication while +## also minimizing symbols exposed in system.nim +# +# TODO: move other things here that should not be exposed in system.nim + +proc lineInfoToString(file: string, line, column: int): string = + file & "(" & $line & ", " & $column & ")" + +proc `$`(info: type(instantiationInfo(0))): string = + # The +1 is needed here + lineInfoToString(info.fileName, info.line, info.column+1) diff --git a/tests/assert/testhelper.nim b/tests/assert/testhelper.nim new file mode 100644 index 000000000..754d562ec --- /dev/null +++ b/tests/assert/testhelper.nim @@ -0,0 +1,12 @@ +from strutils import endsWith, split +from ospaths import isAbsolute + +proc checkMsg*(msg, expectedEnd, name: string)= + let filePrefix = msg.split(' ', maxSplit = 1)[0] + if not filePrefix.isAbsolute: + echo name, ":not absolute: `", msg & "`" + elif not msg.endsWith expectedEnd: + echo name, ":expected suffix:\n`" & expectedEnd & "`\ngot:\n`" & msg & "`" + else: + echo name, ":ok" + diff --git a/tests/assert/tfailedassert.nim b/tests/assert/tfailedassert.nim index 8b260a3ab..c3231bb8d 100644 --- a/tests/assert/tfailedassert.nim +++ b/tests/assert/tfailedassert.nim @@ -1,20 +1,65 @@ discard """ output: ''' -WARNING: false first assertion from bar -ERROR: false second assertion from bar +test1:ok +test2:ok +test3:ok +test4:ok +test5:ok +test6:ok +test7:ok -1 -tfailedassert.nim:27 false assertion from foo +tfailedassert.nim +test7:ok ''' """ +import testhelper + type TLineInfo = tuple[filename: string, line: int, column: int] - TMyError = object of Exception lineinfo: TLineInfo - EMyError = ref TMyError +echo("") + + +# NOTE: when entering newlines, adjust `expectedEnd` ouptuts + +try: + doAssert(false, "msg1") # doAssert test +except AssertionError as e: + checkMsg(e.msg, "tfailedassert.nim(30, 11) `false` msg1", "test1") + +try: + assert false, "msg2" # assert test +except AssertionError as e: + checkMsg(e.msg, "tfailedassert.nim(35, 10) `false` msg2", "test2") + +try: + assert false # assert test with no msg +except AssertionError as e: + checkMsg(e.msg, "tfailedassert.nim(40, 10) `false` ", "test3") + +try: + let a = 1 + doAssert(a+a==1) # assert test with Ast expression + # BUG: const folding would make "1+1==1" appear as `false` in + # assert message +except AssertionError as e: + checkMsg(e.msg, "`a + a == 1` ", "test4") + +try: + let a = 1 + doAssert a+a==1 # ditto with `doAssert` and no parens +except AssertionError as e: + checkMsg(e.msg, "`a + a == 1` ", "test5") + +proc fooStatic() = + # protect against https://github.com/nim-lang/Nim/issues/8758 + static: doAssert(true) +fooStatic() + # module-wide policy to change the failed assert # exception type in order to include a lineinfo onFailedAssert(msg): @@ -26,26 +71,26 @@ onFailedAssert(msg): proc foo = assert(false, "assertion from foo") + proc bar: int = - # local overrides that are active only - # in this proc - onFailedAssert(msg): echo "WARNING: " & msg + # local overrides that are active only in this proc + onFailedAssert(msg): + checkMsg(msg, "tfailedassert.nim(80, 9) `false` first assertion from bar", "test6") assert(false, "first assertion from bar") onFailedAssert(msg): - echo "ERROR: " & msg + checkMsg(msg, "tfailedassert.nim(86, 9) `false` second assertion from bar", "test7") return -1 assert(false, "second assertion from bar") return 10 -echo("") echo(bar()) try: foo() except: let e = EMyError(getCurrentException()) - echo e.lineinfo.filename, ":", e.lineinfo.line, " ", e.msg - + echo e.lineinfo.filename + checkMsg(e.msg, "tfailedassert.nim(72, 9) `false` assertion from foo", "test7") diff --git a/tests/assert/tfaileddoassert.nim b/tests/assert/tfaileddoassert.nim index 3195fb406..e1245f578 100644 --- a/tests/assert/tfaileddoassert.nim +++ b/tests/assert/tfaileddoassert.nim @@ -1,11 +1,20 @@ discard """ cmd: "nim $target -d:release $options $file" output: ''' -assertion occured!!!!!! false +test1:ok +test2:ok ''' """ +import testhelper + +onFailedAssert(msg): + checkMsg(msg, "tfaileddoassert.nim(15, 9) `a == 2` foo", "test1") + +var a = 1 +doAssert(a == 2, "foo") + onFailedAssert(msg): - echo("assertion occured!!!!!! ", msg) + checkMsg(msg, "tfaileddoassert.nim(20, 10) `a == 3` ", "test2") -doAssert(1 == 2) +doAssert a == 3 -- cgit 1.4.1-2-gfad0 From 5cd152bfda29adefc8ba6eb72117c91dbc2e9d7f Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Tue, 28 Aug 2018 22:59:28 +0200 Subject: Allow `hint` and `warning` to specify its loc info (#8771) Let's bring those to feature-parity with `error`. --- compiler/vm.nim | 20 ++++++++++++-------- compiler/vmgen.nim | 4 ++-- lib/core/macros.nim | 4 ++-- tests/macros/tmsginfo.nim | 24 ++++++++++++++++++++++++ 4 files changed, 40 insertions(+), 12 deletions(-) create mode 100644 tests/macros/tmsginfo.nim (limited to 'lib/core') diff --git a/compiler/vm.nim b/compiler/vm.nim index fcbd97b8d..827185737 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -81,14 +81,16 @@ proc stackTraceAux(c: PCtx; x: PStackFrame; pc: int; recursionLimit=100) = msgWriteln(c.config, s) proc stackTrace(c: PCtx, tos: PStackFrame, pc: int, - msg: string, n: PNode = nil) = + msg: string, lineInfo: TLineInfo) = msgWriteln(c.config, "stack trace: (most recent call last)") stackTraceAux(c, tos, pc) # XXX test if we want 'globalError' for every mode - let lineInfo = if n == nil: c.debug[pc] else: n.info if c.mode == emRepl: globalError(c.config, lineInfo, msg) else: localError(c.config, lineInfo, msg) +proc stackTrace(c: PCtx, tos: PStackFrame, pc: int, msg: string) = + stackTrace(c, tos, pc, msg, c.debug[pc]) + proc bailOut(c: PCtx; tos: PStackFrame) = stackTrace(c, tos, c.exceptionInstr, "unhandled exception: " & c.currentExceptionA.sons[3].skipColon.strVal) @@ -1385,15 +1387,17 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = c.debug[pc], c.config)[0] else: globalError(c.config, c.debug[pc], "VM is not built with 'gorge' support") - of opcNError: + of opcNError, opcNWarning, opcNHint: decodeB(rkNode) let a = regs[ra].node let b = regs[rb].node - stackTrace(c, tos, pc, a.strVal, if b.kind == nkNilLit: nil else: b) - of opcNWarning: - message(c.config, c.debug[pc], warnUser, regs[ra].node.strVal) - of opcNHint: - message(c.config, c.debug[pc], hintUser, regs[ra].node.strVal) + let info = if b.kind == nkNilLit: c.debug[pc] else: b.info + if instr.opcode == opcNError: + stackTrace(c, tos, pc, a.strVal, info) + elif instr.opcode == opcNWarning: + message(c.config, info, warnUser, a.strVal) + elif instr.opcode == opcNHint: + message(c.config, info, hintUser, a.strVal) of opcParseExprToAst: decodeB(rkNode) # c.debug[pc].line.int - countLines(regs[rb].strVal) ? diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index a36f559ca..cb4603164 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -1192,10 +1192,10 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = else: internalAssert c.config, false of mNHint: unused(c, n, dest) - genUnaryStmt(c, n, opcNHint) + genBinaryStmt(c, n, opcNHint) of mNWarning: unused(c, n, dest) - genUnaryStmt(c, n, opcNWarning) + genBinaryStmt(c, n, opcNWarning) of mNError: if n.len <= 1: # query error condition: diff --git a/lib/core/macros.nim b/lib/core/macros.nim index bac9ce7a2..5b29d5e94 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -336,10 +336,10 @@ proc copyNimTree*(n: NimNode): NimNode {.magic: "NCopyNimTree", noSideEffect.} proc error*(msg: string, n: NimNode = nil) {.magic: "NError", benign.} ## writes an error message at compile time -proc warning*(msg: string) {.magic: "NWarning", benign.} +proc warning*(msg: string, n: NimNode = nil) {.magic: "NWarning", benign.} ## writes a warning message at compile time -proc hint*(msg: string) {.magic: "NHint", benign.} +proc hint*(msg: string, n: NimNode = nil) {.magic: "NHint", benign.} ## writes a hint message at compile time proc newStrLitNode*(s: string): NimNode {.compileTime, noSideEffect.} = diff --git a/tests/macros/tmsginfo.nim b/tests/macros/tmsginfo.nim new file mode 100644 index 000000000..bf6c9d537 --- /dev/null +++ b/tests/macros/tmsginfo.nim @@ -0,0 +1,24 @@ +discard """ + nimout: '''tmsginfo.nim(21, 1) Warning: foo1 [User] +tmsginfo.nim(22, 11) template/generic instantiation from here +tmsginfo.nim(15, 10) Warning: foo2 [User] +tmsginfo.nim(23, 1) Hint: foo3 [User] +tmsginfo.nim(19, 7) Hint: foo4 [User] +''' +""" + +import macros + +macro foo1(y: untyped): untyped = + warning("foo1", y) +macro foo2(y: untyped): untyped = + warning("foo2") +macro foo3(y: untyped): untyped = + hint("foo3", y) +macro foo4(y: untyped): untyped = + hint("foo4") + +proc x1() {.foo1.} = discard +proc x2() {.foo2.} = discard +proc x3() {.foo3.} = discard +proc x4() {.foo4.} = discard -- cgit 1.4.1-2-gfad0 From e63c66b8104225cefe0413471410ef4c072f9954 Mon Sep 17 00:00:00 2001 From: cooldome Date: Mon, 3 Sep 2018 12:25:59 +0100 Subject: Add sym owner to macros (#8253) --- compiler/ast.nim | 2 +- compiler/condsyms.nim | 1 + compiler/vm.nim | 9 +++++++++ compiler/vmdef.nim | 3 ++- compiler/vmgen.nim | 1 + lib/core/macros.nim | 6 ++++++ tests/macros/tgetimpl.nim | 31 ++++++++++++++++++++++++++++++- 7 files changed, 50 insertions(+), 3 deletions(-) (limited to 'lib/core') diff --git a/compiler/ast.nim b/compiler/ast.nim index 001d96060..694944631 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -657,7 +657,7 @@ type mNHint, mNWarning, mNError, mInstantiationInfo, mGetTypeInfo, mNimvm, mIntDefine, mStrDefine, mRunnableExamples, - mException, mBuiltinType + mException, mBuiltinType, mSymOwner # things that we can evaluate safely at compile time, even if not asked for it: const diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index 78645b6fd..a22b613f0 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -75,6 +75,7 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimNoZeroTerminator") defineSymbol("nimNotNil") defineSymbol("nimVmExportFixed") + defineSymbol("nimHasSymOwnerInMacro") defineSymbol("nimNewRuntime") defineSymbol("nimIncrSeqV3") defineSymbol("nimAshr") diff --git a/compiler/vm.nim b/compiler/vm.nim index 9143051a9..e38642de8 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -920,6 +920,15 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = regs[ra].node.flags.incl nfIsRef else: stackTrace(c, tos, pc, "node is not a symbol") + of opcSymOwner: + decodeB(rkNode) + let a = regs[rb].node + if a.kind == nkSym: + regs[ra].node = if a.sym.owner.isNil: newNode(nkNilLit) + else: newSymNode(a.sym.skipGenericOwner) + regs[ra].node.flags.incl nfIsRef + else: + stackTrace(c, tos, pc, "node is not a symbol") of opcEcho: let rb = instr.regB if rb == 1: diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim index 0aee079f5..d642043dc 100644 --- a/compiler/vmdef.nim +++ b/compiler/vmdef.nim @@ -141,7 +141,8 @@ type opcSetType, # dest.typ = types[Bx] opcTypeTrait, opcMarshalLoad, opcMarshalStore, - opcToNarrowInt + opcToNarrowInt, + opcSymOwner TBlock* = object label*: PSym diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index dcf76f639..e87347ec8 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -1118,6 +1118,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = of mStaticExec: genBinaryABCD(c, n, dest, opcGorge) of mNLen: genUnaryABI(c, n, dest, opcLenSeq, nimNodeFlag) of mGetImpl: genUnaryABC(c, n, dest, opcGetImpl) + of mSymOwner: genUnaryABC(c, n, dest, opcSymOwner) of mNChild: genBinaryABC(c, n, dest, opcNChild) of mNSetChild: genVoidABC(c, n, dest, opcNSetChild) of mNDel: genVoidABC(c, n, dest, opcNDel) diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 5b29d5e94..4d76d60c2 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -237,6 +237,12 @@ else: # bootstrapping substitute else: n.strValOld +when defined(nimHasSymOwnerInMacro): + proc owner*(sym: NimNode): NimNode {.magic: "SymOwner", noSideEffect.} + ## accepts node of kind nnkSym and returns its owner's symbol. + ## result is also mnde of kind nnkSym if owner exists otherwise + ## nnkNilLit is returned + proc getType*(n: NimNode): NimNode {.magic: "NGetType", noSideEffect.} ## with 'getType' you can access the node's `type`:idx:. A Nim type is ## mapped to a Nim AST too, so it's slightly confusing but it means the same diff --git a/tests/macros/tgetimpl.nim b/tests/macros/tgetimpl.nim index d38492934..715c969f3 100644 --- a/tests/macros/tgetimpl.nim +++ b/tests/macros/tgetimpl.nim @@ -1,6 +1,7 @@ discard """ msg: '''"muhaha" proc poo(x, y: int) = + let y = x echo ["poo"]''' """ @@ -10,11 +11,39 @@ const foo = "muhaha" proc poo(x, y: int) = + let y = x echo "poo" macro m(x: typed): untyped = - echo repr x.symbol.getImpl + echo repr x.getImpl result = x discard m foo discard m poo + +#------------ + +macro checkOwner(x: typed, check_id: static[int]): untyped = + let sym = case check_id: + of 0: x + of 1: x.getImpl.body[0][0][0] + of 2: x.getImpl.body[0][0][^1] + of 3: x.getImpl.body[1][0] + else: x + result = newStrLitNode($sym.owner.symKind) + +macro isSameOwner(x, y: typed): untyped = + result = + if x.owner == y.owner: bindSym"true" + else: bindSym"false" + + +static: + doAssert checkOwner(foo, 0) == "nskModule" + doAssert checkOwner(poo, 0) == "nskModule" + doAssert checkOwner(poo, 1) == "nskProc" + doAssert checkOwner(poo, 2) == "nskProc" + doAssert checkOwner(poo, 3) == "nskModule" + doAssert isSameOwner(foo, poo) + doAssert isSameOwner(foo, echo) == false + doAssert isSameOwner(poo, len) == false -- cgit 1.4.1-2-gfad0