compiler: Construct slice range metadata in checked code

37360ae782d97c0bb80f93a5a31f46c5129b4f10335697294e6c1cea77424dc2
Alexis Sellier committed ago 1 parent b7d3a956
lib/std/lang/resolver.rad +32 -44
1924 1924
        return index;
1925 1925
    }
1926 1926
    return nil;
1927 1927
}
1928 1928
1929 -
/// Get the slice range metadata for a subscript expression with a range index.
1929 +
/// Get the range metadata for a slice borrow or range assignment.
1930 1930
export fn sliceRangeInfoFor 'arena (self: &Resolver 'arena, node: *ast::Node) -> ?SliceRangeInfo {
1931 1931
    if let case NodeExtra::SliceRange(info) = self.nodeData.entries[node.id].extra {
1932 1932
        return info;
1933 1933
    }
1934 1934
    return nil;
7745 7745
                throw emitError(self, container, ErrorKind::ImmutableBinding);
7746 7746
            }
7747 7747
            let subjectTy = autoDeref(containerTy);
7748 7748
            try checkSliceRangeIndices(self, range);
7749 7749
7750 -
            let mut item: *Type = undefined;
7751 -
            let mut capacity: ?u32 = nil;
7752 -
7753 -
            if let case Type::Slice { item: sliceItem, mutable: sliceMutable, .. } = subjectTy {
7754 -
                if not sliceMutable {
7755 -
                    throw emitError(self, container, ErrorKind::ImmutableBinding);
7756 -
                }
7757 -
                set item = sliceItem;
7758 -
            } else {
7759 -
                match subjectTy {
7760 -
                    case Type::Array(a) => {
7761 -
                        try validateArraySliceBounds(self, range, a.length, node);
7762 -
                        set item = a.item;
7763 -
                        set capacity = a.length;
7764 -
                    }
7765 -
                    else => throw emitError(self, container, ErrorKind::ExpectedIndexable),
7766 -
                }
7750 +
            let info = sliceRangeInfo(subjectTy)
7751 +
                else throw emitError(self, container, ErrorKind::ExpectedIndexable);
7752 +
            if not info.mutable {
7753 +
                throw emitError(self, container, ErrorKind::ImmutableBinding);
7767 7754
            }
7755 +
            if let capacity = info.capacity {
7756 +
                try validateArraySliceBounds(self, range, capacity, node);
7757 +
            }
7758 +
            let item = info.itemType;
7768 7759
            // RHS is either a fill value or a source slice.
7769 7760
            let rhsTy = try infer(self, assign.right);
7770 7761
            if let case Type::Slice { item: sourceItem, .. } = rhsTy {
7771 7762
                if *sourceItem <> *item {
7772 7763
                    throw emitTypeMismatch(
7777 7768
                }
7778 7769
            } else {
7779 7770
                try checkAssignable(self, assign.right, *item);
7780 7771
            }
7781 7772
            try validateRegionalStore(self, assign.left, assign.right, *item);
7782 -
            setSliceRangeInfo(self, node, SliceRangeInfo { itemType: item, mutable: true, capacity });
7773 +
            setSliceRangeInfo(self, node, info);
7783 7774
            setNodeType(self, assign.left, *item);
7784 7775
7785 7776
            return setNodeType(self, node, Type::Void);
7786 7777
        }
7787 7778
    }
7807 7798
    try validateRegionalStore(self, assign.left, assign.right, leftTy);
7808 7799
7809 7800
    return setNodeType(self, node, leftTy);
