compiler: Look up trait methods through checked slices
84c0637c18e601a3a109f721f7ac0772bd4611ef8b8540fabdda33add49a5001
1 parent
2a299560
lib/std/lang/lower.rad
+1 -1
| 1185 | 1185 | }; |
|
| 1186 | 1186 | let qualName = instanceMethodName(self, nil, typeName, mName); |
|
| 1187 | 1187 | let func = try lowerMethod(self, methodNode, qualName, receiverName, sig, body, functionArena) |
|
| 1188 | 1188 | else continue; |
|
| 1189 | 1189 | ||
| 1190 | - | let method = resolver::findTraitMethod(traitInfo, mName) |
|
| 1190 | + | let method = resolver::findTraitMethod(&traitInfo.methods[..], mName) |
|
| 1191 | 1191 | else panic "lowerInstanceDecl: method not found in trait"; |
|
| 1192 | 1192 | ||
| 1193 | 1193 | set state.methodNames[method.index] = qualName; |
|
| 1194 | 1194 | set state.methodNameSet[method.index] = true; |
|
| 1195 | 1195 | return func; |
lib/std/lang/resolver.rad
+14 -14
| 4868 | 4868 | setNodeType(self, name, Type::Void); |
|
| 4869 | 4869 | ||
| 4870 | 4870 | return sym; |
|
| 4871 | 4871 | } |
|
| 4872 | 4872 | ||
| 4873 | - | /// Find a trait method by name. |
|
| 4874 | - | export unsafe fn findTraitMethod(traitType: *unsafe TraitType, name: *[u8]) -> ?*unsafe TraitMethod { |
|
| 4875 | - | for i in 0..traitType.methods.len { |
|
| 4876 | - | if mem::eq(traitType.methods[i].name, name) { |
|
| 4877 | - | return &traitType.methods[i]; |
|
| 4873 | + | /// Find a trait method by name and return its resolved metadata. |
|
| 4874 | + | export fn findTraitMethod(methods: &[TraitMethod], name: *[u8]) -> ?TraitMethod { |
|
| 4875 | + | for method in methods { |
|
| 4876 | + | if mem::eq(method.name, name) { |
|
| 4877 | + | return method; |
|
| 4878 | 4878 | } |
|
| 4879 | 4879 | } |
|
| 4880 | 4880 | return nil; |
|
| 4881 | 4881 | } |
|
| 4882 | 4882 |
| 4915 | 4915 | actual: traitType.methods.len as u32 + superTrait.methods.len as u32, |
|
| 4916 | 4916 | })); |
|
| 4917 | 4917 | } |
|
| 4918 | 4918 | // Copy inherited methods into this trait's method table. |
|
| 4919 | 4919 | for inherited in superTrait.methods { |
|
| 4920 | - | if let _ = findTraitMethod(traitType, inherited.name) { |
|
| 4920 | + | if let _ = findTraitMethod(&traitType.methods[..], inherited.name) { |
|
| 4921 | 4921 | throw emitError(self, superNode, ErrorKind::DuplicateBinding(inherited.name)); |
|
| 4922 | 4922 | } |
|
| 4923 | 4923 | traitType.methods.append(TraitMethod { |
|
| 4924 | 4924 | name: inherited.name, |
|
| 4925 | 4925 | fnType: inherited.fnType, |
| 4946 | 4946 | let attrMask = resolveAttributes(attrs); |
|
| 4947 | 4947 | let previousRegions = self.regionScope; |
|
| 4948 | 4948 | set self.regionScope = try bindRegions(self, methodNode, modifiers.regions); |
|
| 4949 | 4949 | ||
| 4950 | 4950 | // Reject duplicate method names. |
|
| 4951 | - | if let _ = findTraitMethod(traitType, methodName) { |
|
| 4951 | + | if let _ = findTraitMethod(&traitType.methods[..], methodName) { |
|
| 4952 | 4952 | throw emitError(self, name, ErrorKind::DuplicateBinding(methodName)); |
|
| 4953 | 4953 | } |
|
| 4954 | 4954 | // Determine the receiver class and mutability, and validate that it |
|
| 4955 | 4955 | // points to the declaring trait. |
|
| 4956 | 4956 | let case ast::NodeValue::TypeSig(typeSig) = receiver.value |
| 5084 | 5084 | } |
|
| 5085 | 5085 | ||
| 5086 | 5086 | /// Resolved implementation of one trait method. |
|
| 5087 | 5087 | record ResolvedInstanceMethod: Copy { |
|
| 5088 | 5088 | /// Canonical trait method. |
|
| 5089 | - | method: *unsafe TraitMethod, |
|
| 5089 | + | method: TraitMethod, |
|
| 5090 | 5090 | /// Concrete function symbol. |
|
| 5091 | 5091 | symbol: *unsafe mut Symbol, |
|
| 5092 | 5092 | } |
|
| 5093 | 5093 | ||
| 5094 | 5094 | /// Shared declaration state for instance method resolution. |
| 5115 | 5115 | let methodScope = try bindRegions(self, methodNode, combinedRegions); |
|
| 5116 | 5116 | set self.regionScope = methodScope; |
|
| 5117 | 5117 | ||
| 5118 | 5118 | let methodName = try nodeName(self, name); |
|
| 5119 | 5119 | let attrMask = resolveAttributes(modifiers.attrs); |
|
| 5120 | - | let tm = findTraitMethod(context.traitInfo, methodName) |
|
| 5120 | + | let tm = findTraitMethod(&context.traitInfo.methods[..], methodName) |
|
| 5121 | 5121 | else throw emitError(self, name, ErrorKind::UnresolvedSymbol(methodName)); |
|
| 5122 | 5122 | if ast::hasAttribute(attrMask, ast::Attribute::Unsafe) <> tm.fnType.isUnsafe { |
|
| 5123 | 5123 | throw emitError(self, methodNode, ErrorKind::TraitMethodSafetyMismatch); |
|
| 5124 | 5124 | } |
|
| 5125 | 5125 |
| 5311 | 5311 | // Fill inherited method slots from supertrait instances. |
|
| 5312 | 5312 | for superTrait in traitInfo.supertraits { |
|
| 5313 | 5313 | let superInst = findInstance(self, superTrait, concreteType) |
|
| 5314 | 5314 | else throw emitError(self, node, ErrorKind::MissingSupertraitInstance(superTrait.name)); |
|
| 5315 | 5315 | for superMethod, mi in superTrait.methods { |
|
| 5316 | - | let merged = findTraitMethod(traitInfo, superMethod.name) |
|
| 5316 | + | let merged = findTraitMethod(&traitInfo.methods[..], superMethod.name) |
|
| 5317 | 5317 | else panic "resolveInstanceDecl: inherited method not found"; |
|
| 5318 | 5318 | if not covered[merged.index] { |
|
| 5319 | 5319 | set entry.methods[merged.index] = superInst.methods[mi]; |
|
| 5320 | 5320 | set covered[merged.index] = true; |
|
| 5321 | 5321 | } |
| 7252 | 7252 | } |
|
| 7253 | 7253 | ||
| 7254 | 7254 | /// Validate the reservation ABI used by typed session allocation. |
|
| 7255 | 7255 | unsafe fn sessionRuntime 'arena ( |
|
| 7256 | 7256 | self: &mut Resolver 'arena, node: *ast::Node, slice: bool |
|
| 7257 | - | ) -> *unsafe TraitMethod |
|
| 7257 | + | ) -> TraitMethod |
|
| 7258 | 7258 | throws (ResolveError) |
|
| 7259 | 7259 | { |
|
| 7260 | 7260 | let allocTrait = allocationSymbol(self, "Alloc") |
|
| 7261 | 7261 | else throw emitError(self, node, ErrorKind::InvalidAllocationRuntime); |
|
| 7262 | 7262 | let case SymbolData::Trait(allocInfo) = allocTrait.data |
|
| 7263 | 7263 | else throw emitError(self, node, ErrorKind::InvalidAllocationRuntime); |
|
| 7264 | 7264 | let name = "reserveSlice" if slice else "reserve"; |
|
| 7265 | - | let method = findTraitMethod(allocInfo, name) |
|
| 7265 | + | let method = findTraitMethod(&allocInfo.methods[..], name) |
|
| 7266 | 7266 | else throw emitError(self, node, ErrorKind::InvalidAllocationRuntime); |
|
| 7267 | 7267 | let error = allocationSymbol(self, "AllocError") |
|
| 7268 | 7268 | else throw emitError(self, node, ErrorKind::InvalidAllocationRuntime); |
|
| 7269 | 7269 | let case SymbolData::Type(errorType) = error.data |
|
| 7270 | 7270 | else throw emitError(self, node, ErrorKind::InvalidAllocationRuntime); |
| 7483 | 7483 | } |
|
| 7484 | 7484 | let subjectTy = autoDeref(parentTy); |
|
| 7485 | 7485 | ||
| 7486 | 7486 | if let case Type::TraitObject { traitInfo, mutable: objMutable, .. } = subjectTy { |
|
| 7487 | 7487 | let methodName = try nodeName(self, access.child); |
|
| 7488 | - | let method = findTraitMethod(traitInfo, methodName) |
|
| 7488 | + | let method = findTraitMethod(&traitInfo.methods[..], methodName) |
|
| 7489 | 7489 | else throw emitError(self, access.child, ErrorKind::RecordFieldUnknown(methodName)); |
|
| 7490 | 7490 | ||
| 7491 | 7491 | // Reject mutable-receiver methods called on immutable trait objects. |
|
| 7492 | 7492 | if method.mutable { |
|
| 7493 | 7493 | if not objMutable or not try canMutateThrough(self, access.parent) { |
| 8209 | 8209 | } |
|
| 8210 | 8210 | throw emitError(self, node, ErrorKind::SliceFieldUnknown(fieldName)); |
|
| 8211 | 8211 | } |
|
| 8212 | 8212 | if let case Type::TraitObject { traitInfo, .. } = subjectTy { |
|
| 8213 | 8213 | let fieldName = try nodeName(self, access.child); |
|
| 8214 | - | let method = findTraitMethod(traitInfo, fieldName) |
|
| 8214 | + | let method = findTraitMethod(&traitInfo.methods[..], fieldName) |
|
| 8215 | 8215 | else throw emitError(self, node, ErrorKind::RecordFieldUnknown(fieldName)); |
|
| 8216 | 8216 | return setNodeType(self, node, Type::Fn(method.fnType)); |
|
| 8217 | 8217 | } |
|
| 8218 | 8218 | ||
| 8219 | 8219 | match subjectTy { |
lib/std/lang/resolver/tests/regions.rad
+25 -0
| 24 | 24 | assert firstBinding.id == first.id; |
|
| 25 | 25 | assert secondBinding.id == second.id; |
|
| 26 | 26 | } |
|
| 27 | 27 | } |
|
| 28 | 28 | ||
| 29 | + | /// Trait lookup accepts empty tables from safe code. |
|
| 30 | + | @test fn testEmptyTraitMethodLookup() throws (testing::TestError) { |
|
| 31 | + | assert resolver::findTraitMethod(&[], "missing") == nil; |
|
| 32 | + | } |
|
| 33 | + | ||
| 34 | + | /// Trait lookup returns the matching method metadata in declaration order. |
|
| 35 | + | @test unsafe fn testTraitMethodLookup() throws (testing::TestError) { |
|
| 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, "trait R { fn (&R) first(); fn (&mut R) second(); }"); |
|
| 40 | + | try super::expectNoErrors(&result); |
|
| 41 | + | let case ast::NodeValue::Block(block) = result.root.value else throw testing::TestError::Failed; |
|
| 42 | + | let sym = resolver::symbolFor(&res, block.statements[0]) else throw testing::TestError::Failed; |
|
| 43 | + | let case resolver::SymbolData::Trait(info) = sym.data else throw testing::TestError::Failed; |
|
| 44 | + | let first = resolver::findTraitMethod(&info.methods[..], "first") else throw testing::TestError::Failed; |
|
| 45 | + | let last = resolver::findTraitMethod(&info.methods[..], "second") else throw testing::TestError::Failed; |
|
| 46 | + | assert first.index == 0; |
|
| 47 | + | assert not first.mutable; |
|
| 48 | + | assert last.index == 1; |
|
| 49 | + | assert last.mutable; |
|
| 50 | + | assert resolver::findTraitMethod(&info.methods[..], "missing") == nil; |
|
| 51 | + | } |
|
| 52 | + | } |
|
| 53 | + | ||
| 29 | 54 | /// Iteration over raw slices requires permission to read their storage. |
|
| 30 | 55 | @test unsafe fn testRawSliceIterationRequiresUnsafe() throws (testing::TestError) { |
|
| 31 | 56 | for program in [ |
|
| 32 | 57 | "fn f(p: *unsafe [u32]) { for item in p { assert item == 0; } }", |
|
| 33 | 58 | "fn f(p: *unsafe mut [u32]) { for item, index in p { assert item == index; } }", |