Skip to content

Commit

Permalink
nim has a real alias
Browse files Browse the repository at this point in the history
  • Loading branch information
timotheecour committed Jul 25, 2019
1 parent 44aadd5 commit ec5ef44
Show file tree
Hide file tree
Showing 9 changed files with 164 additions and 16 deletions.
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: 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
27 changes: 27 additions & 0 deletions compiler/semexprs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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)

Expand Down
12 changes: 12 additions & 0 deletions lib/system.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
93 changes: 93 additions & 0 deletions tests/magics/talias2.nim
Original file line number Diff line number Diff line change
@@ -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()

0 comments on commit ec5ef44

Please sign in to comment.