Skip to content

Commit 1df5c48

Browse files
committed
fix(lower): don't double-borrow a read-PARAM passed as a read argument
A read-PARAM already lowers to &T, but the read-arg lowering added another & -> &&T, producing ill-typed Rust (E0308) when e.g. pushing a read-param into an owned collection (list_push(&mut v, &&node)) or building a recursive node from read children. Mirror the existing mut-param case: when the read value is an ident bound to a read param, emit the ident directly (already &T). Also generalized the managed_handle_arg retain arms via lower_managed_read_ref. Unblocks ergonomic recursive-IR/graph code. Regression fixture: tests/fixtures/pass/read-param-into-owned-collection.rss. cargo test green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent f49ac9a commit 1df5c48

2 files changed

Lines changed: 47 additions & 2 deletions

File tree

crates/rsscript/src/rust_lower/lowerer.rs

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3061,7 +3061,7 @@ impl<'a> RustLowerer<'a> {
30613061
&& self.expr_lowers_to_managed_handle(value)
30623062
{
30633063
return match effect {
3064-
DataEffect::Read => format!("&{}", self.lower_expr(value)),
3064+
DataEffect::Read => self.lower_managed_read_ref(value),
30653065
DataEffect::Mut => format!("&mut {}", self.lower_expr(value)),
30663066
DataEffect::Take => self.lower_expr(value),
30673067
};
@@ -3072,7 +3072,7 @@ impl<'a> RustLowerer<'a> {
30723072
&& self.expr_lowers_to_managed_handle(value)
30733073
{
30743074
return match effect {
3075-
DataEffect::Read => format!("&{}", self.lower_expr(value)),
3075+
DataEffect::Read => self.lower_managed_read_ref(value),
30763076
DataEffect::Mut => format!("&mut {}", self.lower_expr(value)),
30773077
DataEffect::Take => self.lower_expr(value),
30783078
};
@@ -3149,6 +3149,14 @@ impl<'a> RustLowerer<'a> {
31493149
&& matches!(value.as_ref(), Expr::ArrayLiteral { .. }))
31503150
{
31513151
format!("&{}", self.lower_expr_for_expected_type(value, expected))
3152+
} else if let Expr::Ident(name, _) = value.as_ref()
3153+
&& self.param_effects.get(name) == Some(&DataEffect::Read)
3154+
{
3155+
// A `read`-PARAM already lowers to `&T`; passing it on as a `read`
3156+
// argument must NOT add another `&` (that produced `&&T` and an
3157+
// ill-typed call, e.g. `list_push(&mut v, &&node)`). Mirrors the
3158+
// `mut`-param case just below.
3159+
rust_value_ident(name)
31523160
} else {
31533161
format!("&{}", self.lower_expr(value))
31543162
}
@@ -3349,6 +3357,20 @@ impl<'a> RustLowerer<'a> {
33493357
}
33503358
}
33513359

3360+
// Lower a `read`-effect managed-handle argument to a `&T`. A managed `let` local lowers to
3361+
// an owned `T`, so it needs a leading `&`. A managed `read`-PARAM already lowers to `&T`
3362+
// (its Rust param type is a reference), so adding another `&` produced `&&T` and an
3363+
// ill-typed push into an owned collection (RS1101/RS1102 E0308). Detect the already-ref
3364+
// case and don't double-borrow.
3365+
fn lower_managed_read_ref(&mut self, value: &Expr) -> String {
3366+
if let Expr::Ident(name, _) = value
3367+
&& matches!(self.param_effects.get(name), Some(DataEffect::Read))
3368+
{
3369+
return self.lower_expr(value);
3370+
}
3371+
format!("&{}", self.lower_expr(value))
3372+
}
3373+
33523374
fn call_arg_is_retained(&self, callee: &Callee, arg: &CallArg, _index: usize) -> bool {
33533375
let Some(name) = arg.name.as_deref() else {
33543376
return false;
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Regression: a `read`-PARAM managed value pushed into an owned collection (or passed on as
2+
// a `read` arg) must not be double-borrowed. Before the fix this emitted `&&T` -> E0308.
3+
features: local
4+
5+
struct Node derives(Clone, Eq, Hash) {
6+
tag: Int
7+
src: List<Node>
8+
}
9+
10+
fn leaf(tag: Int) -> fresh Node { return Node(tag: tag, src: List.new<Node>()) }
11+
12+
fn bin(a: read Node, b: read Node) -> fresh Node {
13+
local s = List.new<Node>()
14+
List.push(list: mut s, value: read a)
15+
List.push(list: mut s, value: read b)
16+
return Node(tag: 0, src: take s)
17+
}
18+
19+
fn main() -> Unit {
20+
let p = bin(a: read leaf(tag: 1), b: read leaf(tag: 2))
21+
Log.write(message: read Int.to_string(value: read List.len(list: read p.src)))
22+
return Unit
23+
}

0 commit comments

Comments
 (0)