Skip to content

Commit

Permalink
check constant conditions in generic when in objects (#24042)
Browse files Browse the repository at this point in the history
fixes #24041

`when` statements in generic object types normally just leave their
conditions as expressions and still typecheck their branch bodies.
Instead of this, when the condition can be evaluated as a constant as
well as the ones before it and it resolves to `true`, it now uses the
body of that branch without typechecking the remaining ones.
  • Loading branch information
metagn authored Sep 2, 2024
1 parent 4789af7 commit 5e55e16
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 5 deletions.
17 changes: 12 additions & 5 deletions compiler/semtypes.nim
Original file line number Diff line number Diff line change
Expand Up @@ -789,6 +789,7 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
of nkRecWhen:
var a = copyTree(n)
var branch: PNode = nil # the branch to take
var cannotResolve = false # no branch should be taken
for i in 0..<a.len:
var it = a[i]
if it == nil: illFormedAst(n, c.config)
Expand All @@ -806,24 +807,30 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
let e = semExprWithType(c, it[0], {efDetermineType})
if e.typ.kind == tyFromExpr:
it[0] = makeStaticExpr(c, e)
cannotResolve = true
else:
it[0] = forceBool(c, e)
let val = getConstExpr(c.module, it[0], c.idgen, c.graph)
if val == nil or val.kind != nkIntLit:
cannotResolve = true
elif not cannotResolve and val.intVal != 0 and branch == nil:
branch = it[1]
of nkElse:
checkSonsLen(it, 1, c.config)
if branch == nil: branch = it[0]
if branch == nil and not cannotResolve: branch = it[0]
idx = 0
else: illFormedAst(n, c.config)
if c.inGenericContext > 0:
if c.inGenericContext > 0 and cannotResolve:
# use a new check intset here for each branch:
var newCheck: IntSet = check
var newPos = pos
var newf = newNodeI(nkRecList, n.info)
semRecordNodeAux(c, it[idx], newCheck, newPos, newf, rectype, hasCaseFields)
it[idx] = if newf.len == 1: newf[0] else: newf
if c.inGenericContext > 0:
father.add a
elif branch != nil:
if branch != nil:
semRecordNodeAux(c, branch, check, pos, father, rectype, hasCaseFields)
elif c.inGenericContext > 0:
father.add a
elif father.kind in {nkElse, nkOfBranch}:
father.add newNodeI(nkRecList, n.info)
of nkRecCase:
Expand Down
46 changes: 46 additions & 0 deletions tests/generics/tgenericwhen.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
discard """
targets: "c js"
"""

block: # issue #24041
type ArrayBuf[N: static int, T = byte] = object
when sizeof(int) > sizeof(uint8):
when N <= int(uint8.high):
n: uint8
else:
when sizeof(int) > sizeof(uint16):
when N <= int(uint16.high):
n: uint16
else:
when sizeof(int) > sizeof(uint32):
when N <= int(uint32.high):
n: uint32
else:
n: int
else:
n: int
else:
n: int
else:
n: int

var x: ArrayBuf[8]
doAssert x.n is uint8
when sizeof(int) > sizeof(uint32):
var y: ArrayBuf[int(uint32.high) * 8]
doAssert y.n is int

block: # constant condition after dynamic one
type Foo[T] = object
when T is int:
a: int
elif true:
a: string
else:
a: bool
var x: Foo[string]
doAssert x.a is string
var y: Foo[int]
doAssert y.a is int
var z: Foo[float]
doAssert z.a is string

0 comments on commit 5e55e16

Please sign in to comment.