summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--changelog.md7
-rw-r--r--compiler/semexprs.nim14
-rw-r--r--compiler/semgnrc.nim41
-rw-r--r--compiler/semtempl.nim16
-rw-r--r--compiler/sigmatch.nim8
-rw-r--r--lib/pure/ioselects/ioselectors_select.nim2
-rw-r--r--tests/lookups/mambtype1.nim (renamed from tests/lookups/mqualifiedamb1.nim)0
-rw-r--r--tests/lookups/mambtype2.nim4
-rw-r--r--tests/lookups/mqualifiedamb2.nim4
-rw-r--r--tests/lookups/tambtype.nim20
-rw-r--r--tests/lookups/tqualifiedamb.nim4
11 files changed, 85 insertions, 35 deletions
diff --git a/changelog.md b/changelog.md
index 5056d7b3d..d98c34474 100644
--- a/changelog.md
+++ b/changelog.md
@@ -21,6 +21,13 @@
 
 - `owner` in `std/macros` is deprecated.
 
+- Ambiguous type symbols in generic procs and templates now generate symchoice nodes.
+  Previously; in templates they would error immediately at the template definition,
+  and in generic procs a type symbol would arbitrarily be captured, losing the
+  information of the other symbols. This means that generic code can now give
+  errors for ambiguous type symbols, and macros operating on generic proc AST
+  may encounter symchoice nodes instead of the arbitrarily resolved type symbol nodes.
+
 ## Standard library additions and changes
 
 [//]: # "Changes:"
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 9293f8497..8332d404d 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -3131,12 +3131,16 @@ proc resolveIdentToSym(c: PContext, n: PNode, resultNode: var PNode,
     # unambiguous, or we don't care about ambiguity
     result = candidates[0]
   else:
-    # ambiguous symbols have 1 last chance as a symchoice,
-    # but type symbols cannot participate in symchoices
+    # ambiguous symbols have 1 last chance as a symchoice
     var choice = newNodeIT(nkClosedSymChoice, n.info, newTypeS(tyNone, c))
-    for c in candidates:
-      if c.kind notin {skType, skModule, skPackage}:
-        choice.add newSymNode(c, n.info)
+    for cand in candidates:
+      case cand.kind
+      of skModule, skPackage:
+        discard
+      of skType:
+        choice.add newSymNodeTypeDesc(cand, c.idgen, n.info)
+      else:
+        choice.add newSymNode(cand, n.info)
     if choice.len == 0:
       # we know candidates.len > 1, we just couldn't put any in a symchoice
       errorUseQualifier(c, n.info, candidates)
diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim
index e7f36879f..8efc8a94e 100644
--- a/compiler/semgnrc.nim
+++ b/compiler/semgnrc.nim
@@ -64,6 +64,7 @@ proc newOpenSym*(n: PNode): PNode {.inline.} =
 
 proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
                           ctx: var GenericCtx; flags: TSemGenericFlags,
+                          isAmbiguous: bool,
                           fromDotExpr=false): PNode =
   result = nil
   semIdeForTemplateOrGenericCheck(c.config, n, ctx.cursorInBody)
@@ -133,6 +134,11 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
   of skType:
     if (s.typ != nil) and
        (s.typ.flags * {tfGenericTypeParam, tfImplicitTypeParam} == {}):
+      if isAmbiguous:
+        # ambiguous types should be symchoices since lookup behaves
+        # differently for them in regular expressions
+        maybeDotChoice(c, n, s, fromDotExpr)
+        return
       result = newSymNodeTypeDesc(s, c.idgen, n.info)
       if canOpenSym(result.sym):
         if genericsOpenSym in c.features:
@@ -184,7 +190,7 @@ proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags,
     elif s.isMixedIn:
       result = symChoice(c, n, s, scForceOpen)
     else:
-      result = semGenericStmtSymbol(c, n, s, ctx, flags)
+      result = semGenericStmtSymbol(c, n, s, ctx, flags, amb)
   # else: leave as nkIdent
 
 proc newDot(n, b: PNode): PNode =
@@ -200,10 +206,11 @@ proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags,
 
   let luf = if withinMixin notin flags: {checkUndeclared, checkModule} else: {checkModule}
 
+  c.isAmbiguous = false
   var s = qualifiedLookUp(c, n, luf)
   if s != nil:
     isMacro = s.kind in {skTemplate, skMacro}
-    result = semGenericStmtSymbol(c, n, s, ctx, flags)
+    result = semGenericStmtSymbol(c, n, s, ctx, flags, c.isAmbiguous)
   else:
     n[0] = semGenericStmt(c, n[0], flags, ctx)
     result = n
@@ -217,24 +224,21 @@ proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags,
       isMacro = s.kind in {skTemplate, skMacro}
       if withinBind in flags or s.id in ctx.toBind:
         if s.kind == skType: # don't put types in sym choice
-          result = newDot(result, semGenericStmtSymbol(c, n, s, ctx, flags, fromDotExpr=true))
+          var ambig = false
+          if candidates.len > 1:
+            let s2 = searchInScopes(c, ident, ambig)
+          result = newDot(result, semGenericStmtSymbol(c, n, s, ctx, flags,
+            isAmbiguous = ambig, fromDotExpr = true))
         else:
           result = newDot(result, symChoice(c, n, s, scClosed))
       elif s.isMixedIn:
         result = newDot(result, symChoice(c, n, s, scForceOpen))
       else:
+        var ambig = false
         if s.kind == skType and candidates.len > 1:
-          var ambig = false
-          let s2 = searchInScopes(c, ident, ambig) 
-          if ambig:
-            # this is a type conversion like a.T where T is ambiguous with
-            # other types or routines
-            # in regular code, this never considers a type conversion and
-            # skips to routine overloading
-            # so symchoices are used which behave similarly with type symbols
-            result = newDot(result, symChoice(c, n, s, scForceOpen))
-            return
-        let syms = semGenericStmtSymbol(c, n, s, ctx, flags, fromDotExpr=true)
+          discard searchInScopes(c, ident, ambig)
+        let syms = semGenericStmtSymbol(c, n, s, ctx, flags,
+          isAmbiguous = ambig, fromDotExpr = true)
         result = newDot(result, syms)
 
 proc addTempDecl(c: PContext; n: PNode; kind: TSymKind) =
@@ -301,7 +305,9 @@ proc semGenericStmt(c: PContext, n: PNode,
     # check if it is an expression macro:
     checkMinSonsLen(n, 1, c.config)
     let fn = n[0]
+    c.isAmbiguous = false
     var s = qualifiedLookUp(c, fn, {})
+    let ambig = c.isAmbiguous
     if s == nil and
         {withinMixin, withinConcept}*flags == {} and
         fn.kind in {nkIdent, nkAccQuoted} and
@@ -354,7 +360,12 @@ proc semGenericStmt(c: PContext, n: PNode,
       of skType:
         # bad hack for generics:
         if (s.typ != nil) and (s.typ.kind != tyGenericParam):
-          result[0] = newSymNodeTypeDesc(s, c.idgen, fn.info)
+          if ambig:
+            # ambiguous types should be symchoices since lookup behaves
+            # differently for them in regular expressions
+            result[0] = sc
+          else:
+            result[0] = newSymNodeTypeDesc(s, c.idgen, fn.info)
           onUse(fn.info, s)
           first = 1
       else:
diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim
index c6f2fb60c..f7653a890 100644
--- a/compiler/semtempl.nim
+++ b/compiler/semtempl.nim
@@ -218,7 +218,7 @@ proc addLocalDecl(c: var TemplCtx, n: var PNode, k: TSymKind) =
         if k == skParam and c.inTemplateHeader > 0:
           local.flags.incl sfTemplateParam
 
-proc semTemplSymbol(c: PContext, n: PNode, s: PSym; isField: bool): PNode =
+proc semTemplSymbol(c: PContext, n: PNode, s: PSym; isField, isAmbiguous: bool): PNode =
   incl(s.flags, sfUsed)
   # bug #12885; ideally sem'checking is performed again afterwards marking
   # the symbol as used properly, but the nfSem mechanism currently prevents
@@ -245,6 +245,10 @@ proc semTemplSymbol(c: PContext, n: PNode, s: PSym; isField: bool): PNode =
     result = n
   of skType:
     if isField and sfGenSym in s.flags: result = n
+    elif isAmbiguous:
+      # ambiguous types should be symchoices since lookup behaves
+      # differently for them in regular expressions
+      result = symChoice(c, n, s, scOpen, isField)
     else: result = newSymNodeTypeDesc(s, c.idgen, n.info)
   else:
     if isField and sfGenSym in s.flags: result = n
@@ -345,6 +349,7 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
   case n.kind
   of nkIdent:
     if n.ident.id in c.toInject: return n
+    c.c.isAmbiguous = false
     let s = qualifiedLookUp(c.c, n, {})
     if s != nil:
       if s.owner == c.owner and s.kind == skParam and sfTemplateParam in s.flags:
@@ -362,9 +367,9 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
         result = newSymNode(s, n.info)
         onUse(n.info, s)
       else:
-        if s.kind in {skType, skVar, skLet, skConst}:
+        if s.kind in {skVar, skLet, skConst}:
           discard qualifiedLookUp(c.c, n, {checkAmbiguity, checkModule})
-        result = semTemplSymbol(c.c, n, s, c.noGenSym > 0)
+        result = semTemplSymbol(c.c, n, s, c.noGenSym > 0, c.c.isAmbiguous)
   of nkBind:
     result = semTemplBody(c, n[0])
   of nkBindStmt:
@@ -558,6 +563,7 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
   of nkDotExpr, nkAccQuoted:
     # dotExpr is ambiguous: note that we explicitly allow 'x.TemplateParam',
     # so we use the generic code for nkDotExpr too
+    c.c.isAmbiguous = false
     let s = qualifiedLookUp(c.c, n, {})
     if s != nil:
       # mirror the nkIdent case
@@ -572,9 +578,9 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
       elif contains(c.toMixin, s.name.id):
         return symChoice(c.c, n, s, scForceOpen, c.noGenSym > 0)
       else:
-        if s.kind in {skType, skVar, skLet, skConst}:
+        if s.kind in {skVar, skLet, skConst}:
           discard qualifiedLookUp(c.c, n, {checkAmbiguity, checkModule})
-        return semTemplSymbol(c.c, n, s, c.noGenSym > 0)
+        return semTemplSymbol(c.c, n, s, c.noGenSym > 0, c.c.isAmbiguous)
     if n.kind == nkDotExpr:
       result = n
       result[0] = semTemplBody(c, n[0])
diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim
index f2865347f..d0c9ce029 100644
--- a/compiler/sigmatch.nim
+++ b/compiler/sigmatch.nim
@@ -2381,7 +2381,7 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType,
     result = paramTypesMatchAux(m, f, a, arg, argOrig)
   else:
     # symbol kinds that don't participate in symchoice type disambiguation:
-    let matchSet = {low(TSymKind)..high(TSymKind)} - {skModule, skPackage, skType}
+    let matchSet = {low(TSymKind)..high(TSymKind)} - {skModule, skPackage}
 
     var best = -1
     result = arg
@@ -2426,6 +2426,12 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType,
         if arg[i].sym.kind in matchSet:
           copyCandidate(z, m)
           z.callee = arg[i].typ
+          if arg[i].sym.kind == skType and z.callee.kind != tyTypeDesc:
+            # creating the symchoice with the type sym having typedesc type
+            # breaks a lot of stuff, so we make the typedesc type here
+            # mirrored from `newSymNodeTypeDesc`
+            z.callee = newType(tyTypeDesc, c.idgen, arg[i].sym.owner)
+            z.callee.addSonSkipIntLit(arg[i].sym.typ, c.idgen)
           if tfUnresolved in z.callee.flags: continue
           z.calleeSym = arg[i].sym
           z.calleeScope = cmpScopes(m.c, arg[i].sym)
diff --git a/lib/pure/ioselects/ioselectors_select.nim b/lib/pure/ioselects/ioselectors_select.nim
index 11bc62b78..6c516395b 100644
--- a/lib/pure/ioselects/ioselectors_select.nim
+++ b/lib/pure/ioselects/ioselectors_select.nim
@@ -314,7 +314,7 @@ proc selectInto*[T](s: Selector[T], timeout: int,
 
   if timeout != -1:
     when defined(genode) or defined(freertos) or defined(zephyr) or defined(nuttx):
-      tv.tv_sec = Time(timeout div 1_000)
+      tv.tv_sec = posix.Time(timeout div 1_000)
     else:
       tv.tv_sec = timeout.int32 div 1_000
     tv.tv_usec = (timeout.int32 %% 1_000) * 1_000
diff --git a/tests/lookups/mqualifiedamb1.nim b/tests/lookups/mambtype1.nim
index 47046142e..47046142e 100644
--- a/tests/lookups/mqualifiedamb1.nim
+++ b/tests/lookups/mambtype1.nim
diff --git a/tests/lookups/mambtype2.nim b/tests/lookups/mambtype2.nim
new file mode 100644
index 000000000..cf622466b
--- /dev/null
+++ b/tests/lookups/mambtype2.nim
@@ -0,0 +1,4 @@
+import ./mambtype1
+export mambtype1
+template K*(kind: static int): auto = typedesc[mambtype1.K]
+template B*(kind: static int): auto = typedesc[mambtype1.K]
diff --git a/tests/lookups/mqualifiedamb2.nim b/tests/lookups/mqualifiedamb2.nim
deleted file mode 100644
index 3ea5bd04f..000000000
--- a/tests/lookups/mqualifiedamb2.nim
+++ /dev/null
@@ -1,4 +0,0 @@
-import ./mqualifiedamb1
-export mqualifiedamb1
-template K*(kind: static int): auto = typedesc[mqualifiedamb1.K]
-template B*(kind: static int): auto = typedesc[mqualifiedamb1.K]
diff --git a/tests/lookups/tambtype.nim b/tests/lookups/tambtype.nim
new file mode 100644
index 000000000..a292db83a
--- /dev/null
+++ b/tests/lookups/tambtype.nim
@@ -0,0 +1,20 @@
+import ./mambtype2
+
+block: # issue #23893
+  discard default(K(0))       # works
+  discard default(mambtype2.B(0))     # works
+  discard default(mambtype2.K(0))     # doesn't work
+
+block: # issue #23898, in template
+  template r() =
+    discard default(B(0))     # compiles
+    discard default(mambtype2.B(0))   # compiles
+    discard default(K(0))     # does not compile
+  r()
+
+block: # in generics
+  proc foo[T]() =
+    discard default(B(0))     # compiles
+    discard default(mambtype2.B(0))   # compiles
+    discard default(K(0))     # does not compile
+  foo[int]()
diff --git a/tests/lookups/tqualifiedamb.nim b/tests/lookups/tqualifiedamb.nim
deleted file mode 100644
index a5e1955f3..000000000
--- a/tests/lookups/tqualifiedamb.nim
+++ /dev/null
@@ -1,4 +0,0 @@
-import ./mqualifiedamb2
-discard default(K(0))       # works
-discard default(mqualifiedamb2.B(0))     # works
-discard default(mqualifiedamb2.K(0))     # doesn't work