unordered enum for better interoperability with C (#23585)

ref https://forum.nim-lang.org/t/11564
```nim
block: # unordered enum
  block:
    type
      unordered_enum = enum
        a = 1
        b = 0

    doAssert (ord(a), ord(b)) == (1, 0)

  block:
    type
      unordered_enum = enum
        a = 1
        b = 0
        c

    doAssert (ord(a), ord(b), ord(c)) == (1, 0, 2)

  block:
    type
      unordered_enum = enum
        a = 100
        b
        c = 50
        d

    doAssert (ord(a), ord(b), ord(c), ord(d)) == (100, 101, 50, 51)

  block:
    type
      unordered_enum = enum
        a = 7
        b = 6
        c = 5
        d

    doAssert (ord(a), ord(b), ord(c), ord(d)) == (7, 6, 5, 8)
```
This commit is contained in:
ringabout
2024-05-10 16:32:07 +08:00
committed by GitHub
parent c101490a0c
commit 42486e1b2f
4 changed files with 109 additions and 5 deletions

View File

@@ -21,7 +21,7 @@ import
extccomp
import vtables
import std/[strtabs, math, tables, intsets, strutils]
import std/[strtabs, math, tables, intsets, strutils, packedsets]
when not defined(leanCompiler):
import spawn

View File

@@ -15,7 +15,7 @@ const
errStringLiteralExpected = "string literal expected"
errIntLiteralExpected = "integer literal expected"
errWrongNumberOfVariables = "wrong number of variables"
errInvalidOrderInEnumX = "invalid order in enum '$1'"
errDuplicateAliasInEnumX = "duplicate value in enum '$1'"
errOverflowInEnumX = "The enum '$1' exceeds its maximum value ($2)"
errOrdinalTypeExpected = "ordinal type expected; given: $1"
errSetTooBig = "set is too large; use `std/sets` for ordinal types with more than 2^16 elements"
@@ -69,6 +69,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
e: PSym = nil
base: PType = nil
identToReplace: ptr PNode = nil
counterSet = initPackedSet[BiggestInt]()
counter = 0
base = nil
result = newOrPrevType(tyEnum, prev, c)
@@ -85,6 +86,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
var hasNull = false
for i in 1..<n.len:
if n[i].kind == nkEmpty: continue
var useAutoCounter = false
case n[i].kind
of nkEnumFieldDef:
if n[i][0].kind == nkPragmaExpr:
@@ -112,6 +114,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
of tyString, tyCstring:
strVal = v
x = counter
useAutoCounter = true
else:
if isOrdinalType(v.typ, allowEnumWithHoles=true):
x = toInt64(getOrdValue(v))
@@ -120,22 +123,30 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
localError(c.config, v.info, errOrdinalTypeExpected % typeToString(v.typ, preferDesc))
if i != 1:
if x != counter: incl(result.flags, tfEnumHasHoles)
if x < counter:
localError(c.config, n[i].info, errInvalidOrderInEnumX % e.name.s)
x = counter
e.ast = strVal # might be nil
counter = x
of nkSym:
e = n[i].sym
useAutoCounter = true
of nkIdent, nkAccQuoted:
e = newSymS(skEnumField, n[i], c)
identToReplace = addr n[i]
useAutoCounter = true
of nkPragmaExpr:
e = newSymS(skEnumField, n[i][0], c)
pragma(c, e, n[i][1], enumFieldPragmas)
identToReplace = addr n[i][0]
useAutoCounter = true
else:
illFormedAst(n[i], c.config)
if useAutoCounter:
while counter in counterSet and counter != high(typeof(counter)):
inc counter
counterSet.incl counter
elif counterSet.containsOrIncl(counter):
localError(c.config, n[i].info, errDuplicateAliasInEnumX % e.name.s)
e.typ = result
e.position = int(counter)
let symNode = newSymNode(e)

View File

@@ -184,3 +184,86 @@ block: # bug #12589
A = int64.high()
doAssert ord(A) == int64.high()
import std/enumutils
from std/sequtils import toSeq
import std/macros
block: # unordered enum
block:
type
unordered_enum = enum
a = 1
b = 0
doAssert (ord(a), ord(b)) == (1, 0)
when false: # TODO: fixme pre-existing issues # bug #23586
doAssert unordered_enum.toSeq == @[a, b]
block:
type
unordered_enum = enum
a = 1
b = 0
c
doAssert (ord(a), ord(b), ord(c)) == (1, 0, 2)
block:
type
unordered_enum = enum
a = 100
b
c = 50
d
doAssert (ord(a), ord(b), ord(c), ord(d)) == (100, 101, 50, 51)
block:
type
unordered_enum = enum
a = 7
b = 6
c = 5
d
doAssert (ord(a), ord(b), ord(c), ord(d)) == (7, 6, 5, 8)
when false:
doAssert unordered_enum.toSeq == @[a, b, c, d]
block:
type
unordered_enum = enum
a = 100
b
c = 500
d
e
f = 50
g
h
doAssert (ord(a), ord(b), ord(c), ord(d), ord(e), ord(f), ord(g), ord(h)) ==
(100, 101, 500, 501, 502, 50, 51, 52)
block:
type
unordered_enum = enum
A
B
C = -1
D
E
G = -999
doAssert (ord(A), ord(B), ord(C), ord(D), ord(E), ord(G)) ==
(0, 1, -1, 2, 3, -999)
block:
type
SomeEnum = enum
seA = 3
seB = 2
seC = "foo"
doAssert (ord(seA), ord(seB), ord(seC)) == (3, 2, 4)

View File

@@ -0,0 +1,10 @@
discard """
errormsg: "duplicate value in enum 'd'"
"""
type
unordered_enum = enum
a = 1
b = 0
c
d = 2