Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

better procvar ambiguity errors, clean up after #20457 #20932

Merged
merged 6 commits into from
Dec 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -1803,7 +1824,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 @@ -2499,8 +2520,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 @@ -2515,7 +2536,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 @@ -580,22 +580,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 @@ -640,11 +624,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 @@ -794,7 +776,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:]#