Resolve instance blocks
9de503edfb98f06115572a7017dbb5bb92a014cd57c3480b24b6c1ead41ef39b
After this commit, `instance Trait for Type { ... }` is fully
validated. Missing methods, signature mismatches, and wrong receiver
types are caught and reported.
1 parent
ffdd11dd
lib/std/lang/resolver.rad
+293 -4
| 41 | 41 | /// Nb. This should not be raised above `255`, |
|
| 42 | 42 | /// as tags are stored using 8-bits only. |
|
| 43 | 43 | pub const MAX_UNION_VARIANTS: u32 = 128; |
|
| 44 | 44 | /// Maximum nesting of loops. |
|
| 45 | 45 | pub const MAX_LOOP_DEPTH: u32 = 16; |
|
| 46 | + | /// Maximum trait instances. |
|
| 47 | + | pub const MAX_INSTANCES: u32 = 128; |
|
| 46 | 48 | ||
| 47 | 49 | /// Trait definition stored in the resolver. |
|
| 48 | 50 | pub record TraitType { |
|
| 49 | 51 | /// Trait name. |
|
| 50 | 52 | name: *[u8], |
| 60 | 62 | name: *[u8], |
|
| 61 | 63 | /// Function type for the method, excluding the receiver. |
|
| 62 | 64 | fnType: *FnType, |
|
| 63 | 65 | /// Whether the receiver is mutable. |
|
| 64 | 66 | mutable: bool, |
|
| 65 | - | /// Vtable slot index. |
|
| 67 | + | /// V-table slot index. |
|
| 66 | 68 | index: u32, |
|
| 67 | 69 | } |
|
| 68 | 70 | ||
| 71 | + | /// An entry in the trait instance registry. |
|
| 72 | + | pub record InstanceEntry { |
|
| 73 | + | /// Trait type descriptor. |
|
| 74 | + | traitType: *TraitType, |
|
| 75 | + | /// Concrete type that implements the trait. |
|
| 76 | + | concreteType: Type, |
|
| 77 | + | /// Name of the concrete type. |
|
| 78 | + | concreteTypeName: *[u8], |
|
| 79 | + | /// Module where this instance was declared. |
|
| 80 | + | moduleId: u16, |
|
| 81 | + | /// Method symbols for each trait method, in declaration order. |
|
| 82 | + | methods: [*mut Symbol; ast::MAX_TRAIT_METHODS], |
|
| 83 | + | /// Number of methods. |
|
| 84 | + | methodsLen: u32, |
|
| 85 | + | } |
|
| 86 | + | ||
| 69 | 87 | /// Identifier for the synthetic `len` field. |
|
| 70 | 88 | pub const LEN_FIELD: *[u8] = "len"; |
|
| 71 | 89 | /// Identifier for the synthetic `ptr` field. |
|
| 72 | 90 | pub const PTR_FIELD: *[u8] = "ptr"; |
|
| 73 | 91 |
| 528 | 546 | BuiltinArgCountMismatch(CountMismatch), |
|
| 529 | 547 | /// Instance method receiver mutability does not match the trait declaration. |
|
| 530 | 548 | ReceiverMutabilityMismatch, |
|
| 531 | 549 | /// Duplicate instance declaration for the same (trait, type) pair. |
|
| 532 | 550 | DuplicateInstance, |
|
| 551 | + | /// Instance declaration is missing a required trait method. |
|
| 552 | + | MissingTraitMethod(*[u8]), |
|
| 533 | 553 | /// Trait name used as a value expression. |
|
| 534 | 554 | UnexpectedTraitName, |
|
| 535 | 555 | /// Trait method receiver does not point to the declaring trait. |
|
| 536 | 556 | TraitReceiverMismatch, |
|
| 537 | 557 | /// Function declaration has too many parameters. |
| 720 | 740 | /// Module graph for the current package. |
|
| 721 | 741 | moduleGraph: *module::ModuleGraph, |
|
| 722 | 742 | /// Cache of module scopes indexed by module ID. |
|
| 723 | 743 | // TODO: Why is this optional? |
|
| 724 | 744 | moduleScopes: [?*mut Scope; module::MAX_MODULES], |
|
| 745 | + | /// Trait instance registry. |
|
| 746 | + | instances: [InstanceEntry; MAX_INSTANCES], |
|
| 747 | + | /// Number of registered instances. |
|
| 748 | + | instancesLen: u32, |
|
| 725 | 749 | } |
|
| 726 | 750 | ||
| 727 | 751 | /// Internal error sentinel thrown when analysis cannot proceed. |
|
| 728 | 752 | pub union ResolveError { |
|
| 729 | 753 | Failure, |
| 861 | 885 | types: nil, |
|
| 862 | 886 | errors: errorList(storage.errors), |
|
| 863 | 887 | // TODO: Shouldn't be undefined. |
|
| 864 | 888 | moduleGraph: undefined, |
|
| 865 | 889 | moduleScopes, |
|
| 890 | + | instances: undefined, |
|
| 891 | + | instancesLen: 0, |
|
| 866 | 892 | }; |
|
| 867 | 893 | } |
|
| 868 | 894 | ||
| 869 | 895 | /// Return `true` if there are no errors in the diagnostics. |
|
| 870 | 896 | pub fn success(diag: *Diagnostics) -> bool { |
| 2436 | 2462 | // Handled in previous pass. |
|
| 2437 | 2463 | } |
|
| 2438 | 2464 | case ast::NodeValue::Use(_) => { |
|
| 2439 | 2465 | // Handled in previous pass. |
|
| 2440 | 2466 | } |
|
| 2467 | + | case ast::NodeValue::InstanceDecl { traitName, targetType, methods } => { |
|
| 2468 | + | try resolveInstanceDecl(self, node, traitName, targetType, &methods); |
|
| 2469 | + | } |
|
| 2441 | 2470 | else => { |
|
| 2442 | 2471 | // Ignore non-declaration nodes. |
|
| 2443 | 2472 | } |
|
| 2444 | 2473 | } |
|
| 2445 | 2474 | } |
| 2470 | 2499 | // Skip: already analyzed in declaration phase. |
|
| 2471 | 2500 | } |
|
| 2472 | 2501 | case ast::NodeValue::Use(_) => { |
|
| 2473 | 2502 | // Skip: already analyzed in declaration phase. |
|
| 2474 | 2503 | } |
|
| 2504 | + | case ast::NodeValue::TraitDecl { .. } => { |
|
| 2505 | + | // Skip: already analyzed in declaration phase. |
|
| 2506 | + | } |
|
| 2507 | + | case ast::NodeValue::InstanceDecl { methods, .. } => { |
|
| 2508 | + | try resolveInstanceMethodBodies(self, &methods); |
|
| 2509 | + | } |
|
| 2475 | 2510 | else => { |
|
| 2476 | 2511 | // FIXME: This allows module-level statements that should |
|
| 2477 | 2512 | // normally only be valid inside function bodies. We currently |
|
| 2478 | 2513 | // need this because of how tests are written, but it should |
|
| 2479 | 2514 | // be eventually removed. |
| 3323 | 3358 | ||
| 3324 | 3359 | try setNodeType(self, methodNode, Type::Void); |
|
| 3325 | 3360 | } |
|
| 3326 | 3361 | } |
|
| 3327 | 3362 | ||
| 3363 | + | /// Resolve a name path node to a symbol. |
|
| 3364 | + | /// Used for trait and type references in instance declarations and trait objects. |
|
| 3365 | + | fn resolveNamePath(self: *mut Resolver, node: *ast::Node) -> *mut Symbol |
|
| 3366 | + | throws (ResolveError) |
|
| 3367 | + | { |
|
| 3368 | + | match node.value { |
|
| 3369 | + | case ast::NodeValue::Ident(name) => { |
|
| 3370 | + | let sym = findAnySymbol(self.scope, name) |
|
| 3371 | + | else throw emitError(self, node, ErrorKind::UnresolvedSymbol(name)); |
|
| 3372 | + | return sym; |
|
| 3373 | + | } |
|
| 3374 | + | case ast::NodeValue::ScopeAccess(access) => { |
|
| 3375 | + | return try resolveAccess(self, node, access, self.scope); |
|
| 3376 | + | } |
|
| 3377 | + | else => { |
|
| 3378 | + | throw emitError(self, node, ErrorKind::ExpectedIdentifier); |
|
| 3379 | + | } |
|
| 3380 | + | } |
|
| 3381 | + | } |
|
| 3382 | + | ||
| 3383 | + | /// Resolve an instance declaration. |
|
| 3384 | + | /// Validates that the trait exists, the target type exists, and all methods |
|
| 3385 | + | /// match the trait's signatures. |
|
| 3386 | + | fn resolveInstanceDecl( |
|
| 3387 | + | self: *mut Resolver, |
|
| 3388 | + | node: *ast::Node, |
|
| 3389 | + | traitName: *ast::Node, |
|
| 3390 | + | targetType: *ast::Node, |
|
| 3391 | + | methods: *ast::NodeList |
|
| 3392 | + | ) throws (ResolveError) { |
|
| 3393 | + | // Look up the trait. |
|
| 3394 | + | let traitSym = try resolveNamePath(self, traitName); |
|
| 3395 | + | let case SymbolData::Trait(traitInfo) = traitSym.data |
|
| 3396 | + | else throw emitError(self, traitName, ErrorKind::Internal); |
|
| 3397 | + | ||
| 3398 | + | try setNodeSymbol(self, traitName, traitSym); |
|
| 3399 | + | ||
| 3400 | + | // Look up the target type. |
|
| 3401 | + | let typeSym = try resolveNamePath(self, targetType); |
|
| 3402 | + | let case SymbolData::Type(nominalTy) = typeSym.data |
|
| 3403 | + | else throw emitError(self, targetType, ErrorKind::Internal); |
|
| 3404 | + | try setNodeSymbol(self, targetType, typeSym); |
|
| 3405 | + | // Ensure the concrete type body is resolved. |
|
| 3406 | + | try ensureNominalResolved(self, nominalTy, targetType); |
|
| 3407 | + | ||
| 3408 | + | // Reject duplicate instance for the same (trait, type) pair. |
|
| 3409 | + | let concreteType = Type::Nominal(nominalTy); |
|
| 3410 | + | if let _ = findInstance(self, traitInfo, concreteType) { |
|
| 3411 | + | throw emitError(self, node, ErrorKind::DuplicateInstance); |
|
| 3412 | + | } |
|
| 3413 | + | ||
| 3414 | + | // Build the instance entry. |
|
| 3415 | + | if self.instancesLen >= MAX_INSTANCES { |
|
| 3416 | + | throw emitError(self, node, ErrorKind::Internal); |
|
| 3417 | + | } |
|
| 3418 | + | let mut entry = InstanceEntry { |
|
| 3419 | + | traitType: traitInfo, |
|
| 3420 | + | concreteType, |
|
| 3421 | + | concreteTypeName: typeSym.name, |
|
| 3422 | + | moduleId: self.currentMod, |
|
| 3423 | + | methods: undefined, |
|
| 3424 | + | methodsLen: 0, |
|
| 3425 | + | }; |
|
| 3426 | + | // Track which trait methods are covered by the instance. |
|
| 3427 | + | let mut covered: [bool; ast::MAX_TRAIT_METHODS] = [false; ast::MAX_TRAIT_METHODS]; |
|
| 3428 | + | ||
| 3429 | + | // Match each instance method to a trait method. |
|
| 3430 | + | for i in 0..methods.len { |
|
| 3431 | + | let methodNode = methods.list[i]; |
|
| 3432 | + | let case ast::NodeValue::InstanceMethodDecl { |
|
| 3433 | + | name, receiverName, receiverType, sig, body |
|
| 3434 | + | } = methodNode.value else continue; |
|
| 3435 | + | ||
| 3436 | + | let methodName = try nodeName(self, name); |
|
| 3437 | + | ||
| 3438 | + | // Find the matching trait method. |
|
| 3439 | + | let mut traitMethod: ?*TraitMethod = nil; |
|
| 3440 | + | for j in 0..traitInfo.methodsLen { |
|
| 3441 | + | if traitInfo.methods[j].name == methodName { |
|
| 3442 | + | traitMethod = &traitInfo.methods[j]; |
|
| 3443 | + | break; |
|
| 3444 | + | } |
|
| 3445 | + | } |
|
| 3446 | + | let tm = traitMethod |
|
| 3447 | + | else throw emitError(self, name, ErrorKind::UnresolvedSymbol(methodName)); |
|
| 3448 | + | ||
| 3449 | + | // Determine receiver mutability and validate receiver type. |
|
| 3450 | + | let mut receiverMut = false; |
|
| 3451 | + | if let case ast::NodeValue::TypeSig(typeSig) = receiverType.value { |
|
| 3452 | + | if let case ast::TypeSig::Pointer { mutable, valueType } = typeSig { |
|
| 3453 | + | receiverMut = mutable; |
|
| 3454 | + | // Validate that the receiver type annotation matches the |
|
| 3455 | + | // concrete type from the instance declaration. |
|
| 3456 | + | let annotatedTy = try infer(self, valueType); |
|
| 3457 | + | if not typesEqual(annotatedTy, concreteType) { |
|
| 3458 | + | throw emitTypeMismatch(self, receiverType, TypeMismatch { |
|
| 3459 | + | expected: concreteType, |
|
| 3460 | + | actual: annotatedTy, |
|
| 3461 | + | }); |
|
| 3462 | + | } |
|
| 3463 | + | } |
|
| 3464 | + | } |
|
| 3465 | + | ||
| 3466 | + | // Check receiver mutability matches in both directions. |
|
| 3467 | + | if tm.mutable and not receiverMut { |
|
| 3468 | + | throw emitError(self, receiverType, ErrorKind::ImmutableBinding); |
|
| 3469 | + | } |
|
| 3470 | + | if receiverMut and not tm.mutable { |
|
| 3471 | + | throw emitError(self, receiverType, ErrorKind::ReceiverMutabilityMismatch); |
|
| 3472 | + | } |
|
| 3473 | + | ||
| 3474 | + | // Build the function type for the instance method. |
|
| 3475 | + | // The receiver becomes the first parameter. |
|
| 3476 | + | let mut fnType = FnType { |
|
| 3477 | + | paramTypes: undefined, |
|
| 3478 | + | paramTypesLen: 0, |
|
| 3479 | + | returnType: allocType(self, Type::Void), |
|
| 3480 | + | throwList: undefined, |
|
| 3481 | + | throwListLen: 0, |
|
| 3482 | + | localCount: 0, |
|
| 3483 | + | }; |
|
| 3484 | + | // First param is the receiver. |
|
| 3485 | + | let receiverPtrType = Type::Pointer { |
|
| 3486 | + | target: allocType(self, concreteType), |
|
| 3487 | + | mutable: receiverMut, |
|
| 3488 | + | }; |
|
| 3489 | + | fnType.paramTypes[0] = allocType(self, receiverPtrType); |
|
| 3490 | + | fnType.paramTypesLen = 1; |
|
| 3491 | + | ||
| 3492 | + | // Add the other params from the trait method and validate that |
|
| 3493 | + | // the instance method's declared parameter types match. |
|
| 3494 | + | if sig.params.len != tm.fnType.paramTypesLen { |
|
| 3495 | + | throw emitError(self, methodNode, ErrorKind::FnArgCountMismatch(CountMismatch { |
|
| 3496 | + | expected: tm.fnType.paramTypesLen, |
|
| 3497 | + | actual: sig.params.len, |
|
| 3498 | + | })); |
|
| 3499 | + | } |
|
| 3500 | + | for j in 0..tm.fnType.paramTypesLen { |
|
| 3501 | + | // Resolve the instance method's declared parameter type. |
|
| 3502 | + | let paramNode = sig.params.list[j]; |
|
| 3503 | + | let case ast::NodeValue::FnParam(param) = paramNode.value |
|
| 3504 | + | else throw emitError(self, paramNode, ErrorKind::ExpectedIdentifier); |
|
| 3505 | + | let instanceParamTy = try resolveValueType(self, param.type); |
|
| 3506 | + | let traitParamTy = *tm.fnType.paramTypes[j]; |
|
| 3507 | + | ||
| 3508 | + | if not typesEqual(instanceParamTy, traitParamTy) { |
|
| 3509 | + | throw emitTypeMismatch(self, paramNode, TypeMismatch { |
|
| 3510 | + | expected: traitParamTy, |
|
| 3511 | + | actual: instanceParamTy, |
|
| 3512 | + | }); |
|
| 3513 | + | } |
|
| 3514 | + | fnType.paramTypes[fnType.paramTypesLen] = tm.fnType.paramTypes[j]; |
|
| 3515 | + | fnType.paramTypesLen += 1; |
|
| 3516 | + | } |
|
| 3517 | + | fnType.returnType = tm.fnType.returnType; |
|
| 3518 | + | ||
| 3519 | + | // Copy throws list from the trait method. |
|
| 3520 | + | for j in 0..tm.fnType.throwListLen { |
|
| 3521 | + | fnType.throwList[fnType.throwListLen] = tm.fnType.throwList[j]; |
|
| 3522 | + | fnType.throwListLen += 1; |
|
| 3523 | + | } |
|
| 3524 | + | ||
| 3525 | + | // Create a symbol for the instance method without binding it into the |
|
| 3526 | + | // module scope. Instance methods are dispatched via v-table, so they |
|
| 3527 | + | // must not pollute the enclosing scope. |
|
| 3528 | + | let fnTy = Type::Fn(allocFnType(self, fnType)); |
|
| 3529 | + | let mName = try nodeName(self, name); |
|
| 3530 | + | let sym = allocSymbol(self, SymbolData::Value { |
|
| 3531 | + | mutable: false, alignment: 0, type: fnTy, addressTaken: false, |
|
| 3532 | + | }, mName, methodNode, 0); |
|
| 3533 | + | ||
| 3534 | + | try setNodeSymbol(self, methodNode, sym); |
|
| 3535 | + | try setNodeType(self, methodNode, fnTy); |
|
| 3536 | + | try setNodeType(self, name, fnTy); |
|
| 3537 | + | ||
| 3538 | + | // Store in instance entry at the matching v-table slot. |
|
| 3539 | + | entry.methods[tm.index] = sym; |
|
| 3540 | + | entry.methodsLen += 1; |
|
| 3541 | + | ||
| 3542 | + | covered[tm.index] = true; |
|
| 3543 | + | } |
|
| 3544 | + | ||
| 3545 | + | // Check that all trait methods are implemented. |
|
| 3546 | + | for i in 0..traitInfo.methodsLen { |
|
| 3547 | + | if not covered[i] { |
|
| 3548 | + | throw emitError(self, node, ErrorKind::MissingTraitMethod(traitInfo.methods[i].name)); |
|
| 3549 | + | } |
|
| 3550 | + | } |
|
| 3551 | + | self.instances[self.instancesLen] = entry; |
|
| 3552 | + | self.instancesLen += 1; |
|
| 3553 | + | ||
| 3554 | + | try setNodeType(self, node, Type::Void); |
|
| 3555 | + | } |
|
| 3556 | + | ||
| 3557 | + | /// Resolve instance method bodies. |
|
| 3558 | + | fn resolveInstanceMethodBodies(self: *mut Resolver, methods: *ast::NodeList) |
|
| 3559 | + | throws (ResolveError) |
|
| 3560 | + | { |
|
| 3561 | + | for i in 0..methods.len { |
|
| 3562 | + | let methodNode = methods.list[i]; |
|
| 3563 | + | let case ast::NodeValue::InstanceMethodDecl { |
|
| 3564 | + | name, receiverName, receiverType, sig, body |
|
| 3565 | + | } = methodNode.value else continue; |
|
| 3566 | + | ||
| 3567 | + | // Symbol may be absent if [`resolveInstanceDecl`] reported an error |
|
| 3568 | + | // for this method (eg. unknown method name). Skip gracefully. |
|
| 3569 | + | let sym = symbolFor(self, methodNode) |
|
| 3570 | + | else continue; |
|
| 3571 | + | let case SymbolData::Value { type, .. } = sym.data |
|
| 3572 | + | else panic "resolveInstanceMethodBodies: expected value symbol"; |
|
| 3573 | + | let case Type::Fn(fnType) = type |
|
| 3574 | + | else panic "resolveInstanceMethodBodies: expected function type"; |
|
| 3575 | + | ||
| 3576 | + | // Enter function scope. |
|
| 3577 | + | enterFn(self, methodNode, fnType); |
|
| 3578 | + | ||
| 3579 | + | // Bind the receiver parameter. |
|
| 3580 | + | let receiverTy = *fnType.paramTypes[0]; |
|
| 3581 | + | try bindValueIdent(self, receiverName, receiverName, receiverTy, false, 0, 0) catch { |
|
| 3582 | + | exitFn(self); |
|
| 3583 | + | throw ResolveError::Failure; |
|
| 3584 | + | }; |
|
| 3585 | + | // Bind the remaining parameters from the signature. |
|
| 3586 | + | for j in 0..sig.params.len { |
|
| 3587 | + | let paramNode = sig.params.list[j]; |
|
| 3588 | + | let paramTy = try infer(self, paramNode) catch { |
|
| 3589 | + | exitFn(self); |
|
| 3590 | + | throw ResolveError::Failure; |
|
| 3591 | + | }; |
|
| 3592 | + | } |
|
| 3593 | + | ||
| 3594 | + | // Resolve the body. |
|
| 3595 | + | let retTy = *fnType.returnType; |
|
| 3596 | + | let bodyTy = try checkAssignable(self, body, Type::Void) catch { |
|
| 3597 | + | exitFn(self); |
|
| 3598 | + | throw ResolveError::Failure; |
|
| 3599 | + | }; |
|
| 3600 | + | if retTy != Type::Void and bodyTy != Type::Never { |
|
| 3601 | + | exitFn(self); |
|
| 3602 | + | throw emitError(self, body, ErrorKind::FnMissingReturn); |
|
| 3603 | + | } |
|
| 3604 | + | exitFn(self); |
|
| 3605 | + | } |
|
| 3606 | + | } |
|
| 3607 | + | ||
| 3608 | + | /// Look up an instance entry by trait and concrete type. |
|
| 3609 | + | fn findInstance(self: *Resolver, traitInfo: *TraitType, concreteType: Type) -> ?*InstanceEntry { |
|
| 3610 | + | for i in 0..self.instancesLen { |
|
| 3611 | + | let entry = &self.instances[i]; |
|
| 3612 | + | if entry.traitType == traitInfo and typesEqual(entry.concreteType, concreteType) { |
|
| 3613 | + | return entry; |
|
| 3614 | + | } |
|
| 3615 | + | } |
|
| 3616 | + | return nil; |
|
| 3617 | + | } |
|
| 3618 | + | ||
| 3328 | 3619 | /// Resolve union variant types after all type names are bound (Phase 2 of type resolution). |
|
| 3329 | 3620 | fn resolveUnionBody(self: *mut Resolver, node: *ast::Node, decl: ast::UnionDecl) |
|
| 3330 | 3621 | throws (ResolveError) |
|
| 3331 | 3622 | { |
|
| 3332 | 3623 | // Get the type symbol that was bound to this declaration node. |
| 5591 | 5882 | fnType.returnType = allocType(self, Type::Void); |
|
| 5592 | 5883 | } |
|
| 5593 | 5884 | return Type::Fn(allocFnType(self, fnType)); |
|
| 5594 | 5885 | } |
|
| 5595 | 5886 | case ast::TypeSig::TraitObject { traitName, mutable } => { |
|
| 5596 | - | let name = try nodeName(self, traitName); |
|
| 5597 | - | let sym = findAnySymbol(self.scope, name) |
|
| 5598 | - | else throw emitError(self, traitName, ErrorKind::UnresolvedSymbol(name)); |
|
| 5887 | + | let sym = try resolveNamePath(self, traitName); |
|
| 5599 | 5888 | let case SymbolData::Trait(traitInfo) = sym.data |
|
| 5600 | 5889 | else throw emitError(self, traitName, ErrorKind::Internal); |
|
| 5601 | 5890 | try setNodeSymbol(self, traitName, sym); |
|
| 5602 | 5891 | ||
| 5603 | 5892 | return Type::TraitObject { traitInfo, mutable }; |
lib/std/lang/resolver/printer.rad
+5 -0
| 556 | 556 | io::print("instance receiver mutability does not match trait declaration"); |
|
| 557 | 557 | } |
|
| 558 | 558 | case super::ErrorKind::DuplicateInstance => { |
|
| 559 | 559 | io::print("duplicate instance declaration for the same trait and type"); |
|
| 560 | 560 | } |
|
| 561 | + | case super::ErrorKind::MissingTraitMethod(name) => { |
|
| 562 | + | io::print("missing trait method '"); |
|
| 563 | + | io::print(name); |
|
| 564 | + | io::print("'"); |
|
| 565 | + | } |
|
| 561 | 566 | case super::ErrorKind::UnexpectedTraitName => { |
|
| 562 | 567 | io::print("trait name cannot be used as a value"); |
|
| 563 | 568 | } |
|
| 564 | 569 | case super::ErrorKind::TraitReceiverMismatch => { |
|
| 565 | 570 | io::print("trait method receiver must be a pointer to the declaring trait"); |
lib/std/lang/resolver/tests.rad
+20 -0
| 247 | 247 | if let case super::ErrorKind::UnionMatchNonExhaustive(actualName) = *actual { |
|
| 248 | 248 | return mem::eq(actualName, expectedName); |
|
| 249 | 249 | } |
|
| 250 | 250 | return false; |
|
| 251 | 251 | } |
|
| 252 | + | if let case super::ErrorKind::MissingTraitMethod(expectedName) = expected { |
|
| 253 | + | if let case super::ErrorKind::MissingTraitMethod(actualName) = *actual { |
|
| 254 | + | return mem::eq(actualName, expectedName); |
|
| 255 | + | } |
|
| 256 | + | return false; |
|
| 257 | + | } |
|
| 252 | 258 | return *actual == expected; |
|
| 253 | 259 | } |
|
| 254 | 260 | ||
| 255 | 261 | /// Extract the first error and ensure it has the expected kind. |
|
| 256 | 262 | fn expectErrorKind(result: *TestResult, kind: super::ErrorKind) -> *super::Error |
| 4495 | 4501 | let mut a = testResolver(); |
|
| 4496 | 4502 | let program = "union ErrA { A } union ErrB { B } fn f() -> i32 throws (ErrA, ErrB) { throw ErrA::A(); return 0; } fn g() -> i32 { return try f() catch e { return 0; }; }"; |
|
| 4497 | 4503 | let result = try resolveProgramStr(&mut a, program); |
|
| 4498 | 4504 | try expectErrorKind(&result, super::ErrorKind::TryCatchMultiError); |
|
| 4499 | 4505 | } |
|
| 4506 | + | ||
| 4507 | + | @test fn testInstanceMissingMethod() throws (testing::TestError) { |
|
| 4508 | + | let mut a = testResolver(); |
|
| 4509 | + | let program = "trait S { fn (*S) f() -> i32; } record R { x: i32 } instance S for R {}"; |
|
| 4510 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 4511 | + | try expectErrorKind(&result, super::ErrorKind::MissingTraitMethod("f")); |
|
| 4512 | + | } |
|
| 4513 | + | ||
| 4514 | + | @test fn testInstanceUnknownMethod() throws (testing::TestError) { |
|
| 4515 | + | let mut a = testResolver(); |
|
| 4516 | + | let program = "trait S { fn (*S) f() -> i32; } record R { x: i32 } instance S for R { fn (self: *R) x() -> i32 { return 0; } }"; |
|
| 4517 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 4518 | + | try expectErrorKind(&result, super::ErrorKind::UnresolvedSymbol("x")); |
|
| 4519 | + | } |