Resolve forward-declared supertrait methods

a6f4d48e4ec20096653486bab610d64195d0b11b7c124804125279a5429aac74
A child trait declared before its supertrait copied an empty method table.
Instance resolution then failed to find inherited method slots and trapped
inside the resolver.

Resolve later-declared supertraits before copying their methods. Declaration
order prevents recursive re-entry, while a populated method table prevents
duplicate resolution without adding state to every trait descriptor.

Assisted-by: Codex:gpt-5.6
Alexis Sellier committed ago 1 parent 7ff30933
lib/std/lang/resolver.rad +12 -1
3252 3252
{
3253 3253
    let sym = symbolFor(self, node)
3254 3254
        else return;
3255 3255
    let case SymbolData::Trait(traitType) = sym.data
3256 3256
        else return;
3257 +
    if traitType.methods.len > 0 {
3258 +
        return;
3259 +
    }
3257 3260
3258 3261
    // Resolve supertrait bounds and copy their methods into this trait.
3259 3262
    for superNode in supertraits {
3260 3263
        let superSym = try resolveNamePath(self, superNode);
3261 3264
        let case SymbolData::Trait(superTrait) = superSym.data
3262 3265
            else throw emitError(self, superNode, ErrorKind::Internal);
3266 +
        // Trait bodies are otherwise resolved in source order. Recursively
3267 +
        // resolve a supertrait only when it is declared later.
3268 +
        if superSym.node.id > node.id {
3269 +
            let case ast::NodeValue::TraitDecl {
3270 +
                supertraits: inheritedTraits, methods: inheritedMethods, ..
3271 +
            } = superSym.node.value else throw emitError(self, superNode, ErrorKind::Internal);
3272 +
            try resolveTraitBody(self, superSym.node, inheritedTraits, inheritedMethods);
3273 +
        }
3274 +
3263 3275
        setNodeSymbol(self, superNode, superSym);
3264 3276
3265 3277
        let a = alloc::arenaAllocator(&mut self.arena);
3266 3278
        if traitType.methods.len + superTrait.methods.len > ast::MAX_TRAIT_METHODS {
3267 3279
            throw emitError(self, node, ErrorKind::TraitMethodOverflow(CountMismatch {
3298 3310
3299 3311
        // Reject duplicate method names.
3300 3312
        if let _ = findTraitMethod(traitType, methodName) {
3301 3313
            throw emitError(self, name, ErrorKind::DuplicateBinding(methodName));
3302 3314
        }
3303 -
3304 3315
        // Determine receiver mutability from the receiver type node
3305 3316
        // and validate that the receiver points to the declaring trait.
3306 3317
        let case ast::NodeValue::TypeSig(typeSig) = receiver.value
3307 3318
            else throw emitError(self, receiver, ErrorKind::TraitReceiverMismatch);
3308 3319
        let case ast::TypeSig::Pointer { mutable, valueType } = typeSig
test/tests/trait.supertrait.forward.rad added +34 -0
1 +
//! returns: 0
2 +
//! A supertrait declared later must contribute methods to its child trait.
3 +
4 +
trait Child: Parent {
5 +
    fn (*Child) child() -> i32;
6 +
}
7 +
8 +
trait Parent {
9 +
    fn (*Parent) parent() -> i32;
10 +
}
11 +
12 +
record Value {
13 +
    n: i32,
14 +
}
15 +
16 +
instance Parent for Value {
17 +
    fn (self: *Value) parent() -> i32 {
18 +
        return self.n;
19 +
    }
20 +
}
21 +
22 +
instance Child for Value {
23 +
    fn (self: *Value) child() -> i32 {
24 +
        return self.n + 1;
25 +
    }
26 +
}
27 +
28 +
@default fn main() -> i32 {
29 +
    let value = Value { n: 41 };
30 +
    let object: *opaque Child = &value;
31 +
    assert object.parent() == 41;
32 +
    assert object.child() == 42;
33 +
    return 0;
34 +
}