diff options
Diffstat (limited to 'tvix/eval')
3 files changed, 40 insertions, 18 deletions
diff --git a/tvix/eval/src/compiler/mod.rs b/tvix/eval/src/compiler/mod.rs index 3830c01712f2..a624cf11c74d 100644 --- a/tvix/eval/src/compiler/mod.rs +++ b/tvix/eval/src/compiler/mod.rs @@ -258,34 +258,48 @@ impl Compiler<'_, '_> { } fn compile_str(&mut self, slot: LocalIdx, node: ast::Str) { - // TODO: thunk string construction if it is not a literal - let mut count = 0; + let parts = node.normalized_parts(); + let count = parts.len(); - // The string parts are produced in literal order, however - // they need to be reversed on the stack in order to - // efficiently create the real string in case of - // interpolation. - for part in node.normalized_parts().into_iter().rev() { - count += 1; + if count != 1 { + self.thunk(slot, &node, |c, n, s| { + // The string parts are produced in literal order, however + // they need to be reversed on the stack in order to + // efficiently create the real string in case of + // interpolation. + for part in parts.into_iter().rev() { + match part { + // Interpolated expressions are compiled as normal and + // dealt with by the VM before being assembled into + // the final string. We need to force them here, + // so OpInterpolate definitely has a string to consume. + // TODO(sterni): coerce to string + ast::InterpolPart::Interpolation(ipol) => { + c.compile(s, ipol.expr().unwrap()); + c.emit_force(&ipol); + } + + ast::InterpolPart::Literal(lit) => { + c.emit_constant(Value::String(lit.into()), n); + } + } + } - match part { - // Interpolated expressions are compiled as normal and - // dealt with by the VM before being assembled into - // the final string. + c.push_op(OpCode::OpInterpolate(Count(count)), n); + }); + } else { + match &parts[0] { + // Since we only have a single part, it is okay if this yields a thunk + // TODO(sterni): coerce to string ast::InterpolPart::Interpolation(node) => { self.compile(slot, node.expr().unwrap()); - self.emit_force(&node); } ast::InterpolPart::Literal(lit) => { - self.emit_constant(Value::String(lit.into()), &node); + self.emit_constant(Value::String(lit.as_str().into()), &node); } } } - - if count != 1 { - self.push_op(OpCode::OpInterpolate(Count(count)), &node); - } } fn compile_unary_op(&mut self, slot: LocalIdx, op: ast::UnaryOp) { diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-thunked-string-interpolation.exp b/tvix/eval/src/tests/tvix_tests/eval-okay-thunked-string-interpolation.exp new file mode 100644 index 000000000000..fc2f21e9305c --- /dev/null +++ b/tvix/eval/src/tests/tvix_tests/eval-okay-thunked-string-interpolation.exp @@ -0,0 +1 @@ +"strict literal" diff --git a/tvix/eval/src/tests/tvix_tests/eval-okay-thunked-string-interpolation.nix b/tvix/eval/src/tests/tvix_tests/eval-okay-thunked-string-interpolation.nix new file mode 100644 index 000000000000..bd3555bb2412 --- /dev/null +++ b/tvix/eval/src/tests/tvix_tests/eval-okay-thunked-string-interpolation.nix @@ -0,0 +1,7 @@ +let + final = { text = "strict literal"; inherit x y; }; + x = "lazy ${throw "interpolation"}"; + y = "${throw "also lazy!"}"; +in + +final.text |