import { FunctionParameter, VariableType } from "../types.js"; import { type WalkAncestor } from "../utils/node.js"; import type { Scope as WalkScope } from "../types.js"; import { ScopeInfo } from "./types.js"; import type { TypeCheckerContext } from "./types.js"; export type ParamSlot = { type: VariableType | undefined; validated: boolean; /** Original parameter name. Absent for builtins (which can't take named args). */ name?: string; }; export type SlotRequest = { kind: "positional"; index: number; } | { kind: "named"; name: string; }; export type ParamSignature = { minArgs: number; maxArgs: number; /** Declared parameter count (excludes splat pads). Used by splat * checking to iterate each declared slot once. */ paramCount: number; /** * Resolve the slot that an argument fills. * * - `{ kind: "positional", index }` — returns an element-typed slot for a * variadic, matching the spread calling convention. * - `{ kind: "named", name }` — for variadics, returns a slot whose `type` * is the *array* form (`T[]`) so the call-site value `foo(rest: [1,2])` * type-checks against the whole array. Returns undefined for unknown * names (caught earlier by `checkNamedArgStructure`). * * No consumer should branch on `param.variadic` to pick element-vs-array * itself — that's the rule this resolver encapsulates. */ resolveSlot(req: SlotRequest): ParamSlot | undefined; }; export declare function paramListSignature(params: FunctionParameter[], argCount: number): ParamSignature; export declare function checkScopes(scopes: ScopeInfo[], ctx: TypeCheckerContext): void; /** * `walkNodes` descends into nested function/graphNode/method bodies, * yielding their inner expressions with their own scope chain. When we're * checking a single scope, we want to skip items that belong to nested * scopes — they get checked separately when their own ScopeInfo is * processed. Without this filter, an expression inside `node main()` * would also be checked under the global scope, which would lose any * type aliases declared inside the node body. * * Items inside intra-def constructs that don't open a new scope (if / * while / for / handle / fork bodies) keep the parent's scope chain, so * they pass through. * * walkNodes seeds an empty scopes list with [globalScope]. For a per-def * ScopeInfo, the body passed to walkNodes is the def's body, so direct * children appear with scopes=[globalScope] — accept those when info is * a per-def scope. * * Class methods don't currently get their own ScopeInfo (buildScopes * only iterates functionDefs/nodeDefs), so the global pass owns them — * accept method-body nodes when info is the global scope, otherwise * those expressions never get checked. */ export declare function isInScope(scopes: WalkScope[], info: ScopeInfo): boolean; export declare function isInsideHandler(ancestors: WalkAncestor[]): boolean;