Implement deref for single-field unlabeled records
30d36e9fed91ed56db40c903ddfa3292c3af8c4a535bb0886f7494a7b029f1aa
Allow `*r` on single-field unlabeled records (e.g. `record Wrap(i32)`) as syntactic sugar for accessing the only field. Supports reading (`*r`), assignment (`*r = val`), address-of (`&(*r)`), and mutable borrows (`&mut *r`). Multi-field and labeled records still correctly reject dereference with 'expected pointer type'.
1 parent
f0e50b22
lib/std/lang/lower.rad
+4 -1
| 4945 | 4945 | ||
| 4946 | 4946 | return ElemPtrResult { elemReg, elemType }; |
|
| 4947 | 4947 | } |
|
| 4948 | 4948 | ||
| 4949 | 4949 | /// Lower a dereference expression. |
|
| 4950 | + | /// Handles both pointer deref (`*ptr`) and record deref (`*r` on single-field |
|
| 4951 | + | /// unlabeled record). Both read at offset 0 using the resolver-assigned type. |
|
| 4950 | 4952 | fn lowerDeref(self: *mut FnLowerer, node: *ast::Node, target: *ast::Node) -> il::Val throws (LowerError) { |
|
| 4951 | 4953 | let type = try typeOf(self, node); |
|
| 4952 | 4954 | let ptrVal = try lowerExpr(self, target); |
|
| 4953 | 4955 | let ptrReg = emitValToReg(self, ptrVal); |
|
| 4954 | 4956 |
| 5131 | 5133 | case ast::NodeValue::FieldAccess(access) => { |
|
| 5132 | 5134 | let fieldRef = try lowerFieldRef(self, access); |
|
| 5133 | 5135 | try emitStore(self, fieldRef.base, fieldRef.offset, fieldRef.fieldType, rhs); |
|
| 5134 | 5136 | } |
|
| 5135 | 5137 | case ast::NodeValue::Deref(target) => { |
|
| 5136 | - | // Assignment through pointer dereference: `*ptr = value`. |
|
| 5138 | + | // Assignment through dereference: `*ptr = value` or `*r = value`. |
|
| 5139 | + | // Both store at offset 0 using the resolver-assigned type. |
|
| 5137 | 5140 | let ptrVal = try lowerExpr(self, target); |
|
| 5138 | 5141 | let ptrReg = emitValToReg(self, ptrVal); |
|
| 5139 | 5142 | let targetTy = try typeOf(self, a.left); |
|
| 5140 | 5143 | try emitStore(self, ptrReg, 0, targetTy, rhs); |
|
| 5141 | 5144 | } |
lib/std/lang/resolver.rad
+15 -2
| 5718 | 5718 | return mutable; |
|
| 5719 | 5719 | } |
|
| 5720 | 5720 | if let case Type::Slice { mutable, .. } = innerTy { |
|
| 5721 | 5721 | return mutable; |
|
| 5722 | 5722 | } |
|
| 5723 | + | // Record deref: mutability depends on the inner binding. |
|
| 5724 | + | if let case Type::Nominal(NominalType::Record(recInfo)) = innerTy { |
|
| 5725 | + | if not recInfo.labeled and recInfo.fields.len == 1 { |
|
| 5726 | + | return try canBorrowMutFrom(self, inner); |
|
| 5727 | + | } |
|
| 5728 | + | } |
|
| 5723 | 5729 | return false; |
|
| 5724 | 5730 | } |
|
| 5725 | 5731 | else => { |
|
| 5726 | 5732 | return false; |
|
| 5727 | 5733 | } |
| 5824 | 5830 | // Disallow dereferencing opaque pointers. |
|
| 5825 | 5831 | if *target == Type::Opaque { |
|
| 5826 | 5832 | throw emitError(self, targetNode, ErrorKind::OpaqueTypeDeref); |
|
| 5827 | 5833 | } |
|
| 5828 | 5834 | return setNodeType(self, node, *target); |
|
| 5829 | - | } else { |
|
| 5830 | - | throw emitError(self, targetNode, ErrorKind::ExpectedPointer); |
|
| 5831 | 5835 | } |
|
| 5836 | + | // Auto-deref for single-field unlabeled records. |
|
| 5837 | + | if let case Type::Nominal(NominalType::Record(recInfo)) = operandTy { |
|
| 5838 | + | if not recInfo.labeled and recInfo.fields.len == 1 { |
|
| 5839 | + | let fieldTy = recInfo.fields[0].fieldType; |
|
| 5840 | + | setRecordFieldIndex(self, node, 0); |
|
| 5841 | + | return setNodeType(self, node, fieldTy); |
|
| 5842 | + | } |
|
| 5843 | + | } |
|
| 5844 | + | throw emitError(self, targetNode, ErrorKind::ExpectedPointer); |
|
| 5832 | 5845 | } |
|
| 5833 | 5846 | ||
| 5834 | 5847 | /// Check if a type is a pointer to opaque. |
|
| 5835 | 5848 | fn isOpaquePointer(ty: Type) -> bool { |
|
| 5836 | 5849 | if let case Type::Pointer { target, .. } = ty { |
test/tests/record.unlabeled.deref.rad
added
+82 -0
| 1 | + | //! returns: 0 |
|
| 2 | + | // Test deref behavior for single-field unlabeled records. |
|
| 3 | + | ||
| 4 | + | record Wrap(i32); |
|
| 5 | + | record WrapBool(bool); |
|
| 6 | + | record WrapU8(u8); |
|
| 7 | + | record WrapU64(u64); |
|
| 8 | + | ||
| 9 | + | fn getInner(w: Wrap) -> i32 { |
|
| 10 | + | return *w; |
|
| 11 | + | } |
|
| 12 | + | ||
| 13 | + | fn passThrough(w: Wrap) -> Wrap { |
|
| 14 | + | return Wrap(*w); |
|
| 15 | + | } |
|
| 16 | + | ||
| 17 | + | @default fn main() -> i32 { |
|
| 18 | + | let w: Wrap = Wrap(42); |
|
| 19 | + | ||
| 20 | + | // Basic deref. |
|
| 21 | + | if *w != 42 { |
|
| 22 | + | return 1; |
|
| 23 | + | } |
|
| 24 | + | ||
| 25 | + | // Deref in function call. |
|
| 26 | + | if getInner(w) != 42 { |
|
| 27 | + | return 2; |
|
| 28 | + | } |
|
| 29 | + | ||
| 30 | + | // Deref bool. |
|
| 31 | + | let b: WrapBool = WrapBool(true); |
|
| 32 | + | if *b != true { |
|
| 33 | + | return 3; |
|
| 34 | + | } |
|
| 35 | + | ||
| 36 | + | // Deref u8. |
|
| 37 | + | let u: WrapU8 = WrapU8(7); |
|
| 38 | + | if *u != 7 { |
|
| 39 | + | return 4; |
|
| 40 | + | } |
|
| 41 | + | ||
| 42 | + | // Deref in arithmetic. |
|
| 43 | + | let a: Wrap = Wrap(10); |
|
| 44 | + | let b2: Wrap = Wrap(20); |
|
| 45 | + | if *a + *b2 != 30 { |
|
| 46 | + | return 5; |
|
| 47 | + | } |
|
| 48 | + | ||
| 49 | + | // Deref and reconstruct. |
|
| 50 | + | let w2: Wrap = passThrough(w); |
|
| 51 | + | if *w2 != 42 { |
|
| 52 | + | return 6; |
|
| 53 | + | } |
|
| 54 | + | ||
| 55 | + | // Deref u64. |
|
| 56 | + | let big: WrapU64 = WrapU64(1234567890); |
|
| 57 | + | if *big != 1234567890 { |
|
| 58 | + | return 7; |
|
| 59 | + | } |
|
| 60 | + | ||
| 61 | + | // Deref in comparison. |
|
| 62 | + | let x: Wrap = Wrap(5); |
|
| 63 | + | let y: Wrap = Wrap(5); |
|
| 64 | + | if *x != *y { |
|
| 65 | + | return 8; |
|
| 66 | + | } |
|
| 67 | + | ||
| 68 | + | // Deref in let binding. |
|
| 69 | + | let val: i32 = *Wrap(99); |
|
| 70 | + | if val != 99 { |
|
| 71 | + | return 9; |
|
| 72 | + | } |
|
| 73 | + | ||
| 74 | + | // Assignment through deref on mutable binding. |
|
| 75 | + | let mut m: Wrap = Wrap(0); |
|
| 76 | + | *m = 77; |
|
| 77 | + | if *m != 77 { |
|
| 78 | + | return 10; |
|
| 79 | + | } |
|
| 80 | + | ||
| 81 | + | return 0; |
|
| 82 | + | } |