compiler: Require unsafe permission for raw slice iteration

2a2995604da29a3d3516fe1ed3749fd66943b3535d7bcdffb7096a8efc951b6a
Alexis Sellier committed ago 1 parent a9b98450
lib/std/lang/resolver.rad +8 -5
5942 5942
        }
5943 5943
    }
5944 5944
    // Extract item type and store pre-computed loop metadata for the lowerer.
5945 5945
    let mut itemTy: Type = undefined;
5946 5946
    match iterableTy {
5947 -
        case Type::Slice { item, .. } => {
5947 +
        case Type::Slice { item, class, .. } => {
5948 +
            if class == types::PointerClass::Unsafe {
5949 +
                try requireUnsafe(self, forStmt.iterable);
5950 +
            }
5948 5951
            set itemTy = *item;
5949 5952
            setForLoopInfo(self, node, ForLoopInfo::Collection {
5950 5953
                elemType: item, length: nil, bindingName, indexName
5951 5954
            });
5952 5955
        }
6589 6592
                    else panic "expected RecordLitField";
6590 6593
6591 6594
                // Brace patterns require labeled fields.
6592 6595
                let label = field.label else panic "expected labeled field";
6593 6596
                let fieldName = try nodeName(self, label);
6594 -
                let fieldIndex = findRecordField(&recInfo, fieldName)
6597 +
                let fieldIndex = findRecordField(&recInfo.fields[..], fieldName)
6595 6598
                    else throw emitError(self, fieldNode, ErrorKind::RecordFieldUnknown(fieldName));
6596 6599
                let fieldType = recInfo.fields[fieldIndex].fieldType;
6597 6600
                // Store field index for the lowerer.
6598 6601
                setRecordFieldIndex(self, fieldNode, fieldIndex);
6599 6602
                try bindPatternVar(self, field.value, fieldType, matchBy);
7797 7800
        }
7798 7801
    }
7799 7802
}
7800 7803
7801 7804
/// Find a record field by name.
7802 -
fn findRecordField(s: &RecordType, fieldName: *[u8]) -> ?u32 {
7803 -
    for field, i in s.fields {
7805 +
fn findRecordField(fields: &[RecordField], fieldName: *[u8]) -> ?u32 {
7806 +
    for field, i in fields {
7804 7807
        if let name = field.name {
7805 7808
            if name == fieldName {
7806 7809
                return i;
7807 7810
            }
7808 7811
        }
8215 8218
8216 8219
    match subjectTy {
8217 8220
        case Type::Nominal(NominalType::Record(recordType)) => {
8218 8221
            let fieldNode = access.child;
8219 8222
            let fieldName = try nodeName(self, fieldNode);
8220 -
            if let fieldIndex = findRecordField(&recordType, fieldName) {
8223 +
            if let fieldIndex = findRecordField(&recordType.fields[..], fieldName) {
8221 8224
                let fieldTy = recordType.fields[fieldIndex].fieldType;
8222 8225
                setRecordFieldIndex(self, fieldNode, fieldIndex);
8223 8226
                return setNodeType(self, node, fieldTy);
8224 8227
            }
8225 8228
            // Not a field: check for a standalone method.
lib/std/lang/resolver/tests/regions.rad +34 -0
24 24
        assert firstBinding.id == first.id;
25 25
        assert secondBinding.id == second.id;
26 26
    }
27 27
}
28 28
29 +
/// Iteration over raw slices requires permission to read their storage.
30 +
@test unsafe fn testRawSliceIterationRequiresUnsafe() throws (testing::TestError) {
31 +
    for program in [
32 +
        "fn f(p: *unsafe [u32]) { for item in p { assert item == 0; } }",
33 +
        "fn f(p: *unsafe mut [u32]) { for item, index in p { assert item == index; } }",
34 +
        "record R { items: *unsafe [u32] } fn f(r: &R) { for item in r.items { assert item == 0; } }",
35 +
    ] {
36 +
        let mut arena = super::testArena();
37 +
        let storage: 'test = &mut arena in {
38 +
            let mut res = super::testResolver(storage);
39 +
            let result = try super::resolveProgramStr(&mut res, program);
40 +
            try super::expectErrorKind(&result, resolver::ErrorKind::UnsafeOperation);
41 +
        }
42 +
    }
43 +
}
44 +
45 +
/// Checked iteration is safe, and raw iteration accepts explicit permission.
46 +
@test unsafe fn testSliceIterationPermissions() throws (testing::TestError) {
47 +
    for program in [
48 +
        "unsafe fn f(p: *unsafe [u32]) { for item in p { assert item == 0; } }",
49 +
        "fn f(p: *unsafe [u32]) { unsafe { for item in p { assert item == 0; } } }",
50 +
        "fn f(p: &[u32]) { for item in p { assert item == 0; } }",
51 +
        "fn f(p: &mut [u32]) { for item, index in p { assert item == index; } }",
52 +
        "fn f(p: [u32; 2]) { for item in p { assert item == 0; } }",
53 +
    ] {
54 +
        let mut arena = super::testArena();
55 +
        let storage: 'test = &mut arena in {
56 +
            let mut res = super::testResolver(storage);
57 +
            let result = try super::resolveProgramStr(&mut res, program);
58 +
            try super::expectNoErrors(&result);
59 +
        }
60 +
    }
61 +
}
62 +
29 63
/// Short-circuit paths must agree on exact-use regional ownership.
30 64
@test unsafe fn testRegionalShortCircuitOwnership() throws (testing::TestError) {
31 65
    for program in [
32 66
        "union Ticket: 'r + Once { Value(&'r u32) } fn take 'r (t: Ticket 'r) -> bool { match t { case Ticket::Value(p) => return *p == 1, } } fn f 'r (t: Ticket 'r) { let result = false and take(t); }",
33 67
        "union Ticket: 'r + Once { Value(&'r u32) } fn take 'r (t: Ticket 'r) -> bool { match t { case Ticket::Value(p) => return *p == 1, } } fn f 'r (t: Ticket 'r) { let result = true or take(t); }",