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
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 | + | } |