diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index 332a802ab8a5..10f4b86abefd 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -143,3 +143,4 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimHasTopDownInference") defineSymbol("nimHasTemplateRedefinitionPragma") defineSymbol("nimHasCstringCase") + defineSymbol("nimHasAmbiguousEnumHint") diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim index ec16439603eb..05f4eba5a93d 100644 --- a/compiler/lineinfos.nim +++ b/compiler/lineinfos.nim @@ -96,6 +96,7 @@ type hintPattern = "Pattern", hintExecuting = "Exec", hintLinking = "Link", hintDependency = "Dependency", hintSource = "Source", hintPerformance = "Performance", hintStackTrace = "StackTrace", hintGCStats = "GCStats", hintGlobalVar = "GlobalVar", hintExpandMacro = "ExpandMacro", + hintAmbiguousEnum = "AmbiguousEnum", hintUser = "User", hintUserRaw = "UserRaw", hintExtendedContext = "ExtendedContext", hintMsgOrigin = "MsgOrigin", # since 1.3.5 hintDeclaredLoc = "DeclaredLoc", # since 1.5.1 @@ -209,6 +210,7 @@ const hintGCStats: "$1", hintGlobalVar: "global variable declared here", hintExpandMacro: "expanded macro: $1", + hintAmbiguousEnum: "$1", hintUser: "$1", hintUserRaw: "$1", hintExtendedContext: "$1", diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index a0753010a2a2..0c6500408e69 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -87,6 +87,14 @@ proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType 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] elif result.typ == nil or result.typ == c.enforceVoidContext: localError(c.config, n.info, errExprXHasNoType % renderTree(result, {renderNoComments})) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 1b98f5c1a836..4f01f508e1f4 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -574,9 +574,13 @@ proc semVarMacroPragma(c: PContext, a: PNode, n: PNode): PNode = pragma(c, defs[lhsPos][namePos].sym, defs[lhsPos][pragmaPos], validPragmas) return result -proc errorSymChoiceUseQualifier(c: PContext; n: PNode) = +proc msgSymChoiceUseQualifier(c: PContext; n: PNode; note = errGenerated) = assert n.kind in nkSymChoices - var err = "ambiguous identifier: '" & $n[0] & "'" + 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 @@ -584,7 +588,7 @@ proc errorSymChoiceUseQualifier(c: PContext; n: PNode) = else: err.add "\n" err.add " " & candidate.owner.name.s & "." & candidate.name.s inc i - localError(c.config, n.info, errGenerated, err) + message(c.config, n.info, note, err) proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = var b: PNode @@ -611,8 +615,8 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = if a[^1].kind != nkEmpty: def = semExprWithType(c, a[^1], {}, typ) - if def.kind in nkSymChoices and def[0].typ.skipTypes(abstractInst).kind == tyEnum: - errorSymChoiceUseQualifier(c, def) + 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}: typFlags.incl taIsTemplateOrMacro elif def.typ.kind == tyTypeDesc and c.p.owner.kind != skMacro: diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 0a823f20ad6d..d033d5f4403d 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -591,7 +591,9 @@ proc semCaseBranch(c: PContext, t, branch: PNode, branchIndex: int, branch[i] = semCaseBranchRange(c, t, b, covered) else: # constant sets and arrays are allowed: - var r = semConstExpr(c, b) + # set expected type to selector type for type inference + # even if it can be a different type like a set or array + var r = semConstExpr(c, b, expectedType = t[0].typ) if r.kind in {nkCurly, nkBracket} and r.len == 0 and branch.len == 2: # discarding ``{}`` and ``[]`` branches silently delSon(branch, 0) diff --git a/config/nim.cfg b/config/nim.cfg index 55b7a41c15c4..9f50a1f841d7 100644 --- a/config/nim.cfg +++ b/config/nim.cfg @@ -15,6 +15,10 @@ cc = gcc --parallel_build: "0" # 0 to auto-detect number of processors hint[LineTooLong]=off +@if nimHasAmbiguousEnumHint: + # not needed if hint is a style check + hint[AmbiguousEnum]=off +@end #hint[XDeclaredButNotUsed]=off threads:on diff --git a/tests/ambsym/tambsym3.nim b/tests/ambsym/tambsym3.nim index 0558517bd076..e50f9c4618a3 100644 --- a/tests/ambsym/tambsym3.nim +++ b/tests/ambsym/tambsym3.nim @@ -1,12 +1,15 @@ discard """ - errormsg: "ambiguous identifier" + errormsg: "ambiguous enum field" file: "tambsym3.nim" - line: 11 + line: 14 """ # Test ambiguous symbols import mambsym1, times +{.hint[AmbiguousEnum]: on.} +{.hintAsError[AmbiguousEnum]: on.} + var v = mDec #ERROR_MSG ambiguous identifier diff --git a/tests/enum/tcrossmodule.nim b/tests/enum/tcrossmodule.nim index 1e97fd1ee49d..c21072198ee8 100644 --- a/tests/enum/tcrossmodule.nim +++ b/tests/enum/tcrossmodule.nim @@ -8,3 +8,8 @@ template t = doAssert some(Success) t() + +block: # legacy support for behavior before overloadableEnums + # warning: ambiguous enum field 'Success' assumed to be of type MyEnum + let x = {Success} + doAssert x is set[MyEnum]