mirror of
https://github.com/Nexoniarz/Finch.git
synced 2026-11-03 00:44:27 +00:00
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:
parent
ca1d938087
commit
393327b353
9 changed files with 228 additions and 17 deletions
7
tests/fail/map_add_while_looping.fch
Normal file
7
tests/fail/map_add_while_looping.fch
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
// expect: a key was added to the map while looping over it
|
||||
fn main() {
|
||||
m := ["a": 1]
|
||||
for k in m {
|
||||
m[k + "!"] = 2
|
||||
}
|
||||
}
|
||||
96
tests/run/early_exit.fch
Normal file
96
tests/run/early_exit.fch
Normal 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)
|
||||
}
|
||||
12
tests/run/early_exit.out
Normal file
12
tests/run/early_exit.out
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
xxx1okxxx2 | ["yya", "yyb", "ok"] | Pair(a: "zza", b: "ok")
|
||||
ww1ok2 | okok! | uuuu?ok
|
||||
{"kk": "kk1", "kkx": "ok"}
|
||||
failed at 1 | ["-"] | Pair(a: "-", b: "-")
|
||||
failed at 1 | failed at 1 | failed at 1
|
||||
{"-": "-"}
|
||||
index: failed
|
||||
0 qqaokqqb
|
||||
2 qqaokqqb
|
||||
before break: 0 qqaokqqb
|
||||
{"a": 1, "c": 3}
|
||||
{"a": 10, "c": 30}
|
||||
Loading…
Reference in a new issue