resolver: Share executable body checking

6436757e85d1519152f8b695d5a316919929f69a70b6d7b2def22099384825ff
Resolve function and method bodies through one scope and unsafe-context
cleanup path. Share body type, required return, and ownership checks,
while keeping receiver and parameter binding specific to methods.

Restore the enclosing context before reporting a missing return or
propagating a resolution error.

Assisted-by: Codex:gpt-6-astra
Alexis Sellier committed ago 1 parent a7f15291
lib/std/lang/resolver.rad +59 -70
3399 3399
        return;
3400 3400
    };
3401 3401
    let case SymbolData::Value { type: Type::Fn(fnType), .. } = sym.data else {
3402 3402
        panic "resolveFnDeclBody: unexpected symbol data for function";
3403 3403
    };
3404 -
    let retTy = *fnType.returnType;
3405 3404
    let isExtern = ast::hasAttribute(sym.attrs, ast::Attribute::Extern);
3406 3405
    let isIntrinsic = ast::hasAttribute(sym.attrs, ast::Attribute::Intrinsic);
3407 -
    let isUnsafe = fnType.isUnsafe;
3408 3406
3409 3407
    if let body = decl.body {
3410 3408
        if isIntrinsic {
3411 3409
            throw emitError(self, node, ErrorKind::IntrinsicUnexpectedBody);
3412 3410
        }
3413 3411
        if isExtern {
3414 3412
            throw emitError(self, node, ErrorKind::FnUnexpectedBody);
3415 3413
        }
3416 -
        if isUnsafe {
3417 -
            set self.unsafeDepth += 1;
3418 -
        }
3419 -
        enterFn(self, node, fnType); // Enter function scope for body analysis.
3414 +
        try resolveExecutableBody(self, node, fnType, nil, decl.sig.params, body);
3415 +
    } else if not isExtern {
3416 +
        throw emitError(self, node, ErrorKind::FnMissingBody);
3417 +
    }
3418 +
}
3420 3419
3421 -
        let bodyTy = try checkAssignable(self, body, Type::Void) catch e {
3422 -
            exitFn(self);
3423 -
            if isUnsafe { set self.unsafeDepth -= 1; }
3424 -
            throw e;
3425 -
        };
3426 -
        if retTy <> Type::Void and bodyTy <> Type::Never {
3427 -
            exitFn(self);
3428 -
            if isUnsafe { set self.unsafeDepth -= 1; }
3429 -
            throw emitError(self, body, ErrorKind::FnMissingReturn);
3430 -
        }
3431 -
        try checkLinearFn(self, nil, decl.sig.params, body) catch e {
3432 -
            exitFn(self);
3433 -
            if isUnsafe { set self.unsafeDepth -= 1; }
3434 -
            throw e;
3435 -
        };
