Files
Nim/tests/casestmt/tcasestmt.nim
metagn 56a3dd57fb evaluate all hidden conversions in case branches (#24187)
fixes #11422, refs #8336/#8333, refs #20130

The compiler generates conversion nodes *after* evaluating the branches
of case statements as constants, the reasoning is that case branches
accept constants of different types, like arrays or sets. But this means
that conversion nodes that need to be evaluated like converter calls
don't get evaluated as a constant for codegen. #8336 fixed this by
re-evaluating the node if an `nkHiddenCallConv` was created, and in
#20130 this logic also had to be added for `nkHiddenStdConv` for
cstrings. This logic was only for single case elements, it has now been
added to range elements as well to fix #11422. Additionally, all
conversion nodes are now evaluated for simplicity, but maybe this won't
pass CI.
2024-09-27 11:09:49 +02:00

320 lines
5.6 KiB
Nim

discard """
output:
'''
Not found!
Found!
1
compiles for 1
i am always two
default for 3
set is 4 not 5
array is 6 not 7
default for 8
an identifier
OK
OK
OK
ayyydd
'''
"""
block arrayconstr:
const md_extension = [".md", ".markdown"]
proc test(ext: string) =
case ext
of ".txt", md_extension:
echo "Found!"
else:
echo "Not found!"
test(".something")
# ensure it's not evaluated at compile-time:
var foo = ".markdown"
test(foo)
converter toInt(x: char): int =
x.int
block t8333:
case 0
of 'a': echo 0
else: echo 1
block: # issue #11422
var c: int = 5
case c
of 'a' .. 'c': discard
else: discard
block emptyset_when:
proc whenCase(a: int) =
case a
of (when compiles(whenCase(1)): 1 else: {}): echo "compiles for 1"
of {}: echo "me not fail"
of 2: echo "i am always two"
of []: echo "me neither"
of {4,5}: echo "set is 4 not 5"
of [6,7]: echo "array is 6 not 7"
of (when compiles(neverCompilesIBet()): 3 else: {}): echo "compiles for 3"
#of {},[]: echo "me neither"
else: echo "default for ", a
whenCase(1)
whenCase(2)
whenCase(3)
whenCase(4)
whenCase(6)
whenCase(8)
block setconstr:
const
SymChars: set[char] = {'a'..'z', 'A'..'Z', '\x80'..'\xFF'}
proc classify(s: string) =
case s[0]
of SymChars, '_': echo "an identifier"
of {'0'..'9'}: echo "a number"
else: echo "other"
classify("Hurra")
block tduplicates:
type Kind = enum A, B
var k = A
template reject(b) =
static: doAssert(not compiles(b))
reject:
var i = 2
case i
of [1, 1]: discard
else: discard
reject:
var i = 2
case i
of 1, { 1..2 }: discard
else: discard
reject:
var i = 2
case i
of { 1, 1 }: discard
of { 1, 1 }: discard
else: discard
reject:
case k
of [A, A]: discard
var i = 2
case i
of { 1, 1 }: discard
of { 2, 2 }: echo "OK"
else: discard
case i
of { 10..30, 15..25, 5..15, 25..35 }: discard
else: echo "OK"
case k
of {A, A..A}: echo "OK"
of B: discard
block tcasestm:
type
Tenum = enum eA, eB, eC
var
x: string = "yyy"
y: Tenum = eA
i: int
case y
of eA: write(stdout, "a")
of eB, eC: write(stdout, "b or c")
case x
of "Andreas", "Rumpf": write(stdout, "Hallo Meister!")
of "aa", "bb": write(stdout, "Du bist nicht mein Meister")
of "cc", "hash", "when": discard
of "will", "it", "finally", "be", "generated": discard
var z = case i
of 1..5, 8, 9: "aa"
of 6, 7: "bb"
elif x == "Ha":
"cc"
elif x == "yyy":
write(stdout, x)
"dd"
else:
"zz"
echo z
#OUT ayyy
let str1 = "Y"
let str2 = "NN"
let a = case str1:
of "Y": true
of "N": false
else:
echo "no good"
quit("quitting")
proc toBool(s: string): bool =
case s:
of "": raise newException(ValueError, "Invalid boolean")
elif s[0] == 'Y': true
elif s[0] == 'N': false
else: "error".quit(2)
let b = "NN".toBool()
doAssert(a == true)
doAssert(b == false)
static:
#bug #7407
let bstatic = "N".toBool()
doAssert(bstatic == false)
var bb: bool
doAssert(not compiles(
bb = case str2:
of "": raise newException(ValueError, "Invalid boolean")
elif str.startsWith("Y"): true
elif str.startsWith("N"): false
))
doAssert(not compiles(
bb = case str2:
of "Y": true
of "N": false
))
doAssert(not compiles(
bb = case str2:
of "Y": true
of "N": raise newException(ValueError, "N not allowed")
))
doAssert(not compiles(
bb = case str2:
of "Y": raise newException(ValueError, "Invalid Y")
else: raise newException(ValueError, "Invalid N")
))
doAssert(not compiles(
bb = case str2:
of "Y":
raise newException(ValueError, "Invalid Y")
true
else: raise newException(ValueError, "Invalid")
))
doAssert(not compiles(
bb = case str2:
of "Y":
"invalid Y".quit(3)
true
else: raise newException(ValueError, "Invalid")
))
#issue #11552
proc positiveOrNegative(num: int): string =
result = case num
of (low(int)+2) .. -1:
"negative"
of 0:
"zero"
else:
"impossible"
#issue #11551
proc negativeOrNot(num: int): string =
result = case num
of low(int) .. -1:
"negative"
else:
"zero or positive"
doAssert negativeOrNot(-1) == "negative"
doAssert negativeOrNot(10000000) == "zero or positive"
doAssert negativeOrNot(0) == "zero or positive"
########################################################
# issue #13490
import strutils
func foo(input: string): int =
try:
parseInt(input)
except:
return
func foo2(b, input: string): int =
case b:
of "Y":
for c in input:
result = if c in '0'..'9': parseInt($c)
else: break
of "N":
for c in input:
result = if c in '0'..'9': parseInt($c)
else: continue
else: return
static:
doAssert(foo("3") == 3)
doAssert(foo("a") == 0)
doAssert(foo2("Y", "a2") == 0)
doAssert(foo2("Y", "2a") == 2)
doAssert(foo2("N", "a3") == 3)
doAssert(foo2("z", "2") == 0)
doAssert(foo("3") == 3)
doAssert(foo("a") == 0)
doAssert(foo2("Y", "a2") == 0)
doAssert(foo2("Y", "2a") == 2)
doAssert(foo2("N", "a3") == 3)
doAssert(foo2("z", "2") == 0)
# bug #20031
proc main(a: uint64) =
case a
else:
discard
static:
main(10)
main(10)
block:
# Just needs to compile
proc bar(): int {.discardable.} = discard
proc foo() {.noreturn.} = discard
case "*"
of "*":
bar()
else:
# Make sure this noreturn doesn't
# cause the discardable to not discard
foo()