compiler: Require unsafe permission for raw slice iteration
2a2995604da29a3d3516fe1ed3749fd66943b3535d7bcdffb7096a8efc951b6a
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); }", |