compiler: Require unsafe permission for raw slice iteration
4f712f07d2684b5cd03ab08f0a5d323876ab6965aaeaff2fb0d3c7d84507f406
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 | + |