7810 7801
}
7811 7802
7803 +
/// Construct complete range metadata for an array or slice type.
7804 +
/// Callers check array place access and select the resulting borrow access.
7805 +
fn sliceRangeInfo(ty: Type) -> ?SliceRangeInfo {
7806 +
    match ty {
7807 +
        case Type::Slice { item, mutable, .. } =>
7808 +
            return SliceRangeInfo { itemType: item, mutable, capacity: nil },
7809 +
        case Type::Array(array) =>
7810 +
            return SliceRangeInfo { itemType: array.item, mutable: true, capacity: array.length },
7811 +
        else => return nil,
7812 +
    }
7813 +
}
7814 +
7812 7815
/// Ensure slice range bounds are valid `u32` values.
7813 7816
unsafe fn checkSliceRangeIndices 'arena (self: &mut Resolver 'arena, range: ast::Range) throws (ResolveError) {
7814 7817
    if let start = range.start {
7815 7818
        try checkIndex(self, start);
7816 7819
    }
8784 8787
            let containerTy = try infer(self, container);
8785 8788
            let subjectTy = autoDeref(containerTy);
8786 8789
8787 8790
            try checkSliceRangeIndices(self, range);
8788 8791
8789 -
            let mut item: *Type = undefined;
8790 -
            let mut capacity: ?u32 = nil;
8791 -
8792 -
            if let case Type::Slice { item: sliceItem, mutable: sliceMutable, .. } = subjectTy {
8793 -
                if ast::isExclusiveAddress(addr) and not sliceMutable {
8794 -
                    throw emitError(self, addr.target, ErrorKind::ImmutableBinding);
8795 -
                }
8796 -
                set item = sliceItem;
8797 -
            } else {
8798 -
                match subjectTy {
8799 -
                    case Type::Array(arrayInfo) => {
8800 -
                        try validateArraySliceBounds(self, range, arrayInfo.length, node);
8801 -
                        set item = arrayInfo.item;
8802 -
                        set capacity = arrayInfo.length;
8803 -
                    }
8804 -
                    else => {
8805 -
                        throw emitError(self, container, ErrorKind::ExpectedIndexable);
8806 -
                    }
8807 -
                }
8792 +
            let mut info = sliceRangeInfo(subjectTy)
8793 +
                else throw emitError(self, container, ErrorKind::ExpectedIndexable);
8794 +
            if ast::isExclusiveAddress(addr) and not info.mutable {
8795 +
                throw emitError(self, addr.target, ErrorKind::ImmutableBinding);
8808 8796
            }
8797 +
            if let capacity = info.capacity {
8798 +
                try validateArraySliceBounds(self, range, capacity, node);
8799 +
            }
8800 +
            set info.mutable = addr.kind == ast::AddressKind::Mutable;
8809 8801
            let class = try addressClass(self, addr.target, hint);
8810 -
            let sliceTy = Type::Slice { class, item, mutable: addr.kind == ast::AddressKind::Mutable };
8802 +
            let sliceTy = Type::Slice { class, item: info.itemType, mutable: info.mutable };
8811 8803
            let alloc = allocType(self, sliceTy);
8812 -
            setSliceRangeInfo(self, node, SliceRangeInfo {
8813 -
                itemType: item,
8814 -
                mutable: addr.kind == ast::AddressKind::Mutable,
8815 -
                capacity,
8816 -
            });
8804 +
            setSliceRangeInfo(self, node, info);
8817 8805
            setNodeType(self, addr.target, *alloc);
8818 8806
            return setNodeType(self, node, *alloc);
8819 8807
        }
8820 8808
    }
8821 8809
    // Derive a hint for the target type from the slice hint.
lib/std/lang/resolver/tests.rad +73 -0
53 53
            }
54 54
        }
55 55
    }
56 56
}
57 57
58 +
/// Slice operations retain initialized element, capacity, and access metadata.
59 +
@test unsafe fn testSliceRangeMetadata() throws (testing::TestError) {
60 +
    let mut arena = testArena();
61 +
    let storage: 'test = &mut arena in {
62 +
        let mut res = testResolver(storage);
63 +
        let result = try resolveProgramStr(&mut res,
64 +
            "fn f() { let mut xs: [u32; 4] = [1, 2, 3, 4]; set xs[1..3] = 7; let view = &mut xs[1..3]; set view[..] = 3; let shared = &view[..]; }"
65 +
        );
66 +
        try expectNoErrors(&result);
67 +
        let body = try getFnBody(&res, result.root, "f");
68 +
        for index in [1 as u32, 3] {
69 +
            let stmt = body.statements[index];
70 +
            let case ast::NodeValue::Assign(_) = stmt.value else throw testing::TestError::Failed;
71 +
            let info = super::sliceRangeInfoFor(&res, stmt) else throw testing::TestError::Failed;
72 +
            assert *info.itemType == super::Type::U32;
73 +
            assert info.mutable;
74 +
            let capacity: ?u32 = 4 if index == 1 else nil;
75 +
            assert info.capacity == capacity;
76 +
        }
77 +
        for index in [2 as u32, 4] {
78 +
            let stmt = body.statements[index];
79 +
            let case ast::NodeValue::Let(binding) = stmt.value else throw testing::TestError::Failed;
80 +
            let case ast::NodeValue::AddressOf(_) = binding.value.value else throw testing::TestError::Failed;
81 +
            let info = super::sliceRangeInfoFor(&res, binding.value) else throw testing::TestError::Failed;
82 +
            assert *info.itemType == super::Type::U32;
83 +
            assert info.mutable == (index == 2);
84 +
            let capacity: ?u32 = 4 if index == 2 else nil;
85 +
            assert info.capacity == capacity;
86 +
        }
87 +
    }
