compiler: Inspect reference shapes and allocator fields safely
b72fe7367782c217edaa043b472db28e2562cdcd12bed9bb5e617e8072fb5567
1 parent
0ed82ea3
lib/std/lang/resolver.rad
+18 -14
| 2881 | 2881 | else => return false, |
|
| 2882 | 2882 | } |
|
| 2883 | 2883 | } |
|
| 2884 | 2884 | ||
| 2885 | 2885 | /// Return whether a stored type contains a reference without a named region. |
|
| 2886 | - | unsafe fn containsUnscopedRef(ty: Type) -> bool { |
|
| 2886 | + | fn containsUnscopedRef(ty: Type) -> bool { |
|
| 2887 | 2887 | if isRefType(ty) and referenceRegion(ty) == nil { |
|
| 2888 | 2888 | return true; |
|
| 2889 | 2889 | } |
|
| 2890 | 2890 | if let case Type::Pointer { target, .. } = ty { |
|
| 2891 | 2891 | return containsUnscopedRef(*target); |
| 7537 | 7537 | ||
| 7538 | 7538 | // Associate return type to call. |
|
| 7539 | 7539 | return setNodeType(self, node, *applied.returnType); |
|
| 7540 | 7540 | } |
|
| 7541 | 7541 | ||
| 7542 | - | /// Check the allocator layout and the callback ABI used by slice append. |
|
| 7543 | - | unsafe fn isSliceAllocator(ty: Type) -> bool { |
|
| 7544 | - | let case Type::Nominal(NominalType::Record(rec)) = ty else return false; |
|
| 7545 | - | if rec.fields.len <> 2 or not rec.labeled { |
|
| 7542 | + | /// Check labeled record fields against the slice allocator layout and callback ABI. |
|
| 7543 | + | fn isSliceAllocator(fields: &[RecordField]) -> bool { |
|
| 7544 | + | if fields.len <> 2 { |
|
| 7546 | 7545 | return false; |
|
| 7547 | 7546 | } |
|
| 7548 | - | let func = rec.fields[0]; |
|
| 7549 | - | let ctx = rec.fields[1]; |
|
| 7547 | + | let func = fields[0]; |
|
| 7548 | + | let ctx = fields[1]; |
|
| 7550 | 7549 | let funcName = func.name else return false; |
|
| 7551 | 7550 | let ctxName = ctx.name else return false; |
|
| 7552 | 7551 | if not mem::eq(funcName, "func") or not mem::eq(ctxName, "ctx") or |
|
| 7553 | 7552 | func.offset <> 0 or ctx.offset <> 8 |
|
| 7554 | 7553 | { |
| 7593 | 7592 | // The allocator stores its callback and context at fixed offsets. |
|
| 7594 | 7593 | let allocatorTy = try infer(self, args[1]); |
|
| 7595 | 7594 | if let case Type::Nominal(info) = allocatorTy { |
|
| 7596 | 7595 | try ensureNominalResolved(self, info, args[1]); |
|
| 7597 | 7596 | } |
|
| 7598 | - | if not isSliceAllocator(allocatorTy) { |
|
| 7597 | + | let mut validAllocator = false; |
|
| 7598 | + | if let case Type::Nominal(NominalType::Record(rec)) = allocatorTy; rec.labeled { |
|
| 7599 | + | set validAllocator = isSliceAllocator(&rec.fields[..]); |
|
| 7600 | + | } |
|
| 7601 | + | if not validAllocator { |
|
| 7599 | 7602 | throw emitError(self, args[1], ErrorKind::InvalidSliceAllocator); |
|
| 7600 | 7603 | } |
|
| 7601 | 7604 | set self.nodeData.entries[node.id].extra = NodeExtra::SliceAppend { elemType }; |
|
| 7602 | 7605 | ||
| 7603 | 7606 | // Return the parent's type so the caller can rebind: |
| 8637 | 8640 | else => return false, |
|
| 8638 | 8641 | } |
|
| 8639 | 8642 | } |
|
| 8640 | 8643 | ||
| 8641 | 8644 | /// Find the exclusive handle that owns an addressed place. |
|
| 8642 | - | unsafe fn addressOwner 'arena (self: &Resolver 'arena, node: *ast::Node) -> ?*ast::Node { |
|
| 8643 | - | let mut parent: *ast::Node = undefined; |
|
| 8645 | + | fn addressOwner 'arena (self: &Resolver 'arena, node: *ast::Node) -> ?*ast::Node { |
|
| 8646 | + | let mut parent: ?*ast::Node = nil; |
|
| 8644 | 8647 | match node.value { |
|
| 8645 | 8648 | case ast::NodeValue::Deref(target) => set parent = target, |
|
| 8646 | 8649 | case ast::NodeValue::FieldAccess(access) => set parent = access.parent, |
|
| 8647 | 8650 | case ast::NodeValue::Subscript { container, .. } => set parent = container, |
|
| 8648 | - | else => return nil, |
|
| 8651 | + | else => {} |
|
| 8649 | 8652 | } |
|
| 8650 | - | if let ty = typeFor(self, parent) { |
|
| 8653 | + | let parentNode = parent else return nil; |
|
| 8654 | + | if let ty = typeFor(self, parentNode) { |
|
| 8651 | 8655 | match ty { |
|
| 8652 | - | case Type::Pointer { mutable: true, .. }, Type::Slice { mutable: true, .. } => return parent, |
|
| 8656 | + | case Type::Pointer { mutable: true, .. }, Type::Slice { mutable: true, .. } => return parentNode, |
|
| 8653 | 8657 | else => {} |
|
| 8654 | 8658 | } |
|
| 8655 | 8659 | } |
|
| 8656 | - | return addressOwner(self, parent); |
|
| 8660 | + | return addressOwner(self, parentNode); |
|
| 8657 | 8661 | } |
|
| 8658 | 8662 | ||
| 8659 | 8663 | /// Analyze an address-of expression. |
|
| 8660 | 8664 | unsafe fn resolveAddressOf 'arena (self: &mut Resolver 'arena, node: *ast::Node, addr: ast::AddressOf, hint: Type) -> Type |
|
| 8661 | 8665 | throws (ResolveError) |
lib/std/lang/resolver/tests.rad
+3 -0
| 7842 | 7842 | } |
|
| 7843 | 7843 | ||
| 7844 | 7844 | /// Slice append requires the allocator layout and callback signature. |
|
| 7845 | 7845 | @test unsafe fn testSliceAppendAllocatorRejected() throws (testing::TestError) { |
|
| 7846 | 7846 | let programs = &[ |
|
| 7847 | + | "record A {} fn run(s: *mut [u8], a: A) { s.append(1, a); }", |
|
| 7848 | + | "record A { func: fn(*mut opaque, u32, u32) -> *mut opaque } fn run(s: *mut [u8], a: A) { s.append(1, a); }", |
|
| 7849 | + | "record A(fn(*mut opaque, u32, u32) -> *mut opaque, *mut opaque); fn run(s: *mut [u8], a: A) { s.append(1, a); }", |
|
| 7847 | 7850 | "fn run(s: *mut [u8]) { s.append(1, 0); }", |
|
| 7848 | 7851 | "record A { ctx: *mut opaque, func: fn(*mut opaque, u32, u32) -> *mut opaque } fn run(s: *mut [u8], a: A) { s.append(1, a); }", |
|
| 7849 | 7852 | "record A { func: u64, ctx: u64 } fn run(s: *mut [u8], a: A) { s.append(1, a); }", |
|
| 7850 | 7853 | "record A { func: fn(*mut opaque, u64, u32) -> *mut opaque, ctx: *mut opaque } fn run(s: *mut [u8], a: A) { s.append(1, a); }", |
|
| 7851 | 7854 | "record A { func: fn(*mut opaque, u32, u32) -> *opaque, ctx: *mut opaque } fn run(s: *mut [u8], a: A) { s.append(1, a); }", |