compiler: Require unsafe permission for raw slice iteration
3aa3f740dc5bf3ed38882502a3a60c0cd1fd290c4e3958e3b2b935d470e9709c
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 | + | } |