88 +
}
89 +
90 +
/// Slice assignment and borrowing enforce static bounds and source access.
91 +
@test unsafe fn testSliceRangeErrors() throws (testing::TestError) {
92 +
    for program in [
93 +
        "fn f() { let mut xs: [u32; 2] = [1, 2]; set xs[..3] = 0; }",
94 +
        "fn f() { let mut xs: [u32; 2] = [1, 2]; set xs[2..1] = 0; }",
95 +
        "fn f() { let mut xs: [u32; 2] = [1, 2]; let view = &mut xs[..3]; }",
96 +
    ] {
97 +
        let mut arena = testArena();
98 +
        let storage: 'test = &mut arena in {
99 +
            let mut res = testResolver(storage);
100 +
            let result = try resolveProgramStr(&mut res, program);
101 +
            let err = try expectError(&result);
102 +
            assert err.kind == super::ErrorKind::SliceRangeOutOfBounds, program;
103 +
        }
104 +
    }
105 +
    for program in [
106 +
        "fn f() { let mut value: u32 = 1; set value[..] = 0; }",
107 +
        "fn f() { let mut value: u32 = 1; let view = &value[..]; }",
108 +
    ] {
109 +
        let mut arena = testArena();
110 +
        let storage: 'test = &mut arena in {
111 +
            let mut res = testResolver(storage);
112 +
            let result = try resolveProgramStr(&mut res, program);
113 +
            let err = try expectError(&result);
114 +
            assert err.kind == super::ErrorKind::ExpectedIndexable, program;
115 +
        }
116 +
    }
117 +
    for program in [
118 +
        "fn f() { let xs: [u32; 2] = [1, 2]; let view = &xs[..]; set view[..] = 0; }",
119 +
        "fn f() { let xs: [u32; 2] = [1, 2]; let view = &xs[..]; let other = &mut view[..]; }",
120 +
    ] {
121 +
        let mut arena = testArena();
122 +
        let storage: 'test = &mut arena in {
123 +
            let mut res = testResolver(storage);
124 +
            let result = try resolveProgramStr(&mut res, program);
125 +
            let err = try expectError(&result);
126 +
            assert err.kind == super::ErrorKind::ImmutableBinding, program;
127 +
        }
128 +
    }
129 +
}
130 +
58 131
/// Array layouts retain element alignment for empty and populated arrays.
59 132
@test fn testArrayLayoutFromElementLayout() throws (testing::TestError) {
60 133
    for alignment in [1 as u32, 2, 4, 8, 16] {
61 134
        let item = super::Layout { size: alignment, alignment };
62 135
        for count in [0 as u32, 1, 3] {
test/tests/regions.slice.metadata.rad added +27 -0
1 +
//! returns: 0
2 +
3 +
/// Slice access retains element types and respects nested borrow regions.
4 +
@default fn main() -> u32 {
5 +
    let mut values: [u32; 4] = [1, 2, 3, 4];
6 +
    set values[1..3] = 7;
7 +
    let view: 'outer = &mut values[..] in {
8 +
        set view[1..3] = 9;
9 +
        let inner: 'inner = &mut view[1..3] in {
10 +
            set inner[..] = 5;
11 +
            set inner[1..1] = 99;
12 +
            assert inner.len == 2;
13 +
        }
14 +
        let shared: 'read = &view[..] in {
15 +
            assert shared[0] == 1;
16 +
            assert shared[1] == 5;
17 +
            assert shared[2] == 5;
18 +
            assert shared[3] == 4;
19 +
        }
20 +
    }
21 +
    assert values == [1, 5, 5, 4];
22 +
    let empty: [u32; 0] = [];
23 +
    let view: 'empty = &empty[..] in {
24 +
        assert view.len == 0;
25 +
    }
26 +
    return 0;
27 +
}