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'.
Alexis Sellier committed ago 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 +
}