diff --git a/self-host/ir_driver b/self-host/ir_driver index 178804c..21bc172 100755 Binary files a/self-host/ir_driver and b/self-host/ir_driver differ diff --git a/self-host/ir_emitter_core.pith b/self-host/ir_emitter_core.pith index d103fe3..36dae52 100644 --- a/self-host/ir_emitter_core.pith +++ b/self-host/ir_emitter_core.pith @@ -3897,11 +3897,53 @@ fn ir_emit_assign_stmt(node: Node): ir_var_regs.insert(name, r) ir_emit("store " + ir_resolve_storage_name(name) + " " + r.to_string()) +# Assign to a struct field target: `obj.field = value`. Returns false if the +# target is not a known struct field (so the caller falls back to other forms). +fn ir_emit_field_assignment(target_idx: Int, value_idx: Int) -> Bool: + target := get_node(target_idx) + if target.kind != "field_access" or target.children.len() == 0: + return false + fname := node_value(target_idx) + recv_idx := target.children[0] + lookup_key := ir_field_lookup_key(recv_idx, fname) + if not ir_struct_field_index_lookup.contains_key(lookup_key): + return false + field_index := ir_unpack_field_index(ir_struct_field_index_lookup[lookup_key]) + obj_r := ir_expr(recv_idx) + mut value_type := ir_checked_struct_field_type(recv_idx, fname) + if value_type.len() == 0: + value_type = ir_infer_type(value_idx) + value_r := ir_emit_value_for_target(value_idx, value_type) + ir_emit("sstore " + obj_r.to_string() + " " + field_index.to_string() + " " + value_r.to_string()) + return true + +# Compound assign to a struct field: `obj.field += value` (and -=, *=, /=). +fn ir_emit_field_compound_assignment(target_idx: Int, value_idx: Int, compound_op: String) -> Bool: + target := get_node(target_idx) + if target.kind != "field_access" or target.children.len() == 0: + return false + fname := node_value(target_idx) + recv_idx := target.children[0] + lookup_key := ir_field_lookup_key(recv_idx, fname) + if not ir_struct_field_index_lookup.contains_key(lookup_key): + return false + field_index := ir_unpack_field_index(ir_struct_field_index_lookup[lookup_key]) + obj_r := ir_expr(recv_idx) + cur := ir_reg() + ir_emit_named_field_access(cur, obj_r, lookup_key, fname, ir_checked_struct_field_type(recv_idx, fname)) + rhs := ir_expr(value_idx) + res := ir_reg() + ir_emit_simple_binary(res, compound_op, cur, rhs) + ir_emit("sstore " + obj_r.to_string() + " " + field_index.to_string() + " " + res.to_string()) + return true + fn ir_emit_compound_assignment(node: Node): op := node.value if op == "=" and node.children.len() >= 2: if ir_emit_index_assignment(node.children[0], node.children[1]): return + if ir_emit_field_assignment(node.children[0], node.children[1]): + return tn := get_node(node.children[0]) name := tn.value target_type := ir_checked_target_type(node.children[0], name) @@ -3910,6 +3952,8 @@ fn ir_emit_compound_assignment(node: Node): return compound_op := ir_compound_assignment_ir_op(op) if compound_op.len() > 0 and node.children.len() >= 2: + if ir_emit_field_compound_assignment(node.children[0], node.children[1], compound_op): + return tn := get_node(node.children[0]) name := tn.value cur := ir_reg() diff --git a/tests/cases/test_struct_field_assign.pith b/tests/cases/test_struct_field_assign.pith new file mode 100644 index 0000000..83832e7 --- /dev/null +++ b/tests/cases/test_struct_field_assign.pith @@ -0,0 +1,45 @@ +# assigning to struct fields: locals, compound ops, through functions +# (reference semantics), and from methods via implicit self + +struct Counter: + value: Int + label: String + +struct Point: + x: Int + y: Int + +impl Counter: + fn step(): + self.value = self.value + 1 + + fn add(n: Int): + self.value += n + +fn bump(c: Counter): + c.value = c.value + 100 + +fn main(): + mut c := Counter(5, "hits") + c.value = 10 + print("set: {c.value}") + c.value += 3 + print("compound: {c.value}") + c.label = "changed" + print("label: {c.label}") + + # mutation through a function persists (structs are shared handles) + bump(c) + print("after bump: {c.value}") + + # mutation through methods (implicit self) + c.step() + c.step() + print("after steps: {c.value}") + c.add(20) + print("after add: {c.value}") + + p := Point(1, 2) + p.x = 42 + p.y = p.x + 1 + print("point: {p.x},{p.y}") diff --git a/tests/expected/test_struct_field_assign.txt b/tests/expected/test_struct_field_assign.txt new file mode 100644 index 0000000..0e074f8 --- /dev/null +++ b/tests/expected/test_struct_field_assign.txt @@ -0,0 +1,7 @@ +set: 10 +compound: 13 +label: changed +after bump: 113 +after steps: 115 +after add: 135 +point: 42,43