Skip to content

Commit

Permalink
unordered enum for better interoperability with C (#23585)
Browse files Browse the repository at this point in the history
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)
```
  • Loading branch information
ringabout authored May 10, 2024
1 parent c101490 commit 42486e1
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 5 deletions.
2 changes: 1 addition & 1 deletion compiler/sem.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
19 changes: 15 additions & 4 deletions compiler/semtypes.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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)
Expand All @@ -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:
Expand Down Expand Up @@ -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))
Expand All @@ -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)
Expand Down
83 changes: 83 additions & 0 deletions tests/enum/tenum.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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)
10 changes: 10 additions & 0 deletions tests/enum/tenum_duplicate.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
discard """
errormsg: "duplicate value in enum 'd'"
"""

type
unordered_enum = enum
a = 1
b = 0
c
d = 2

0 comments on commit 42486e1

Please sign in to comment.