implements overloadable enum values; WIP (#18470)
* implements overloadable enum values * simpler code
This commit is contained in:
parent
4c1202972a
commit
a273ea70e8
10 changed files with 125 additions and 11 deletions
|
|
@ -993,7 +993,7 @@ type
|
||||||
|
|
||||||
const
|
const
|
||||||
OverloadableSyms* = {skProc, skFunc, skMethod, skIterator,
|
OverloadableSyms* = {skProc, skFunc, skMethod, skIterator,
|
||||||
skConverter, skModule, skTemplate, skMacro}
|
skConverter, skModule, skTemplate, skMacro, skEnumField}
|
||||||
|
|
||||||
GenericTypes*: TTypeKinds = {tyGenericInvocation, tyGenericBody,
|
GenericTypes*: TTypeKinds = {tyGenericInvocation, tyGenericBody,
|
||||||
tyGenericParam}
|
tyGenericParam}
|
||||||
|
|
|
||||||
|
|
@ -177,13 +177,16 @@ iterator allSyms*(c: PContext): (PSym, int, bool) =
|
||||||
|
|
||||||
proc someSymFromImportTable*(c: PContext; name: PIdent; ambiguous: var bool): PSym =
|
proc someSymFromImportTable*(c: PContext; name: PIdent; ambiguous: var bool): PSym =
|
||||||
var marked = initIntSet()
|
var marked = initIntSet()
|
||||||
|
var symSet = OverloadableSyms
|
||||||
|
if overloadableEnums notin c.features:
|
||||||
|
symSet.excl skEnumField
|
||||||
result = nil
|
result = nil
|
||||||
for im in c.imports.mitems:
|
for im in c.imports.mitems:
|
||||||
for s in symbols(im, marked, name, c.graph):
|
for s in symbols(im, marked, name, c.graph):
|
||||||
if result == nil:
|
if result == nil:
|
||||||
result = s
|
result = s
|
||||||
else:
|
else:
|
||||||
if s.kind notin OverloadableSyms or result.kind notin OverloadableSyms:
|
if s.kind notin symSet or result.kind notin symSet:
|
||||||
ambiguous = true
|
ambiguous = true
|
||||||
|
|
||||||
proc searchInScopes*(c: PContext, s: PIdent; ambiguous: var bool): PSym =
|
proc searchInScopes*(c: PContext, s: PIdent; ambiguous: var bool): PSym =
|
||||||
|
|
|
||||||
|
|
@ -203,7 +203,8 @@ type
|
||||||
vmopsDanger,
|
vmopsDanger,
|
||||||
strictFuncs,
|
strictFuncs,
|
||||||
views,
|
views,
|
||||||
strictNotNil
|
strictNotNil,
|
||||||
|
overloadableEnums
|
||||||
|
|
||||||
LegacyFeature* = enum
|
LegacyFeature* = enum
|
||||||
allowSemcheckedAstModification,
|
allowSemcheckedAstModification,
|
||||||
|
|
|
||||||
|
|
@ -90,6 +90,12 @@ proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode =
|
||||||
# error correction:
|
# error correction:
|
||||||
result = copyTree(arg)
|
result = copyTree(arg)
|
||||||
result.typ = formal
|
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:
|
else:
|
||||||
result = indexTypesMatch(c, formal, arg.typ, arg)
|
result = indexTypesMatch(c, formal, arg.typ, arg)
|
||||||
if result == nil:
|
if result == nil:
|
||||||
|
|
@ -356,6 +362,8 @@ proc semConstExpr(c: PContext, n: PNode): PNode =
|
||||||
if e == nil:
|
if e == nil:
|
||||||
localError(c.config, n.info, errConstExprExpected)
|
localError(c.config, n.info, errConstExprExpected)
|
||||||
return n
|
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)
|
result = getConstExpr(c.module, e, c.idgen, c.graph)
|
||||||
if result == nil:
|
if result == nil:
|
||||||
#if e.kind == nkEmpty: globalError(n.info, errConstExprExpected)
|
#if e.kind == nkEmpty: globalError(n.info, errConstExprExpected)
|
||||||
|
|
|
||||||
|
|
@ -2707,6 +2707,34 @@ proc getNilType(c: PContext): PType =
|
||||||
result.align = c.config.target.ptrSize.int16
|
result.align = c.config.target.ptrSize.int16
|
||||||
c.nilTypeCache = result
|
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 =
|
proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||||
when defined(nimCompilerStacktraceHints):
|
when defined(nimCompilerStacktraceHints):
|
||||||
setFrameMsg c.config$n.info & " " & $n.kind
|
setFrameMsg c.config$n.info & " " & $n.kind
|
||||||
|
|
@ -2730,7 +2758,8 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||||
{checkUndeclared, checkModule, checkAmbiguity, checkPureEnumFields}
|
{checkUndeclared, checkModule, checkAmbiguity, checkPureEnumFields}
|
||||||
var s = qualifiedLookUp(c, n, checks)
|
var s = qualifiedLookUp(c, n, checks)
|
||||||
if c.matchedConcept == nil: semCaptureSym(s, c.p.owner)
|
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)
|
#performProcvarCheck(c, n, s)
|
||||||
result = symChoice(c, n, s, scClosed)
|
result = symChoice(c, n, s, scClosed)
|
||||||
if result.kind == nkSym:
|
if result.kind == nkSym:
|
||||||
|
|
@ -2740,6 +2769,11 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||||
# "procs literals" are 'owned'
|
# "procs literals" are 'owned'
|
||||||
if optOwnedRefs in c.config.globalOptions:
|
if optOwnedRefs in c.config.globalOptions:
|
||||||
result.typ = makeVarType(c, result.typ, tyOwned)
|
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:
|
else:
|
||||||
result = semSym(c, n, s, flags)
|
result = semSym(c, n, s, flags)
|
||||||
of nkSym:
|
of nkSym:
|
||||||
|
|
|
||||||
|
|
@ -490,6 +490,18 @@ proc semLowerLetVarCustomPragma(c: PContext, a: PNode, n: PNode): PNode =
|
||||||
ret.add result
|
ret.add result
|
||||||
result = semExprNoType(c, ret)
|
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 =
|
proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
|
||||||
if n.len == 1:
|
if n.len == 1:
|
||||||
result = semLowerLetVarCustomPragma(c, n[0], n)
|
result = semLowerLetVarCustomPragma(c, n[0], n)
|
||||||
|
|
@ -514,7 +526,9 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
|
||||||
if a[^1].kind != nkEmpty:
|
if a[^1].kind != nkEmpty:
|
||||||
def = semExprWithType(c, a[^1], {})
|
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
|
typFlags.incl taIsTemplateOrMacro
|
||||||
elif def.typ.kind == tyTypeDesc and c.p.owner.kind != skMacro:
|
elif def.typ.kind == tyTypeDesc and c.p.owner.kind != skMacro:
|
||||||
typFlags.incl taProcContextIsNotMacro
|
typFlags.incl taProcContextIsNotMacro
|
||||||
|
|
|
||||||
|
|
@ -144,8 +144,13 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
|
||||||
styleCheckDef(c.config, e)
|
styleCheckDef(c.config, e)
|
||||||
onDef(e.info, e)
|
onDef(e.info, e)
|
||||||
if sfGenSym notin e.flags:
|
if sfGenSym notin e.flags:
|
||||||
if not isPure: addInterfaceDecl(c, e)
|
if not isPure:
|
||||||
else: declarePureEnumField(c, e)
|
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):
|
if isPure and (let conflict = strTableInclReportConflict(symbols, e); conflict != nil):
|
||||||
wrongRedefinition(c, e.info, e.name.s, conflict.info)
|
wrongRedefinition(c, e.info, e.name.s, conflict.info)
|
||||||
inc(counter)
|
inc(counter)
|
||||||
|
|
@ -240,7 +245,8 @@ proc semRangeAux(c: PContext, n: PNode, prev: PType): PType =
|
||||||
|
|
||||||
if not hasUnknownTypes:
|
if not hasUnknownTypes:
|
||||||
if not sameType(rangeT[0].skipTypes({tyRange}), rangeT[1].skipTypes({tyRange})):
|
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
|
elif not isOrdinalType(rangeT[0]) and rangeT[0].kind notin {tyFloat..tyFloat128} or
|
||||||
rangeT[0].kind == tyBool:
|
rangeT[0].kind == tyBool:
|
||||||
localError(c.config, n.info, "ordinal or float type expected")
|
localError(c.config, n.info, "ordinal or float type expected")
|
||||||
|
|
|
||||||
|
|
@ -2197,7 +2197,7 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType,
|
||||||
var best = -1
|
var best = -1
|
||||||
for i in 0..<arg.len:
|
for i in 0..<arg.len:
|
||||||
if arg[i].sym.kind in {skProc, skFunc, skMethod, skConverter,
|
if arg[i].sym.kind in {skProc, skFunc, skMethod, skConverter,
|
||||||
skIterator, skMacro, skTemplate}:
|
skIterator, skMacro, skTemplate, skEnumField}:
|
||||||
copyCandidate(z, m)
|
copyCandidate(z, m)
|
||||||
z.callee = arg[i].typ
|
z.callee = arg[i].typ
|
||||||
if tfUnresolved in z.callee.flags: continue
|
if tfUnresolved in z.callee.flags: continue
|
||||||
|
|
|
||||||
48
tests/enum/toverloadable_enums.nim
Normal file
48
tests/enum/toverloadable_enums.nim
Normal file
|
|
@ -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
|
||||||
|
|
@ -2,7 +2,6 @@ discard """
|
||||||
action:reject
|
action:reject
|
||||||
cmd: "nim check $options $file"
|
cmd: "nim check $options $file"
|
||||||
nimout: '''
|
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)
|
t10251.nim(19, 23) Error: redefinition of 'goo1'; previous declaration here: t10251.nim(19, 11)
|
||||||
'''
|
'''
|
||||||
"""
|
"""
|
||||||
|
|
@ -14,6 +13,7 @@ type
|
||||||
Enum2 = enum
|
Enum2 = enum
|
||||||
foo, bar, baz
|
foo, bar, baz
|
||||||
|
|
||||||
|
|
||||||
type
|
type
|
||||||
Enum3 {.pure.} = enum # fixed (by accident?) in https://github.com/nim-lang/Nim/pull/18263
|
Enum3 {.pure.} = enum # fixed (by accident?) in https://github.com/nim-lang/Nim/pull/18263
|
||||||
goo0, goo1, goo2, goo1
|
goo0, goo1, goo2, goo1
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue