summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/semdata.nim5
-rw-r--r--compiler/semexprs.nim47
-rw-r--r--compiler/semstmts.nim24
-rw-r--r--doc/manual.md17
-rw-r--r--tests/ambsym/tambprocvar.nim19
5 files changed, 76 insertions, 36 deletions
diff --git a/compiler/semdata.nim b/compiler/semdata.nim
index 73276e50a..29c2c139f 100644
--- a/compiler/semdata.nim
+++ b/compiler/semdata.nim
@@ -70,10 +70,11 @@ type
     efWantStmt, efAllowStmt, efDetermineType, efExplain,
     efWantValue, efOperand, efNoSemCheck,
     efNoEvaluateGeneric, efInCall, efFromHlo, efNoSem2Check,
-    efNoUndeclared, efIsDotCall, efCannotBeDotCall
+    efNoUndeclared, efIsDotCall, efCannotBeDotCall,
       # Use this if undeclared identifiers should not raise an error during
       # overload resolution.
-    efNoDiagnostics
+    efNoDiagnostics,
+    efTypeAllowed # typeAllowed will be called after
 
   TExprFlags* = set[TExprFlag]
 
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index d6745e8e4..cb7b0ae4a 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -83,18 +83,39 @@ proc semExprCheck(c: PContext, n: PNode, flags: TExprFlags, expectedType: PType
     # do not produce another redundant error message:
     result = errorNode(c, n)
 
+proc ambiguousSymChoice(c: PContext, orig, n: PNode): PNode =
+  let first = n[0].sym
+  if first.kind == skEnumField:
+    # choose the first resolved enum field, i.e. the latest in scope
+    # to mirror behavior before overloadable enums
+    if hintAmbiguousEnum in c.config.notes:
+      var err = "ambiguous enum field '" & first.name.s &
+        "' assumed to be of type " & typeToString(first.typ) &
+        " -- use one of the following:\n"
+      for child in n:
+        let candidate = child.sym
+        err.add "  " & candidate.owner.name.s & "." & candidate.name.s & "\n"
+      message(c.config, orig.info, hintAmbiguousEnum, err)
+    result = n[0]
+  else:
+    var err = "ambiguous identifier '" & first.name.s &
+      "' -- use one of the following:\n"
+    for child in n:
+      let candidate = child.sym
+      err.add "  " & candidate.owner.name.s & "." & candidate.name.s
+      err.add ": " & typeToString(candidate.typ) & "\n"
+    localError(c.config, orig.info, err)
+    n.typ = errorType(c)
+    result = n
+
 proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType = nil): PNode =
   result = semExprCheck(c, n, flags, expectedType)
   if result.typ == nil and efInTypeof in flags:
     result.typ = c.voidType
   elif (result.typ == nil or result.typ.kind == tyNone) and
-      result.kind == nkClosedSymChoice and
-      result[0].sym.kind == skEnumField:
-    # if overloaded enum field could not choose a type from a closed list,
-    # choose the first resolved enum field, i.e. the latest in scope
-    # to mirror old behavior
-    msgSymChoiceUseQualifier(c, result, hintAmbiguousEnum)
-    result = result[0]
+      efTypeAllowed in flags and
+      result.kind == nkClosedSymChoice and result.len > 0:
+    result = ambiguousSymChoice(c, n, result)
   elif result.typ == nil or result.typ == c.enforceVoidContext:
     localError(c.config, n.info, errExprXHasNoType %
                 renderTree(result, {renderNoComments}))
@@ -634,7 +655,7 @@ proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PTyp
         lastValidIndex = lastOrd(c.config, indexType)
         x = x[1]
 
-    let yy = semExprWithType(c, x, expectedType = expectedElementType)
+    let yy = semExprWithType(c, x, {efTypeAllowed}, expectedElementType)
     var typ = yy.typ
     if expectedElementType == nil:
       expectedElementType = typ
@@ -655,7 +676,7 @@ proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PTyp
           localError(c.config, x.info, "invalid order in array constructor")
         x = x[1]
 
-      let xx = semExprWithType(c, x, {}, expectedElementType)
+      let xx = semExprWithType(c, x, {efTypeAllowed}, expectedElementType)
       result.add xx
       typ = commonType(c, typ, xx.typ)
       #n[i] = semExprWithType(c, x, {})
@@ -1806,7 +1827,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
                renderTree(a, {renderNoComments}))
   else:
     let lhs = n[0]
-    let rhs = semExprWithType(c, n[1], {}, le)
+    let rhs = semExprWithType(c, n[1], {efTypeAllowed}, le)
     if lhs.kind == nkSym and lhs.sym.kind == skResult:
       n.typ = c.enforceVoidContext
       if c.p.owner.kind != skMacro and resultTypeIsInferrable(lhs.sym.typ):
@@ -2502,8 +2523,8 @@ proc semSetConstr(c: PContext, n: PNode, expectedType: PType = nil): PNode =
     for i in 0..<n.len:
       if isRange(n[i]):
         checkSonsLen(n[i], 3, c.config)
