compiler: a string slice is a fresh temporary too

s[a .. b] is always a copy, so it is freed once a +, a comparison or print has read it. Reseeded.
string_temps.ludic adds a slice compared and a slice concatenated each round (960 KB over 20,000
before, 0 after) and a kept slice read after its +.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-28 12:25:02 +03:00
parent 174a32c285
commit 328dee77c8
5 changed files with 940 additions and 861 deletions

View file

@ -7,3 +7,5 @@ of those as soon as the next `+`, the comparison or `print` has read it, and `st
the start of what it allocated so its text can be freed at all. A string held anywhere - a
variable, an argument, a function's result - is left alone. A loop of 20,000 template builds grew
2.9 MB and now grows 0 (`examples/lang/string_temps.ludic`).
A slice of a string (`s[a .. b]`, always a copy) counts too: `path[0 .. n - 4] + ".dds"` and
`path[n - 4 .. n] == ".png"` no longer leave the slice behind.

View file

@ -1,5 +1,5 @@
# string_temps.ludic - a string made inside an expression and held by nothing (`a + "/"` in
# `a + "/" + b`, a template's pieces, a number's text, a side only compared) is freed as soon as it
# `a + "/" + b`, a template's pieces, a number's text, a slice, a side only compared) is freed as soon as it
# has been used, and one that IS held - in a variable, handed to a function, a function's result -
# is not. Prints 1 2 3 4 5 6 7 and `grew 0` (the heap is read on the Mac; 0 elsewhere).
program StringTemps {
@ -10,6 +10,8 @@ program StringTemps {
function round(i: int, a: string, b: string) -> int {
var bad = 0
if a + "/" + b + "/" + string(i) != `{a}/{b}/{i}` { bad += 1 }
let p = "lake/camp.png" # a literal: the slices are what is made here
if p[len(p) - 4 .. len(p)] != ".png" or p[0 .. len(p) - 4] + ".dds" != `lake/{b[0 .. 0]}camp.dds` { bad += 1 }
if `{i}-{i * 2}-{long(i) * 1000000000}` != string(i) + "-" + string(i * 2) + "-" + string(long(i) * 1000000000) { bad += 1 }
return bad
}
@ -20,7 +22,9 @@ program StringTemps {
# held intermediates stay good after the `+` that read them
let mid = a + "/"
let full = mid + b
if mid == "dir/" and full == "dir/file" { print(1) }
let head = full[0 .. 3]
let head2 = head + "!" + head
if mid == "dir/" and full == "dir/file" and head == "dir" and head2 == "dir!dir" { print(1) }
let n = string(-42)
let t = n + "!" + n
if n == "-42" and t == "-42!-42" { print(2) }

View file

@ -1056,7 +1056,8 @@ function emit_expr(e: Node) -> Val {
let lo = emit_expr(e.b)
let hi = emit_expr(e.c)
g_uses_strslice = true
return val(emit_bind(`call ptr @lp_str_slice(ptr {base.code}, i32 {lo.code}, i32 {hi.code})`), "string")
# a copy, so it is fresh: `s[0 .. n - 4] + ".dds"` and `s[a .. b] == "x"` free it once read
return fresh_val(emit_bind(`call ptr @lp_str_slice(ptr {base.code}, i32 {lo.code}, i32 {hi.code})`), "string")
}
if e.kind == E_STR { return val(emit_str_const(e.s), "string") }
if e.kind == E_NEW {

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff