implements overloadable enum values; WIP (#18470)

* implements overloadable enum values
* simpler code
This commit is contained in:
Andreas Rumpf
2021-07-28 12:46:28 +02:00
committed by GitHub
parent 4c1202972a
commit a273ea70e8
10 changed files with 125 additions and 11 deletions

View File

@@ -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}

View File

@@ -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:

View File

@@ -203,7 +203,8 @@ type
vmopsDanger,
strictFuncs,
views,
strictNotNil
strictNotNil,
overloadableEnums
LegacyFeature* = enum
allowSemcheckedAstModification,

View File

@@ -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)

View File

@@ -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:

View File

@@ -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

View File

@@ -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")

View File

@@ -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

View 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

View File

@@ -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