Skip to content

Commit

Permalink
open syms
Browse files Browse the repository at this point in the history
  • Loading branch information
metagn committed Sep 29, 2023
1 parent a8d55fd commit 795d0d1
Show file tree
Hide file tree
Showing 9 changed files with 220 additions and 22 deletions.
3 changes: 2 additions & 1 deletion compiler/ast.nim
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,7 @@ type
nfFirstWrite # this node is a first write
nfHasComment # node has a comment
nfSkipFieldChecking # node skips field visable checking
nfOpenSym # node is a captured sym but can be overriden by local symbols

TNodeFlags* = set[TNodeFlag]
TTypeFlag* = enum # keep below 32 for efficiency reasons (now: 47)
Expand Down Expand Up @@ -1092,7 +1093,7 @@ const
nfIsRef, nfIsPtr, nfPreventCg, nfLL,
nfFromTemplate, nfDefaultRefsParam,
nfExecuteOnReload, nfLastRead,
nfFirstWrite, nfSkipFieldChecking}
nfFirstWrite, nfSkipFieldChecking, nfOpenSym}
namePos* = 0
patternPos* = 1 # empty except for term rewriting macros
genericParamsPos* = 2
Expand Down
25 changes: 23 additions & 2 deletions compiler/lookups.nim
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,7 @@ type
m*: PSym
mode*: TOverloadIterMode
symChoiceIndex*: int
fallback: PSym
currentScope: PScope
importIdx: int
marked: IntSet
Expand Down Expand Up @@ -590,6 +591,10 @@ proc lookUp*(c: PContext, n: PNode): PSym =
if result == nil: result = errorUndeclaredIdentifierHint(c, n, n.ident)
of nkSym:
result = n.sym
if nfOpenSym in n.flags:
let alt = searchInScopes(c, result.name, amb)
if alt != nil and alt != result and not amb:
result = alt
of nkAccQuoted:
var ident = considerQuotedIdent(c, n)
result = searchInScopes(c, ident, amb)
Expand Down Expand Up @@ -639,6 +644,11 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym =
c.isAmbiguous = amb
of nkSym:
result = n.sym
if nfOpenSym in n.flags:
var amb = false
let alt = searchInScopes(c, result.name, amb)
if alt != nil and alt != result and not amb:
result = alt
of nkDotExpr:
result = nil
var m = qualifiedLookUp(c, n[0], (flags * {checkUndeclared}) + {checkModule})
Expand Down Expand Up @@ -700,8 +710,16 @@ proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
return nil

