compiler: Require unsafe permission for raw slice iteration

4f712f07d2684b5cd03ab08f0a5d323876ab6965aaeaff2fb0d3c7d84507f406
Alexis Sellier committed ago 1 parent b06316b7
lib/std/lang/resolver.rad +8 -5
4718 4718
        }
4719 4719
    }
4720 4720
    // Extract item type and store pre-computed loop metadata for the lowerer.
4721 4721
    let mut itemTy: Type = undefined;
4722 4722
    match iterableTy {
4723 -
        case Type::Slice { item, .. } => {
4723 +
        case Type::Slice { item, class, .. } => {
4724 +
            if class == types::PointerClass::Unsafe {
4725 +
                try requireUnsafe(self, forStmt.iterable);
4726 +
            }
4724 4727
            set itemTy = *item;
4725 4728
            setForLoopInfo(self, node, ForLoopInfo::Collection {
4726 4729
                elemType: item, length: nil, bindingName, indexName
4727 4730
            });
4728 4731
        }
5342 5345
                    else panic "expected RecordLitField";
5343 5346
5344 5347
                // Brace patterns require labeled fields.
5345 5348
                let label = field.label else panic "expected labeled field";
5346 5349
                let fieldName = try nodeName(self, label);
5347 -
                let fieldIndex = findRecordField(&recInfo, fieldName)
5350 +
                let fieldIndex = findRecordField(&recInfo.fields[..], fieldName)
5348 5351
                    else throw emitError(self, fieldNode, ErrorKind::RecordFieldUnknown(fieldName));
5349 5352
                let fieldType = recInfo.fields[fieldIndex].fieldType;
5350 5353
                // Store field index for the lowerer.
5351 5354
                setRecordFieldIndex(self, fieldNode, fieldIndex);
5352 5355
                try bindPatternVar(self, field.value, fieldType, matchBy);
5938 5941
        }
5939 5942
    }
5940 5943
}
5941 5944
5942 5945
/// Find a record field by name.
5943 -
fn findRecordField(s: &RecordType, fieldName: *[u8]) -> ?u32 {
5944 -
    for field, i in s.fields {
5946 +
fn findRecordField(fields: &[RecordField], fieldName: *[u8]) -> ?u32 {
5947 +
    for field, i in fields {
5945 5948
        if let name = field.name {
5946 5949
            if name == fieldName {
5947 5950
                return i;
5948 5951
            }
5949 5952
        }
6332 6335
6333 6336
    match subjectTy {
6334 6337
        case Type::Nominal(NominalType::Record(recordType)) => {
6335 6338
            let fieldNode = access.child;
6336 6339
            let fieldName = try nodeName(self, fieldNode);
6337 -
            if let fieldIndex = findRecordField(&recordType, fieldName) {
6340 +
            if let fieldIndex = findRecordField(&recordType.fields[..], fieldName) {
6338 6341
                let fieldTy = recordType.fields[fieldIndex].fieldType;
6339 6342
                setRecordFieldIndex(self, fieldNode, fieldIndex);
6340 6343
                return setNodeType(self, node, fieldTy);
6341 6344
            }
6342 6345
            // Not a field: check for a standalone method.
lib/std/lang/resolver/tests.rad +29 -0
6365 6365
    let mut res = testResolver();
6366 6366
    let result = try resolveProgramStr(&mut res,
6367 6367
        "unsafe fn f(x: u32) {} fn g() { f(1, 2); }");
6368 6368
    try expectErrorKind(&result, super::ErrorKind::UnsafeCall);
6369 6369
}
6370 +
6371 +
/// Iteration over raw slices requires permission to read their storage.
6372 +
@test unsafe fn testRawSliceIterationRequiresUnsafe() throws (testing::TestError) {
6373 +
    for program in [
6374 +
        "fn f(p: *unsafe [u32]) { for item in p { assert item == 0; } }",
6375 +
        "fn f(p: *unsafe mut [u32]) { for item, index in p { assert item == index; } }",
6376 +
        "record R { items: *unsafe [u32] } fn f(r: &R) { for item in r.items { assert item == 0; } }",
6377 +
    ] {
6378 +
        let mut res = testResolver();
6379 +
        let result = try resolveProgramStr(&mut res, program);
6380 +
        try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
6381 +
    }
6382 +
}
6383 +
6384 +
/// Checked iteration is safe, and raw iteration accepts explicit permission.
6385 +
@test unsafe fn testSliceIterationPermissions() throws (testing::TestError) {
6386 +
    for program in [
6387 +
        "unsafe fn f(p: *unsafe [u32]) { for item in p { assert item == 0; } }",
6388 +
        "fn f(p: *unsafe [u32]) { unsafe { for item in p { assert item == 0; } } }",
6389 +
        "fn f(p: &[u32]) { for item in p { assert item == 0; } }",
6390 +
        "fn f(p: &mut [u32]) { for item, index in p { assert item == index; } }",
6391 +
        "fn f(p: [u32; 2]) { for item in p { assert item == 0; } }",
6392 +
    ] {
6393 +
        let mut res = testResolver();
6394 +
        let result = try resolveProgramStr(&mut res, program);
6395 +
        try expectNoErrors(&result);
6396 +
    }
6397 +
}
6398 +