diff --git a/compiler/src/CODE-LOGIC.md b/compiler/src/CODE-LOGIC.md index 2cfc085..21b5877 100644 --- a/compiler/src/CODE-LOGIC.md +++ b/compiler/src/CODE-LOGIC.md @@ -140,8 +140,12 @@ so a Text-typed SINGLE-SEGMENT interpolation of a place register through a `let`/assignment boundary uncopied — the binding aliased the row's field and its overwrite freed it (release-build crash the arena hid from ASan). It now asks `is_borrowed_value_t && not is_container_read`, -exactly `drop_fresh_text`'s place test. Pinned by -`tests/corpus/run/interp-borrowed-field`. +exactly `drop_fresh_text`'s place test. The RETURN boundary had the same +hole (`return "${p.content}"` handed the caller the part's own string — +the multipart slice's arena corruption, two requests removed from the +crash): emit_return's place test now sees through `Interp` the same way, +while bare Ident/Field/Index behavior there is unchanged. Both flavors +pinned by `tests/corpus/run/interp-borrowed-field`. Two rules the measurements imposed, both easy to get backwards: diff --git a/compiler/src/emit.ml b/compiler/src/emit.ml index 58971d8..4a28fac 100644 --- a/compiler/src/emit.ml +++ b/compiler/src/emit.ml @@ -3756,7 +3756,16 @@ and emit_return (p : pctx) (f : fstate) (v : views) (s : Ast.stmt) (opt : Ast.ex `return lv` inside `for lv in [...]` is WO-E304 and the workload's own level classifier cannot be written at all. *) let t = - let is_place = match e.Ast.kind with Ast.Ident _ | Ast.Field _ | Ast.Index _ -> true | _ -> false in + (* an interpolation is seen through exactly as copy_place_text sees + it: `return "${p.content}"` (p a loop borrow) handed the caller the + part's own string — the caller's drop then freed it under the + container, the multipart-400 arena corruption *) + let is_place = + match e.Ast.kind with + | Ast.Ident _ | Ast.Field _ | Ast.Index _ -> true + | Ast.Interp _ -> is_borrowed_value_t p f e && not (is_container_read e) + | _ -> false + in let is_text = match ty_of_expr p f e with Some ty -> field_kind p ty = 3 | None -> false in if is_place && is_text then begin let w = alloc_temps p f e.pos 1 in diff --git a/tests/corpus/run/interp-borrowed-field/fixture.wo b/tests/corpus/run/interp-borrowed-field/fixture.wo index 33779ef..d7635ca 100644 --- a/tests/corpus/run/interp-borrowed-field/fixture.wo +++ b/tests/corpus/run/interp-borrowed-field/fixture.wo @@ -16,6 +16,15 @@ class Tab { } return out; } + + -- the RETURN flavor: an interpolation of a borrowed field handed straight + -- to the caller must be the caller's own copy, not the row's string + fn first() -> Text { + for r in self.rows { + return "${r.method}"; + } + return ""; + } } fn main() -> Int { @@ -26,6 +35,8 @@ fn main() -> Int { while i < 50 { let s = t.scan(); if s != "GET, POST" { print("bad: ${s} at ${i}"); return 1; } + let h = t.first(); + if h != "GET" { print("bad first: ${h} at ${i}"); return 1; } i = i + 1; } print("ok");