diff options
-rw-r--r-- | changelog.md | 6 | ||||
-rw-r--r-- | compiler/ast.nim | 1 | ||||
-rw-r--r-- | compiler/condsyms.nim | 1 | ||||
-rw-r--r-- | compiler/lineinfos.nim | 2 | ||||
-rw-r--r-- | compiler/pragmas.nim | 5 | ||||
-rw-r--r-- | compiler/semexprs.nim | 4 | ||||
-rw-r--r-- | compiler/semtempl.nim | 3 | ||||
-rw-r--r-- | compiler/wordrecg.nim | 1 | ||||
-rw-r--r-- | doc/manual.md | 16 | ||||
-rw-r--r-- | lib/pure/asyncmacro.nim | 4 | ||||
-rw-r--r-- | lib/pure/ioselects/ioselectors_select.nim | 12 | ||||
-rw-r--r-- | lib/std/assertions.nim | 6 | ||||
-rw-r--r-- | tests/lookups/tredef.nim | 2 | ||||
-rw-r--r-- | tests/macros/tstructuredlogging.nim | 2 | ||||
-rw-r--r-- | tests/parser/tstmtlists.nim | 2 | ||||
-rw-r--r-- | tests/template/template_various.nim | 4 | ||||
-rw-r--r-- | tests/template/tredefinition_override.nim | 33 | ||||
-rw-r--r-- | tests/template/utemplates.nim | 2 | ||||
-rw-r--r-- | tests/trmacros/trmacros_various2.nim | 4 |
19 files changed, 85 insertions, 25 deletions
diff --git a/changelog.md b/changelog.md index 30bfc5c77..80692e996 100644 --- a/changelog.md +++ b/changelog.md @@ -123,6 +123,12 @@ Baz = object ``` +- Redefining templates with the same signature implicitly was previously + allowed to support certain macro code. A `{.redefine.}` pragma has been + added to make this work explicitly, and a warning is generated in the case + where it is implicit. This behavior only applies to templates, redefinition + is generally disallowed for other symbols. + ## Compiler changes - The `gc` switch has been renamed to `mm` ("memory management") in order to reflect the diff --git a/compiler/ast.nim b/compiler/ast.nim index 910c95451..c720a76fb 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -334,6 +334,7 @@ const sfEscapes* = sfProcvar # param escapes sfBase* = sfDiscriminant sfCustomPragma* = sfRegister # symbol is custom pragma template + sfTemplateRedefinition* = sfExportc # symbol is a redefinition of an earlier template const # getting ready for the future expr/stmt merge diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index 5a7e78d5f..4d4358b16 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -140,3 +140,4 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimHasEffectsOf") defineSymbol("nimHasEnforceNoRaises") + defineSymbol("nimHasTemplateRedefinitionPragma") diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim index 07cc988a9..ec1643960 100644 --- a/compiler/lineinfos.nim +++ b/compiler/lineinfos.nim @@ -81,6 +81,7 @@ type warnCstringConv = "CStringConv", warnEffect = "Effect", warnCastSizes = "CastSizes" + warnTemplateRedefinition = "TemplateRedefinition", warnUser = "User", # hints hintSuccess = "Success", hintSuccessX = "SuccessX", @@ -175,6 +176,7 @@ const warnCstringConv: "$1", warnEffect: "$1", warnCastSizes: "$1", + warnTemplateRedefinition: "template '$1' is implicitly redefined, consider adding an explicit .redefine pragma", warnUser: "$1", hintSuccess: "operation successful: $#", # keep in sync with `testament.isSuccess` diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index d5c728418..b662e09c5 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -38,7 +38,7 @@ const converterPragmas* = procPragmas methodPragmas* = procPragmas+{wBase}-{wImportCpp} templatePragmas* = {wDeprecated, wError, wGensym, wInject, wDirty, - wDelegator, wExportNims, wUsed, wPragma} + wDelegator, wExportNims, wUsed, wPragma, wRedefine} macroPragmas* = declPragmas + {FirstCallConv..LastCallConv, wMagic, wNoSideEffect, wCompilerProc, wNonReloadable, wCore, wDiscardable, wGensym, wInject, wDelegator} @@ -870,6 +870,9 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, of wDirty: if sym.kind == skTemplate: incl(sym.flags, sfDirty) else: invalidPragma(c, it) + of wRedefine: + if sym.kind == skTemplate: incl(sym.flags, sfTemplateRedefinition) + else: invalidPragma(c, it) of wImportCpp: processImportCpp(c, sym, getOptionalStr(c, it, "$1"), it.info) of wCppNonPod: diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 119daa8b3..b47aff429 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -2112,10 +2112,12 @@ proc semQuoteAst(c: PContext, n: PNode): PNode = ids[0] = newAnonSym(c, skParam, n.info).newSymNode processQuotations(c, quotedBlock, op, quotes, ids) + let dummyTemplateSym = newAnonSym(c, skTemplate, n.info) + incl(dummyTemplateSym.flags, sfTemplateRedefinition) var dummyTemplate = newProcNode( nkTemplateDef, quotedBlock.info, body = quotedBlock, params = c.graph.emptyNode, - name = newAnonSym(c, skTemplate, n.info).newSymNode, + name = dummyTemplateSym.newSymNode, pattern = c.graph.emptyNode, genericParams = c.graph.emptyNode, pragmas = c.graph.emptyNode, exceptions = c.graph.emptyNode) diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index 1f76aff75..c72e9db05 100644 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -691,6 +691,9 @@ proc semTemplateDef(c: PContext, n: PNode): PNode = if proto == nil: addInterfaceOverloadableSymAt(c, c.currentScope, s) elif not comesFromShadowscope: + if {sfTemplateRedefinition, sfGenSym} * s.flags == {}: + #wrongRedefinition(c, n.info, proto.name.s, proto.info) + message(c.config, n.info, warnTemplateRedefinition, s.name.s) symTabReplace(c.currentScope.symbols, proto, s) if n[patternPos].kind != nkEmpty: c.patterns.add(s) diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim index 71b2e6384..d33982b0f 100644 --- a/compiler/wordrecg.nim +++ b/compiler/wordrecg.nim @@ -87,6 +87,7 @@ type wGlobal = "global", wCodegenDecl = "codegenDecl", wUnchecked = "unchecked", wGuard = "guard", wLocks = "locks", wPartial = "partial", wExplain = "explain", wLiftLocals = "liftlocals", wEnforceNoRaises = "enforceNoRaises", + wRedefine = "redefine", wAuto = "auto", wBool = "bool", wCatch = "catch", wChar = "char", wClass = "class", wCompl = "compl", wConstCast = "const_cast", wDefault = "default", diff --git a/doc/manual.md b/doc/manual.md index 0a7231d23..4642fdb5c 100644 --- a/doc/manual.md +++ b/doc/manual.md @@ -7050,6 +7050,22 @@ immediate pragma The immediate pragma is obsolete. See `Typed vs untyped parameters <#templates-typed-vs-untyped-parameters>`_. +redefine pragma +--------------- + +Redefinition of template symbols with the same signature is allowed. +This can be made explicit with the `redefine` pragma: + +```nim +template foo: int = 1 +echo foo() # 1 +template foo: int {.redefine.} = 2 +echo foo() # 2 +# warning: implicit redefinition of template +template foo: int = 3 +``` + +This is mostly intended for macro generated code. compilation option pragmas -------------------------- diff --git a/lib/pure/asyncmacro.nim b/lib/pure/asyncmacro.nim index 63c8e6e5c..d85e3e621 100644 --- a/lib/pure/asyncmacro.nim +++ b/lib/pure/asyncmacro.nim @@ -126,7 +126,9 @@ template await*(f: typed): untyped {.used.} = error "await expects Future[T], got " & $typeof(f) template await*[T](f: Future[T]): auto {.used.} = - template yieldFuture = yield FutureBase() + when not defined(nimHasTemplateRedefinitionPragma): + {.pragma: redefine.} + template yieldFuture {.redefine.} = yield FutureBase() when compiles(yieldFuture): var internalTmpFuture: FutureBase = f diff --git a/lib/pure/ioselects/ioselectors_select.nim b/lib/pure/ioselects/ioselectors_select.nim index 4c38ba604..34c88d85e 100644 --- a/lib/pure/ioselects/ioselectors_select.nim +++ b/lib/pure/ioselects/ioselectors_select.nim @@ -398,18 +398,6 @@ proc contains*[T](s: Selector[T], fd: SocketHandle|int): bool {.inline.} = if s.fds[i].ident == fdi: return true -when hasThreadSupport: - template withSelectLock[T](s: Selector[T], body: untyped) = - acquire(s.lock) - {.locks: [s.lock].}: - try: - body - finally: - release(s.lock) -else: - template withSelectLock[T](s: Selector[T], body: untyped) = - body - proc getData*[T](s: Selector[T], fd: SocketHandle|int): var T = s.withSelectLock(): let fdi = int(fd) diff --git a/lib/std/assertions.nim b/lib/std/assertions.nim index 3d2112b1a..0bc4653f2 100644 --- a/lib/std/assertions.nim +++ b/lib/std/assertions.nim @@ -85,7 +85,9 @@ template onFailedAssert*(msg, code: untyped): untyped {.dirty.} = onFailedAssert(msg): raise (ref MyError)(msg: msg, lineinfo: instantiationInfo(-2)) doAssertRaises(MyError): doAssert false - template failedAssertImpl(msgIMPL: string): untyped {.dirty.} = + when not defined(nimHasTemplateRedefinitionPragma): + {.pragma: redefine.} + template failedAssertImpl(msgIMPL: string): untyped {.dirty, redefine.} = let msg = msgIMPL code @@ -98,7 +100,7 @@ template doAssertRaises*(exception: typedesc, code: untyped) = var wrong = false const begin = "expected raising '" & astToStr(exception) & "', instead" const msgEnd = " by: " & astToStr(code) - template raisedForeign = raiseAssert(begin & " raised foreign exception" & msgEnd) + template raisedForeign {.gensym.} = raiseAssert(begin & " raised foreign exception" & msgEnd) when Exception is exception: try: if true: diff --git a/tests/lookups/tredef.nim b/tests/lookups/tredef.nim index 8f1b38bd1..7c1df238e 100644 --- a/tests/lookups/tredef.nim +++ b/tests/lookups/tredef.nim @@ -4,7 +4,7 @@ foo(1, "test") proc bar(a: int, b: string) = discard bar(1, "test") -template foo(a: int, b: string) = bar(a, b) +template foo(a: int, b: string) {.redefine.} = bar(a, b) foo(1, "test") block: diff --git a/tests/macros/tstructuredlogging.nim b/tests/macros/tstructuredlogging.nim index 05bb52a40..5e4fdea3f 100644 --- a/tests/macros/tstructuredlogging.nim +++ b/tests/macros/tstructuredlogging.nim @@ -66,7 +66,7 @@ macro mergeScopes(scopeHolders: typed, newBindings: untyped): untyped = newScopeDefinition.add newAssignment(newIdentNode(k), v) result = quote: - template scopeHolder = `newScopeDefinition` + template scopeHolder {.redefine.} = `newScopeDefinition` template scope(newBindings: untyped) {.dirty.} = mergeScopes(bindSym"scopeHolder", newBindings) diff --git a/tests/parser/tstmtlists.nim b/tests/parser/tstmtlists.nim index fa47ba364..158c93340 100644 --- a/tests/parser/tstmtlists.nim +++ b/tests/parser/tstmtlists.nim @@ -158,7 +158,7 @@ template dim2: int = else: int.high) -template dim: int = +template dim3: int = ( if int.high == 0: int.high diff --git a/tests/template/template_various.nim b/tests/template/template_various.nim index e7a2be748..6b1290fb9 100644 --- a/tests/template/template_various.nim +++ b/tests/template/template_various.nim @@ -274,10 +274,10 @@ parse9: block gensym1: template x: untyped = -1 template t1() = - template x: untyped {.gensym.} = 1 + template x: untyped {.gensym, redefine.} = 1 echo x() # 1 template t2() = - template x: untyped = 1 # defaults to {.inject.} + template x: untyped {.redefine.} = 1 # defaults to {.inject.} echo x() # -1 injected x not available during template definition t1() t2() diff --git a/tests/template/tredefinition_override.nim b/tests/template/tredefinition_override.nim new file mode 100644 index 000000000..0bda4025b --- /dev/null +++ b/tests/template/tredefinition_override.nim @@ -0,0 +1,33 @@ +{.push warningAsError[TemplateRedefinition]: on.} + +doAssert not (compiles do: + template foo(): int = 1 + template foo(): int = 2) +doAssert (compiles do: + template foo(): int = 1 + template foo(): int {.redefine.} = 2) +doAssert not (compiles do: + block: + template foo() = + template bar: string {.gensym.} = "a" + template bar: string {.gensym.} = "b" + foo()) +doAssert (compiles do: + block: + template foo() = + template bar: string {.gensym.} = "a" + template bar: string {.gensym, redefine.} = "b" + foo()) + +block: + template foo(): int = 1 + template foo(): int {.redefine.} = 2 + doAssert foo() == 2 +block: + template foo(): string = + template bar: string {.gensym.} = "a" + template bar: string {.gensym, redefine.} = "b" + bar() + doAssert foo() == "b" + +{.pop.} diff --git a/tests/template/utemplates.nim b/tests/template/utemplates.nim index 7674ba7c0..d70746578 100644 --- a/tests/template/utemplates.nim +++ b/tests/template/utemplates.nim @@ -22,7 +22,7 @@ block: # templates can be redefined multiple times if not cond: fail(msg) template assertionFailed(body: untyped) {.dirty.} = - template fail(msg: string): typed = + template fail(msg: string): typed {.redefine.} = body assertionFailed: diff --git a/tests/trmacros/trmacros_various2.nim b/tests/trmacros/trmacros_various2.nim index c1367cb1b..257ee8ba6 100644 --- a/tests/trmacros/trmacros_various2.nim +++ b/tests/trmacros/trmacros_various2.nim @@ -33,8 +33,8 @@ block tpartial: proc p(x, y: int; cond: bool): int = result = if cond: x + y else: x - y - template optP{p(x, y, true)}(x, y): untyped = x - y - template optP{p(x, y, false)}(x, y): untyped = x + y + template optPTrue{p(x, y, true)}(x, y): untyped = x - y + template optPFalse{p(x, y, false)}(x, y): untyped = x + y echo p(2, 4, true) |