Add support for standalone methods

29fdb962aefa472d7106707547edef9cd332b0e5791c0972ed5949eba3b35546
Eg. `fn (f *Fnord) f() -> i32 { ... }`
Alexis Sellier committed ago 1 parent d91f35a2
lib/std/lang/ast.rad +6 -3
791 791
    InstanceDecl {
792 792
        /// Trait name identifier.
793 793
        traitName: *Node,
794 794
        /// Target type identifier.
795 795
        targetType: *Node,
796 -
        /// Method definition nodes ([`InstanceMethodDecl`]).
796 +
        /// Method definition nodes ([`MethodDecl`]).
797 797
        methods: *mut [*Node],
798 798
    },
799 -
    /// Method definition inside an instance block.
800 -
    InstanceMethodDecl {
799 +
    /// Method definition with a receiver.
800 +
    /// Used both inside `instance` blocks and as standalone methods.
801 +
    MethodDecl {
801 802
        /// Method name identifier.
802 803
        name: *Node,
803 804
        /// Receiver binding name ([`Ident`] node).
804 805
        receiverName: *Node,
805 806
        /// Receiver type node (eg. `*mut Arena`).
806 807
        receiverType: *Node,
807 808
        /// Function signature.
808 809
        sig: FnSig,
809 810
        /// Method body.
810 811
        body: *Node,
812 +
        /// Optional attribute list.
813 +
        attrs: ?Attributes,
811 814
    },
812 815
}
813 816
814 817
/// Full AST node with shared metadata and variant-specific payload.
815 818
pub record Node {
lib/std/lang/ast/printer.rad +1 -1
453 453
        }
454 454
        case super::NodeValue::InstanceDecl { traitName, targetType, methods } => {
455 455
            let children = nodeListToExprs(a, &methods[..]);
456 456
            return sexpr::block(a, "instance", &[toExpr(a, traitName), toExpr(a, targetType)], children);
457 457
        }
458 -
        case super::NodeValue::InstanceMethodDecl { name, receiverName, receiverType, sig, body } => {
458 +
        case super::NodeValue::MethodDecl { name, receiverName, receiverType, sig, body, .. } => {
459 459
            let params = sexpr::list(a, "params", nodeListToExprs(a, &sig.params[..]));
460 460
            let ret = toExprOrNull(a, sig.returnType);
461 461
            return sexpr::block(a, "method", &[toExpr(a, receiverType), toExpr(a, receiverName), toExpr(a, name), params, ret], &[toExpr(a, body)]);
462 462
        }
463 463
        else => return sexpr::sym("?"),
lib/std/lang/lower.rad +168 -124
776 776
                try lowerDataDecl(low, node, decl.value, false);
777 777
            }
778 778
            case ast::NodeValue::InstanceDecl { traitName, targetType, methods } => {
779 779
                try lowerInstanceDecl(low, node, traitName, targetType, methods);
780 780
            }
781 +
            case ast::NodeValue::MethodDecl { name, receiverName, receiverType, sig, body, .. } => {
782 +
                if let f = try lowerMethodDecl(low, node, name, receiverName, sig, body) {
783 +
                    low.fns.append(f, low.allocator);
784 +
                }
785 +
            }
781 786
            else => {},
782 787
        }
783 788
    }
784 789
    return defaultFnIdx;
785 790
}
1025 1030
    // later from inherited supertrait methods.
1026 1031
    let mut methodNames: [*[u8]; ast::MAX_TRAIT_METHODS] = undefined;
1027 1032
    let mut methodNameSet: [bool; ast::MAX_TRAIT_METHODS] = [false; ast::MAX_TRAIT_METHODS];
1028 1033
1029 1034
    for methodNode in methods {
1030 -
        let case ast::NodeValue::InstanceMethodDecl {
1031 -
            name, receiverName, receiverType, sig, body
1035 +
        let case ast::NodeValue::MethodDecl {
1036 +
            name, receiverName, receiverType, sig, body, ..
1032 1037
        } = methodNode.value else continue;
1033 1038
1034 1039
        let case ast::NodeValue::Ident(mName) = name.value else {
1035 1040
            throw LowerError::ExpectedIdentifier;
1036 1041
        };
1037 1042
        let qualName = instanceMethodName(self, nil, typeName, mName);
1038 -
1039 -
        // Get the function type from the resolver.
1040 -
        let data = resolver::nodeData(self.resolver, methodNode);
1041 -
        let case resolver::Type::Fn(fnType) = data.ty else {
1042 -
            throw LowerError::ExpectedFunction;
1043 -
        };
1044 -
1045 -
        // Register the method symbol.
1046 -
        if let sym = data.sym {
1047 -
            registerFnSym(self, sym, qualName);
1048 -
        }
1049 -
1050 -
        // Lower the method as a normal function.
1051 -
        let mut fnLow = fnLowerer(self, methodNode, fnType, qualName);
1052 -
        if requiresReturnParam(fnType) {
1053 -
            fnLow.returnReg = nextReg(&mut fnLow);
1054 -
        }
1055 -
        let lowParams = try lowerParams(&mut fnLow, *fnType, sig.params, receiverName);
1056 -
        let func = try! alloc::alloc(self.arena, @sizeOf(il::Fn), @alignOf(il::Fn)) as *mut il::Fn;
1057 -
1058 -
        *func = il::Fn {
1059 -
            name: qualName,
1060 -
            params: lowParams,
1061 -
            returnType: ilType(self, *fnType.returnType),
1062 -
            isExtern: false,
1063 -
            isLeaf: true,
1064 -
            blocks: &[],
1065 -
        };
1066 -
        if fnType.throwList.len > 0 {
1067 -
            func.returnType = il::Type::W64;
1068 -
        }
1069 -
        func.blocks = try lowerFnBody(&mut fnLow, body);
1070 -
        func.isLeaf = fnLow.isLeaf;
1043 +
        let func = try lowerMethod(self, methodNode, qualName, receiverName, sig, body)
1044 +
            else continue;
1071 1045
        self.fns.append(func, self.allocator);
1072 1046
1073 1047
        let method = resolver::findTraitMethod(traitInfo, mName)
1074 1048
            else panic "lowerInstanceDecl: method not found in trait";
1075 1049
1106 1080
        isUndefined: false,
1107 1081
        values: &values[..traitInfo.methods.len as u32],
1108 1082
    }, self.allocator);
