diff --git a/compiler/ast.nim b/compiler/ast.nim index 8d52f12ff8be0..e9e58e9975aaf 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -571,7 +571,9 @@ type # file (it is loaded on demand, which may # mean: never) skPackage, # symbol is a package (used for canonicalization) - skAlias # an alias (needs to be resolved immediately) + skAlias, # an alias (needs to be resolved immediately) + skAliasDeprecated, # an skAlias for a deprecated symbol + TSymKinds* = set[TSymKind] const @@ -665,7 +667,8 @@ type mInstantiationInfo, mGetTypeInfo, mNimvm, mIntDefine, mStrDefine, mBoolDefine, mRunnableExamples, mException, mBuiltinType, mSymOwner, mUncheckedArray, mGetImplTransf, - mSymIsInstantiationOf + mSymIsInstantiationOf, + mAlias, # things that we can evaluate safely at compile time, even if not asked for it: const @@ -977,8 +980,8 @@ const NilableTypes*: TTypeKinds = {tyPointer, tyCString, tyRef, tyPtr, tyProc, tyError} ExportableSymKinds* = {skVar, skConst, skProc, skFunc, skMethod, skType, - skIterator, - skMacro, skTemplate, skConverter, skEnumField, skLet, skStub, skAlias} + skIterator, skMacro, skTemplate, skConverter, skEnumField, skLet, skStub, + skAlias, skAliasDeprecated} PersistentNodeFlags*: TNodeFlags = {nfBase2, nfBase8, nfBase16, nfDotSetter, nfDotField, nfIsRef, nfPreventCg, nfLL, diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index 4100c3629454c..1451e0d38350c 100644 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -992,3 +992,10 @@ proc isAddrNode*(n: PNode): bool = if n[0].kind == nkSym and n[0].sym.magic == mAddr: true else: false else: false + +proc skipAliasAux*(s: PSym): PSym = + ## similar to `skipAlias` without extra error message processing + result = s + while true: + if result.kind in {skAlias, skAliasDeprecated}: result=result.owner + else: return result diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index ce295c8b937af..ffa9f085b3f29 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -87,6 +87,7 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimHasDefault") defineSymbol("nimMacrosSizealignof") defineSymbol("nimNoZeroExtendMagic") + defineSymbol("nimHasAlias") for f in low(Feature)..high(Feature): defineSymbol("nimHas" & $f) diff --git a/compiler/lookups.nim b/compiler/lookups.nim index 4de1fc371e595..5ef8a0822ed09 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -81,15 +81,20 @@ iterator walkScopes*(scope: PScope): PScope = current = current.parent proc skipAlias*(s: PSym; n: PNode; conf: ConfigRef): PSym = - if s == nil or s.kind != skAlias: - result = s - else: - result = s.owner - if conf.cmd == cmdPretty: - prettybase.replaceDeprecated(conf, n.info, s, result) + result = s + while true: + if result == nil: return result + elif result.kind == skAlias: result=result.owner + elif result.kind == skAliasDeprecated: + let old = result + result=result.owner + if conf.cmd == cmdPretty: + prettybase.replaceDeprecated(conf, n.info, old, result) + else: + message(conf, n.info, warnDeprecated, "use " & result.name.s & " instead; " & + old.name.s & " is deprecated") else: - message(conf, n.info, warnDeprecated, "use " & result.name.s & " instead; " & - s.name.s & " is deprecated") + return result proc localSearchInScope*(c: PContext, s: PIdent): PSym = result = strTableGet(c.currentScope.symbols, s) @@ -213,11 +218,11 @@ proc addInterfaceDeclAt*(c: PContext, scope: PScope, sym: PSym) = addInterfaceDeclAux(c, sym) proc addOverloadableSymAt*(c: PContext; scope: PScope, fn: PSym) = - if fn.kind notin OverloadableSyms: + if skipAliasAux(fn).kind notin OverloadableSyms: internalError(c.config, fn.info, "addOverloadableSymAt") return let check = strTableGet(scope.symbols, fn.name) - if check != nil and check.kind notin OverloadableSyms: + if check != nil and skipAliasAux(check).kind notin OverloadableSyms: wrongRedefinition(c, fn.info, fn.name.s, check.info) else: scope.addSym(fn) diff --git a/compiler/magicsys.nim b/compiler/magicsys.nim index 38240061ff64c..3388c88dda81d 100644 --- a/compiler/magicsys.nim +++ b/compiler/magicsys.nim @@ -31,7 +31,7 @@ proc getSysSym*(g: ModuleGraph; info: TLineInfo; name: string): PSym = localError(g.config, info, "system module needs: " & name) result = newSym(skError, getIdent(g.cache, name), g.systemModule, g.systemModule.info, {}) result.typ = newType(tyError, g.systemModule) - if result.kind == skAlias: result = result.owner + result = skipAliasAux(result) proc getSysMagic*(g: ModuleGraph; info: TLineInfo; name: string, m: TMagic): PSym = var ti: TIdentIter diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 9e3976e7331df..32db24369d7d9 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -696,7 +696,7 @@ proc deprecatedStmt(c: PContext; outerPragma: PNode) = if dest == nil or dest.kind in routineKinds: localError(c.config, n.info, warnUser, "the .deprecated pragma is unreliable for routines") let src = considerQuotedIdent(c, n[0]) - let alias = newSym(skAlias, src, dest, n[0].info, c.config.options) + let alias = newSym(skAliasDeprecated, src, dest, n[0].info, c.config.options) incl(alias.flags, sfExported) if sfCompilerProc in dest.flags: markCompilerProc(c, alias) addInterfaceDecl(c, alias) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 733df2c404bcd..41b6e28c3b676 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -2125,8 +2125,33 @@ proc semSizeof(c: PContext, n: PNode): PNode = n.typ = getSysType(c.graph, n.info, tyInt) result = foldSizeOf(c.config, n, n) +proc semAlias(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = + assert n.kind in {nkCall, nkCommand} + assert n.len == 3 + let nodeAlias = n[1] + let nodeOrigin = n[2] + let sym = qualifiedLookUp(c, nodeOrigin, {checkUndeclared, checkModule}) + if sym == nil: + globalError(c.config, n.info, errUser, "undeclared symbol:" & renderTree(nodeOrigin)) + else: + let ident = considerQuotedIdent(c, nodeAlias) + let info = nodeAlias.info + let sc: PNode = symChoice(c, nodeOrigin, sym, scClosed) + case sc.kind + of nkSym: + let alias = newSym(skAlias, ident, sym, info, c.config.options) + addInterfaceDecl(c, alias) + of nkClosedSymChoice: + for nodei in sc: + let alias = newSym(skAlias, ident, nodei.sym, info, c.config.options) + addInterfaceOverloadableSymAt(c, c.currentScope, alias) + else: + assert false, $sc.kind + result = c.graph.emptyNode + proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = # this is a hotspot in the compiler! + # see also `magicsAfterOverloadResolution` result = n case s.magic # magics that need special treatment of mAddr: @@ -2218,6 +2243,8 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = result = c.graph.emptyNode of mSizeOf: result = semSizeof(c, setMs(n, s)) + of mAlias: + result = semAlias(c, n, s, flags) else: result = semDirectOp(c, n, flags) diff --git a/lib/system.nim b/lib/system.nim index 28eb1a1ef4bca..025a442c554de 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -149,6 +149,18 @@ else: template runnableExamples*(body: untyped) = discard +when defined(nimHasAlias): + proc aliasImpl[T1, T2](name: T1, expr: T2) {.magic: "Alias".} + + template `:=`*(name, expr) = + ## Declares `a` as alias of `expr`, which must resolve to a symbol. + runnableExamples: + echo2:=system.echo + echo2 "hello" + declared2:=system.declared + doAssert declared2(echo2) + aliasImpl(name, expr) + proc declared*(x: untyped): bool {.magic: "Defined", noSideEffect, compileTime.} ## Special compile-time procedure that checks whether `x` is ## declared. `x` has to be an identifier or a qualified identifier. diff --git a/tests/magics/talias2.nim b/tests/magics/talias2.nim new file mode 100644 index 0000000000000..584fc28dfcdad --- /dev/null +++ b/tests/magics/talias2.nim @@ -0,0 +1,93 @@ +import std/macros + +proc fun0(a: int): auto = $a +template fun3(a: int): untyped = $a +template fun3(a = 1.2): untyped = $a + +proc main() = + proc fun0(a: float): auto = $a + proc fun0(a: bool): auto = $a + + block: # works with macros, even with all optional parameters + macro fun2(a = 10, b = 11): untyped = quote do: (`a`,`b`) + fun2a:=fun2 + doAssert fun2a() == (10, 11) + doAssert fun2a(12) == (12, 11) + block: + doAssert fun2a(12) == (12, 11) + + block: # ditto with templates + template fun2(a = 10, b = 11): untyped = (a,b) + fun2a:=fun2 + doAssert fun2a(12) == (12, 11) + doAssert fun2a() == (10, 11) + + block: # works with types + int2:=system.int + doAssert int2 is int + + block: # ditto + int2:=int + doAssert int2 is int + + block: # works with modules + system2:=system + doAssert system2.int is int + int2:=system2.int + doAssert int2 is int + + block: # usage of alias is identical to usage of aliased symbol + currentSourcePath2:=system.currentSourcePath + doAssert currentSourcePath2 == currentSourcePath + doAssert currentSourcePath2() == currentSourcePath() + + block: # works with overloaded symbols + toStr:=`$` + doAssert 12.toStr == "12" + doAssert true.toStr == "true" + + block: # CT error if symbol does not exist in scope + doAssert compiles(echo2:=echo) + doAssert not compiles(echo2:=echo_nonexistant) + echo2:=echo + doAssert compiles(echo2()) + doAssert not compiles(echo2()) # echo2 not in scope anymore + + block: # works with variables + var x = @[1,2,3] + xa:=x + xa[1] = 10 + doAssert x == @[1,10,3] + doAssert not compiles(xa2:=x[1]) + when false: + xa:=x # correctly would give: Error: redefinition of 'xa' + # doAssert not compiles(xa:=x) # we can't test that using `compiles` though + + block: # works with const + const L = 12 + L2:=L + const L3 = L2 + doAssert L3 == L + + block: # works with overloaded symbols, including local overloads, including generics + proc fun0[T](a: T, b: float): auto = $(a,b) + fun0a:=fun0 + doAssert fun0a(true) == "true" + doAssert fun0a(1.2) == "1.2" + doAssert fun0a(1, 2.0) == "(1, 2.0)" + + block: # works with overloaded templates + fun3a:=fun3 + doAssert fun3a(12.1) == "12.1" + doAssert fun3a() == "1.2" + + block: # works with iterator + iterator fun4(): auto = + yield 10 + yield 3 + fun4a := fun4 + var s: seq[int] + for ai in fun4a(): s.add ai + doAssert s == [10,3] + +main()