summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorAndreas Rumpf <rumpf_a@web.de>2021-07-28 12:46:28 +0200
committerGitHub <noreply@github.com>2021-07-28 12:46:28 +0200
commita273ea70e8817e3509014a1b3dcd16a360ed400b (patch)
tree3a3d26701cf7bc84240426cde277d44e6194e4a4
parent4c1202972abdfe99232e5d15a6169c7b2e0f5d75 (diff)
downloadNim-a273ea70e8817e3509014a1b3dcd16a360ed400b.tar.gz
implements overloadable enum values; WIP (#18470)
* implements overloadable enum values
* simpler code
-rw-r--r--compiler/ast.nim2
-rw-r--r--compiler/lookups.nim7
-rw-r--r--compiler/options.nim3
-rw-r--r--compiler/sem.nim8
-rw-r--r--compiler/semexprs.nim36
-rw-r--r--compiler/semstmts.nim16
-rw-r--r--compiler/semtypes.nim12
-rw-r--r--compiler/sigmatch.nim2
-rw-r--r--tests/enum/toverloadable_enums.nim48
-rw-r--r--tests/errmsgs/t10251.nim2
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