of nkSym:
result = n.sym
o.mode = oimDone
if nfOpenSym notin n.flags:
result = n.sym
o.mode = oimDone
else:
result = initOverloadIter(o, c, newIdentNode(n.sym.name, n.info))
if result == nil:
result = n.sym
o.mode = oimDone
elif n.sym != result:
o.fallback = n.sym
of nkDotExpr:
result = nil
o.mode = oimOtherModule
Expand Down Expand Up @@ -798,6 +816,9 @@ proc nextOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
result = nextOverloadIterImports(o, c, n)
else:
result = nil
if result == nil and o.fallback != nil and o.fallback.id notin o.marked:
result = o.fallback
o.fallback = nil
of oimSelfModule:
result = nextIdentIter(o.it, c.topLevelScope.symbols)
of oimOtherModule:
Expand Down
37 changes: 22 additions & 15 deletions compiler/semexprs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -55,16 +55,16 @@ proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
result = semExpr(c, n, flags + {efOperand, efAllowSymChoice})
if result.typ != nil:
# XXX tyGenericInst here?
if result.typ.kind == tyProc and hasUnresolvedParams(result, {efOperand}):
#and tfUnresolved in result.typ.flags:
let owner = result.typ.owner
let err =
# consistent error message with evaltempl/semMacroExpr
if owner != nil and owner.kind in {skTemplate, skMacro}:
errMissingGenericParamsForTemplate % n.renderTree
else:
errProcHasNoConcreteType % n.renderTree
localError(c.config, n.info, err)
#if result.typ.kind == tyProc and hasUnresolvedParams(result, {efOperand}):
# #and tfUnresolved in result.typ.flags:
# let owner = result.typ.owner
# let err =
# # consistent error message with evaltempl/semMacroExpr
# if owner != nil and owner.kind in {skTemplate, skMacro}:
# errMissingGenericParamsForTemplate % n.renderTree
# else:
# errProcHasNoConcreteType % n.renderTree
# localError(c.config, n.info, err)
if result.typ.kind in {tyVar, tyLent}: result = newDeref(result)
elif {efWantStmt, efAllowStmt} * flags != {}:
result.typ = newTypeS(tyVoid, c)
Expand Down Expand Up @@ -101,8 +101,9 @@ proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType
elif result.typ.kind == tyError:
# associates the type error to the current owner
result.typ = errorType(c)
elif efTypeAllowed in flags and result.typ.kind == tyProc and
hasUnresolvedParams(result, {}):
elif {efTypeAllowed, efOperand} * flags != {} and
result.typ.kind == tyProc and
containsGenericType(result.typ):
# mirrored with semOperand but only on efTypeAllowed
let owner = result.typ.owner
let err =
Expand Down Expand Up @@ -1025,7 +1026,8 @@ proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags; expectedTy
of skMacro: result = semMacroExpr(c, result, orig, callee, flags, expectedType)
of skTemplate: result = semTemplateExpr(c, result, callee, flags, expectedType)
else:
semFinishOperands(c, result)
if callee.magic != mArrGet:
semFinishOperands(c, result)
activate(c, result)
fixAbstractType(c, result)
analyseIfAddressTakenInCall(c, result)
Expand Down Expand Up @@ -1285,6 +1287,12 @@ proc readTypeParameter(c: PContext, typ: PType,
proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
result = nil
assert n.kind in nkIdentKinds + {nkDotExpr}
if n.kind == nkSym and nfOpenSym in n.flags:
let id = newIdentNode(n.sym.name, n.info)
c.isAmbiguous = false
let s2 = qualifiedLookUp(c, id, {})
if s2 != nil and s2 != sym and not c.isAmbiguous:
return semSym(c, id, s2, flags)
let s = getGenSym(c, sym)
case s.kind
of skConst:
Expand Down Expand Up @@ -1543,8 +1551,7 @@ proc builtinFieldAccess(c: PContext; n: PNode; flags: var TExprFlags): PNode =

proc dotTransformation(c: PContext, n: PNode): PNode =
if isSymChoice(n[1]) or
# generics usually leave field names as symchoices, but not types
(n[1].kind == nkSym and n[1].sym.kind == skType):
(n[1].kind == nkSym and n[1].sym.kind in routineKinds + {skType}):
result = newNodeI(nkDotCall, n.info)
result.add n[1]
result.add copyTree(n[0])
Expand Down
8 changes: 7 additions & 1 deletion compiler/semgnrc.nim
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,9 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
if fromDotExpr:
result = symChoice(c, n, s, scForceOpen)
if result.kind == nkOpenSymChoice and result.len == 1:
result.transitionSonsKind(nkClosedSymChoice)
result = result[0]
result.flags.incl nfOpenSym
result.typ = nil
else:
result = symChoice(c, n, s, scOpen)
case s.kind
Expand Down Expand Up @@ -110,6 +112,9 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
else:
result = newSymNode(s, n.info)
onUse(n.info, s)
if withinMixin in flags and result.kind == nkSym:
result.flags.incl nfOpenSym
result.typ = nil

proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags,
ctx: var GenericCtx): PNode =
Expand Down Expand Up @@ -148,6 +153,7 @@ proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags,

