Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[superseded] alias: myecho=echo to alias any symbol #11822

Closed
wants to merge 11 commits into from
11 changes: 7 additions & 4 deletions compiler/ast.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down
7 changes: 7 additions & 0 deletions compiler/astalgo.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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
1 change: 1 addition & 0 deletions compiler/condsyms.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
25 changes: 15 additions & 10 deletions compiler/lookups.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion compiler/magicsys.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions compiler/options.nim
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,8 @@ type
## This requires building nim with `-d:nimHasLibFFI`
## which itself requires `nimble install libffi`, see #10150
## Note: this feature can't be localized with {.push.}
aliasSym,
## enable `Alias` magic to alias symbols

SymbolFilesOption* = enum
disabledSf, writeOnlySf, readOnlySf, v2Sf
Expand Down
2 changes: 1 addition & 1 deletion compiler/pragmas.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
52 changes: 52 additions & 0 deletions compiler/semexprs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -2125,8 +2125,55 @@ 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 == 2, $n.len
var def = n[1]
doAssert def.kind == nkStmtList, $def.kind
doAssert def.len == 1, $def.len
def = def[0]
var nodeAlias, nodeOrigin: PNode
var exported = false
case def.kind
of nkAsgn:
nodeAlias = def[0]
nodeOrigin = def[1]
of nkInfix:
if def[0].ident.s != "*=":
globalError(c.config, n.info, errUser, "expected `*=`, got " & renderTree(def))
nodeAlias = def[1]
nodeOrigin = def[2]
exported = true
else:
globalError(c.config, n.info, errUser, "expected " & ${nkAsgn, nkInfix} & ", got " & $def.kind)

template maybeExport(alias) =
if exported: alias.flags.incl sfExported

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)
maybeExport(alias)
addInterfaceDecl(c, alias)
of nkClosedSymChoice:
for nodei in sc:
let alias = newSym(skAlias, ident, nodei.sym, info, c.config.options)
maybeExport(alias)
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:
Expand Down Expand Up @@ -2218,6 +2265,11 @@ 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:
if aliasSym in c.features:
result = semAlias(c, n, s, flags)
else:
globalError(c.config, n.info, "requires --experimental:aliasSym")
else:
result = semDirectOp(c, n, flags)

Expand Down
13 changes: 13 additions & 0 deletions lib/pure/sugar.nim
Original file line number Diff line number Diff line change
Expand Up @@ -238,3 +238,16 @@ macro distinctBase*(T: typedesc): untyped =
while typeSym.typeKind == ntyDistinct:
typeSym = getTypeImpl(typeSym)[0]
typeSym.freshIdentNodes

when defined(nimHasAlias):
proc alias*(aliasDef: untyped) {.magic: "Alias".} =
## Declares `name` as alias of `expr`, which must resolve to a symbol.
## Works with any symbol, e.g. iterator, template, macro, module, proc etc.
runnableExamples:
{.push experimental: "aliasSym".}
alias: echo2=echo
echo2 "hello"
alias: declared2=system.declared
doAssert declared2(echo2)
alias: echoPub*=echo # would export `echoPub`
discard
6 changes: 6 additions & 0 deletions tests/magics/malias2.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import std/sugar

template fun6(): untyped = 42

{.push experimental: "aliasSym".}
alias: fun6a*=fun6 # alias with export
142 changes: 142 additions & 0 deletions tests/magics/talias2.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import std/macros
import std/sugar

{.push experimental: "aliasSym".}

proc fun0(a: int): auto = $a
template fun3(a: int): untyped = $a
template fun3(a = 1.2): untyped = $a

proc main1() =
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`)
alias: 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)
alias:fun2a=fun2
doAssert fun2a(12) == (12, 11)
doAssert fun2a() == (10, 11)

block: # works with types
alias:int2=system.int
doAssert int2 is int

block: # ditto
alias:int2=int
doAssert int2 is int

block: # works with modules
alias:system2=system
doAssert system2.int is int
alias:int2=system2.int
doAssert int2 is int

block: # usage of alias is identical to usage of aliased symbol
alias:currentSourcePath2=system.currentSourcePath
doAssert currentSourcePath2 == currentSourcePath
doAssert currentSourcePath2() == currentSourcePath()

block: # works with overloaded symbols
alias:toStr=`$`
doAssert 12.toStr == "12"
doAssert true.toStr == "true"

block: # CT error if symbol does not exist in scope
doAssert compiles(block: alias: echo2=echo)
doAssert not compiles(block: alias: echo2=echoNonexistant)
alias: echo2=echo
doAssert compiles(echo2())
doAssert not compiles(echo2()) # echo2 not in scope anymore

block: # works with variables
var x = @[1,2,3]
alias: xa=x
xa[1] = 10
doAssert x == @[1,10,3]
doAssert not compiles(block: alias: xa2=x[1])
when false:
alias: xa=x # correctly would give: Error: redefinition of 'xa'

block: # works with const
const L = 12
alias: 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)
alias: 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
alias: fun3a=fun3
doAssert fun3a(12.1) == "12.1"
doAssert fun3a() == "1.2"

block: # works with iterator
iterator fun4(): auto =
yield 10
yield 3
alias: fun4a = fun4
var s: seq[int]
for ai in fun4a(): s.add ai
doAssert s == [10,3]

block: # works with generics
proc fun5[T](a: T): auto = a
alias: fun5a = fun5
doAssert fun5a(3.2) == 3.2

proc main2() = # using `alias` avoids the issues mentioned in #8935
# const myPrint = echo # Error: invalid type for const: proc
# let myPuts = system.echo # Error: invalid type: 'typed'
alias: myPrint=echo # works
# myPrint (1,2)
doAssert compiles(myPrint (1,2))
when false:
const testForMe = assert
testForMe(1 + 1 == 2) # Error: VM problem: dest register is not set

alias: testForMe=assert
testForMe(1 + 1 == 2)
doAssertRaises(AssertionError): testForMe(1 + 1 == 3)

block: # somewhat related to #11047
proc foo(): int {.compileTime.} = 100
# var f {.compileTime.} = foo # would give: Undefined symbols error
# let f {.compileTime.} = foo # would give: Undefined symbols error
# const f = foo # this would work
alias: f=foo
doAssert f() == 100
static: doAssert f() == 100

block: # fix https://forum.nim-lang.org/t/5015
proc getLength(i: int): int = sizeof(i)
proc getLength(s: string): int = s.len
# const length = getLength # Error: cannot generate VM code for getLength
alias: length = getLength # works
doAssert length("alias") == 5

block: # works with `result` variable too, as asked here:
# https://forum.nim-lang.org/t/5015#31650
proc foo(): string =
alias: r=result
r.add "ba"
r.add "bo"
doAssert foo() == "babo"

import ./malias2
doAssert fun6a() == 42

main1()
main2()