1109 1083
}
1110 1084
1085 +
/// Lower a method node into an IL function with the given qualified name.
1086 +
/// Shared by both instance methods and standalone methods.
1087 +
fn lowerMethod(
1088 +
    self: *mut Lowerer,
1089 +
    node: *ast::Node,
1090 +
    qualName: *[u8],
1091 +
    receiverName: *ast::Node,
1092 +
    sig: ast::FnSig,
1093 +
    body: *ast::Node,
1094 +
) -> ?*il::Fn throws (LowerError) {
1095 +
    let data = resolver::nodeData(self.resolver, node);
1096 +
    let case resolver::Type::Fn(fnType) = data.ty else {
1097 +
        throw LowerError::ExpectedFunction;
1098 +
    };
1099 +
    let sym = data.sym else throw LowerError::MissingSymbol(node);
1100 +
    registerFnSym(self, sym, qualName);
1101 +
1102 +
    let mut fnLow = fnLowerer(self, node, fnType, qualName);
1103 +
    if requiresReturnParam(fnType) {
1104 +
        fnLow.returnReg = nextReg(&mut fnLow);
1105 +
    }
1106 +
    let lowParams = try lowerParams(&mut fnLow, *fnType, sig.params, receiverName);
1107 +
    let func = try! alloc::alloc(self.arena, @sizeOf(il::Fn), @alignOf(il::Fn)) as *mut il::Fn;
1108 +
1109 +
    *func = il::Fn {
1110 +
        name: qualName,
1111 +
        params: lowParams,
1112 +
        returnType: ilType(self, *fnType.returnType),
1113 +
        isExtern: false,
1114 +
        isLeaf: true,
1115 +
        blocks: &[],
1116 +
    };
1117 +
    if fnType.throwList.len > 0 {
1118 +
        func.returnType = il::Type::W64;
1119 +
    }
1120 +
    func.blocks = try lowerFnBody(&mut fnLow, body);
1121 +
    func.isLeaf = fnLow.isLeaf;
1122 +
1123 +
    return func;
1124 +
}
1125 +
1126 +
/// Lower a standalone method declaration.
1127 +
/// Produces a function with qualified name `Type::method`.
1128 +
fn lowerMethodDecl(
1129 +
    self: *mut Lowerer,
1130 +
    node: *ast::Node,
1131 +
    name: *ast::Node,
1132 +
    receiverName: *ast::Node,
1133 +
    sig: ast::FnSig,
1134 +
    body: *ast::Node,
1135 +
) -> ?*il::Fn throws (LowerError) {
1136 +
    let sym = resolver::nodeData(self.resolver, node).sym
1137 +
        else throw LowerError::MissingSymbol(node);
1138 +
    let case ast::NodeValue::Ident(mName) = name.value
1139 +
        else throw LowerError::ExpectedIdentifier;
1140 +
    let me = resolver::findMethodBySymbol(self.resolver, sym)
1141 +
        else throw LowerError::MissingMetadata;
1142 +
    let qualName = instanceMethodName(self, nil, me.concreteTypeName, mName);
1143 +
1144 +
    return try lowerMethod(self, node, qualName, receiverName, sig, body);
1145 +
}
1146 +
1111 1147
/// Check if a function should be lowered.
1112 1148
fn shouldLowerFn(decl: *ast::FnDecl, buildTest: bool) -> bool {
1113 1149
    if checkAttr(decl.attrs, ast::Attribute::Test) {
1114 1150
        return buildTest;
1115 1151
    }
6025 6061
    // Type of the try expression, which is either the return type of the function
6026 6062
    // if successful, or an optional of it, if using `try?`.
6027 6063
    let tryExprTy = resolver::typeFor(self.low.resolver, node) else {
6028 6064
        throw LowerError::MissingType(node);
6029 6065
    };
6030 -
    // Check for trait method dispatch.
6066 +
    // Check for trait method dispatch or standalone method call.
6031 6067
    let mut resVal: il::Val = undefined;
6068 +
    let callNodeExtra = resolver::nodeData(self.low.resolver, t.expr).extra;
6032 6069
    if let case resolver::NodeExtra::TraitMethodCall {
6033 6070
        traitInfo, methodIndex
6034 -
    } = resolver::nodeData(self.low.resolver, t.expr).extra {
6071 +
    } = callNodeExtra {
6035 6072
        resVal = try lowerTraitMethodCall(self, t.expr, callExpr, traitInfo, methodIndex);
6073 +
    } else if let case resolver::NodeExtra::MethodCall { method } = callNodeExtra {
6074 +
        resVal = try lowerMethodCall(self, t.expr, callExpr, method);
6036 6075
    } else {
6037 6076
        resVal = try lowerCall(self, t.expr, callExpr);
6038 6077
    }
6039 6078
    let base = emitValToReg(self, resVal); // The result value.
6040 6079
    let tagReg = resultTagReg(self, base); // The result tag.
6443 6482
    }
6444 6483
    // Check for trait method dispatch.
6445 6484
    if let case resolver::NodeExtra::TraitMethodCall { traitInfo, methodIndex } = nodeData {
6446 6485
        return try lowerTraitMethodCall(self, node, call, traitInfo, methodIndex);
6447 6486
    }
6487 +
    // Check for standalone method call.
6488 +
    if let case resolver::NodeExtra::MethodCall { method } = nodeData {
6489 +
        return try lowerMethodCall(self, node, call, method);
6490 +
    }
6448 6491
    if let sym = resolver::nodeData(self.low.resolver, call.callee).sym {
6449 6492
        if let case resolver::SymbolData::Type(nominal) = sym.data {
6450 6493
            let case resolver::NominalType::Record(_) = *nominal else {
6451 6494
                throw LowerError::ExpectedRecord;
6452 6495
            };
6509 6552
        typ: il::Type::W64,
6510 6553
        dst: fnPtrReg,
6511 6554
        src: vtableReg,
6512 6555
        offset: slotOffset,
6513 6556
    });
6514 -
6515 -
    // Check if the method needs a hidden return parameter.
6516 6557
    let methodFnType = traitInfo.methods[methodIndex].fnType;
6517 -
    let retTy = *methodFnType.returnType;
6518 -
    let returnParam = requiresReturnParam(methodFnType);
6519 6558
6520 -
    // Build args: data pointer (receiver) + user args.
6521 -
    let argOffset: u32 = 1 if returnParam else 0;
6559 +
    // Build args: optional return param slot + data pointer (receiver) + user args.
6560 +
    let argOffset: u32 = 1 if requiresReturnParam(methodFnType) else 0;
6522 6561
    let args = try allocVals(self, call.args.len + 1 + argOffset);
6523 -
6524 -
    // Data pointer is the receiver (first argument after hidden return param).
6525 6562
    args[argOffset] = il::Val::Reg(dataReg);
6526 6563
6527 -
    // Lower user arguments.
6528 6564
    for arg, i in call.args {
6529 6565
        args[i + 1 + argOffset] = try lowerExpr(self, arg);
6530 6566
    }
6567 +
    return try emitCallValue(self, il::Val::Reg(fnPtrReg), methodFnType, args);
6568 +
}
6531 6569
6532 -
    // Allocate the return buffer when needed.
