mirror of
https://github.com/nim-lang/Nim.git
synced 2026-04-19 14:00:35 +00:00
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:
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
tests/enum/tenum_duplicate.nim
Normal file
10
tests/enum/tenum_duplicate.nim
Normal file
@@ -0,0 +1,10 @@
|
||||
discard """
|
||||
errormsg: "duplicate value in enum 'd'"
|
||||
"""
|
||||
|
||||
type
|
||||
unordered_enum = enum
|
||||
a = 1
|
||||
b = 0
|
||||
c
|
||||
d = 2
|
||||
Reference in New Issue
Block a user