diff options
-rw-r--r-- | compiler/ast.nim | 2 | ||||
-rw-r--r-- | compiler/lookups.nim | 7 | ||||
-rw-r--r-- | compiler/options.nim | 3 | ||||
-rw-r--r-- | compiler/sem.nim | 8 | ||||
-rw-r--r-- | compiler/semexprs.nim | 36 | ||||
-rw-r--r-- | compiler/semstmts.nim | 16 | ||||
-rw-r--r-- | compiler/semtypes.nim | 12 | ||||
-rw-r--r-- | compiler/sigmatch.nim | 2 | ||||
-rw-r--r-- | tests/enum/toverloadable_enums.nim | 48 | ||||
-rw-r--r-- | tests/errmsgs/t10251.nim | 2 |
10 files changed, 125 insertions, 11 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index 0eeaa8133..5b54dd8ef 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -993,7 +993,7 @@ type const OverloadableSyms* = {skProc, skFunc, skMethod, skIterator, - skConverter, skModule, skTemplate, skMacro} + skConverter, skModule, skTemplate, skMacro, skEnumField} GenericTypes*: TTypeKinds = {tyGenericInvocation, tyGenericBody, tyGenericParam} diff --git a/compiler/lookups.nim b/compiler/lookups.nim index 44a30eefa..331eef525 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -177,13 +177,16 @@ iterator allSyms*(c: PContext): (PSym, int, bool) = proc someSymFromImportTable*(c: PContext; name: PIdent; ambiguous: var bool): PSym = var marked = initIntSet() + var symSet = OverloadableSyms + if overloadableEnums notin c.features: + symSet.excl skEnumField result = nil for im in c.imports.mitems: for s in symbols(im, marked, name, c.graph): if result == nil: result = s else: - if s.kind notin OverloadableSyms or result.kind notin OverloadableSyms: + if s.kind notin symSet or result.kind notin symSet: ambiguous = true proc searchInScopes*(c: PContext, s: PIdent; ambiguous: var bool): PSym = @@ -384,7 +387,7 @@ proc mergeShadowScope*(c: PContext) = ## ## Merges: ## shadow -> shadow: add symbols to the parent but check for redefinitions etc - ## shadow -> non-shadow: the above, but also handle exports and all that + ## shadow -> non-shadow: the above, but also handle exports and all that let shadowScope = c.currentScope c.rawCloseScope for sym in shadowScope.symbols: diff --git a/compiler/options.nim b/compiler/options.nim index f02f02256..e37dea1b6 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -203,7 +203,8 @@ type vmopsDanger, strictFuncs, views, - strictNotNil + strictNotNil, + overloadableEnums LegacyFeature* = enum allowSemcheckedAstModification, diff --git a/compiler/sem.nim b/compiler/sem.nim index 879f6efc9..804325e56 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -90,6 +90,12 @@ proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode = # error correction: result = copyTree(arg) result.typ = formal + elif arg.kind in nkSymChoices and formal.skipTypes(abstractInst).kind == tyEnum: + # Pick the right 'sym' from the sym choice by looking at 'formal' type: + for ch in arg: + if sameType(ch.typ, formal): + return getConstExpr(c.module, ch, c.idgen, c.graph) + typeMismatch(c.config, info, formal, arg.typ, arg) else: result = indexTypesMatch(c, formal, arg.typ, arg) if result == nil: @@ -356,6 +362,8 @@ proc semConstExpr(c: PContext, n: PNode): PNode = if e == nil: localError(c.config, n.info, errConstExprExpected) return n + if e.kind in nkSymChoices and e[0].typ.skipTypes(abstractInst).kind == tyEnum: + return e result = getConstExpr(c.module, e, c.idgen, c.graph) if result == nil: #if e.kind == nkEmpty: globalError(n.info, errConstExprExpected) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 42c6b465e..f2fe54fad 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -2707,6 +2707,34 @@ proc getNilType(c: PContext): PType = result.align = c.config.target.ptrSize.int16 c.nilTypeCache = result +proc enumFieldSymChoice(c: PContext, n: PNode, s: PSym): PNode = + var o: TOverloadIter + var i = 0 + var a = initOverloadIter(o, c, n) + while a != nil: + if a.kind in OverloadableSyms-{skModule}: + inc(i) + if i > 1: break + a = nextOverloadIter(o, c, n) + let info = getCallLineInfo(n) + if i <= 1: + if sfGenSym notin s.flags: + result = newSymNode(s, info) + markUsed(c, info, s) + onUse(info, s) + else: + result = n + else: + result = newNodeIT(nkClosedSymChoice, info, newTypeS(tyNone, c)) + a = initOverloadIter(o, c, n) + while a != nil: + if a.kind in OverloadableSyms-{skModule}: + incl(a.flags, sfUsed) + markOwnerModuleAsUsed(c, a) + result.add newSymNode(a, info) + onUse(info, a) + a = nextOverloadIter(o, c, n) + proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = when defined(nimCompilerStacktraceHints): setFrameMsg c.config$n.info & " " & $n.kind @@ -2730,7 +2758,8 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = {checkUndeclared, checkModule, checkAmbiguity, checkPureEnumFields} var s = qualifiedLookUp(c, n, checks) if c.matchedConcept == nil: semCaptureSym(s, c.p.owner) - if s.kind in {skProc, skFunc, skMethod, skConverter, skIterator}: + case s.kind + of skProc, skFunc, skMethod, skConverter, skIterator: #performProcvarCheck(c, n, s) result = symChoice(c, n, s, scClosed) if result.kind == nkSym: @@ -2740,6 +2769,11 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = # "procs literals" are 'owned' if optOwnedRefs in c.config.globalOptions: result.typ = makeVarType(c, result.typ, tyOwned) + of skEnumField: + if overloadableEnums in c.features: + result = enumFieldSymChoice(c, n, s) + else: + result = semSym(c, n, s, flags) else: result = semSym(c, n, s, flags) of nkSym: diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 75323a95e..cc09291c5 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -490,6 +490,18 @@ proc semLowerLetVarCustomPragma(c: PContext, a: PNode, n: PNode): PNode = ret.add result result = semExprNoType(c, ret) +proc errorSymChoiceUseQualifier(c: PContext; n: PNode) = + assert n.kind in nkSymChoices + var err = "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 + localError(c.config, n.info, errGenerated, err) + proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = if n.len == 1: result = semLowerLetVarCustomPragma(c, n[0], n) @@ -514,7 +526,9 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = if a[^1].kind != nkEmpty: def = semExprWithType(c, a[^1], {}) - if def.kind == nkSym and def.sym.kind in {skTemplate, skMacro}: + if def.kind in nkSymChoices and def[0].typ.skipTypes(abstractInst).kind == tyEnum: + errorSymChoiceUseQualifier(c, def) + 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: typFlags.incl taProcContextIsNotMacro diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 484997e3a..db895cab6 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -144,8 +144,13 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType = styleCheckDef(c.config, e) onDef(e.info, e) if sfGenSym notin e.flags: - if not isPure: addInterfaceDecl(c, e) - else: declarePureEnumField(c, e) + if not isPure: + if overloadableEnums in c.features: + addInterfaceOverloadableSymAt(c, c.currentScope, e) + else: + addInterfaceDecl(c, e) + else: + declarePureEnumField(c, e) if isPure and (let conflict = strTableInclReportConflict(symbols, e); conflict != nil): wrongRedefinition(c, e.info, e.name.s, conflict.info) inc(counter) @@ -240,7 +245,8 @@ proc semRangeAux(c: PContext, n: PNode, prev: PType): PType = if not hasUnknownTypes: if not sameType(rangeT[0].skipTypes({tyRange}), rangeT[1].skipTypes({tyRange})): - localError(c.config, n.info, "type mismatch") + typeMismatch(c.config, n.info, rangeT[0], rangeT[1], n) + elif not isOrdinalType(rangeT[0]) and rangeT[0].kind notin {tyFloat..tyFloat128} or rangeT[0].kind == tyBool: localError(c.config, n.info, "ordinal or float type expected") diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 6bc641bd6..674dd7e2e 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -2197,7 +2197,7 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType, var best = -1 for i in 0..<arg.len: if arg[i].sym.kind in {skProc, skFunc, skMethod, skConverter, - skIterator, skMacro, skTemplate}: + skIterator, skMacro, skTemplate, skEnumField}: copyCandidate(z, m) z.callee = arg[i].typ if tfUnresolved in z.callee.flags: continue diff --git a/tests/enum/toverloadable_enums.nim b/tests/enum/toverloadable_enums.nim new file mode 100644 index 000000000..6f3aac3e2 --- /dev/null +++ b/tests/enum/toverloadable_enums.nim @@ -0,0 +1,48 @@ +discard """ + output: '''B +0''' +joinable: false +""" + +{.experimental: "overloadableEnums".} + +type + E1 = enum + value1, + value2 + E2 = enum + value1, + value2 = 4 + +const + Lookuptable = [ + E1.value1: "1", + value2: "2" + ] + +when false: + const + Lookuptable: array[E1, string] = [ + value1: "1", + value2: "2" + ] + + +proc p(e: E1): int = + # test that the 'case' statement is smart enough: + case e + of value1: echo "A" + of value2: echo "B" + + +let v = p value2 # ERROR: ambiguous! +# (value2|value2) nkClosedSymChoice -> nkSym + +proc x(p: int) = discard +proc x(p: string) = discard + +proc takeCallback(param: proc(p: int)) = discard + +takeCallback x + +echo ord v diff --git a/tests/errmsgs/t10251.nim b/tests/errmsgs/t10251.nim index 0c7fe0b3d..19adf02eb 100644 --- a/tests/errmsgs/t10251.nim +++ b/tests/errmsgs/t10251.nim @@ -2,7 +2,6 @@ discard """ action:reject cmd: "nim check $options $file" nimout: ''' -t10251.nim(15, 5) Error: redefinition of 'foo'; previous declaration here: t10251.nim(13, 5) t10251.nim(19, 23) Error: redefinition of 'goo1'; previous declaration here: t10251.nim(19, 11) ''' """ @@ -14,6 +13,7 @@ type Enum2 = enum foo, bar, baz + type Enum3 {.pure.} = enum # fixed (by accident?) in https://github.com/nim-lang/Nim/pull/18263 goo0, goo1, goo2, goo1 |