var s = qualifiedLookUp(c, n, luf)
if s != nil:
isMacro = s.kind in {skTemplate, skMacro}
result = semGenericStmtSymbol(c, n, s, ctx, flags)
else:
n[0] = semGenericStmt(c, n[0], flags, ctx)
Expand Down
9 changes: 8 additions & 1 deletion compiler/semtempl.nim
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ proc symChoice(c: PContext, n: PNode, s: PSym, r: TSymChoiceRule;
# for instance 'nextTry' is both in tables.nim and astalgo.nim ...
if not isField or sfGenSym notin s.flags:
result = newSymNode(s, info)
if r in {scOpen, scForceOpen}:
result.flags.incl nfOpenSym
result.typ = nil
markUsed(c, info, s)
onUse(info, s)
else:
Expand Down Expand Up @@ -255,6 +258,9 @@ proc semTemplSymbol(c: PContext, n: PNode, s: PSym; isField: bool): PNode =
# field access (dot expr) will be handled by builtinFieldAccess
if not isField:
styleCheckUse(c, n.info, s)
if result.kind == nkSym:
result.flags.incl nfOpenSym
result.typ = nil

proc semRoutineInTemplName(c: var TemplCtx, n: PNode): PNode =
result = n
Expand Down Expand Up @@ -568,7 +574,8 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
inc c.noGenSym
result[1] = semTemplBody(c, n[1])
dec c.noGenSym
if result[1].kind == nkSym and result[1].sym.kind in routineKinds:
if false and result[1].kind == nkSym and result[1].sym.kind in routineKinds and
nfOpenSym notin result[1].flags:
# prevent `dotTransformation` from rewriting this node to `nkIdent`
# by making it a symchoice
# in generics this becomes `nkClosedSymChoice` but this breaks code
Expand Down
2 changes: 1 addition & 1 deletion compiler/sigmatch.nim
Original file line number Diff line number Diff line change
Expand Up @@ -2644,7 +2644,7 @@ proc semFinishOperands*(c: PContext, n: PNode) =
# this needs to be called to ensure that after overloading resolution every
# argument has been sem'checked:
for i in 1..<n.len:
n[i] = prepareOperand(c, n[i])
n[i] = c.semExprWithType(c, n[i], {efOperand, efAllowSymChoice})

proc partialMatch*(c: PContext, n, nOrig: PNode, m: var TCandidate) =
# for 'suggest' support:
Expand Down
23 changes: 23 additions & 0 deletions tests/lookups/mopensym.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# foo0 has 0 overloads

template myTemplate0*(): string =
foo0(bar)

# foo1 has 1 overload

proc foo1(arg: int): string =
"foo1 bad"

template myTemplate1*(): string =
foo1(bar)

# foo2 has 2 overloads

proc foo2(arg: int): string =
"foo2 bad 1"

proc foo2(arg: string): string =
"foo2 bad 2"

template myTemplate2*(): string =
foo2(bar)
124 changes: 124 additions & 0 deletions tests/lookups/topensym.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
block: # issue #22605, normal call syntax
const error = "bad"

template valueOr(self: int, def: untyped): untyped =
case false
of true: ""
of false:
template error: untyped {.used, inject.} = "good"
def

proc g(T: type): string =
let x = valueOr 123:
return $error

"ok"

doAssert g(int) == "good"

block: # issu #22605, method call syntax
const error = "bad"

template valueOr(self: int, def: untyped): untyped =
case false
of true: ""
of false:
template error: untyped {.used, inject.} = "good"
def

proc g(T: type): string =
let x = 123.valueOr:
return $error

"ok"

doAssert g(int) == "good"

block: # issue #22605, template case
template valueOr(self, def: untyped): untyped =
block:
template error: untyped {.used, inject.} = "good"
def

const error = "bad"
template g: untyped =
let x = 123.valueOr:
$error
x
doAssert g == "good"

block: # issue #22605, original complex example
type Xxx = enum
error
value

type
Result[T, E] = object
when T is void:
when E is void:
oResultPrivate*: bool
else:
case oResultPrivate*: bool
of false:
eResultPrivate*: E
of true:
discard
else:
when E is void:
case oResultPrivate*: bool
of false:
discard
of true:
vResultPrivate*: T
else:
case oResultPrivate*: bool
of false:
eResultPrivate*: E
of true:
vResultPrivate*: T

template valueOr[T: not void, E](self: Result[T, E], def: untyped): untyped =
let s = (self) # TODO avoid copy
case s.oResultPrivate
of true:
s.vResultPrivate
of false:
when E isnot void:
template error: untyped {.used, inject.} = s.eResultPrivate
def

proc f(): Result[int, cstring] =
Result[int, cstring](oResultPrivate: false, eResultPrivate: "f")

proc g(T: type): string =
let x = f().valueOr:
return $error

"ok"

doAssert g(int) == "f"

# issue #11184

import mopensym

type
MyType = object

proc foo0(arg: MyType): string =
"foo0"

proc foo1(arg: MyType): string =
"foo1"

proc foo2(arg: MyType): string =
"foo2"

proc test() =
var bar: MyType

doAssert myTemplate0() == "foo0"
doAssert myTemplate1() == "foo1"
doAssert myTemplate2() == "foo2"

test()
11 changes: 10 additions & 1 deletion tests/template/tinnerouterproc.nim
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
block: # #20002
block: # issue #20002
proc bar(x: int): int = 10
template foo =
proc bar(x: int): int {.gensym.} = x + 2
doAssert bar(3) == 5
discard 3.bar # evaluates to 10 but only check if it compiles for now
block:
foo()

block: # issue #20000, no gensym
proc bar(x: int): int = 10
template foo =
proc bar(x: int): int = x + 2
doAssert bar(3) == 5
doAssert 3.bar == 5
block:
foo()

0 comments on commit 795d0d1

Please sign in to comment.