compiler: Look up trait methods through checked slices

84c0637c18e601a3a109f721f7ac0772bd4611ef8b8540fabdda33add49a5001
Alexis Sellier committed ago 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; } }",