Fix the two known limits: adding map keys while looping over the map is a runtime error; temporaries held by an expression are freed when try / or-blocks leave early. err usable after or, inner err shadows outer.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Nexoniarz 2026-10-09 01:26:53 +02:00
commit 393327b353
9 changed files with 228 additions and 17 deletions

96
tests/run/early_exit.fch Normal file
View file

@ -0,0 +1,96 @@
// Leaving an expression half way (try, or { break / continue / return }) frees what it was holding.
// MEMCHECK=1 tests/run.sh checks this with valgrind.
struct Pair {
str a
str b
}
fn check(int n) -> str! {
if n > 0 {
return error("failed at " + str(n))
}
return "ok"
}
fn join3(str a, str b, str c) -> str {
return a + b + c
}
fn Pair.both() -> str {
return self.a + self.b
}
fn args(int n) -> str! {
s := "x".repeat(3)
return join3(s + "1", try check(n), s + "2")
}
fn array(int n) -> []str! {
s := "y".repeat(2)
return [s + "a", s + "b", try check(n)]
}
fn pair(int n) -> Pair! {
s := "z".repeat(2)
return Pair(s + "a", try check(n))
}
fn concat(int n) -> str! {
s := "w".repeat(2)
return s + "1" + (try check(n)) + "2"
}
fn method_on_temp(int n) -> str! {
s := "v".repeat(2)
return (s + "!").replace("v", try check(n))
}
fn struct_method(int n) -> str! {
s := "u".repeat(2)
t := Pair(s, s + "?").both() + (try check(n))
return t
}
fn map_literal(int n) -> map[str]str! {
s := "k".repeat(2)
return [s: s + "1", s + "x": try check(n)]
}
fn index_temp(int n) -> str! {
s := "i".repeat(2)
return [s + "0", s + "1"][int(try check(n)) or 0]
}
fn main() {
for n in 0..2 {
print(args(n) or err, "|", array(n) or ["-"], "|", pair(n) or Pair("-", "-"))
print(concat(n) or err, "|", method_on_temp(n) or err, "|", struct_method(n) or err)
print(map_literal(n) or ["-": "-"])
}
print(index_temp(1) or "index: failed")
// break / continue out of an or { } block in the middle of a call
s := "q".repeat(2)
for i in 0..4 {
t := join3(s + "a", check(i % 2) or { continue }, s + "b")
print(i, t)
}
for i in 0..4 {
t := join3(s + "a", check(i) or { break }, s + "b")
print("before break:", i, t)
}
// removing keys while looping over a map is fine
m := ["a": 1, "b": 2, "c": 3, "d": 4]
for k, v in m {
if v % 2 == 0 {
m.remove(k)
}
}
print(m)
for k in m {
m[k] = m[k] * 10 // changing values is fine too
}
print(m)
}