Skip to content

Commit

Permalink
allow replacing captured syms in macro calls in generics
Browse files Browse the repository at this point in the history
fixes nim-lang#22605, separated from nim-lang#22744
  • Loading branch information
metagn committed Dec 18, 2023
1 parent 080a072 commit 341b04d
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 3 deletions.
4 changes: 3 additions & 1 deletion compiler/ast.nim
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,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 @@ -1095,7 +1096,8 @@ 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
10 changes: 9 additions & 1 deletion compiler/semexprs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -3063,9 +3063,17 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType
of nkClosedSymChoice, nkOpenSymChoice:
result = semSymChoice(c, result, flags, expectedType)
of nkSym:
let s = n.sym
if nfOpenSym in n.flags:
let id = newIdentNode(s.name, n.info)
c.isAmbiguous = false
let s2 = qualifiedLookUp(c, id, {})
if s2 != nil and s2 != s and not c.isAmbiguous and s2.owner == c.p.owner:
result = semExpr(c, id, flags, expectedType)
return
# because of the changed symbol binding, this does not mean that we
# don't have to check the symbol for semantics here again!
result = semSym(c, n, n.sym, flags)
result = semSym(c, n, s, flags)
of nkEmpty, nkNone, nkCommentStmt, nkType:
discard
of nkNilLit:
Expand Down
24 changes: 23 additions & 1 deletion compiler/semgnrc.nim
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
result.transitionSonsKind(nkClosedSymChoice)
else:
result = symChoice(c, n, s, scOpen)
if withinMixin in flags and result.kind == nkSym:
result.flags.incl nfOpenSym
result.typ = nil
case s.kind
of skUnknown:
# Introduced in this pass! Leave it as an identifier.
Expand Down Expand Up @@ -96,6 +99,9 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
result = n
else:
result = newSymNodeTypeDesc(s, c.idgen, n.info)
if withinMixin in flags:
result.flags.incl nfOpenSym
result.typ = nil
onUse(n.info, s)
of skParam:
result = n
Expand All @@ -104,11 +110,17 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
if (s.typ != nil) and
(s.typ.flags * {tfGenericTypeParam, tfImplicitTypeParam} == {}):
result = newSymNodeTypeDesc(s, c.idgen, n.info)
if withinMixin in flags:
result.flags.incl nfOpenSym
result.typ = nil
else:
result = n
onUse(n.info, s)
else:
result = newSymNode(s, n.info)
if withinMixin in flags:
result.flags.incl nfOpenSym
result.typ = nil
onUse(n.info, s)

proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags,
Expand Down Expand Up @@ -148,6 +160,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 Expand Up @@ -224,7 +237,16 @@ proc semGenericStmt(c: PContext, n: PNode,
var dummy: bool
result = fuzzyLookup(c, n, flags, ctx, dummy)
of nkSym:
let a = n.sym
var a = n.sym
if nfOpenSym in n.flags:
let id = newIdentNode(a.name, n.info)
c.isAmbiguous = false
let s2 = qualifiedLookUp(c, id, {})
if s2 != nil and s2 != a and not c.isAmbiguous and s2.owner == c.p.owner:
n.sym = s2
a = s2
if withinMixin notin flags:
n.flags.excl nfOpenSym
let b = getGenSym(c, a)
if b != a: n.sym = b
of nkEmpty, succ(nkSym)..nkNilLit, nkComesFrom:
Expand Down
86 changes: 86 additions & 0 deletions tests/generics/tmacroinjectedsym.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
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: # issue #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, 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"

0 comments on commit 341b04d

Please sign in to comment.