Documentation
¶
Overview ¶
Package check implements a multi-phase, fixpoint-iterative type checking system for Lua.
ARCHITECTURE OVERVIEW ¶
The checker performs interprocedural type analysis through a fixpoint iteration loop that processes all functions until inter-function information stabilizes. Interproc facts are produced during analysis and captured in a stable snapshot at iteration boundaries. Effect facts are accumulated during analysis and swapped into a stable snapshot at the boundary alongside constructor field facts.
PHASE PIPELINE ¶
Each function is analyzed through a five-phase pipeline:
Phase A (Resolve): Resolves type annotations from AST nodes into concrete types. This processes @type, @param, @return annotations and user-defined type aliases. Phase B (Scope): Builds lexical scope states for each CFG point, populating declared types from annotations and synthesizing function literal signatures. Also extracts flow constraints from control flow and assignments. Phase C (Solve): Solves the extracted flow constraint system to compute reachability conditions and type narrowing facts at each CFG point. Phase D (Narrow): Applies flow solution to narrow declared types, computing final effective types for all expressions and generating function effects.
INTERPROCEDURAL ANALYSIS ¶
The checker supports interprocedural analysis through a unified interproc snapshot:
- ReturnSummaries: Inferred return types for local functions
- ParamHints: Inferred parameter types from call sites
- FuncTypes: Canonical local function types for sibling lookups
- LiteralSigs: Synthesized signatures for function literals
- Effects: Function effects (side effects, termination), stored per symbol
DETERMINISTIC ORDERING ¶
All analysis is performed in deterministic order:
- Root function first, then nested functions in CFG point order
- Scope groups sorted by earliest definition point
- Functions within scope groups sorted by source position
MEMOIZATION ¶
Function analysis results are memoized by (GraphID, ParentHash, StoreRevision). The memoization cache is cleared at each iteration boundary to force recomputation with updated inter-function summaries.
CONVERGENCE ¶
The fixpoint loop terminates when interproc facts and effect/constructor snapshots stabilize. Maximum iteration count is bounded to detect non-convergent analysis.
session.go defines the Session type that holds per-run state for type checking. Session is the primary interface for accessing analysis results. SessionStore lives in compiler/check/store and manages fixpoint iteration state and interproc snapshots.
LIFECYCLE SEPARATION ¶
SessionStore is structured into three lifecycle-scoped components (see store package):
ModuleStore: Immutable module-wide state created once at check start. Contains binding tables, CFG graphs, and module aliases. Never modified during fixpoint iteration.
IterationStore: Iteration-local state used during fixpoint convergence (revision counter and constructor field collection).
IterationScratch: Single-iteration state cleared at each boundary. Tracks which literals have been analyzed, pending parameter hints, and change detection flags.
SNAPSHOT PROTOCOL ¶
Interproc facts and effects follow a snapshot protocol:
- During iteration: Functions read from the previous snapshot
- At boundary: New facts/effects replace the snapshot
- Convergence: Iteration stops when snapshots are unchanged
This protocol ensures all functions within an iteration see consistent cross-function information, enabling deterministic analysis regardless of function processing order.
PARALLELIZATION ¶
Functions within a single fixpoint iteration are independent (read-only inputs). To parallelize: run with per-worker db.QueryContext, merge results into next maps deterministically at iteration boundary. db.QueryContext is NOT safe for concurrent access from multiple goroutines.
Index ¶
- type Checker
- type Deps
- type Option
- type Pass
- type Session
- func (s *Session) AppendDiagnostics(diags ...diag.Diagnostic)
- func (s *Session) Context() *db.QueryContext
- func (s *Session) DiagnosticsSlice() []diag.Diagnostic
- func (s *Session) EffectsForExport() map[cfg.SymbolID]*constraint.FunctionEffect
- func (s *Session) ExportManifest(modulePath string) *io.Manifest
- func (s *Session) ExportType() typ.Type
- func (s *Session) ExportTypes() map[string]typ.Type
- func (s *Session) GetOrBuildCFG(fn *ast.FunctionExpr) *cfg.Graph
- func (s *Session) PluginLoad(key any) any
- func (s *Session) PluginStore(key, val any)
- func (s *Session) RegisterGraphHierarchy(root *cfg.Graph)
- func (s *Session) Release()
- func (s *Session) ResetDiagnostics()
- func (s *Session) ResultsMap() map[*ast.FunctionExpr]*api.FuncResult
- func (s *Session) RootFuncNode() *ast.FunctionExpr
- func (s *Session) RootGraph() *cfg.Graph
- func (s *Session) RootResultValue() *api.FuncResult
- func (s *Session) ScopeDepthDiagState() map[*ast.FunctionExpr]bool
- func (s *Session) SetRootFuncNode(fn *ast.FunctionExpr)
- func (s *Session) SetRootResultValue(result *api.FuncResult)
- func (s *Session) Source() string
- func (s *Session) StoreHandle() api.IterationStore
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Checker ¶
type Checker struct {
// contains filtered or unexported fields
}
Checker orchestrates the multi-phase type analysis pipeline for Lua modules. It is the main entry point for type checking and coordinates the fixpoint iteration loop that processes all functions until types stabilize.
DESIGN PRINCIPLE: Checker is stateless with respect to individual analysis runs. All per-run state (function results, inter-function channels, diagnostics) lives in Session and SessionStore. This allows a single Checker instance to be reused for analyzing multiple files in sequence or parallel.
MEMOIZATION: Function analysis is memoized through funcResultQ keyed by FuncKey. The cache is cleared at each fixpoint iteration boundary to ensure fresh computation with updated inter-function summaries.
EXTENSION POINTS: Checker supports two extension mechanisms:
- Pass: Diagnostic generators that run after fixpoint convergence
- ComputePass: Analysis phases that run during iteration and store results in Extras
func NewChecker ¶
NewChecker creates a new Checker instance with the given database, dependencies, and options.
The database provides query infrastructure for memoization and type caching. Dependencies supply external type information (stdlib types, global functions, type operations). Options configure analysis passes and other behaviors.
The returned Checker is safe to reuse for multiple Check calls. Each call creates an independent Session with its own fixpoint iteration state.
Example:
db := db.NewDB()
deps := check.Deps{
Types: typeOps,
Stdlib: stdlibScope,
GlobalTypes: builtinTypes,
Resolver: narrowResolver,
}
checker := check.NewChecker(db, deps, check.WithPass(fieldCheckPass))
sess := checker.Check(source, "module.lua")
func (*Checker) Check ¶
Check parses and analyzes Lua source code, returning a Session containing all results.
The method performs the complete analysis pipeline:
- Parse source into AST
- Build module bindings and collect module aliases
- Run fixpoint loop until inter-function types stabilize
- Execute diagnostic passes
- Sort and deduplicate diagnostics
Parse errors are captured as diagnostics in the returned Session. Even with parse errors, the Session is valid and contains the error information.
The name parameter identifies the source file for diagnostic reporting and module path resolution.
The returned Session contains:
- Results: Per-function analysis results (types, flow facts, effects)
- Diagnostics: Type errors, warnings, and suggestions
- Store: Inter-function channel data for advanced introspection
func (*Checker) CheckChunk ¶
CheckChunk analyzes a pre-parsed AST chunk, returning a Session with results. Use this method when the AST is already available (e.g., from a custom parser or AST transformation pipeline).
The chunk is wrapped in a synthetic FunctionExpr representing the module body. Analysis proceeds identically to Check: binding, CFG construction, fixpoint iteration, and diagnostic generation.
func (*Checker) ClearCache ¶
func (c *Checker) ClearCache()
ClearCache removes all memoized function analysis results from the query cache.
Call this between Check calls when analyzing unrelated files to prevent stale cache entries and reduce memory usage. The cache is automatically cleared at each fixpoint iteration boundary, so this is primarily useful for batch processing scenarios where the checker analyzes many independent files.
type Deps ¶
type Deps struct {
// Types provides type construction and manipulation operations. Used for
// building function types, records, unions, and other composite types
// during type synthesis and constraint solving.
Types core.TypeOps
// Stdlib provides the type namespace for standard library type aliases.
// This scope contains type definitions like "table", "string", "number"
// that are available in all Lua programs without explicit import.
// Note: This is type namespace only, not the runtime value namespace.
Stdlib *scope.State
// GlobalTypes provides the value namespace for built-in global functions
// like print, pairs, ipairs, type, assert, error, etc. These are runtime
// values with known types, not type definitions.
GlobalTypes map[string]typ.Type
// Resolver provides constraint resolution for the flow solver (Phase C).
// It implements type narrowing operations like type guards, nil checks,
// and assertion-based refinements.
Resolver narrow.Resolver
}
Deps contains required dependencies injected into the Checker at construction time. These dependencies provide external type information and resolution capabilities that the checker cannot derive from the source code alone.
type Option ¶
type Option func(*Checker)
Option configures optional behaviors of a Checker.
func WithComputePass ¶
func WithComputePass(p api.ComputePass) Option
WithComputePass registers a compute pass for additional analysis.
func WithMaxIterations ¶
WithMaxIterations configures the maximum number of fixpoint iterations. Values less than 1 are clamped to 1.
func WithMaxScopeDepth ¶
WithMaxScopeDepth configures a maximum lexical scope nesting depth. A value <= 0 disables the limit.
func WithScopeDepthDiagnostics ¶
WithScopeDepthDiagnostics enables diagnostics when the scope depth limit is exceeded.
type Pass ¶
type Pass func(sess *Session, fn *ast.FunctionExpr, result *api.FuncResult) []diag.Diagnostic
Pass is a diagnostic generation function that runs after the fixpoint converges. Passes inspect the fully analyzed function results and emit diagnostics for type errors, unused variables, unreachable code, or other semantic issues.
Passes are registered via WithPass and execute in registration order. Each pass receives the analysis session, the function AST node, and the complete analysis result including narrowed types, flow facts, and effects.
Passes run only after all iterations complete, so they see the final converged type information. They should not modify session state.
type Session ¶
type Session struct {
// Ctx provides query infrastructure for memoization. NOT concurrency-safe.
Ctx *db.QueryContext
// SourceName identifies the module for diagnostic reporting and path resolution.
SourceName string
// Imports tracks loaded module manifests keyed by require path.
Imports map[string]*io.Manifest
// Store holds all inter-function channel state. Attached to Ctx for query access.
Store *store.SessionStore
// Results contains per-function analysis output keyed by FunctionExpr pointer.
// Includes root function and all nested functions.
Results map[*ast.FunctionExpr]*api.FuncResult
// RootFunc is the synthetic FunctionExpr wrapping the module chunk.
// Its body contains the module's top-level statements.
RootFunc *ast.FunctionExpr
// RootResult provides direct access to the root function's analysis output.
// Equivalent to Results[RootFunc] but avoids map lookup.
RootResult *api.FuncResult
// Diagnostics accumulates type errors, warnings, and suggestions from all phases.
// Sorted by position after analysis completes.
Diagnostics []diag.Diagnostic
// Terminators tracks which functions are known to never return (error, assert(false)).
Terminators map[string]bool
// ManifestVars captures @manifest annotations for module metadata export.
ManifestVars map[string]string
// contains filtered or unexported fields
}
Session holds all state and results for analyzing a single Lua module. One Session is created per Check call and contains the complete analysis output including per-function results, diagnostics, and inter-function channel data.
USAGE PATTERN:
sess := checker.Check(source, "module.lua")
for _, diag := range sess.Diagnostics {
fmt.Println(diag)
}
exportType := sess.ExportType()
CONCURRENCY: db.QueryContext is NOT safe for concurrent access. For parallel analysis, create one Session per worker with independent QueryContexts, then merge results. Functions within a single fixpoint iteration read from shared snapshots and write to independent per-iteration maps, enabling future parallelization.
MEMORY MANAGEMENT: Call Release() after extracting Manifest data to free heavy allocations (CFGs, scopes, flow data). The Session remains valid for Diagnostics access after Release.
func New ¶
func New(ctx *db.QueryContext, name string) *Session
New creates a session for checking a file.
func (*Session) AppendDiagnostics ¶
func (s *Session) AppendDiagnostics(diags ...diag.Diagnostic)
AppendDiagnostics appends diagnostics to the session.
func (*Session) Context ¶
func (s *Session) Context() *db.QueryContext
Context returns the query context for the session.
func (*Session) DiagnosticsSlice ¶
func (s *Session) DiagnosticsSlice() []diag.Diagnostic
DiagnosticsSlice returns the current diagnostics slice.
func (*Session) EffectsForExport ¶
func (s *Session) EffectsForExport() map[cfg.SymbolID]*constraint.FunctionEffect
EffectsForExport extracts computed function effects for manifest generation. Returns effects from the final converged interproc snapshot.
The returned map associates each function's SymbolID with its computed effect, including IO effects (row), termination status, and conditional effects. This enables importers to see side effect information for exported functions.
func (*Session) ExportManifest ¶ added in v1.5.8
ExportManifest builds a module manifest from this session using canonical export policy.
The manifest includes:
- Export type from root returns (with converged function effects applied)
- Exported type definitions
- Function summaries for exported functions (ensures/effects for cross-module narrowing)
Use this helper instead of manually stitching ExportType/ExportTypes so callers do not accidentally omit function summaries.
func (*Session) ExportType ¶
ExportType computes the module's exported type from return statements. This is the primary method for extracting the module's public interface for manifest generation and import resolution.
RETURN TYPE COMPUTATION: The export type is computed from all return statements in the root function:
- Multiple returns are joined into a union type
- Unreachable returns (dead code) are excluded via flow analysis
- Empty returns contribute nil to the union
EFFECT ENRICHMENT: If the returned value is a table/record, its method types are enriched with function effects computed during analysis. This ensures exported functions carry their side effect annotations.
USAGE:
sess := checker.Check(source, "module.lua") exportType := sess.ExportType() manifest := io.NewManifest(modulePath, exportType, sess.ExportTypes())
func (*Session) ExportTypes ¶
ExportTypes extracts module-local type definitions for manifest generation. Returns a map of type names to their resolved types.
INCLUDED: Types defined via @type or type alias syntax in this module. EXCLUDED: Standard library types, imported types from other modules.
The returned map is used to populate Manifest.Types for import resolution. When another module imports this one, these types become available in the importer's type namespace.
func (*Session) GetOrBuildCFG ¶
func (s *Session) GetOrBuildCFG(fn *ast.FunctionExpr) *cfg.Graph
GetOrBuildCFG returns a cached CFG for the function or builds and caches a new one.
func (*Session) PluginLoad ¶
PluginLoad retrieves a value stored by a plugin extension.
func (*Session) PluginStore ¶
PluginStore stores a value for a plugin extension.
func (*Session) RegisterGraphHierarchy ¶
RegisterGraphHierarchy registers the root graph and all nested graphs. This populates graph/function maps and nested metadata for query lookups.
func (*Session) Release ¶
func (s *Session) Release()
Release frees heavy allocations to reduce memory pressure after analysis. Call this after extracting all needed data (ExportType, ExportTypes, Diagnostics).
WHAT IS FREED:
- CFG graphs and binding tables
- Scope states and flow solutions
- Inter-function channel data
- Synthesis engines
WHAT REMAINS VALID:
- Diagnostics slice (still accessible)
- Session struct itself
WHEN TO CALL: After manifest generation in batch processing scenarios. For single-file analysis, releasing is optional as Session is GC'd normally.
func (*Session) ResetDiagnostics ¶
func (s *Session) ResetDiagnostics()
ResetDiagnostics clears all diagnostics.
func (*Session) ResultsMap ¶
func (s *Session) ResultsMap() map[*ast.FunctionExpr]*api.FuncResult
ResultsMap returns the function result map.
func (*Session) RootFuncNode ¶
func (s *Session) RootFuncNode() *ast.FunctionExpr
RootFuncNode returns the root function for this session.
func (*Session) RootGraph ¶
RootGraph returns the root function's control flow graph. The root function wraps the module's top-level statements. Its CFG contains:
- Module-level assignments and function definitions
- Module-level control flow (if, for, while)
- Return statements for module export
Use for advanced introspection or manifest generation that needs CFG access.
func (*Session) RootResultValue ¶
func (s *Session) RootResultValue() *api.FuncResult
RootResultValue returns the root function result.
func (*Session) ScopeDepthDiagState ¶
func (s *Session) ScopeDepthDiagState() map[*ast.FunctionExpr]bool
ScopeDepthDiagState returns the map tracking scope depth diagnostics.
func (*Session) SetRootFuncNode ¶
func (s *Session) SetRootFuncNode(fn *ast.FunctionExpr)
SetRootFuncNode sets the root function for this session.
func (*Session) SetRootResultValue ¶
func (s *Session) SetRootResultValue(result *api.FuncResult)
SetRootResultValue sets the root function result.
func (*Session) StoreHandle ¶
func (s *Session) StoreHandle() api.IterationStore
StoreHandle returns the session store as an interation-capable interface.
Directories
¶
| Path | Synopsis |
|---|---|
|
Package api defines interfaces and types for the Lua type checker.
|
Package api defines interfaces and types for the Lua type checker. |
|
Package effectops provides operations for function effect propagation.
|
Package effectops provides operations for function effect propagation. |
|
Package flowbuild implements flow constraint extraction from control flow graphs.
|
Package flowbuild implements flow constraint extraction from control flow graphs. |
|
assign
Package assign provides flow-sensitive assignment analysis for the type checker.
|
Package assign provides flow-sensitive assignment analysis for the type checker. |
|
cond
Package cond implements condition constraint extraction for the flow type system.
|
Package cond implements condition constraint extraction for the flow type system. |
|
constprop
Package constprop performs constant propagation for flow analysis.
|
Package constprop performs constant propagation for flow analysis. |
|
core
Package base provides shared context types for flowbuild operations.
|
Package base provides shared context types for flowbuild operations. |
|
decl
Package decl extracts type declarations from function parameters and locals.
|
Package decl extracts type declarations from function parameters and locals. |
|
guard
Package guard extracts type guard conditions from control flow branches.
|
Package guard extracts type guard conditions from control flow branches. |
|
keyscoll
Package keyscoll collects table keys from expressions for type inference.
|
Package keyscoll collects table keys from expressions for type inference. |
|
literal
Package literal handles literal expression type synthesis for flow analysis.
|
Package literal handles literal expression type synthesis for flow analysis. |
|
mutator
Package mutator extracts container mutation operations from call sites.
|
Package mutator extracts container mutation operations from call sites. |
|
numconst
Package numconst handles numeric constant analysis for type inference.
|
Package numconst handles numeric constant analysis for type inference. |
|
path
Package path constructs constraint paths from AST expressions.
|
Package path constructs constraint paths from AST expressions. |
|
predicate
Package predicate builds type predicates from comparison expressions.
|
Package predicate builds type predicates from comparison expressions. |
|
resolve
Package resolve provides symbol and type resolution utilities for flow constraint extraction.
|
Package resolve provides symbol and type resolution utilities for flow constraint extraction. |
|
returns
Package returns extracts return type information from function bodies.
|
Package returns extracts return type information from function bodies. |
|
sibling
Package sibling handles sibling function analysis in nested scopes.
|
Package sibling handles sibling function analysis in nested scopes. |
|
tblutil
Package tblutil provides utilities for table type analysis.
|
Package tblutil provides utilities for table type analysis. |
|
assign_check.go implements assignment type validation for the type checker.
|
assign_check.go implements assignment type validation for the type checker. |
|
infer
|
|
|
captured
Package captured computes captured variable types for nested functions.
|
Package captured computes captured variable types for nested functions. |
|
interproc
Package interproc handles interprocedural analysis after flow solving.
|
Package interproc handles interprocedural analysis after flow solving. |
|
nested
Package nestedinfer processes nested function definitions during type analysis.
|
Package nestedinfer processes nested function definitions during type analysis. |
|
paramhints
Package paramhints infers parameter types from call site arguments.
|
Package paramhints infers parameter types from call site arguments. |
|
return
Package infer infers function return types from body analysis.
|
Package infer infers function return types from body analysis. |
|
Package modules handles module export type computation.
|
Package modules handles module export type computation. |
|
Package nested handles nested function analysis coordination.
|
Package nested handles nested function analysis coordination. |
|
Package phase implements individual analysis phases for type checking.
|
Package phase implements individual analysis phases for type checking. |
|
This package handles post-analysis diagnostic operations:
|
This package handles post-analysis diagnostic operations: |
|
Package returns provides interprocedural return type analysis.
|
Package returns provides interprocedural return type analysis. |
|
Package scope provides lexical scope state for type checking.
|
Package scope provides lexical scope state for type checking. |
|
Package siblings manages sibling function type coordination.
|
Package siblings manages sibling function type coordination. |
|
Package store provides session state management for type checking.
|
Package store provides session state management for type checking. |
|
Package synth implements type synthesis for Lua expressions.
|
Package synth implements type synthesis for Lua expressions. |
|
intercept
Package intercept handles special-case type synthesis interceptions.
|
Package intercept handles special-case type synthesis interceptions. |
|
ops
Package ops implements type synthesis operations for the type checker.
|
Package ops implements type synthesis operations for the type checker. |
|
phase/core
Package shared provides shared utilities for synthesis phases.
|
Package shared provides shared utilities for synthesis phases. |
|
phase/extract
Package extract handles expression extraction from CFG for synthesis.
|
Package extract handles expression extraction from CFG for synthesis. |
|
phase/resolve
Package resolve performs final type resolution after synthesis.
|
Package resolve performs final type resolution after synthesis. |
|
transform
Package transform provides type transformation operations.
|
Package transform provides type transformation operations. |
|
tests
|
|