-        n[i][1] = semExprWithType(c, n[i][1], {}, expectedElementType)
-        n[i][2] = semExprWithType(c, n[i][2], {}, expectedElementType)
+        n[i][1] = semExprWithType(c, n[i][1], {efTypeAllowed}, expectedElementType)
+        n[i][2] = semExprWithType(c, n[i][2], {efTypeAllowed}, expectedElementType)
         if typ == nil:
           typ = skipTypes(n[i][1].typ,
                           {tyGenericInst, tyVar, tyLent, tyOrdinal, tyAlias, tySink})
@@ -2518,7 +2539,7 @@ proc semSetConstr(c: PContext, n: PNode, expectedType: PType = nil): PNode =
           if expectedElementType == nil:
             expectedElementType = typ
       else:
-        n[i] = semExprWithType(c, n[i], {}, expectedElementType)
+        n[i] = semExprWithType(c, n[i], {efTypeAllowed}, expectedElementType)
         if typ == nil:
           typ = skipTypes(n[i].typ, {tyGenericInst, tyVar, tyLent, tyOrdinal, tyAlias, tySink})
           if expectedElementType == nil:
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index f6b193f3d..7a3bb48a6 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -585,22 +585,6 @@ proc semVarMacroPragma(c: PContext, a: PNode, n: PNode): PNode =
                 pragma(c, defs[lhsPos][namePos].sym, defs[lhsPos][pragmaPos], validPragmas)
         return result
 
-proc msgSymChoiceUseQualifier(c: PContext; n: PNode; note = errGenerated) =
-  assert n.kind in nkSymChoices
-  var err =
-    if note == hintAmbiguousEnum:
-      "ambiguous enum field '$1' assumed to be of type $2, this will become an error in the future" % [$n[0], typeToString(n[0].typ)]
-    else:
-      "ambiguous identifier: '" & $n[0] & "'"
-  var i = 0
-  for child in n:
-    let candidate = child.sym
-    if i == 0: err.add " -- use one of the following:\n"
-    else: err.add "\n"
-    err.add "  " & candidate.owner.name.s & "." & candidate.name.s
-    inc i
-  message(c.config, n.info, note, err)
-
 template isLocalVarSym(n: PNode): bool =
   n.kind == nkSym and 
     (n.sym.kind in {skVar, skLet} and not 
@@ -645,11 +629,9 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
 
     var def: PNode = c.graph.emptyNode
     if a[^1].kind != nkEmpty:
-      def = semExprWithType(c, a[^1], {}, typ)
+      def = semExprWithType(c, a[^1], {efTypeAllowed}, typ)
 
-      if def.kind in nkSymChoices and def[0].sym.kind == skEnumField:
-        msgSymChoiceUseQualifier(c, def, errGenerated)
-      elif def.kind == nkSym and def.sym.kind in {skTemplate, skMacro}:
+      if def.kind == nkSym and def.sym.kind in {skTemplate, skMacro}:
         typFlags.incl taIsTemplateOrMacro
       elif def.typ.kind == tyTypeDesc and c.p.owner.kind != skMacro:
         typFlags.incl taProcContextIsNotMacro
@@ -799,7 +781,7 @@ proc semConst(c: PContext, n: PNode): PNode =
     var typFlags: TTypeAllowedFlags
 
     # don't evaluate here since the type compatibility check below may add a converter
-    var def = semExprWithType(c, a[^1], {}, typ)
+    var def = semExprWithType(c, a[^1], {efTypeAllowed}, typ)
 
     if def.kind == nkSym and def.sym.kind in {skTemplate, skMacro}:
       typFlags.incl taIsTemplateOrMacro
diff --git a/doc/manual.md b/doc/manual.md
index a4f864eaa..378aaed90 100644
--- a/doc/manual.md
+++ b/doc/manual.md
@@ -1365,6 +1365,23 @@ ambiguous, a static error will be produced.
   p value2
   ```
 
+In some cases, ambiguity of enums is resolved depending on the relation
+between the current scope and the scope the enums were defined in.
+
+  ```nim
+  # a.nim
+  type Foo* = enum abc
+
+  # b.nim
+  import a
+  type Bar = enum abc
+  echo abc is Bar # true
+
+  block:
+    type Baz = enum abc
+    echo abc is Baz # true
+  ```
+
 To implement bit fields with enums see [Bit fields].
 
 
diff --git a/tests/ambsym/tambprocvar.nim b/tests/ambsym/tambprocvar.nim
new file mode 100644
index 000000000..2a9921bad
--- /dev/null
+++ b/tests/ambsym/tambprocvar.nim
@@ -0,0 +1,19 @@
+discard """
+  action: reject
+  cmd: "nim check $file"
+  nimout: '''
+tambprocvar.nim(15, 11) Error: ambiguous identifier 'foo' -- use one of the following:
+  tambprocvar.foo: proc (x: int){.noSideEffect, gcsafe.}
+  tambprocvar.foo: proc (x: float){.noSideEffect, gcsafe.}
+'''
+"""
+
+block:
+  proc foo(x: int) = discard
+  proc foo(x: float) = discard
+
+  let x = foo
+
+block:
+  let x = `+` #[tt.Error
+          ^ ambiguous identifier '+' -- use one of the following:]#