diff options
author | metagn <metagngn@gmail.com> | 2024-09-02 19:11:59 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-09-02 18:11:59 +0200 |
commit | 5e55e16ad83483f15f5a5df7dfdccf70ed9e3e8e (patch) | |
tree | dbf1d4bae4468dc74fb08bfa2d5fdf5c12771023 | |
parent | 4789af71fe107e137524bc7746b902e644aca2dc (diff) | |
download | Nim-5e55e16ad83483f15f5a5df7dfdccf70ed9e3e8e.tar.gz |
check constant conditions in generic `when` in objects (#24042)
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.
-rw-r--r-- | compiler/semtypes.nim | 17 | ||||
-rw-r--r-- | tests/generics/tgenericwhen.nim | 46 |
2 files changed, 58 insertions, 5 deletions
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 2b6a7b6a7..8d293a753 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -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) @@ -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: diff --git a/tests/generics/tgenericwhen.nim b/tests/generics/tgenericwhen.nim new file mode 100644 index 000000000..e1b23873b --- /dev/null +++ b/tests/generics/tgenericwhen.nim @@ -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 |