6533 -
    if returnParam {
6534 -
        if methodFnType.throwList.len > 0 {
6535 -
            let successType = *methodFnType.returnType;
6536 -
            let layout = resolver::getResultLayout(
6537 -
                successType, methodFnType.throwList);
6570 +
/// Emit a function call with return-parameter and small-aggregate handling.
6571 +
///
6572 +
/// All call lowering paths (regular, trait method, standalone method) converge
6573 +
/// here after preparing the callee value, function type, and argument array.
6574 +
/// The `args` slice must already include a slot at index zero for the hidden
6575 +
/// return parameter; that slot is filled by this function.
6576 +
fn emitCallValue(
6577 +
    self: *mut FnLowerer,
6578 +
    callee: il::Val,
6579 +
    fnInfo: *resolver::FnType,
6580 +
    args: *mut [il::Val],
6581 +
) -> il::Val throws (LowerError) {
6582 +
    let retTy = *fnInfo.returnType;
6538 6583
6584 +
    if requiresReturnParam(fnInfo) {
6585 +
        if fnInfo.throwList.len > 0 {
6586 +
            let layout = resolver::getResultLayout(retTy, fnInfo.throwList);
6539 6587
            args[0] = il::Val::Reg(emitReserveLayout(self, layout));
6540 6588
        } else {
6541 6589
            args[0] = il::Val::Reg(try emitReserve(self, retTy));
6542 6590
        }
6543 6591
        let dst = nextReg(self);
6544 6592
6545 6593
        emit(self, il::Instr::Call {
6546 6594
            retTy: il::Type::W64,
6547 6595
            dst,
6548 -
            func: il::Val::Reg(fnPtrReg),
6596 +
            func: callee,
6549 6597
            args,
6550 6598
        });
6551 6599
        return il::Val::Reg(dst);
6552 6600
    }
6553 -
6554 -
    // Scalar call: allocate destination register for non-void return types.
6555 6601
    let mut dst: ?il::Reg = nil;
6556 6602
    if retTy != resolver::Type::Void {
6557 6603
        dst = nextReg(self);
6558 6604
    }
6559 6605
    emit(self, il::Instr::Call {
6560 6606
        retTy: ilType(self.low, retTy),
6561 6607
        dst,
6562 -
        func: il::Val::Reg(fnPtrReg),
6608 +
        func: callee,
6563 6609
        args,
6564 6610
    });
6565 6611
6566 6612
    if let d = dst {
6567 6613
        if isSmallAggregate(retTy) {
6568 -
            let slot = emitReserveLayout(
6569 -
                self,
6570 -
                resolver::Layout {
6571 -
                    size: resolver::PTR_SIZE,
6572 -
                    alignment: resolver::PTR_SIZE
6573 -
                });
6574 -
6614 +
            let slot = emitReserveLayout(self, resolver::Layout {
6615 +
                size: resolver::PTR_SIZE,
6616 +
                alignment: resolver::PTR_SIZE,
6617 +
            });
6575 6618
            emit(self, il::Instr::Store {
6576 6619
                typ: il::Type::W64,
6577 6620
                src: il::Val::Reg(d),
6578 6621
                dst: slot,
6579 6622
                offset: 0,
6583 6626
        return il::Val::Reg(d);
6584 6627
    }
6585 6628
    return il::Val::Undef;
6586 6629
}
6587 6630
6631 +
/// Lower a method receiver expression to a pointer value.
6632 +
///
6633 +
/// If the parent is already a pointer type, the value is used directly.
6634 +
/// If the parent is a value type (eg. a local record), its address is taken.
6635 +
fn lowerReceiver(self: *mut FnLowerer, parent: *ast::Node, parentTy: resolver::Type) -> il::Val
6636 +
    throws (LowerError)
6637 +
{
6638 +
    if let case resolver::Type::Pointer { .. } = parentTy {
6639 +
        // Already a pointer: lower and use directly.
6640 +
        return try lowerExpr(self, parent);
6641 +
    }
6642 +
    // Value type: take its address by lowering it and returning the slot pointer.
6643 +
    // Aggregate types are already lowered as pointers to stack slots.
6644 +
    let val = try lowerExpr(self, parent);
6645 +
    if isAggregateType(parentTy) {
6646 +
        return val;
6647 +
    }
6648 +
    // Scalar value: store to a stack slot and return the slot pointer.
6649 +
    let layout = resolver::getLayout(self.low.resolver, parent, parentTy);
6650 +
    let slot = emitReserveLayout(self, layout);
6651 +
    try emitStore(self, slot, 0, parentTy, val);
6652 +
6653 +
    return il::Val::Reg(slot);
6654 +
}
6655 +
6656 +
/// Lower a standalone method call via direct dispatch.
6657 +
///
6658 +
/// Given `obj.method(args)` where `method` is a standalone method on a concrete type,
6659 +
/// emits a direct call with the receiver address as the first argument:
6660 +
///
6661 +
///     call <retTy> %ret @Type::method(&obj, args...)
6662 +
///
6663 +
fn lowerMethodCall(
6664 +
    self: *mut FnLowerer,
6665 +
    node: *ast::Node,
6666 +
    call: ast::Call,
6667 +
    method: *resolver::MethodEntry,
6668 +
) -> il::Val throws (LowerError) {
6669 +
    let case ast::NodeValue::FieldAccess(access) = call.callee.value
6670 +
        else throw LowerError::MissingMetadata;
6671 +
6672 +
    // Get the receiver as a pointer.
6673 +
    let parentTy = resolver::typeFor(self.low.resolver, access.parent)
6674 +
        else throw LowerError::MissingType(access.parent);
6675 +
    let receiverVal = try lowerReceiver(self, access.parent, parentTy);
6676 +
6677 +
    let qualName = instanceMethodName(self.low, nil, method.concreteTypeName, method.name);
6678 +
    let case resolver::SymbolData::Value { type: resolver::Type::Fn(fnInfo), .. } = method.symbol.data
6679 +
        else panic "lowerMethodCall: expected Fn type on method symbol";
6680 +
6681 +
    // Build args: optional return param slot + receiver + user args.
6682 +
    let argOffset: u32 = 1 if requiresReturnParam(fnInfo) else 0;
6683 +
    let args = try allocVals(self, call.args.len + 1 + argOffset);
6684 +
    args[argOffset] = receiverVal;
6685 +
    for arg, i in call.args {
6686 +
        args[i + 1 + argOffset] = try lowerExpr(self, arg);
6687 +
    }
6688 +
    return try emitCallValue(self, il::Val::FnAddr(qualName), fnInfo, args);
6689 +
}
6690 +
6588 6691
/// Check if a call is to a compiler intrinsic and lower it directly.
6589 6692
fn lowerIntrinsicCall(self: *mut FnLowerer, call: ast::Call) -> ?il::Val throws (LowerError) {
6590 6693
    // Get the callee symbol and check if it's marked as an intrinsic.
6591 6694
    let sym = resolver::nodeData(self.low.resolver, call.callee).sym else {
6592 6695
        // Expressions or function pointers may not have an associated symbol.
6665 6768
        throw LowerError::MissingType(call.callee);
6666 6769
    };
6667 6770
    let case resolver::Type::Fn(fnInfo) = calleeTy else {
6668 6771
        throw LowerError::ExpectedFunction;
6669 6772
    };
6670 -
    let retTy = resolver::typeFor(self.low.resolver, node) else {
6671 -
        throw LowerError::MissingType(node);
6672 -
    };
6673 -
    let returnParam = requiresReturnParam(fnInfo);
6674 -
6675 -
    // Lower function value and arguments, reserving an extra slot for the
6676 -
    // hidden return buffer when needed.
6677 6773
    let callee = try lowerCallee(self, call.callee);
6678 -
    let offset: u32 = 1 if returnParam else 0;
6774 +
    let offset: u32 = 1 if requiresReturnParam(fnInfo) else 0;
6679 6775
    let args = try allocVals(self, call.args.len + offset);
6680 6776
    for arg, i in call.args {
6681 6777
        args[i + offset] = try lowerExpr(self, arg);
6682 6778
    }
6683 6779
6684 -
    // Allocate the return buffer when needed.
6685 -
    if returnParam {
6686 -
        if fnInfo.throwList.len > 0 {
6687 -
            let successType = *fnInfo.returnType;
6688 -
            let layout = resolver::getResultLayout(successType, fnInfo.throwList);
6689 -
6690 -
            args[0] = il::Val::Reg(emitReserveLayout(self, layout));
6691 -
        } else {
6692 -
            args[0] = il::Val::Reg(try emitReserve(self, retTy));
6693 -
        }
6694 -
        let dst = nextReg(self);
6695 -
        emit(self, il::Instr::Call {
6696 -
            retTy: il::Type::W64,
6697 -
            dst,
6698 -
            func: callee,
6699 -
            args,
6700 -
        });
6701 -
        return il::Val::Reg(dst);
6702 -
    }
6703 -
6704 -
    // Scalar call: allocate destination register for non-void return types.
6705 -
    let mut dst: ?il::Reg = nil;
6706 -
    if retTy != resolver::Type::Void {
6707 -
        dst = nextReg(self);
6708 -
    }
6709 -
    emit(self, il::Instr::Call {
6710 -
        retTy: ilType(self.low, retTy),
6711 -
        dst,
6712 -
        func: callee,
6713 -
        args,
6714 -
    });
6715 -
6716 -
    // Non-void functions produce a value in a register, while void functions
6717 -
    // return an undefined value that shouldn't be used.
6718 -
    if let d = dst {
6719 -
        if isSmallAggregate(retTy) {
6720 -
            let slot = emitReserveLayout(
6721 -
                self,
6722 -
                resolver::Layout {
6723 -
                    size: resolver::PTR_SIZE,
6724 -
                    alignment: resolver::PTR_SIZE
6725 -
                });
6726 -
            emit(self, il::Instr::Store {
6727 -
                typ: il::Type::W64,
6728 -
                src: il::Val::Reg(d),
6729 -
                dst: slot,
6730 -
                offset: 0,
6731 -
            });
6732 -
            return il::Val::Reg(slot);
6733 -
        }
6734 -
        return il::Val::Reg(d);
6735 -
    }
6736 -
    return il::Val::Undef;
6780 +
    return try emitCallValue(self, callee, fnInfo, args);
6737 6781
}
6738 6782
6739 6783
/// Apply coercions requested by the resolver.
6740 6784
fn applyCoercion(self: *mut FnLowerer, node: *ast::Node, val: il::Val) -> il::Val throws (LowerError) {
6741 6785
    let coerce = resolver::coercionFor(self.low.resolver, node) else {
lib/std/lang/parser.rad +16 -6
683 683
        case ast::NodeValue::FnDecl(_) => return false,
684 684
        case ast::NodeValue::RecordDecl(_) => return false,
685 685
        case ast::NodeValue::UnionDecl(_) => return false,
686 686
        case ast::NodeValue::TraitDecl { .. } => return false,
687 687
        case ast::NodeValue::InstanceDecl { .. } => return false,
688 +
        case ast::NodeValue::MethodDecl { .. } => return false,
688 689
        else => return true,
689 690
    }
690 691
}
691 692
692 693
/// Parse a primary leaf expression without postfix operators.
1965 1966
/// Parse a function declaration.
1966 1967
fn parseFnDecl(p: *mut Parser, attrs: ?ast::Attributes) -> *ast::Node
1967 1968
    throws (ParseError)
1968 1969
{
1969 1970
    try expect(p, scanner::TokenKind::Fn, "expected `fn`");
1971 +
1972 +
    // Method syntax: `fn (recv: *Type) name(params) { body }`.
1973 +
    if check(p, scanner::TokenKind::LParen) {
1974 +
        return try parseMethodDecl(p, attrs);
1975 +
    }
1970 1976
    let name = try parseIdent(p, "expected function name");
1971 1977
    let sig = try parseFnTypeSig(p);
1972 1978
    let mut body: ?*ast::Node = nil;
1973 1979
1974 1980
    if let a = attrs; ast::attributesContains(&a, ast::Attribute::Extern) {
2413 2419
2414 2420
    let mut methods = ast::nodeSlice(p.arena, ast::MAX_TRAIT_METHODS);
2415 2421
    while not check(p, scanner::TokenKind::RBrace) and
2416 2422
          not check(p, scanner::TokenKind::Eof)
2417 2423
    {
2418 -
        let method = try parseInstanceMethodDecl(p);
2424 +
        try expect(p, scanner::TokenKind::Fn, "expected `fn`");
2425 +
        let method = try parseMethodDecl(p, nil);
2426 +
2419 2427
        methods.append(method, p.allocator);
2420 2428
    }
2421 2429
    try expect(p, scanner::TokenKind::RBrace, "expected `}` after instance methods");
2422 2430
2423 2431
    return node(p, ast::NodeValue::InstanceDecl { traitName, targetType, methods });
2424 2432
}
2425 2433
2426 -
/// Parse an instance method declaration.
2434 +
/// Parse a method declaration with a receiver.
2427 2435
/// Syntax: `fn (t: *mut Type) fnord(<params>) -> ReturnType { body }`
2428 -
fn parseInstanceMethodDecl(p: *mut Parser) -> *ast::Node
2436 +
///
2437 +
/// Used both inside `instance` blocks and as standalone methods at the top level.
2438 +
/// Expects the `fn` token to have already been consumed.
2439 +
fn parseMethodDecl(p: *mut Parser, attrs: ?ast::Attributes) -> *ast::Node
2429 2440
    throws (ParseError)
2430 2441
{
2431 -
    try expect(p, scanner::TokenKind::Fn, "expected `fn`");
2432 2442
    try expect(p, scanner::TokenKind::LParen, "expected `(` before receiver");
2433 2443
2434 2444
    let receiverName = try parseIdent(p, "expected receiver name");
2435 2445
    try expect(p, scanner::TokenKind::Colon, "expected `:` after receiver name");
2436 2446
    let receiverType = try parseType(p);
2439 2449
2440 2450
    let name = try parseIdent(p, "expected method name");
2441 2451
    let sig = try parseFnTypeSig(p);
2442 2452
    let body = try parseBlock(p);
2443 2453
2444 -
    return node(p, ast::NodeValue::InstanceMethodDecl {
2445 -
        name, receiverName, receiverType, sig, body,
2454 +
    return node(p, ast::NodeValue::MethodDecl {
2455 +
        name, receiverName, receiverType, sig, body, attrs,
2446 2456
    });
2447 2457
}
2448 2458
2449 2459
/// Parse a comma-separated list enclosed by the given delimiters.
2450 2460
fn parseList(
lib/std/lang/resolver.rad +261 -36
42 42
pub const MAX_UNION_VARIANTS: u32 = 128;
43 43
/// Maximum nesting of loops.
44 44
pub const MAX_LOOP_DEPTH: u32 = 16;
45 45
/// Maximum trait instances.
46 46
pub const MAX_INSTANCES: u32 = 128;
47 +
/// Maximum standalone methods (across all types).
48 +
pub const MAX_METHODS: u32 = 256;
47 49
48 50
/// Trait definition stored in the resolver.
49 51
pub record TraitType {
50 52
    /// Trait name.
51 53
    name: *[u8],
79 81
    moduleId: u16,
80 82
    /// Method symbols for each trait method, in declaration order.
81 83
    methods: *mut [*mut Symbol],
82 84
}
83 85
86 +
/// An entry in the method registry.
87 +
pub record MethodEntry {
88 +
    /// Concrete type that owns the method.
89 +
    concreteType: Type,
90 +
    /// Name of the concrete type.
91 +
    concreteTypeName: *[u8],
92 +
    /// Method name.
93 +
    name: *[u8],
94 +
    /// Function type excluding the receiver.
95 +
    fnType: *FnType,
96 +
    /// Whether the receiver is mutable.
97 +
    mutable: bool,
98 +
    /// Symbol for the method.
99 +
    symbol: *mut Symbol,
100 +
}
101 +
84 102
/// Identifier for the synthetic `len` field.
85 103
pub const LEN_FIELD: *[u8] = "len";
86 104
/// Identifier for the synthetic `ptr` field.
87 105
pub const PTR_FIELD: *[u8] = "ptr";
88 106
/// Identifier for the synthetic `cap` field.
618 636
        /// Trait definition.
619 637
        traitInfo: *TraitType,
620 638
        /// Method index in the v-table.
621 639
        methodIndex: u32,
622 640
    },
641 +
    /// Standalone method call metadata.
642 +
    MethodCall { method: *MethodEntry },
623 643
    /// Slice `.append(val, allocator)` method call.
624 644
    SliceAppend { elemType: *Type },
625 645
    /// Slice `.delete(index)` method call.
626 646
    SliceDelete { elemType: *Type },
627 647
}
755 775
    moduleScopes: [?*mut Scope; module::MAX_MODULES],
756 776
    /// Trait instance registry.
757 777
    instances: [InstanceEntry; MAX_INSTANCES],
758 778
    /// Number of registered instances.
759 779
    instancesLen: u32,
780 +
    /// Standalone method registry.
781 +
    methods: [MethodEntry; MAX_METHODS],
782 +
    /// Number of registered standalone methods.
783 +
    methodsLen: u32,
760 784
}
761 785
762 786
/// Internal error sentinel thrown when analysis cannot proceed.
763 787
pub union ResolveError {
764 788
    Failure,
900 924
        // TODO: Shouldn't be undefined.
901 925
        moduleGraph: undefined,
902 926
        moduleScopes,
903 927
        instances: undefined,
904 928
        instancesLen: 0,
929 +
        methods: undefined,
930 +
        methodsLen: 0,
905 931
    };
906 932
}
907 933
908 934
/// Return `true` if there are no errors in the diagnostics.
909 935
pub fn success(diag: *Diagnostics) -> bool {
2452 2478
            // Handled in previous pass.
2453 2479
        }
2454 2480
        case ast::NodeValue::InstanceDecl { traitName, targetType, methods } => {
2455 2481
            try resolveInstanceDecl(self, node, traitName, targetType, methods);
2456 2482
        }
2483 +
        case ast::NodeValue::MethodDecl { name, receiverName, receiverType, sig, body, attrs } => {
2484 +
            try resolveMethodDecl(self, node, name, receiverName, receiverType, sig, attrs);
2485 +
        }
2457 2486
        else => {
2458 2487
            // Ignore non-declaration nodes.
2459 2488
        }
2460 2489
    }
2461 2490
}
2492 2521
            // Skip: already analyzed in declaration phase.
2493 2522
        }
2494 2523
        case ast::NodeValue::InstanceDecl { methods, .. } => {
2495 2524
            try resolveInstanceMethodBodies(self, methods);
2496 2525
        }
2526 +
        case ast::NodeValue::MethodDecl { receiverName, sig, body, .. } => {
2527 +
            try resolveMethodBody(self, node, receiverName, sig, body);
2528 +
        }
2497 2529
        else => {
2498 2530
            // FIXME: This allows module-level statements that should
2499 2531
            // normally only be valid inside function bodies. We currently
2500 2532
            // need this because of how tests are written, but it should
2501 2533
            // be eventually removed.
3442 3474
    // Track which trait methods are covered by the instance.
3443 3475
    let mut covered: [bool; ast::MAX_TRAIT_METHODS] = [false; ast::MAX_TRAIT_METHODS];
3444 3476
3445 3477
    // Match each instance method to a trait method.
3446 3478
    for methodNode in methods {
3447 -
        let case ast::NodeValue::InstanceMethodDecl {
3448 -
            name, receiverName, receiverType, sig, body
3479 +
        let case ast::NodeValue::MethodDecl {
3480 +
            name, receiverName, receiverType, sig, body, ..
3449 3481
        } = methodNode.value else continue;
3450 3482
3451 3483
        let methodName = try nodeName(self, name);
3452 3484
3453 3485
        // Find the matching trait method.
3594 3626
/// Resolve instance method bodies.
3595 3627
fn resolveInstanceMethodBodies(self: *mut Resolver, methods: *mut [*ast::Node])
3596 3628
    throws (ResolveError)
3597 3629
{
3598 3630
    for methodNode in methods {
3599 -
        let case ast::NodeValue::InstanceMethodDecl {
3600 -
            name, receiverName, receiverType, sig, body
3631 +
        let case ast::NodeValue::MethodDecl {
3632 +
            name, receiverName, receiverType, sig, body, ..
3601 3633
        } = methodNode.value else continue;
3602 3634
3603 3635
        // Symbol may be absent if [`resolveInstanceDecl`] reported an error
3604 3636
        // for this method (eg. unknown method name). Skip gracefully.
3605 3637
        let sym = symbolFor(self, methodNode)
3606 3638
            else continue;
3607 -
        let case SymbolData::Value { type: Type::Fn(fnType), .. } = sym.data
3608 -
            else panic "resolveInstanceMethodBodies: expected value symbol";
3609 3639
3610 -
        // Enter function scope.
3611 -
        enterFn(self, methodNode, fnType);
3640 +
        try resolveMethodBody(self, methodNode, receiverName, sig, body);
3641 +
    }
3642 +
}
3643 +
3644 +
/// Resolve a method body shared by instance methods and standalone methods.
3645 +
/// Binds the receiver and parameters, then type-checks the body.
3646 +
fn resolveMethodBody(
3647 +
    self: *mut Resolver,
3648 +
    node: *ast::Node,
3649 +
    receiverName: *ast::Node,
3650 +
    sig: ast::FnSig,
3651 +
    body: *ast::Node,
3652 +
) throws (ResolveError) {
3653 +
    let sym = symbolFor(self, node)
3654 +
        else throw emitError(self, node, ErrorKind::Internal);
3655 +
    let case SymbolData::Value { type: Type::Fn(fnType), .. } = sym.data
3656 +
        else panic "resolveMethodBody: expected value symbol";
3612 3657
3613 -
        // Bind the receiver parameter.
3614 -
        let receiverTy = *fnType.paramTypes[0];
3615 -
        try bindValueIdent(self, receiverName, receiverName, receiverTy, false, 0, 0) catch {
3616 -
            exitFn(self);
3617 -
            throw ResolveError::Failure;
3618 -
        };
3619 -
        // Bind the remaining parameters from the signature.
3620 -
        for paramNode in sig.params {
3621 -
            let paramTy = try infer(self, paramNode) catch {
3622 -
                exitFn(self);
3623 -
                throw ResolveError::Failure;
3624 -
            };
3625 -
        }
3658 +
    // Enter function scope.
3659 +
    enterFn(self, node, fnType);
3626 3660
3627 -
        // Resolve the body.
3628 -
        let retTy = *fnType.returnType;
3629 -
        let bodyTy = try checkAssignable(self, body, Type::Void) catch {
3661 +
    // Bind the receiver parameter.
3662 +
    let receiverTy = *fnType.paramTypes[0];
3663 +
    try bindValueIdent(self, receiverName, receiverName, receiverTy, false, 0, 0) catch {
3664 +
        exitFn(self);
3665 +
        throw ResolveError::Failure;
3666 +
    };
3667 +
    // Bind the remaining parameters from the signature.
3668 +
    for paramNode in sig.params {
3669 +
        let paramTy = try infer(self, paramNode) catch {
3630 3670
            exitFn(self);
3631 3671
            throw ResolveError::Failure;
3632 3672
        };
3633 -
        if retTy != Type::Void and bodyTy != Type::Never {
3634 -
            exitFn(self);
3635 -
            throw emitError(self, body, ErrorKind::FnMissingReturn);
3636 -
        }
3673 +
    }
3674 +
3675 +
    // Resolve the body.
3676 +
    let retTy = *fnType.returnType;
3677 +
    let bodyTy = try checkAssignable(self, body, Type::Void) catch {
3678 +
        exitFn(self);
3679 +
        throw ResolveError::Failure;
3680 +
    };
3681 +
    if retTy != Type::Void and bodyTy != Type::Never {
3637 3682
        exitFn(self);
3683 +
        throw emitError(self, body, ErrorKind::FnMissingReturn);
3638 3684
    }
3685 +
    exitFn(self);
3686 +
}
3687 +
3688 +
/// Resolve a standalone method declaration (signature only).
3689 +
/// Validates the receiver type and registers the method in the method table.
3690 +
/// Extract the type name from a resolved receiver type node (`*T` or `*mut T`).
3691 +
fn receiverTypeName(self: *mut Resolver, receiverType: *ast::Node) -> *[u8] throws (ResolveError) {
3692 +
    let case ast::NodeValue::TypeSig(ast::TypeSig::Pointer { valueType, .. }) = receiverType.value
3693 +
        else throw emitError(self, receiverType, ErrorKind::TraitReceiverMismatch);
3694 +
    let case ast::NodeValue::TypeSig(ast::TypeSig::Nominal(nameNode)) = valueType.value
3695 +
        else throw emitError(self, receiverType, ErrorKind::Internal);
3696 +
    let sym = symbolFor(self, nameNode)
3697 +
        else throw emitError(self, receiverType, ErrorKind::Internal);
3698 +
3699 +
    return sym.name;
3700 +
}
3701 +
3702 +
fn resolveMethodDecl(
3703 +
    self: *mut Resolver,
3704 +
    node: *ast::Node,
3705 +
    name: *ast::Node,
3706 +
    receiverName: *ast::Node,
3707 +
    receiverType: *ast::Node,
3708 +
    sig: ast::FnSig,
3709 +
    attrs: ?ast::Attributes,
3710 +
) throws (ResolveError) {
3711 +
    // Resolve the receiver type: must be `*Type` or `*mut Type` pointing to a
3712 +
    // nominal type.
3713 +
    let fullReceiverTy = try infer(self, receiverType);
3714 +
    let case Type::Pointer { target, mutable: receiverMut } = fullReceiverTy
3715 +
        else throw emitError(self, receiverType, ErrorKind::TraitReceiverMismatch);
3716 +
    let concreteType = *target;
3717 +
    let case Type::Nominal(nominalTy) = concreteType
3718 +
        else throw emitError(self, receiverType, ErrorKind::ExpectedRecord);
3719 +
    try ensureNominalResolved(self, nominalTy, receiverType);
3720 +
3721 +
    // Get the type name from the inner type node's symbol.
3722 +
    let typeName = try receiverTypeName(self, receiverType);
3723 +
    let methodName = try nodeName(self, name);
3724 +
3725 +
    // Reject duplicate method for the same (type, name).
3726 +
    if let _ = findMethod(self, concreteType, methodName) {
3727 +
        throw emitError(self, name, ErrorKind::DuplicateBinding(methodName));
3728 +
    }
3729 +
3730 +
    // Resolve parameter types.
3731 +
    let a = alloc::arenaAllocator(&mut self.arena);
3732 +
    let mut paramTypes: *mut [*Type] = &mut [];
3733 +
3734 +
    // Receiver is the first parameter.
3735 +
    let receiverPtrType = Type::Pointer {
3736 +
        target: allocType(self, concreteType),
3737 +
        mutable: receiverMut,
3738 +
    };
3739 +
    paramTypes.append(allocType(self, receiverPtrType), a);
3740 +
3741 +
    for paramNode in sig.params {
3742 +
        let case ast::NodeValue::FnParam(param) = paramNode.value
3743 +
            else throw emitError(self, paramNode, ErrorKind::ExpectedIdentifier);
3744 +
        let paramTy = try resolveValueType(self, param.type);
3745 +
        paramTypes.append(allocType(self, paramTy), a);
3746 +
    }
3747 +
3748 +
    // Resolve return type.
3749 +
    let mut returnType = Type::Void;
3750 +
    if let retNode = sig.returnType {
3751 +
        returnType = try resolveValueType(self, retNode);
3752 +
    }
3753 +
3754 +
    // Resolve throw list.
3755 +
    let mut throwTypes: *mut [*Type] = &mut [];
3756 +
    for throwNode in sig.throwList {
3757 +
        let throwTy = try resolveValueType(self, throwNode);
3758 +
        throwTypes.append(allocType(self, throwTy), a);
3759 +
    }
3760 +
3761 +
    let retTypePtr = allocType(self, returnType);
3762 +
    let throwList = &throwTypes[..];
3763 +
3764 +
    // Full function type (receiver + params) for lowering.
3765 +
    let fullFnType = FnType {
3766 +
        paramTypes: &paramTypes[..], returnType: retTypePtr, throwList, localCount: 0,
3767 +
    };
3768 +
    let fnTy = Type::Fn(allocFnType(self, fullFnType));
3769 +
3770 +
    // Function type excluding receiver, for call arg checking.
3771 +
    let checkFnType = FnType {
3772 +
        paramTypes: &paramTypes[1..], returnType: retTypePtr, throwList, localCount: 0,
3773 +
    };
3774 +
3775 +
    // Compute attribute mask.
3776 +
    let mut attrMask: u32 = 0;
3777 +
    if let a = attrs {
3778 +
        for attrNode in a.list {
3779 +
            if let case ast::NodeValue::Attribute(attr) = attrNode.value {
3780 +
                attrMask = attrMask | (attr as u32);
3781 +
            }
3782 +
        }
3783 +
    }
3784 +
3785 +
    // Create a symbol for the method without binding it into the module scope.
3786 +
    let sym = allocSymbol(self, SymbolData::Value {
3787 +
        mutable: false, alignment: 0, type: fnTy, addressTaken: false,
3788 +
    }, methodName, node, attrMask);
3789 +
3790 +
    setNodeSymbol(self, node, sym);
3791 +
    setNodeType(self, node, fnTy);
3792 +
    setNodeType(self, name, fnTy);
3793 +
3794 +
    // Register in the method table.
3795 +
    if self.methodsLen >= MAX_METHODS {
3796 +
        throw emitError(self, node, ErrorKind::Internal);
3797 +
    }
3798 +
    self.methods[self.methodsLen] = MethodEntry {
3799 +
        concreteType,
3800 +
        concreteTypeName: typeName,
3801 +
        name: methodName,
3802 +
        fnType: allocFnType(self, checkFnType),
3803 +
        mutable: receiverMut,
3804 +
        symbol: sym,
3805 +
    };
3806 +
    self.methodsLen += 1;
3639 3807
}
3640 3808
3641 3809
/// Look up an instance entry by trait and concrete type.
3642 3810
fn findInstance(self: *Resolver, traitInfo: *TraitType, concreteType: Type) -> ?*InstanceEntry {
3643 3811
    for i in 0..self.instancesLen {
3647 3815
        }
3648 3816
    }
3649 3817
    return nil;
3650 3818
}
3651 3819
3820 +
/// Look up a standalone method by concrete type and name.
3821 +
pub fn findMethod(self: *Resolver, concreteType: Type, name: *[u8]) -> ?*MethodEntry {
3822 +
    for i in 0..self.methodsLen {
3823 +
        let entry = &self.methods[i];
3824 +
        if typesEqual(entry.concreteType, concreteType) and entry.name == name {
3825 +
            return entry;
3826 +
        }
3827 +
    }
3828 +
    return nil;
3829 +
}
3830 +
3831 +
/// Look up a standalone method entry by its symbol.
3832 +
pub fn findMethodBySymbol(self: *Resolver, sym: *mut Symbol) -> ?*MethodEntry {
3833 +
    for i in 0..self.methodsLen {
3834 +
        let entry = &self.methods[i];
3835 +
        if entry.symbol == sym {
3836 +
            return entry;
3837 +
        }
3838 +
    }
3839 +
    return nil;
3840 +
}
3841 +
3652 3842
/// Resolve union variant types after all type names are bound (Phase 2 of type resolution).
3653 3843
fn resolveUnionBody(self: *mut Resolver, node: *ast::Node, decl: ast::UnionDecl)
3654 3844
    throws (ResolveError)
3655 3845
{
3656 3846
    // Get the type symbol that was bound to this declaration node.
4855 5045
            try checkCallArgs(self, node, call, method.fnType, ctx);
4856 5046
            setTraitMethodCall(self, node, traitInfo, method.index);
4857 5047
4858 5048
            return setNodeType(self, node, *method.fnType.returnType);
4859 5049
        }
5050 +
5051 +
        // Check for a standalone method call on a concrete type.
5052 +
        if let case Type::Nominal(_) = subjectTy {
5053 +
            let methodName = try nodeName(self, access.child);
5054 +
            if let method = findMethod(self, subjectTy, methodName) {
5055 +
                // Reject mutable-receiver methods on immutable bindings.
5056 +
                // If the parent is already a mutable pointer, the receiver is fine.
5057 +
                // Otherwise, check that the parent can yield a mutable borrow.
5058 +
                if method.mutable {
5059 +
                    let mut isMutPtr = false;
5060 +
                    if let case Type::Pointer { mutable, .. } = parentTy {
5061 +
                        isMutPtr = mutable;
5062 +
                    }
5063 +
                    if not isMutPtr and not (try canBorrowMutFrom(self, access.parent)) {
5064 +
                        throw emitError(self, access.parent, ErrorKind::ImmutableBinding);
5065 +
                    }
5066 +
                }
5067 +
                // Check arguments (excluding receiver).
5068 +
                try checkCallArgs(self, node, call, method.fnType, ctx);
5069 +
                self.nodeData.entries[node.id].extra = NodeExtra::MethodCall { method };
5070 +
5071 +
                return setNodeType(self, node, *method.fnType.returnType);
5072 +
            }
5073 +
        }
4860 5074
    }
4861 5075
    let case Type::Fn(info) = calleeTy else {
4862 5076
        // TODO: Emit type error.
4863 5077
        panic;
4864 5078
    };
5389 5603
5390 5604
    match subjectTy {
5391 5605
        case Type::Nominal(NominalType::Record(recordType)) => {
5392 5606
            let fieldNode = access.child;
5393 5607
            let fieldName = try nodeName(self, fieldNode);
5394 -
            let fieldIndex = findRecordField(&recordType, fieldName)
5395 -
                else throw emitError(self, node, ErrorKind::RecordFieldUnknown(fieldName));
5396 -
            let fieldTy = recordType.fields[fieldIndex].fieldType;
5397 -
5398 -
            setRecordFieldIndex(self, fieldNode, fieldIndex);
5399 -
5400 -
            return setNodeType(self, node, fieldTy);
5608 +
            if let fieldIndex = findRecordField(&recordType, fieldName) {
5609 +
                let fieldTy = recordType.fields[fieldIndex].fieldType;
5610 +
                setRecordFieldIndex(self, fieldNode, fieldIndex);
5611 +
                return setNodeType(self, node, fieldTy);
5612 +
            }
5613 +
            // Not a field: check for a standalone method.
5614 +
            if let method = findMethod(self, subjectTy, fieldName) {
5615 +
                return setNodeType(self, node, Type::Fn(method.fnType));
5616 +
            }
5617 +
            throw emitError(self, node, ErrorKind::RecordFieldUnknown(fieldName));
5401 5618
        }
5402 5619
        case Type::Array(arrayInfo) => {
5403 5620
            let fieldNode = access.child;
5404 5621
            let fieldName = try nodeName(self, fieldNode);
5405 5622
5438 5655
            let method = findTraitMethod(traitInfo, fieldName)
5439 5656
                else throw emitError(self, node, ErrorKind::RecordFieldUnknown(fieldName));
5440 5657
5441 5658
            return setNodeType(self, node, Type::Fn(method.fnType));
5442 5659
        }
5443 -
        else => {}
5660 +
        else => {
5661 +
            // Check for standalone methods on any nominal type (e.g. unions).
5662 +
            if let case Type::Nominal(_) = subjectTy {
5663 +
                let fieldName = try nodeName(self, access.child);
5664 +
                if let method = findMethod(self, subjectTy, fieldName) {
5665 +
                    return setNodeType(self, node, Type::Fn(method.fnType));
5666 +
                }
5667 +
            }
5668 +
        }
5444 5669
    }
5445 5670
    // FIXME: We can't move this to the `else` branch due to a resolver bug.
5446 5671
    throw emitError(self, access.parent, ErrorKind::ExpectedRecord);
5447 5672
}
5448 5673
test/tests/method.basic.rad added +34 -0
1 +
//! returns: 0
2 +
//! Basic standalone method test.
3 +
4 +
record Point {
5 +
    x: i32,
6 +
    y: i32,
7 +
}
8 +
9 +
fn (p: *Point) sum() -> i32 {
10 +
    return p.x + p.y;
11 +
}
12 +
13 +
fn (p: *mut Point) translate(dx: i32, dy: i32) {
14 +
    p.x = p.x + dx;
15 +
    p.y = p.y + dy;
16 +
}
17 +
18 +
@default fn main() -> i32 {
19 +
    let mut pt = Point { x: 3, y: 4 };
20 +
21 +
    // Call immutable method.
22 +
    assert pt.sum() == 7;
23 +
24 +
    // Call mutable method.
25 +
    pt.translate(10, 20);
26 +
    assert pt.x == 13;
27 +
    assert pt.y == 24;
28 +
29 +
    // Call via pointer.
30 +
    let ptr = &pt;
31 +
    assert ptr.sum() == 37;
32 +
33 +
    return 0;
34 +
}
test/tests/method.chain.rad added +34 -0
1 +
//! returns: 0
2 +
//! Method chaining via mutable pointer returns.
3 +
4 +
record Builder {
5 +
    x: i32,
6 +
    y: i32,
7 +
}
8 +
9 +
fn (b: *mut Builder) setX(x: i32) -> *mut Builder {
10 +
    b.x = x;
11 +
    return b;
12 +
}
13 +
14 +
fn (b: *mut Builder) setY(y: i32) -> *mut Builder {
15 +
    b.y = y;
16 +
    return b;
17 +
}
18 +
19 +
fn (b: *Builder) sum() -> i32 {
20 +
    return b.x + b.y;
21 +
}
22 +
23 +
@default fn main() -> i32 {
24 +
    let mut b = Builder { x: 0, y: 0 };
25 +
26 +
    // Chain method calls.
27 +
    let p = b.setX(10).setY(20);
28 +
    assert p.sum() == 30;
29 +
30 +
    // Original also modified.
31 +
    assert b.sum() == 30;
32 +
33 +
    return 0;
34 +
}
test/tests/method.multiple.rad added +48 -0
1 +
//! returns: 0
2 +
//! Multiple methods on the same type.
3 +
4 +
record Vec2 {
5 +
    x: i32,
6 +
    y: i32,
7 +
}
8 +
9 +
fn (v: *Vec2) magnitudeSq() -> i32 {
10 +
    return v.x * v.x + v.y * v.y;
11 +
}
12 +
13 +
fn (v: *Vec2) dot(other: *Vec2) -> i32 {
14 +
    return v.x * other.x + v.y * other.y;
15 +
}
16 +
17 +
fn (v: *mut Vec2) add(other: *Vec2) {
18 +
    v.x = v.x + other.x;
19 +
    v.y = v.y + other.y;
20 +
}
21 +
22 +
fn (v: *mut Vec2) scale(factor: i32) {
23 +
    v.x = v.x * factor;
24 +
    v.y = v.y * factor;
25 +
}
26 +
27 +
@default fn main() -> i32 {
28 +
    let mut a = Vec2 { x: 3, y: 4 };
29 +
    let b = Vec2 { x: 1, y: 2 };
30 +
31 +
    // Immutable method.
32 +
    assert a.magnitudeSq() == 25;
33 +
34 +
    // Method with pointer parameter.
35 +
    assert a.dot(&b) == 11;
36 +
37 +
    // Mutable method with pointer parameter.
38 +
    a.add(&b);
39 +
    assert a.x == 4;
40 +
    assert a.y == 6;
41 +
42 +
    // Mutable method with scalar parameter.
43 +
    a.scale(2);
44 +
    assert a.x == 8;
45 +
    assert a.y == 12;
46 +
47 +
    return 0;
48 +
}
test/tests/method.ptr.rad added +39 -0
1 +
//! returns: 0
2 +
//! Methods called via various pointer indirections.
3 +
4 +
record Counter {
5 +
    value: i32,
6 +
}
7 +
8 +
fn (c: *Counter) get() -> i32 {
9 +
    return c.value;
10 +
}
11 +
12 +
fn (c: *mut Counter) inc() {
13 +
    c.value = c.value + 1;
14 +
}
15 +
16 +
@default fn main() -> i32 {
17 +
    let mut c = Counter { value: 0 };
18 +
19 +
    // Direct call on value.
20 +
    assert c.get() == 0;
21 +
22 +
    // Mutable method on value.
23 +
    c.inc();
24 +
    assert c.get() == 1;
25 +
26 +
    // Call via immutable pointer.
27 +
    let p = &c;
28 +
    assert p.get() == 1;
29 +
30 +
    // Call via mutable pointer.
31 +
    let mp = &mut c;
32 +
    mp.inc();
33 +
    assert mp.get() == 2;
34 +
35 +
    // Original value also updated.
36 +
    assert c.get() == 2;
37 +
38 +
    return 0;
39 +
}
test/tests/method.pub.rad added +16 -0
1 +
//! returns: 0
2 +
//! Public attribute on standalone methods.
3 +
4 +
record Foo {
5 +
    x: i32,
6 +
}
7 +
8 +
pub fn (f: *Foo) getX() -> i32 {
9 +
    return f.x;
10 +
}
11 +
12 +
@default fn main() -> i32 {
13 +
    let f = Foo { x: 42 };
14 +
    assert f.getX() == 42;
15 +
    return 0;
16 +
}
test/tests/method.return.rad added +37 -0
1 +
//! returns: 0
2 +
//! Methods returning various types and records.
3 +
4 +
record Pair {
5 +
    a: i32,
6 +
    b: i32,
7 +
}
8 +
9 +
fn (p: *Pair) sum() -> i32 {
10 +
    return p.a + p.b;
11 +
}
12 +
13 +
fn (p: *Pair) swapped() -> Pair {
14 +
    return Pair { a: p.b, b: p.a };
15 +
}
16 +
17 +
fn (p: *Pair) firstPtr() -> *i32 {
18 +
    return &p.a;
19 +
}
20 +
21 +
@default fn main() -> i32 {
22 +
    let pair = Pair { a: 10, b: 20 };
23 +
24 +
    // Scalar return.
25 +
    assert pair.sum() == 30;
26 +
27 +
    // Aggregate return.
28 +
    let s = pair.swapped();
29 +
    assert s.a == 20;
30 +
    assert s.b == 10;
31 +
32 +
    // Pointer return.
33 +
    let ptr = pair.firstPtr();
34 +
    assert *ptr == 10;
35 +
36 +
    return 0;
37 +
}
test/tests/method.throws.rad added +47 -0
1 +
//! returns: 0
2 +
//! Methods that throw errors.
3 +
4 +
union ParseError {
5 +
    Invalid,
6 +
}
7 +
8 +
record Parser {
9 +
    pos: i32,
10 +
    len: i32,
11 +
}
12 +
13 +
fn (p: *mut Parser) advance() throws (ParseError) {
14 +
    if p.pos >= p.len {
15 +
        throw ParseError::Invalid;
16 +
    }
17 +
    p.pos = p.pos + 1;
18 +
}
19 +
20 +
fn (p: *Parser) remaining() -> i32 {
21 +
    return p.len - p.pos;
22 +
}
23 +
24 +
@default fn main() -> i32 {
25 +
    let mut parser = Parser { pos: 0, len: 2 };
26 +
27 +
    // Advance successfully.
28 +
    try parser.advance() catch {
29 +
        assert false;
30 +
    };
31 +
    assert parser.remaining() == 1;
32 +
33 +
    // Advance again.
34 +
    try parser.advance() catch {
35 +
        assert false;
36 +
    };
37 +
    assert parser.remaining() == 0;
38 +
39 +
    // Advance past end -- should throw.
40 +
    let mut threw = false;
41 +
    try parser.advance() catch {
42 +
        threw = true;
43 +
    };
44 +
    assert threw;
45 +
46 +
    return 0;
47 +
}
test/tests/method.union.rad added +24 -0
1 +
//! returns: 0
2 +
//! Standalone methods on union types.
3 +
4 +
union Shape {
5 +
    Circle(i32),
6 +
    Rect { w: i32, h: i32 },
7 +
}
8 +
9 +
fn (s: *Shape) isCircle() -> bool {
10 +
    match *s {
11 +
        case Shape::Circle(_) => return true,
12 +
        else => return false,
13 +
    }
14 +
}
15 +
16 +
@default fn main() -> i32 {
17 +
    let c = Shape::Circle(5);
18 +
    let r = Shape::Rect { w: 3, h: 4 };
19 +
20 +
    assert c.isCircle();
21 +
    assert not r.isCircle();
22 +
23 +
    return 0;
24 +
}
test/tests/method.with.trait.rad added +36 -0
1 +
//! returns: 0
2 +
//! Standalone methods coexisting with trait instances on the same type.
3 +
4 +
record Widget {
5 +
    x: i32,
6 +
    y: i32,
7 +
}
8 +
9 +
// Standalone method.
10 +
fn (w: *Widget) area() -> i32 {
11 +
    return w.x * w.y;
12 +
}
13 +
14 +
// Trait with its own method.
15 +
trait Printable {
16 +
    fn (*Printable) code() -> i32;
17 +
}
18 +
19 +
instance Printable for Widget {
20 +
    fn (w: *Widget) code() -> i32 {
21 +
        return w.x + w.y;
22 +
    }
23 +
}
24 +
25 +
@default fn main() -> i32 {
26 +
    let w = Widget { x: 3, y: 5 };
27 +
28 +
    // Standalone method call.
29 +
    assert w.area() == 15;
30 +
31 +
    // Trait method call via trait object.
32 +
    let p: *opaque Printable = &w;
33 +
    assert p.code() == 8;
34 +
35 +
    return 0;
36 +
}