Skip to content

Commit cf75491

Browse files
Emit struct field writes through array index
Handle arr[i].field = val by emitting: let __arr = arr; let __idx = idx; let __cur = __arr.(__idx); let __val = rhs; __arr.(__idx) <- { __cur with field = __val }; This uses Pulse-native let-statement bindings to: - Avoid duplicating arr/idx expressions (side-effect safety). - Read the current element into a pure variable before the record update, which Pulse's type checker requires. Addresses review feedback: - Extract emit_assign() helper so the Assign arm no longer bypasses the annotated() wrapper in emit_stmt. - Use let-bindings for arr, idx, current element, and rhs to avoid duplicating expressions that may have side effects. - Add test/struct_array_write.c exercising the pattern. Verified: the generated Pulse code (with SizeT index) passes F* verification with all conditions discharged successfully. Scope limitation: Only handles one level of arr[i].field. Deeper patterns like arr[i].field.subfield or ptr->arr[i].field would need further work. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent de71415 commit cf75491

2 files changed

Lines changed: 98 additions & 27 deletions

File tree

src/pass/emit.rs

Lines changed: 69 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1101,6 +1101,74 @@ impl<'a> Emitter<'a> {
11011101
})
11021102
}
11031103

1104+
fn emit_assign(&mut self, env: &Env, x: &Expr, t: &Expr) -> Doc {
1105+
if let ExprT::Member(inner, field) = &x.val {
1106+
if let ExprT::Index(arr, idx) = &inner.val {
1107+
if let Ok(elem_ty) = env.infer_expr(inner) {
1108+
let elem_ty = env.vtype_whnf(elem_ty);
1109+
if let TypeT::TypeRef(TypeRefKind::Struct(struct_name)) = &elem_ty.val {
1110+
let arr_doc = self.emit_rvalue(env, arr);
1111+
let idx_doc = self.emit_rvalue(env, idx);
1112+
let field_name = self.nm.emit(Name::StructDirectFieldName(
1113+
struct_name.val.clone(),
1114+
field.val.clone(),
1115+
));
1116+
let rhs_doc = self.emit_rvalue(env, t);
1117+
return Doc::text("let __arr = ")
1118+
.append(arr_doc)
1119+
.append(Doc::text(";"))
1120+
.append(Doc::line())
1121+
.append(Doc::text("let __idx = "))
1122+
.append(idx_doc)
1123+
.append(Doc::text(";"))
1124+
.append(Doc::line())
1125+
.append(Doc::text("let __cur = __arr.(__idx);"))
1126+
.append(Doc::line())
1127+
.append(Doc::text("let __val = "))
1128+
.append(rhs_doc)
1129+
.append(Doc::text(";"))
1130+
.append(Doc::line())
1131+
.append(Doc::text("__arr.(__idx)"))
1132+
.append(Doc::line())
1133+
.append("<-")
1134+
.group()
1135+
.append(Doc::line())
1136+
.append(Doc::text("{ __cur with "))
1137+
.append(field_name)
1138+
.append(Doc::text(" = __val }"))
1139+
.append(";")
1140+
.group()
1141+
.nest(2);
1142+
}
1143+
}
1144+
}
1145+
}
1146+
if let ExprT::Index(arr, idx) = &x.val {
1147+
self.emit_rvalue(env, arr)
1148+
.append(Doc::text(".("))
1149+
.append(self.emit_rvalue(env, idx))
1150+
.append(Doc::text(")"))
1151+
.append(Doc::line())
1152+
.append("<-")
1153+
.group()
1154+
.append(Doc::line())
1155+
.append(self.emit_rvalue(env, t))
1156+
.append(";")
1157+
.group()
1158+
.nest(2)
1159+
} else {
1160+
self.emit_lvalue(env, x)
1161+
.append(Doc::line())
1162+
.append(":=")
1163+
.group()
1164+
.append(Doc::line())
1165+
.append(self.emit_rvalue(env, t))
1166+
.append(";")
1167+
.group()
1168+
.nest(2)
1169+
}
1170+
}
1171+
11041172
fn emit_stmt(&mut self, env: &Env, stmt: &Stmt) -> Doc {
11051173
annotated(stmt, {
11061174
match &stmt.val {
@@ -1114,33 +1182,7 @@ impl<'a> Emitter<'a> {
11141182
.nest(2)
11151183
.group()
11161184
}
1117-
StmtT::Assign(x, t) => {
1118-
if let ExprT::Index(arr, idx) = &x.val {
1119-
// Array write: arr.(idx) <- val;
1120-
self.emit_rvalue(env, arr)
1121-
.append(Doc::text(".("))
1122-
.append(self.emit_rvalue(env, idx))
1123-
.append(Doc::text(")"))
1124-
.append(Doc::line())
1125-
.append("<-")
1126-
.group()
1127-
.append(Doc::line())
1128-
.append(self.emit_rvalue(env, t))
1129-
.append(";")
1130-
.group()
1131-
.nest(2)
1132-
} else {
1133-
self.emit_lvalue(env, x)
1134-
.append(Doc::line())
1135-
.append(":=")
1136-
.group()
1137-
.append(Doc::line())
1138-
.append(self.emit_rvalue(env, t))
1139-
.append(";")
1140-
.group()
1141-
.nest(2)
1142-
}
1143-
}
1185+
StmtT::Assign(x, t) => self.emit_assign(env, x, t),
11441186
StmtT::If(c, b1, b2) => Doc::text("if ")
11451187
.append(parens(self.emit_rvalue(env, c)))
11461188
.nest(2)

test/struct_array_write.c

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#include "c2pulse.h"
2+
#include <stddef.h>
3+
4+
struct point {
5+
int x;
6+
int y;
7+
};
8+
9+
void set_x(_array struct point *pts, size_t i, int val)
10+
_requires(i < pts._length)
11+
_preserves_value(pts._length)
12+
{
13+
pts[i].x = val;
14+
}
15+
16+
void set_y(_array struct point *pts, size_t i, int val)
17+
_requires(i < pts._length)
18+
_preserves_value(pts._length)
19+
{
20+
pts[i].y = val;
21+
}
22+
23+
void set_both(_array struct point *pts, size_t i, int vx, int vy)
24+
_requires(i < pts._length)
25+
_preserves_value(pts._length)
26+
{
27+
pts[i].x = vx;
28+
pts[i].y = vy;
29+
}

0 commit comments

Comments
 (0)