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