@@ -35,7 +35,10 @@ func checkCallSites(prog *ast.Program, ti *TypeInfo, sourceFile string) error {
3535 funcs : collectAnnotatedFuncs (prog ),
3636 ti : ti ,
3737 }
38- if len (c .funcs ) == 0 {
38+ // Tier 4 (variable-bound lambda call sites) lives in TypeInfo.VarFnSigs
39+ // and is independent of whether any top-level def has annotations, so
40+ // only short-circuit when both sources have nothing to check.
41+ if len (c .funcs ) == 0 && (ti == nil || len (ti .VarFnSigs ) == 0 ) {
3942 return nil
4043 }
4144 for _ , s := range prog .Statements {
@@ -425,20 +428,40 @@ func (c *callChecker) walkExpr(e ast.Expr, line int, file string, currentNS stri
425428// resolved as `currentNS.f` (sibling call within the same module) before
426429// falling back to a top-level `f`, matching codegen's resolution order
427430// in buildCallExpr.
431+ //
432+ // If the identifier does not name a top-level def, fall back to Tier 4:
433+ // look up an annotated lambda binding recorded for the call's IdentExpr
434+ // in TypeInfo.VarFnSigs and validate against its signature.
428435func (c * callChecker ) checkCall (call * ast.CallExpr , line int , file string , currentNS string ) error {
429436 ident , ok := call .Func .(* ast.IdentExpr )
430437 if ! ok {
431438 return nil
432439 }
433- fn := c .resolveCallee (ident .Name , currentNS )
434- if fn == nil {
440+ if fn := c .resolveCallee (ident .Name , currentNS ); fn != nil {
441+ return c .checkArgs (call .Args , fn .Params , displayCalleeName (ident .Name , fn .Namespace ), line , file )
442+ }
443+ // Tier 4 fallback: variable-bound annotated lambda.
444+ if c .ti == nil {
435445 return nil
436446 }
437- for i , arg := range call .Args {
438- if i >= len (fn .Params ) {
447+ fnExpr , ok := c .ti .VarFnSigs [ident ]
448+ if ! ok || fnExpr == nil {
449+ return nil
450+ }
451+ return c .checkArgs (call .Args , fnExpr .Params , ident .Name , line , file )
452+ }
453+
454+ // checkArgs validates a list of call arguments against a parameter list
455+ // (from a def or a tracked annotated lambda). It is the inner loop shared
456+ // by both Tier 3 (direct def call) and Tier 4 (variable-bound lambda
457+ // call) checks. calleeName is the user-facing name to put in error
458+ // messages.
459+ func (c * callChecker ) checkArgs (args []ast.Expr , params []ast.Param , calleeName string , line int , file string ) error {
460+ for i , arg := range args {
461+ if i >= len (params ) {
439462 break
440463 }
441- p := fn . Params [i ]
464+ p := params [i ]
442465 if p .TypeAnnot == "" {
443466 continue
444467 }
@@ -453,14 +476,15 @@ func (c *callChecker) checkCall(call *ast.CallExpr, line int, file string, curre
453476 }
454477 return & ast.UserError {Msg : fmt .Sprintf (
455478 "%s:%d: cannot pass %s literal as argument %d to '%s' (parameter '%s' declared as %s)" ,
456- file , line , displayTypeName (argType ), i + 1 , displayCalleeName ( ident . Name , fn . Namespace ) ,
479+ file , line , displayTypeName (argType ), i + 1 , calleeName ,
457480 p .Name , displayTypeName (annot ),
458481 )}
459482 }
460- // Path 2 (Tier 3): variable / non-literal argument — consult the
461- // flow-sensitive type recorded for that exact expression. We only
462- // flag when the inferred type is fully resolved AND incompatible;
463- // dynamic or unknown types pass silently.
483+ // Path 2 (Tier 3 / Tier 4): variable / non-literal argument —
484+ // consult the flow-sensitive type recorded for that exact
485+ // expression. We only flag when the inferred type is fully
486+ // resolved AND incompatible; dynamic or unknown types pass
487+ // silently.
464488 if c .ti == nil {
465489 continue
466490 }
@@ -473,7 +497,7 @@ func (c *callChecker) checkCall(call *ast.CallExpr, line int, file string, curre
473497 }
474498 return & ast.UserError {Msg : fmt .Sprintf (
475499 "%s:%d: cannot pass %s value as argument %d to '%s' (parameter '%s' declared as %s)" ,
476- file , line , displayTypeName (argType ), i + 1 , displayCalleeName ( ident . Name , fn . Namespace ) ,
500+ file , line , displayTypeName (argType ), i + 1 , calleeName ,
477501 p .Name , displayTypeName (annot ),
478502 )}
479503 }
0 commit comments