3420 +
/// Resolve a function or method body and restore the enclosing context.
3421 +
fn resolveExecutableBody(
3422 +
    self: *mut Resolver,
3423 +
    node: *ast::Node,
3424 +
    fnType: *FnType,
3425 +
    receiverName: ?*ast::Node,
3426 +
    params: *mut [*ast::Node],
3427 +
    body: *ast::Node,
3428 +
) throws (ResolveError) {
3429 +
    let isUnsafe = fnType.isUnsafe;
3430 +
    if isUnsafe {
3431 +
        set self.unsafeDepth += 1;
3432 +
    }
3433 +
    // Enter function scope.
3434 +
    enterFn(self, node, fnType); // Enter function scope for body analysis.
3435 +
3436 +
    let missingReturn = try checkExecutableBody(self, fnType, receiverName, params, body) catch e {
3436 3437
        exitFn(self);
3437 -
        if isUnsafe {
3438 -
            set self.unsafeDepth -= 1;
3438 +
        if isUnsafe { set self.unsafeDepth -= 1; }
3439 +
        throw e;
3440 +
    };
3441 +
    exitFn(self);
3442 +
    if isUnsafe {
3443 +
        set self.unsafeDepth -= 1;
3444 +
    }
3445 +
    if missingReturn {
3446 +
        throw emitError(self, body, ErrorKind::FnMissingReturn);
3447 +
    }
3448 +
}
3449 +
3450 +
/// Check parameters, body types, and ownership.
3451 +
/// Return whether a required return is missing.
3452 +
fn checkExecutableBody(
3453 +
    self: *mut Resolver,
3454 +
    fnType: *FnType,
3455 +
    receiverName: ?*ast::Node,
3456 +
    params: *mut [*ast::Node],
3457 +
    body: *ast::Node,
3458 +
) -> bool throws (ResolveError) {
3459 +
    if let receiver = receiverName {
3460 +
        // Bind the receiver parameter.
3461 +
        let receiverTy = *fnType.paramTypes[0];
3462 +
        try bindValueIdent(self, receiver, receiver, receiverTy, false, 0, 0);
3463 +
        // Bind the remaining parameters from the signature.
3464 +
        for paramNode in params {
3465 +
            let paramTy = try infer(self, paramNode);
3439 3466
        }
3440 -
    } else if not isExtern {
3441 -
        throw emitError(self, node, ErrorKind::FnMissingBody);
3442 3467
    }
3468 +
    // Resolve the body.
3469 +
    let retTy = *fnType.returnType;
3470 +
    let bodyTy = try checkAssignable(self, body, Type::Void);
3471 +
    if retTy <> Type::Void and bodyTy <> Type::Never {
3472 +
        return true;
3473 +
    }
3474 +
    try checkLinearFn(self, receiverName, params, body);
3475 +
    return false;
3443 3476
}
3444 3477
3445 3478
/// Analyze a function parameter and bind its identifier.
3446 3479
fn resolveFnParam(self: *mut Resolver, node: *ast::Node, param: ast::FnParam) -> Type
3447 3480
    throws (ResolveError)
4028 4061
) throws (ResolveError) {
4029 4062
    let sym = symbolFor(self, node)
4030 4063
        else throw emitError(self, node, ErrorKind::Internal);
4031 4064
    let case SymbolData::Value { type: Type::Fn(fnType), .. } = sym.data
4032 4065
        else panic "resolveMethodBody: expected value symbol";
4033 -
    let isUnsafe = fnType.isUnsafe;
4034 -
    if isUnsafe {
4035 -
        set self.unsafeDepth += 1;
4036 -
    }
4037 -
4038 -
    // Enter function scope.
4039 -
    enterFn(self, node, fnType);
4040 -
4041 -
    // Bind the receiver parameter.
4042 -
    let receiverTy = *fnType.paramTypes[0];
4043 -
    try bindValueIdent(self, receiverName, receiverName, receiverTy, false, 0, 0) catch e {
4044 -
        exitFn(self);
4045 -
        if isUnsafe { set self.unsafeDepth -= 1; }
4046 -
        throw e;
4047 -
    };
4048 -
    // Bind the remaining parameters from the signature.
4049 -
    for paramNode in sig.params {
4050 -
        let paramTy = try infer(self, paramNode) catch e {
4051 -
            exitFn(self);
4052 -
            if isUnsafe { set self.unsafeDepth -= 1; }
4053 -
            throw e;
4054 -
        };
4055 -
    }
4056 -
4057 -
    // Resolve the body.
4058 -
    let retTy = *fnType.returnType;
4059 -
    let bodyTy = try checkAssignable(self, body, Type::Void) catch e {
4060 -
        exitFn(self);
4061 -
        if isUnsafe { set self.unsafeDepth -= 1; }
4062 -
        throw e;
4063 -
    };
4064 -
    if retTy <> Type::Void and bodyTy <> Type::Never {
4065 -
        exitFn(self);
4066 -
        if isUnsafe { set self.unsafeDepth -= 1; }
4067 -
        throw emitError(self, body, ErrorKind::FnMissingReturn);
4068 -
    }
4069 -
    try checkLinearFn(self, receiverName, sig.params, body) catch e {
4070 -
        exitFn(self);
4071 -
        if isUnsafe { set self.unsafeDepth -= 1; }
4072 -
        throw e;
4073 -
    };
4074 -
    exitFn(self);
4075 -
    if isUnsafe {
4076 -
        set self.unsafeDepth -= 1;
4077 -
    }
4066 +
    try resolveExecutableBody(self, node, fnType, receiverName, sig.params, body);
4078 4067
}
4079 4068
4080 4069
/// Resolve a standalone method declaration (signature only).
4081 4070
/// Validates the receiver type and registers the method in the method table.
4082 4071