From 795d0d1ca7174c2831ca3a95549627ac5fc09318 Mon Sep 17 00:00:00 2001 From: metagn <10591326+metagn@users.noreply.github.com> Date: Sat, 23 Sep 2023 05:25:02 +0300 Subject: [PATCH 1/7] open syms fixes #11184, fixes #22605, fixes #20000 --- compiler/ast.nim | 3 +- compiler/lookups.nim | 25 +++++- compiler/semexprs.nim | 37 +++++---- compiler/semgnrc.nim | 8 +- compiler/semtempl.nim | 9 ++- compiler/sigmatch.nim | 2 +- tests/lookups/mopensym.nim | 23 ++++++ tests/lookups/topensym.nim | 124 +++++++++++++++++++++++++++++ tests/template/tinnerouterproc.nim | 11 ++- 9 files changed, 220 insertions(+), 22 deletions(-) create mode 100644 tests/lookups/mopensym.nim create mode 100644 tests/lookups/topensym.nim diff --git a/compiler/ast.nim b/compiler/ast.nim index dd7561264a804..7fe1b5a583455 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -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) @@ -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 diff --git a/compiler/lookups.nim b/compiler/lookups.nim index 2bdf3a1e07259..f20a459c2b476 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -315,6 +315,7 @@ type m*: PSym mode*: TOverloadIterMode symChoiceIndex*: int + fallback: PSym currentScope: PScope importIdx: int marked: IntSet @@ -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) @@ -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}) @@ -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 @@ -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: diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 633a0cc267583..4dfd364dba533 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -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) @@ -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 = @@ -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) @@ -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: @@ -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]) diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index aa05f8d85d701..0e8893c7cae18 100644 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -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 @@ -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 = @@ -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) diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index 3256b8d85783a..45e668584946f 100644 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -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: @@ -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 @@ -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 diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index ee93321c8b8cc..a61ec5e73d2a7 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -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.. Date: Sat, 23 Sep 2023 06:02:44 +0300 Subject: [PATCH 2/7] fix bootstrapping, spells doom for packages --- compiler/semexprs.nim | 2 +- compiler/semgnrc.nim | 2 ++ compiler/sigmatch.nim | 9 ++++++++- lib/pure/collections/hashcommon.nim | 5 +++-- lib/pure/collections/tables.nim | 5 +++++ 5 files changed, 19 insertions(+), 4 deletions(-) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 4dfd364dba533..8470f19e9619d 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1026,7 +1026,7 @@ 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: - if callee.magic != mArrGet: + if callee.magic == mNone: semFinishOperands(c, result) activate(c, result) fixAbstractType(c, result) diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index 0e8893c7cae18..900b803466200 100644 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -71,6 +71,8 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym, result.typ = nil else: result = symChoice(c, n, s, scOpen) + #if result.kind == nkSym: + # result.flags.excl nfOpenSym case s.kind of skUnknown: # Introduced in this pass! Leave it as an identifier. diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index a61ec5e73d2a7..b30744663b74b 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -2403,6 +2403,13 @@ proc prepareOperand(c: PContext; a: PNode): PNode = result = a considerGenSyms(c, result) +proc finishOperand(c: PContext; a: PNode): PNode = + if a.typ.isNil: + result = c.semExprWithType(c, a, {efOperand, efAllowSymChoice}) + else: + result = a + considerGenSyms(c, result) + proc prepareNamedParam(a: PNode; c: PContext) = if a[0].kind != nkIdent: var info = a[0].info @@ -2644,7 +2651,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.. Date: Sat, 23 Sep 2023 10:14:50 +0300 Subject: [PATCH 3/7] also add preferred syms to not break routines --- compiler/ast.nim | 3 +- compiler/lookups.nim | 33 ++++++++++++++++-- compiler/semexprs.nim | 61 +++++++++++++++++---------------- compiler/semgnrc.nim | 18 +++++----- compiler/semtempl.nim | 36 ++++++++++--------- compiler/sigmatch.nim | 4 +++ lib/pure/collections/tables.nim | 5 --- tests/enum/tcrossmodule.nim | 5 +-- tests/lookups/mopensym.nim | 5 +++ tests/lookups/topensym.nim | 31 ++++++++--------- 10 files changed, 120 insertions(+), 81 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index 7fe1b5a583455..cf533c9cfc4eb 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -519,6 +519,7 @@ type nfHasComment # node has a comment nfSkipFieldChecking # node skips field visable checking nfOpenSym # node is a captured sym but can be overriden by local symbols + nfPreferredSym # node is a preferred sym in a symchoice TNodeFlags* = set[TNodeFlag] TTypeFlag* = enum # keep below 32 for efficiency reasons (now: 47) @@ -1093,7 +1094,7 @@ const nfIsRef, nfIsPtr, nfPreventCg, nfLL, nfFromTemplate, nfDefaultRefsParam, nfExecuteOnReload, nfLastRead, - nfFirstWrite, nfSkipFieldChecking, nfOpenSym} + nfFirstWrite, nfSkipFieldChecking, nfOpenSym, nfPreferredSym} namePos* = 0 patternPos* = 1 # empty except for term rewriting macros genericParamsPos* = 2 diff --git a/compiler/lookups.nim b/compiler/lookups.nim index f20a459c2b476..77bca6a32f354 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -315,6 +315,7 @@ type m*: PSym mode*: TOverloadIterMode symChoiceIndex*: int + symChoiceLastPreferred: bool fallback: PSym currentScope: PScope importIdx: int @@ -599,6 +600,11 @@ proc lookUp*(c: PContext, n: PNode): PSym = var ident = considerQuotedIdent(c, n) result = searchInScopes(c, ident, amb) if result == nil: result = errorUndeclaredIdentifierHint(c, n, ident) + of nkOpenSymChoice, nkClosedSymChoice: + if nfPreferredSym in n[0].flags: + result = n[0].sym + else: + result = nil else: internalError(c.config, n.info, "lookUp") return nil @@ -679,6 +685,11 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym = localError(c.config, n[1].info, "identifier expected, but got: " & renderTree(n[1])) result = errorSym(c, n[1]) + of nkOpenSymChoice, nkClosedSymChoice: + if nfPreferredSym in n[0].flags: + result = n[0].sym + else: + result = nil else: result = nil when false: @@ -712,7 +723,16 @@ proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym = of nkSym: if nfOpenSym notin n.flags: result = n.sym - o.mode = oimDone + if nfPreferredSym in n.flags: + o.mode = oimSymChoiceLocalLookup + o.symChoiceLastPreferred = true + o.currentScope = c.currentScope + o.it.h = result.name.h + o.it.name = result.name + o.marked = initIntSet() + incl(o.marked, result.id) + else: + o.mode = oimDone else: result = initOverloadIter(o, c, newIdentNode(n.sym.name, n.info)) if result == nil: @@ -745,6 +765,7 @@ proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym = o.mode = oimSymChoice if n[0].kind == nkSym: result = n[0].sym + o.symChoiceLastPreferred = nfPreferredSym in n[0].flags else: o.mode = oimDone return nil @@ -763,6 +784,11 @@ proc lastOverloadScope*(o: TOverloadIter): int = else: o.currentScope.depthLevel of oimSelfModule: result = 1 of oimOtherModule: result = 0 + of oimSymChoice, oimSymChoiceLocalLookup: + if o.symChoiceLastPreferred: + result = 999 + else: + result = -1 else: result = -1 proc nextOverloadIterImports(o: var TOverloadIter, c: PContext, n: PNode): PSym = @@ -824,6 +850,7 @@ proc nextOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym = of oimOtherModule: result = nextModuleIter(o.mit, c.graph) of oimSymChoice: + o.symChoiceLastPreferred = false if o.symChoiceIndex < n.len: result = n[o.symChoiceIndex].sym incl(o.marked, result.id) @@ -848,13 +875,15 @@ proc nextOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym = else: result = nil of oimSymChoiceLocalLookup: + o.symChoiceLastPreferred = false if o.currentScope != nil: result = nextIdentExcluding(o.it, o.currentScope.symbols, o.marked) while result == nil: o.currentScope = o.currentScope.parent if o.currentScope != nil: + let name = if n.kind == nkSym: n.sym.name else: n[0].sym.name result = firstIdentExcluding(o.it, o.currentScope.symbols, - n[0].sym.name, o.marked) + name, o.marked) else: o.importIdx = 0 result = symChoiceExtension(o, c, n) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 8470f19e9619d..9f51892203537 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -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) @@ -139,16 +139,11 @@ proc semSymChoice(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: P if isSymChoice(result) and efAllowSymChoice notin flags: # some contexts might want sym choices preserved for later disambiguation # in general though they are ambiguous - let first = n[0].sym - var foundSym: PSym = nil - if first.kind == skEnumField and - not isAmbiguous(c, first.name, {skEnumField}, foundSym) and - foundSym == first: - # choose the first resolved enum field, i.e. the latest in scope - # to mirror behavior before overloadable enums - result = n[0] + let first = n[0] + if nfPreferredSym in first.flags: + result = first else: - var err = "ambiguous identifier '" & first.name.s & + var err = "ambiguous identifier '" & first.sym.name.s & "' -- use one of the following:\n" for child in n: let candidate = child.sym @@ -1026,7 +1021,7 @@ 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: - if callee.magic == mNone: + if callee.magic notin {mArrGet, mArrPut, mNBindSym}: semFinishOperands(c, result) activate(c, result) fixAbstractType(c, result) @@ -1287,12 +1282,6 @@ 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: @@ -2961,13 +2950,17 @@ proc getNilType(c: PContext): PType = c.nilTypeCache = result proc enumFieldSymChoice(c: PContext, n: PNode, s: PSym): PNode = - var o: TOverloadIter + var o: TOverloadIter = default(TOverloadIter) + var firstPreferred = true var i = 0 var a = initOverloadIter(o, c, n) + let firstScope = lastOverloadScope(o) while a != nil: if a.kind == skEnumField: inc(i) - if i > 1: break + if i > 1: + firstPreferred = firstScope > lastOverloadScope(o) + break a = nextOverloadIter(o, c, n) let info = getCallLineInfo(n) if i <= 1: @@ -2987,6 +2980,8 @@ proc enumFieldSymChoice(c: PContext, n: PNode, s: PSym): PNode = result.add newSymNode(a, info) onUse(info, a) a = nextOverloadIter(o, c, n) + if firstPreferred: + result[0].flags.incl nfPreferredSym proc semPragmaStmt(c: PContext; n: PNode) = if c.p.owner.kind == skModule: @@ -3071,9 +3066,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: + 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: diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index 900b803466200..e0e4202b07f0a 100644 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -65,14 +65,8 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym, template maybeDotChoice(c: PContext, n: PNode, s: PSym, fromDotExpr: bool) = if fromDotExpr: result = symChoice(c, n, s, scForceOpen) - if result.kind == nkOpenSymChoice and result.len == 1: - result = result[0] - result.flags.incl nfOpenSym - result.typ = nil else: result = symChoice(c, n, s, scOpen) - #if result.kind == nkSym: - # result.flags.excl nfOpenSym case s.kind of skUnknown: # Introduced in this pass! Leave it as an identifier. @@ -100,6 +94,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 @@ -108,15 +105,18 @@ 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) onUse(n.info, s) - if withinMixin in flags and result.kind == nkSym: - result.flags.incl nfOpenSym - result.typ = nil + if withinMixin in flags: + result.flags.incl nfOpenSym + result.typ = nil proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags, ctx: var GenericCtx): PNode = diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index 45e668584946f..ab7df0adaddb3 100644 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -52,13 +52,17 @@ proc symChoice(c: PContext, n: PNode, s: PSym, r: TSymChoiceRule; isField = false): PNode = var a: PSym - o: TOverloadIter + o: TOverloadIter = default(TOverloadIter) + firstPreferred = true var i = 0 a = initOverloadIter(o, c, n) + let firstScope = lastOverloadScope(o) while a != nil: if a.kind != skModule: inc(i) - if i > 1: break + if i > 1: + firstPreferred = firstScope > lastOverloadScope(o) + break a = nextOverloadIter(o, c, n) let info = getCallLineInfo(n) if i <= 1 and r != scForceOpen: @@ -67,11 +71,18 @@ 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) + if r == scClosed or n.kind == nkDotExpr or + (s.magic != mNone and s.kind in routineKinds): + markUsed(c, info, s) + onUse(info, s) + else: + if s.kind in routineKinds: + result.flags.incl nfPreferredSym + else: + result.flags.incl nfOpenSym + result.typ = nil + incl(s.flags, sfUsed) + markOwnerModuleAsUsed(c, s) else: result = n elif i == 0: @@ -91,6 +102,8 @@ proc symChoice(c: PContext, n: PNode, s: PSym, r: TSymChoiceRule; result.add newSymNode(a, info) onUse(info, a) a = nextOverloadIter(o, c, n) + if firstPreferred: + result[0].flags.incl nfPreferredSym proc semBindStmt(c: PContext, n: PNode, toBind: var IntSet): PNode = result = copyNode(n) @@ -574,15 +587,6 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode = inc c.noGenSym result[1] = semTemplBody(c, n[1]) dec c.noGenSym - 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 - # as the old behavior here was that this became `nkIdent` - var choice = newNodeIT(nkOpenSymChoice, n[1].info, newTypeS(tyNone, c.c)) - choice.add result[1] - result[1] = choice else: result = semTemplBodySons(c, n) of nkExprColonExpr, nkExprEqExpr: diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index b30744663b74b..b35205d899774 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -2351,6 +2351,10 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType, # See tsymchoice_for_expr as an example. 'f.kind == tyUntyped' should match # anyway: if f.kind in {tyUntyped, tyTyped}: result = arg + elif nfPreferredSym in arg[0].flags: + markUsed(m.c, arg.info, arg[0].sym) + onUse(arg.info, arg[0].sym) + result = paramTypesMatchAux(m, f, arg[0].typ, arg[0], argOrig) else: result = nil else: # only one valid interpretation found: diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim index 9d3492d46aa96..39dcddb5a9c0c 100644 --- a/lib/pure/collections/tables.nim +++ b/lib/pure/collections/tables.nim @@ -244,7 +244,6 @@ template get(t, key): untyped = raiseKeyError(key) proc enlarge[A, B](t: var Table[A, B]) = - bind nextTry var n: KeyValuePairSeq[A, B] newSeq(n, len(t.data) * growthFactor) swap(t.data, n) @@ -797,7 +796,6 @@ iterator allValues*[A, B](t: Table[A, B]; key: A): B {.deprecated: for i in 1..3: a.add('z', 10*i) doAssert toSeq(a.pairs).sorted == @[('a', 3), ('b', 5), ('z', 10), ('z', 20), ('z', 30)] doAssert sorted(toSeq(a.allValues('z'))) == @[10, 20, 30] - bind nextTry var h: Hash = genHash(key) and high(t.data) let L = len(t) while isFilled(t.data[h].hcode): @@ -1283,7 +1281,6 @@ proc rawInsert[A, B](t: var OrderedTable[A, B], t.last = h proc enlarge[A, B](t: var OrderedTable[A, B]) = - bind nextTry var n: OrderedKeyValuePairSeq[A, B] newSeq(n, len(t.data) * growthFactor) var h = t.first @@ -2252,7 +2249,6 @@ type proc ctRawInsert[A](t: CountTable[A], data: var seq[tuple[key: A, val: int]], key: A, val: int) = - bind nextTry var h: Hash = hash(key) and high(data) while data[h].val != 0: h = nextTry(h, high(data)) data[h].key = key @@ -2266,7 +2262,6 @@ proc enlarge[A](t: var CountTable[A]) = swap(t.data, n) proc rawGet[A](t: CountTable[A], key: A): int = - bind nextTry if t.data.len == 0: return -1 var h: Hash = hash(key) and high(t.data) # start with real hash value diff --git a/tests/enum/tcrossmodule.nim b/tests/enum/tcrossmodule.nim index c21072198ee87..0ed452796e029 100644 --- a/tests/enum/tcrossmodule.nim +++ b/tests/enum/tcrossmodule.nim @@ -9,7 +9,8 @@ template t = t() -block: # legacy support for behavior before overloadableEnums - # warning: ambiguous enum field 'Success' assumed to be of type MyEnum +block: # account for scope let x = {Success} doAssert x is set[MyEnum] + proc foo[T](a: T): string = $a + doAssert foo(Success) == "Success" diff --git a/tests/lookups/mopensym.nim b/tests/lookups/mopensym.nim index be4253245077a..9a39a3d85cda5 100644 --- a/tests/lookups/mopensym.nim +++ b/tests/lookups/mopensym.nim @@ -21,3 +21,8 @@ proc foo2(arg: string): string = template myTemplate2*(): string = foo2(bar) + +proc overloadToPrefer(x: int): int = x + 1 + +template singleOverload*: untyped = + (overloadToPrefer(123), overloadToPrefer("abc")) diff --git a/tests/lookups/topensym.nim b/tests/lookups/topensym.nim index 064a1507efd5d..1da55e9fcfadb 100644 --- a/tests/lookups/topensym.nim +++ b/tests/lookups/topensym.nim @@ -98,27 +98,24 @@ block: # issue #22605, original complex example doAssert g(int) == "f" -# issue #11184 - import mopensym -type - MyType = object - -proc foo0(arg: MyType): string = - "foo0" +block: # issue #11184 + type MyType = object -proc foo1(arg: MyType): string = - "foo1" + proc foo0(arg: MyType): string = "foo0" + proc foo1(arg: MyType): string = "foo1" + proc foo2(arg: MyType): string = "foo2" -proc foo2(arg: MyType): string = - "foo2" + proc test() = + var bar: MyType -proc test() = - var bar: MyType + doAssert myTemplate0() == "foo0" + doAssert myTemplate1() == "foo1" + doAssert myTemplate2() == "foo2" - doAssert myTemplate0() == "foo0" - doAssert myTemplate1() == "foo1" - doAssert myTemplate2() == "foo2" + test() -test() +block: + proc overloadToPrefer(x: string): string = x & "def" + doAssert singleOverload() == (124, "abcdef") From be2eee0b5f09af849219106349bfd83fd4d4cd96 Mon Sep 17 00:00:00 2001 From: metagn <10591326+metagn@users.noreply.github.com> Date: Sat, 23 Sep 2023 10:18:09 +0300 Subject: [PATCH 4/7] no need to bind nextTry anymore --- lib/pure/collections/hashcommon.nim | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/pure/collections/hashcommon.nim b/lib/pure/collections/hashcommon.nim index fcb96d9cf8f09..70bc04e72044f 100644 --- a/lib/pure/collections/hashcommon.nim +++ b/lib/pure/collections/hashcommon.nim @@ -39,7 +39,6 @@ proc slotsNeeded(count: Natural): int {.inline.} = result = nextPowerOfTwo(count * 3 div 2 + 4) template rawGetKnownHCImpl() = - bind nextTry if t.dataLen == 0: return -1 var h {.inject.}: Hash = hc and maxHash(t) # start with real hash value From 6a9e312629d1b6699c4659b1ca4a3faeaca14279 Mon Sep 17 00:00:00 2001 From: metagn <10591326+metagn@users.noreply.github.com> Date: Sat, 23 Sep 2023 12:28:54 +0300 Subject: [PATCH 5/7] actually intentional behavior for semtempl --- compiler/semexprs.nim | 2 +- compiler/sempass2.nim | 2 +- compiler/semtempl.nim | 20 +++++++++++++------- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 9f51892203537..02c579438fc61 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -103,7 +103,7 @@ proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType result.typ = errorType(c) elif {efTypeAllowed, efOperand} * flags != {} and result.typ.kind == tyProc and - containsGenericType(result.typ): + hasUnresolvedParams(result, {}): # mirrored with semOperand but only on efTypeAllowed let owner = result.typ.owner let err = diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index f542a124476ef..a676adbd7950e 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -630,7 +630,7 @@ proc isTrival(caller: PNode): bool {.inline.} = proc trackOperandForIndirectCall(tracked: PEffects, n: PNode, formals: PType; argIndex: int; caller: PNode) = let a = skipConvCastAndClosure(n) let op = a.typ - let param = if formals != nil and argIndex < formals.len and formals.n != nil: formals.n[argIndex].sym else: nil + let param = if formals != nil and formals.n != nil and argIndex < formals.n.len: formals.n[argIndex].sym else: nil # assume indirect calls are taken here: if op != nil and op.kind == tyProc and n.skipConv.kind != nkNilLit and not isTrival(caller) and diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index ab7df0adaddb3..e7214d4a160f2 100644 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -102,7 +102,7 @@ proc symChoice(c: PContext, n: PNode, s: PSym, r: TSymChoiceRule; result.add newSymNode(a, info) onUse(info, a) a = nextOverloadIter(o, c, n) - if firstPreferred: + if r != scForceOpen and firstPreferred: result[0].flags.incl nfPreferredSym proc semBindStmt(c: PContext, n: PNode, toBind: var IntSet): PNode = @@ -256,24 +256,30 @@ proc semTemplSymbol(c: PContext, n: PNode, s: PSym; isField: bool): PNode = result.typ = nil of skGenericParam: if isField and sfGenSym in s.flags: result = n - else: result = newSymNodeTypeDesc(s, c.idgen, n.info) + else: + result = newSymNodeTypeDesc(s, c.idgen, n.info) + result.flags.incl nfOpenSym + result.typ = nil of skParam: result = n of skType: if isField and sfGenSym in s.flags: result = n - else: result = newSymNodeTypeDesc(s, c.idgen, n.info) + else: + result = newSymNodeTypeDesc(s, c.idgen, n.info) + result.flags.incl nfOpenSym + result.typ = nil else: if isField and sfGenSym in s.flags: result = n - else: result = newSymNode(s, n.info) + else: + result = newSymNode(s, n.info) + result.flags.incl nfOpenSym + result.typ = nil # Issue #12832 when defined(nimsuggest): suggestSym(c.graph, n.info, s, c.graph.usageSym, false) # 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 From e194df3e2f33ece574f07edd923267cc27964967 Mon Sep 17 00:00:00 2001 From: metagn <10591326+metagn@users.noreply.github.com> Date: Sat, 23 Sep 2023 15:49:50 +0300 Subject: [PATCH 6/7] walk back some functionality --- compiler/semexprs.nim | 2 +- compiler/semgnrc.nim | 5 +++++ compiler/sempass2.nim | 2 +- compiler/semtempl.nim | 23 ++++++----------------- tests/lookups/topensym.nim | 15 +-------------- tests/template/tinnerouterproc.nim | 11 +---------- 6 files changed, 15 insertions(+), 43 deletions(-) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 02c579438fc61..1c58aae7822a8 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -140,7 +140,7 @@ proc semSymChoice(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: P # some contexts might want sym choices preserved for later disambiguation # in general though they are ambiguous let first = n[0] - if nfPreferredSym in first.flags: + if result.len == 1 or nfPreferredSym in first.flags: result = first else: var err = "ambiguous identifier '" & first.sym.name.s & diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index e0e4202b07f0a..7cfe3081c015c 100644 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -65,8 +65,13 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym, template maybeDotChoice(c: PContext, n: PNode, s: PSym, fromDotExpr: bool) = if fromDotExpr: result = symChoice(c, n, s, scForceOpen) + if result.kind == nkOpenSymChoice and result.len == 1: + 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. diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index a676adbd7950e..f4d696194d32a 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -1115,7 +1115,7 @@ proc track(tracked: PEffects, n: PNode) = if n.sym.typ != nil and tfHasAsgn in n.sym.typ.flags: tracked.owner.flags.incl sfInjectDestructors # bug #15038: ensure consistency - if not hasDestructor(n.typ) and sameType(n.typ, n.sym.typ): n.typ = n.sym.typ + if n.typ != nil and not hasDestructor(n.typ) and sameType(n.typ, n.sym.typ): n.typ = n.sym.typ of nkHiddenAddr, nkAddr: if n[0].kind == nkSym and isLocalSym(tracked, n[0].sym): useVarNoInitCheck(tracked, n[0], n[0].sym) diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index e7214d4a160f2..2864f9151dcb9 100644 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -76,11 +76,9 @@ proc symChoice(c: PContext, n: PNode, s: PSym, r: TSymChoiceRule; markUsed(c, info, s) onUse(info, s) else: - if s.kind in routineKinds: - result.flags.incl nfPreferredSym - else: - result.flags.incl nfOpenSym - result.typ = nil + # could maybe instead generate a open symchoice with a preferred sym, + # which the logic for is in the top else branch + result.flags.incl nfPreferredSym incl(s.flags, sfUsed) markOwnerModuleAsUsed(c, s) else: @@ -256,24 +254,15 @@ proc semTemplSymbol(c: PContext, n: PNode, s: PSym; isField: bool): PNode = result.typ = nil of skGenericParam: if isField and sfGenSym in s.flags: result = n - else: - result = newSymNodeTypeDesc(s, c.idgen, n.info) - result.flags.incl nfOpenSym - result.typ = nil + else: result = newSymNodeTypeDesc(s, c.idgen, n.info) of skParam: result = n of skType: if isField and sfGenSym in s.flags: result = n - else: - result = newSymNodeTypeDesc(s, c.idgen, n.info) - result.flags.incl nfOpenSym - result.typ = nil + else: result = newSymNodeTypeDesc(s, c.idgen, n.info) else: if isField and sfGenSym in s.flags: result = n - else: - result = newSymNode(s, n.info) - result.flags.incl nfOpenSym - result.typ = nil + else: result = newSymNode(s, n.info) # Issue #12832 when defined(nimsuggest): suggestSym(c.graph, n.info, s, c.graph.usageSym, false) diff --git a/tests/lookups/topensym.nim b/tests/lookups/topensym.nim index 1da55e9fcfadb..9edd0f76236f2 100644 --- a/tests/lookups/topensym.nim +++ b/tests/lookups/topensym.nim @@ -16,7 +16,7 @@ block: # issue #22605, normal call syntax doAssert g(int) == "good" -block: # issu #22605, method call syntax +block: # issue #22605, method call syntax const error = "bad" template valueOr(self: int, def: untyped): untyped = @@ -34,19 +34,6 @@ block: # issu #22605, method call syntax 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 diff --git a/tests/template/tinnerouterproc.nim b/tests/template/tinnerouterproc.nim index cced5f7734b25..1f15fb13e0e40 100644 --- a/tests/template/tinnerouterproc.nim +++ b/tests/template/tinnerouterproc.nim @@ -1,4 +1,4 @@ -block: # issue #20002 +block: # #20002 proc bar(x: int): int = 10 template foo = proc bar(x: int): int {.gensym.} = x + 2 @@ -6,12 +6,3 @@ block: # issue #20002 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() From d76ca1051b3fb6bcd065f413a8bccfd93d993b67 Mon Sep 17 00:00:00 2001 From: metagn <10591326+metagn@users.noreply.github.com> Date: Fri, 29 Sep 2023 20:37:48 +0300 Subject: [PATCH 7/7] walk back more, disable seemingly unnecessary stuff --- compiler/lookups.nim | 10 +++++----- compiler/semexprs.nim | 2 +- compiler/semgnrc.nim | 11 ++++++++++- compiler/semtempl.nim | 8 ++++++++ 4 files changed, 24 insertions(+), 7 deletions(-) diff --git a/compiler/lookups.nim b/compiler/lookups.nim index 77bca6a32f354..116127cae672b 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -592,9 +592,9 @@ 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: + if false and nfOpenSym in n.flags: let alt = searchInScopes(c, result.name, amb) - if alt != nil and alt != result and not amb: + if alt != nil and alt != result and not amb and alt.owner == c.p.owner: result = alt of nkAccQuoted: var ident = considerQuotedIdent(c, n) @@ -650,10 +650,10 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym = c.isAmbiguous = amb of nkSym: result = n.sym - if nfOpenSym in n.flags: + if false and nfOpenSym in n.flags: var amb = false let alt = searchInScopes(c, result.name, amb) - if alt != nil and alt != result and not amb: + if alt != nil and alt != result and not amb and alt.owner == c.p.owner: result = alt of nkDotExpr: result = nil @@ -721,7 +721,7 @@ proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym = return nil of nkSym: - if nfOpenSym notin n.flags: + if true or nfOpenSym notin n.flags: result = n.sym if nfPreferredSym in n.flags: o.mode = oimSymChoiceLocalLookup diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 1c58aae7822a8..31119f563f7af 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -3071,7 +3071,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType 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: + 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 diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index 7cfe3081c015c..db0c3f08d3b12 100644 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -225,7 +225,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: diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index 2864f9151dcb9..cf2369bacf8b9 100644 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -582,6 +582,14 @@ 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: + # prevent `dotTransformation` from rewriting this node to `nkIdent` + # by making it a symchoice + # in generics this becomes `nkClosedSymChoice` but this breaks code + # as the old behavior here was that this became `nkIdent` + var choice = newNodeIT(nkOpenSymChoice, n[1].info, newTypeS(tyNone, c.c)) + choice.add result[1] + result[1] = choice else: result = semTemplBodySons(c, n) of nkExprColonExpr, nkExprEqExpr: