Skip to content

Commit

Permalink
better procvar ambiguity errors, clean up after #20457 (#20932)
Browse files Browse the repository at this point in the history
* better procvar ambiguity errors, clean up after #20457

fixes #6359, fixes #13849

* only trigger on closedsymchoice again

* new approach

* add manual entry for ambiguous enums too

* add indent [skip ci]

* move to proc
  • Loading branch information
metagn authored Dec 1, 2022
1 parent b36f511 commit 2449c37
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 36 deletions.
5 changes: 3 additions & 2 deletions compiler/semdata.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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]

Expand Down
47 changes: 34 additions & 13 deletions compiler/semexprs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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}))
Expand Down Expand Up @@ -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
Expand All @@ -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, {})
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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})
Expand All @@ -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:
Expand Down
24 changes: 3 additions & 21 deletions compiler/semstmts.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
17 changes: 17 additions & 0 deletions doc/manual.md
Original file line number Diff line number Diff line change
Expand Up @@ -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].


Expand Down
19 changes: 19 additions & 0 deletions tests/ambsym/tambprocvar.nim
Original file line number Diff line number Diff line change
@@ -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:]#

0 comments on commit 2449c37

Please sign in to comment.