diff --git a/compiler/ast.nim b/compiler/ast.nim index 62c1e38942..06ff92e9f6 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -617,7 +617,7 @@ var type TMagic* = enum # symbols that require compiler magic: mNone, - mDefined, mDefinedInScope, mCompiles, mArrGet, mArrPut, mAsgn, + mDefined, mDeclared, mDeclaredInScope, mCompiles, mArrGet, mArrPut, mAsgn, mLow, mHigh, mSizeOf, mAlignOf, mOffsetOf, mTypeTrait, mIs, mOf, mAddr, mType, mTypeOf, mPlugin, mEcho, mShallowCopy, mSlurp, mStaticExec, mStatic, diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index b7f7100f12..ad13f3e1e8 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -119,3 +119,4 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimHasStacktraceMsgs") defineSymbol("nimDoesntTrackDefects") defineSymbol("nimHasLentIterators") + defineSymbol("nimHasDeclaredMagic") diff --git a/compiler/lookups.nim b/compiler/lookups.nim index a3a2bf49c2..c0db259502 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -93,6 +93,11 @@ proc skipAlias*(s: PSym; n: PNode; conf: ConfigRef): PSym = proc localSearchInScope*(c: PContext, s: PIdent): PSym = result = strTableGet(c.currentScope.symbols, s) + var shadow = c.currentScope + while result == nil and shadow.parent != nil and shadow.depthLevel == shadow.parent.depthLevel: + # We are in a shadow scope, check in the parent too + result = strTableGet(shadow.parent.symbols, s) + shadow = shadow.parent proc searchInScopes*(c: PContext, s: PIdent): PSym = for scope in walkScopes(c.currentScope): @@ -231,6 +236,23 @@ proc addInterfaceOverloadableSymAt*(c: PContext, scope: PScope, sym: PSym) = addOverloadableSymAt(c, scope, sym) addInterfaceDeclAux(c, sym) +proc openShadowScope*(c: PContext) = + c.currentScope = PScope(parent: c.currentScope, + symbols: newStrTable(), + depthLevel: c.scopeDepth) + +proc closeShadowScope*(c: PContext) = + c.closeScope + +proc mergeShadowScope*(c: PContext) = + let shadowScope = c.currentScope + c.rawCloseScope + for sym in shadowScope.symbols: + if sym.kind in OverloadableSyms: + c.addInterfaceOverloadableSymAt(c.currentScope, sym) + else: + c.addInterfaceDecl(sym) + when defined(nimfix): # when we cannot find the identifier, retry with a changed identifier: proc altSpelling(x: PIdent): PIdent = diff --git a/compiler/sem.nim b/compiler/sem.nim index a07f62ca0d..07c76eed4f 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -383,12 +383,10 @@ when not defined(nimHasSinkInference): include hlo, seminst, semcall -when false: - # hopefully not required: - proc resetSemFlag(n: PNode) = - excl n.flags, nfSem - for i in 0.. 1: m.callee.n[1].sym else: nil while a < n.len: + + c.openShadowScope + if a >= formalLen-1 and f < formalLen and m.callee.n[f].typ.isVarargsUntyped: formal = m.callee.n[f].sym incl(marker, formal.position) @@ -2383,12 +2385,10 @@ proc matchesAux(c: PContext, n, nOrig: PNode, if n[a][0].kind != nkIdent: localError(c.config, n[a].info, "named parameter has to be an identifier") noMatch() - return formal = getNamedParamFromList(m.callee.n, n[a][0].ident) if formal == nil: # no error message! noMatch() - return if containsOrIncl(marker, formal.position): m.firstMismatch.kind = kAlreadyGiven # already in namedParams, so no match @@ -2397,7 +2397,6 @@ proc matchesAux(c: PContext, n, nOrig: PNode, # different parameter names could match later on): when false: localError(n[a].info, errCannotBindXTwice, formal.name.s) noMatch() - return m.baseTypeMatch = false m.typedescMatched = false n[a][1] = prepareOperand(c, formal.typ, n[a][1]) @@ -2407,7 +2406,6 @@ proc matchesAux(c: PContext, n, nOrig: PNode, m.firstMismatch.kind = kTypeMismatch if arg == nil: noMatch() - return checkConstraint(n[a][1]) if m.baseTypeMatch: #assert(container == nil) @@ -2448,16 +2446,13 @@ proc matchesAux(c: PContext, n, nOrig: PNode, checkConstraint(n[a]) else: noMatch() - return else: m.firstMismatch.kind = kExtraArg noMatch() - return else: if m.callee.n[f].kind != nkSym: internalError(c.config, n[a].info, "matches") noMatch() - return formal = m.callee.n[f].sym m.firstMismatch.kind = kTypeMismatch if containsOrIncl(marker, formal.position) and container.isNil: @@ -2465,7 +2460,6 @@ proc matchesAux(c: PContext, n, nOrig: PNode, # positional param already in namedParams: (see above remark) when false: localError(n[a].info, errCannotBindXTwice, formal.name.s) noMatch() - return if formal.typ.isVarargsUntyped: if container.isNil: @@ -2482,7 +2476,6 @@ proc matchesAux(c: PContext, n, nOrig: PNode, n[a], nOrig[a]) if arg == nil: noMatch() - return if m.baseTypeMatch: assert formal.typ.kind == tyVarargs #assert(container == nil) @@ -2511,8 +2504,13 @@ proc matchesAux(c: PContext, n, nOrig: PNode, localError(c.config, n[a].info, "cannot convert $1 to $2" % [ typeToString(n[a].typ), typeToString(formal.typ) ]) noMatch() - return checkConstraint(n[a]) + + if m.state == csMatch and not(m.calleeSym != nil and m.calleeSym.kind in {skTemplate, skMacro}): + c.mergeShadowScope + else: + c.closeShadowScope + inc(a) # for some edge cases (see tdont_return_unowned_from_owned test case) m.firstMismatch.arg = a @@ -2544,7 +2542,7 @@ proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) = if m.state == csNoMatch: return # check that every formal parameter got a value: for f in 1..`_ - ## - ## This can be used to check whether a library provides a certain - ## feature or not: - ## - ## .. code-block:: Nim - ## when not declared(strutils.toUpper): - ## # provide our own toUpper proc here, because strutils is - ## # missing it. +when defined(nimHasDeclaredMagic): + proc declared*(x: untyped): bool {.magic: "Declared", noSideEffect, compileTime.} + ## Special compile-time procedure that checks whether `x` is + ## declared. `x` has to be an identifier or a qualified identifier. + ## + ## See also: + ## * `declaredInScope <#declaredInScope,untyped>`_ + ## + ## This can be used to check whether a library provides a certain + ## feature or not: + ## + ## .. code-block:: Nim + ## when not declared(strutils.toUpper): + ## # provide our own toUpper proc here, because strutils is + ## # missing it. +else: + proc declared*(x: untyped): bool {.magic: "Defined", noSideEffect, compileTime.} -proc declaredInScope*(x: untyped): bool {. - magic: "DefinedInScope", noSideEffect, compileTime.} - ## Special compile-time procedure that checks whether `x` is - ## declared in the current scope. `x` has to be an identifier. +when defined(nimHasDeclaredMagic): + proc declaredInScope*(x: untyped): bool {.magic: "DeclaredInScope", noSideEffect, compileTime.} + ## Special compile-time procedure that checks whether `x` is + ## declared in the current scope. `x` has to be an identifier. +else: + proc declaredInScope*(x: untyped): bool {.magic: "DefinedInScope", noSideEffect, compileTime.} proc `addr`*[T](x: var T): ptr T {.magic: "Addr", noSideEffect.} = ## Builtin `addr` operator for taking the address of a memory location. diff --git a/tests/macros/tmacros_various.nim b/tests/macros/tmacros_various.nim index 3cb5aaa8f0..64bb7345ad 100644 --- a/tests/macros/tmacros_various.nim +++ b/tests/macros/tmacros_various.nim @@ -22,6 +22,7 @@ a[1]: 45 x: some string ([("key", "val"), ("keyB", "2")], [("val", "key"), ("2", "keyB")]) ([("key", "val"), ("keyB", "2")], [("val", "key"), ("2", "keyB")]) +0 ''' """ @@ -222,3 +223,17 @@ block getImplTransformed: doAssert "toExpand" notin code # template is expanded (but that would already be the case with # `a.getImpl.repr`, unlike the other transformations mentioned above + + +# test macro resemming +macro makeVar(): untyped = + quote: + var tensorY {.inject.}: int + +macro noop(a: typed): untyped = + a + +noop: + makeVar +echo tensorY + diff --git a/tests/macros/tmacrotypes.nim b/tests/macros/tmacrotypes.nim index cfc4ef7fb7..43819c81df 100644 --- a/tests/macros/tmacrotypes.nim +++ b/tests/macros/tmacrotypes.nim @@ -43,6 +43,8 @@ macro checkType(ex: typed): untyped = echo ex.getTypeInst.repr, "; ", ex.typeKind, "; ", ex.getType.repr, "; ", ex.getTypeImpl.repr macro checkProcType(fn: typed): untyped = + if fn.kind == nnkProcDef: + result = fn let fn_sym = if fn.kind == nnkProcDef: fn[0] else: fn echo fn_sym, "; ", fn_sym.typeKind, "; ", fn_sym.getType.repr, "; ", fn_sym.getTypeImpl.repr diff --git a/tests/template/tparams_gensymed.nim b/tests/template/tparams_gensymed.nim index 2a1fda41a3..1444667f4f 100644 --- a/tests/template/tparams_gensymed.nim +++ b/tests/template/tparams_gensymed.nim @@ -336,3 +336,61 @@ test(hello): var data = 5 hello(data) + +# bug #5691 + +template bar(x: typed) = discard +macro barry(x: typed) = discard + +var a = 0 + +bar: + var a = 10 + +barry: + var a = 20 + +bar: + var b = 10 + +barry: + var b = 20 + +var b = 30 + +# template bar(x: static int) = discard +#You may think that this should work: +# bar((var c = 1; echo "hey"; c)) +# echo c +#But it must not! Since this would be incorrect: +# bar((var b = 3; const c = 1; echo "hey"; c)) +# echo b # <- b wouldn't exist + +discard not (let xx = 1; true) +discard xx + +template barrel(a: typed): untyped = a + +barrel: + var aa* = 1 + var bb = 3 + export bb + +# Test declaredInScope within params +template test1: untyped = + when not declaredInScope(thing): + var thing {.inject.}: int + +proc chunkedReadLoop = + test1 + test1 + +template test2: untyped = + when not not not declaredInScope(thing): + var thing {.inject.}: int + +proc chunkedReadLoop2 = + test2 + test2 + +test1(); test2() diff --git a/tests/template/tparamscope.nim b/tests/template/tparamscope.nim new file mode 100644 index 0000000000..177c682cf0 --- /dev/null +++ b/tests/template/tparamscope.nim @@ -0,0 +1,10 @@ +discard """ + errormsg: "undeclared identifier: 'a'" + line: 10 +""" + + +template secondArg(a, b: typed): untyped = + b + +echo secondArg((var a = 1; 1), a)