diff options
author | Saem Ghani <saemghani+github@gmail.com> | 2021-06-14 00:21:33 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-06-14 09:21:33 +0200 |
commit | 488acd9d077e8179d35d665ac0591c456bfa93aa (patch) | |
tree | 2c3d5f3dc35063b929aed0a9e9c0397fc354c4f3 | |
parent | e1e8af535ec195e5749dffa728add95770e5cbd7 (diff) | |
download | Nim-488acd9d077e8179d35d665ac0591c456bfa93aa.tar.gz |
fixes #18235 - proc annotation type macro sym leak (#18249)
* fixes #18235 - proc annotation type macro sym leak - also fixed a typo - proc annotations guard symbol exports with shadow scopes - symbol handling is shadow scope aware * test for exporting an existing unexported sym this one is for my homie alaviss. * Special handling not needed in semProcAnnotation * Testcasing * [skip ci] clean-up and add some more comments * [skip ci] rm trailing whitespace Co-authored-by: Clyybber <darkmine956@gmail.com>
-rw-r--r-- | compiler/lookups.nim | 27 | ||||
-rw-r--r-- | compiler/semstmts.nim | 8 | ||||
-rw-r--r-- | tests/macros/m18235.nim | 42 | ||||
-rw-r--r-- | tests/macros/t18235.nim | 18 |
4 files changed, 86 insertions, 9 deletions
diff --git a/compiler/lookups.nim b/compiler/lookups.nim index 64476d816..a21e35ddc 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -314,6 +314,7 @@ proc addDeclAt*(c: PContext; scope: PScope, sym: PSym) = from ic / ic import addHidden proc addInterfaceDeclAux*(c: PContext, sym: PSym, forceExport = false) = + ## adds symbol to the module for either private or public access. if sfExported in sym.flags or forceExport: # add to interface: if c.module != nil: exportSym(c, sym) @@ -324,10 +325,15 @@ proc addInterfaceDeclAux*(c: PContext, sym: PSym, forceExport = false) = addHidden(c.encoder, c.packedRepr, sym) proc addInterfaceDeclAt*(c: PContext, scope: PScope, sym: PSym) = + ## adds a symbol on the scope and the interface if appropriate addDeclAt(c, scope, sym) - addInterfaceDeclAux(c, sym) + if not scope.isShadowScope: + # adding into a non-shadow scope, we need to handle exports, etc + addInterfaceDeclAux(c, sym) proc addOverloadableSymAt*(c: PContext; scope: PScope, fn: PSym) = + ## adds an symbol to the given scope, will check for and raise errors if it's + ## a redefinition as opposed to an overload. if fn.kind notin OverloadableSyms: internalError(c.config, fn.info, "addOverloadableSymAt") return @@ -338,24 +344,35 @@ proc addOverloadableSymAt*(c: PContext; scope: PScope, fn: PSym) = scope.addSym(fn) proc addInterfaceDecl*(c: PContext, sym: PSym) = - # it adds the symbol to the interface if appropriate + ## adds a decl and the interface if appropriate addDecl(c, sym) - addInterfaceDeclAux(c, sym) + if not c.currentScope.isShadowScope: + addInterfaceDeclAux(c, sym) proc addInterfaceOverloadableSymAt*(c: PContext, scope: PScope, sym: PSym) = - # it adds the symbol to the interface if appropriate + ## adds an overloadable symbol on the scope and the interface if appropriate addOverloadableSymAt(c, scope, sym) - addInterfaceDeclAux(c, sym) + if not scope.isShadowScope: + addInterfaceDeclAux(c, sym) proc openShadowScope*(c: PContext) = + ## opens a shadow scope, just like any other scope except the depth is the + ## same as the parent -- see `isShadowScope`. c.currentScope = PScope(parent: c.currentScope, symbols: newStrTable(), depthLevel: c.scopeDepth) proc closeShadowScope*(c: PContext) = + ## closes the shadow scope, but doesn't merge any of the symbols c.closeScope proc mergeShadowScope*(c: PContext) = + ## close the existing scope and merge in all defined symbols, this will also + ## trigger any export related code if this is into a non-shadow scope. + ## + ## Merges: + ## shadow -> shadow: add symbols to the parent but check for redefinitions etc + ## shadow -> non-shadow: the above, but also handle exports and all that let shadowScope = c.currentScope c.rawCloseScope for sym in shadowScope.symbols: diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 8f87ce83c..d6ad7ab54 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1844,7 +1844,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, # before compiling the proc params & body, set as current the scope # where the proc was declared - let delcarationScope = c.currentScope + let declarationScope = c.currentScope pushOwner(c, s) openScope(c) @@ -1889,7 +1889,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, var (proto, comesFromShadowScope) = if isAnon: (nil, false) - else: searchForProc(c, delcarationScope, s) + else: searchForProc(c, declarationScope, s) if proto == nil and sfForward in s.flags: ## In cases such as a macro generating a proc with a gensymmed name we ## know `searchForProc` will not find it and sfForward will be set. In @@ -1916,9 +1916,9 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, if not hasProto and sfGenSym notin s.flags: #and not isAnon: if s.kind in OverloadableSyms: - addInterfaceOverloadableSymAt(c, delcarationScope, s) + addInterfaceOverloadableSymAt(c, declarationScope, s) else: - addInterfaceDeclAt(c, delcarationScope, s) + addInterfaceDeclAt(c, declarationScope, s) pragmaCallable(c, s, n, validPragmas) if not hasProto: diff --git a/tests/macros/m18235.nim b/tests/macros/m18235.nim new file mode 100644 index 000000000..6bb4547e0 --- /dev/null +++ b/tests/macros/m18235.nim @@ -0,0 +1,42 @@ +import macros + +# Necessary code to update the AST on a symbol across module boundaries when +# processed by a type macro. Used by a test of a corresponding name of this +# file. + +macro eexport(n: typed): untyped = + result = copyNimTree(n) + # turn exported nnkSym -> nnkPostfix(*, nnkIdent), forcing re-sem + result[0] = nnkPostfix.newTree(ident"*").add: + n.name.strVal.ident + +macro unexport(n: typed): untyped = + result = copyNimTree(n) + # turn nnkSym -> nnkIdent, forcing re-sem and dropping any exported-ness + # that might be present + result[0] = n.name.strVal.ident + +proc foo*() {.unexport.} = discard +proc bar() {.eexport.} = discard + +proc foooof*() {.unexport, eexport, unexport.} = discard +proc barrab() {.eexport, unexport, eexport.} = discard + +macro eexportMulti(n: typed): untyped = + # use the call version of `eexport` macro for one or more decls + result = copyNimTree(n) + for i in 0..<result.len: + result[i] = newCall(ident"eexport", result[i]) + +macro unexportMulti(n: typed): untyped = + # use the call version of `unexport` macro for one or more decls + result = copyNimTree(n) + for i in 0..<result.len: + result[i] = newCall(ident"unexport", result[i]) + +unexportMulti: + proc oof*() = discard + +eexportMulti: + proc rab() = discard + proc baz*() = discard \ No newline at end of file diff --git a/tests/macros/t18235.nim b/tests/macros/t18235.nim new file mode 100644 index 000000000..ba5c48a24 --- /dev/null +++ b/tests/macros/t18235.nim @@ -0,0 +1,18 @@ +import m18235 + +# this must error out because it was never actually exported +doAssert(not declared(foo)) +doAssert not compiles(foo()) + +doAssert(not declared(foooof)) +doAssert not compiles(foooof()) + +doAssert(not declared(oof)) +doAssert not compiles(oof()) + +# this should have been exported just fine + +bar() +barrab() +rab() +baz() \ No newline at end of file |