compiler: Require unsafe permission for raw slice iteration

3aa3f740dc5bf3ed38882502a3a60c0cd1fd290c4e3958e3b2b935d470e9709c
Alexis Sellier committed ago 1 parent 380a8376
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);
5955 5958
        }
5956 5959
    }
5957 5960
}
5958 5961
5959 5962
/// Find a record field by name.
5960 -
fn findRecordField(s: &RecordType, fieldName: *[u8]) -> ?u32 {
5961 -
    for field, i in s.fields {
5963 +
fn findRecordField(fields: &[RecordField], fieldName: *[u8]) -> ?u32 {
5964 +
    for field, i in fields {
5962 5965
        if let name = field.name {
5963 5966
            if name == fieldName {
5964 5967
                return i;
5965 5968
            }
5966 5969
        }
6349 6352
6350 6353
    match subjectTy {
6351 6354
        case Type::Nominal(NominalType::Record(recordType)) => {
6352 6355
            let fieldNode = access.child;
6353 6356
            let fieldName = try nodeName(self, fieldNode);
6354 -
            if let fieldIndex = findRecordField(&recordType, fieldName) {
6357 +
            if let fieldIndex = findRecordField(&recordType.fields[..], fieldName) {
6355 6358
                let fieldTy = recordType.fields[fieldIndex].fieldType;
6356 6359
                setRecordFieldIndex(self, fieldNode, fieldIndex);
6357 6360
                return setNodeType(self, node, fieldTy);
6358 6361
            }
6359 6362
            // Not a field: check for a standalone method.
lib/std/lang/resolver/tests.rad +52 -0
6365 6365
    let mut a = testResolver();
6366 6366
    let program = "union E: Copy { Full } union F: Copy { Invalid } record A { func: fn(*mut opaque, u32, u32) -> *mut opaque throws (E, F), ctx: *mut opaque } fn run(s: *mut [u8], a: A) { try s.append(1, a) catch e as E {}; }";
6367 6367
    let result = try resolveProgramStr(&mut a, program);
6368 6368
    try expectErrorKind(&result, super::ErrorKind::TryCatchNonExhaustive);
6369 6369
}
6370 +
6371 +
/// Invalid calls in function bodies retain their resolution diagnostics.
6372 +
@test unsafe fn testInvalidBodyCalls() throws (testing::TestError) {
6373 +
    for program in &[
6374 +
        "fn f(x: u32) {} fn g() { f(1, 2); }",
6375 +
        "fn f(x: u32) {} fn g() { if true { f(1, 2); } }",
6376 +
        "fn f(x: u32) {} unsafe fn g() { f(1, 2); }",
6377 +
        "fn f(x: u32) -> u32 { return x; } fn g() { f(f(1, 2)); }",
6378 +
    ] {
6379 +
        let mut res = testResolver();
6380 +
        let result = try resolveProgramStr(&mut res, program);
6381 +
        try expectErrorKind(&result, super::ErrorKind::FnArgCountMismatch(super::CountMismatch {
6382 +
            expected: 1, actual: 2,
6383 +
        }));
6384 +
    }
6385 +
}
6386 +
6387 +
/// Unsafe calls with excess arguments report the unsafe-call diagnostic.
6388 +
@test unsafe fn testInvalidUnsafeBodyCall() throws (testing::TestError) {
6389 +
    let mut res = testResolver();
6390 +
    let result = try resolveProgramStr(&mut res,
6391 +
        "unsafe fn f(x: u32) {} fn g() { f(1, 2); }");
6392 +
    try expectErrorKind(&result, super::ErrorKind::UnsafeCall);
6393 +
}
6394 +
6395 +
/// Iteration over raw slices requires permission to read their storage.
6396 +
@test unsafe fn testRawSliceIterationRequiresUnsafe() throws (testing::TestError) {
6397 +
    for program in [
6398 +
        "fn f(p: *unsafe [u32]) { for item in p { assert item == 0; } }",
6399 +
        "fn f(p: *unsafe mut [u32]) { for item, index in p { assert item == index; } }",
6400 +
        "record R { items: *unsafe [u32] } fn f(r: &R) { for item in r.items { assert item == 0; } }",
6401 +
    ] {
6402 +
        let mut res = testResolver();
6403 +
        let result = try resolveProgramStr(&mut res, program);
6404 +
        try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
6405 +
    }
6406 +
}
6407 +
6408 +
/// Checked iteration is safe, and raw iteration accepts explicit permission.
6409 +
@test unsafe fn testSliceIterationPermissions() throws (testing::TestError) {
6410 +
    for program in [
6411 +
        "unsafe fn f(p: *unsafe [u32]) { for item in p { assert item == 0; } }",
6412 +
        "fn f(p: *unsafe [u32]) { unsafe { for item in p { assert item == 0; } } }",
6413 +
        "fn f(p: &[u32]) { for item in p { assert item == 0; } }",
6414 +
        "fn f(p: &mut [u32]) { for item, index in p { assert item == index; } }",
6415 +
        "fn f(p: [u32; 2]) { for item in p { assert item == 0; } }",
6416 +
    ] {
6417 +
        let mut res = testResolver();
6418 +
        let result = try resolveProgramStr(&mut res, program);
6419 +
        try expectNoErrors(&result);
6420 +
    }
6421 +
}