synth

package
v1.5.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 3, 2026 License: MIT Imports: 14 Imported by: 0

Documentation

Overview

Package synth implements type synthesis for Lua expressions.

This package provides the core expression type inference engine. It synthesizes types for expressions by combining static type information with flow-sensitive type facts.

Synthesis Engine

Engine is the main type synthesizer, providing:

  • Expression type synthesis (TypeOf, Synth)
  • Expected type propagation (SynthWithExpected)
  • Type annotation resolution (ResolveType)

Bidirectional Type Checking

The synthesizer supports bidirectional type checking:

  • Synthesis: infer type from expression structure
  • Checking: validate expression against expected type

For table literals, expected types guide field type inference:

local p: Point = { x = 1, y = 2 }  -- fields typed as Point fields

Flow Integration

The synthesizer queries flow facts to get narrowed types:

if x ~= nil then
    print(x)  -- synthesizes non-nil type for x
end

Subpackages

The synth package has several subpackages:

  • ops: Type operations (call, index, field access)
  • intercept: Special-case handling (type casts, type guards)
  • phase/extract: Expression extraction from CFG
  • phase/resolve: Final type resolution
  • transform: Type transformations (spec returns, etc.)

Package synth provides type synthesis for Lua expressions during type checking.

The synthesis engine operates in two distinct phases:

1. Declared Phase (pre-flow analysis): Synthesizes types using only declared type annotations and structural inference. Used during scope computation before dataflow analysis has been performed.

2. Narrowed Phase (post-flow analysis): Synthesizes types with full flow-sensitive narrowing, incorporating control flow predicates, type guards, and refined type information from assignments.

The Engine struct is the primary entry point, configured via Config depending on the compilation phase. It delegates to the internal extract.Synthesizer which handles the actual synthesis logic.

Key capabilities:

  • Expression type synthesis (TypeOf, MultiTypeOf)
  • Function signature inference (FunctionType, ResolveFunctionSignature)
  • Value expansion for multi-return contexts (ExpandValues)
  • Iterator variable inference (InferIterVars)
  • Type definition resolution (ResolveTypeDef)
  • Field and method lookup (Field, Method)

The engine maintains separate caches for pre-flow (PreCache) and post-flow (NarrowCache) synthesis results to avoid redundant computation.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func FunctionLiteralSignatures

func FunctionLiteralSignatures(graph *cfg.Graph, engine LiteralSynth, declaredReturns []typ.Type) map[*ast.FunctionExpr]*typ.Function

FunctionLiteralSignatures builds a map of function expressions to their signatures.

Unlike FunctionLiteralTypes which produces DeclaredTypes keyed by symbol, this function produces signatures keyed by AST node. This is used by the type checker to look up the signature of any function literal in the code.

Processing:

  1. Local assignments: Extracts signatures from function literals and nested function fields in table literals
  2. Function definitions: Handles field and method definitions on receivers
  3. Return statements: If declaredReturns is provided, uses expected return types to guide function literal inference at return positions

Expected type propagation:

  • For table fields, uses expected table type to infer field function signatures
  • For methods with "self" parameter, infers self type from receiver
  • For return expressions, uses declared return types as expected types

Returns nil if graph or engine is nil, or if no function literals found.

func FunctionLiteralTypes

func FunctionLiteralTypes(graph *cfg.Graph, synth api.ExprSynth) flow.DeclaredTypes

FunctionLiteralTypes synthesizes declared types for function literals and tables.

This function extracts type information from function and table literals assigned to local variables, building a DeclaredTypes map that the flow analysis uses as initial type information before narrowing.

Processing order (via CFG RPO traversal):

  1. Local assignments with function literal RHS: Synthesizes and records function type
  2. Local assignments with table literal RHS: Synthesizes and records table/record type
  3. Function definitions (function field assignments, method definitions): Extends receiver types with method fields

The resulting map provides declared types that flow analysis uses as the base types before applying any narrowing from control flow.

Returns nil if graph or synth is nil, or if no literals are found.

Types

type Config

type Config struct {
	Ctx            *db.QueryContext
	Types          core.TypeOps
	Scopes         api.ScopeMap
	Manifests      io.ManifestQuerier
	Env            api.BaseEnv
	Flow           api.FlowOps
	Paths          api.PathFromExprFunc
	PreCache       api.Cache
	NarrowCache    api.Cache
	Phase          api.Phase
	ModuleBindings *bind.BindingTable
	ModuleAliases  map[cfg.SymbolID]string
}

Config configures a synthesis engine for a given pipeline phase.

Phase controls whether flow-sensitive narrowing is enabled:

  • api.PhaseNarrowing: flow-refined (post-flow)
  • all earlier phases: declared-only (pre-flow)

type Engine

type Engine struct {
	// contains filtered or unexported fields
}

Engine provides type synthesis configured by compilation phase.

Engine is the main entry point for type synthesis during type checking. It wraps the internal extract.Synthesizer and provides a phase-aware API that automatically routes synthesis requests through the appropriate code paths for declared vs narrowed phases.

The engine caches synthesis results to avoid redundant computation:

  • PreCache: Results from declared phase (type annotations only)
  • NarrowCache: Results from narrowed phase (flow-refined types)

Thread safety: Engine instances are not thread-safe. Create separate instances for concurrent synthesis operations.

func New

func New(cfg Config) *Engine

New creates a synthesis engine configured for the requested phase.

func (*Engine) AllowReturnTransforms

func (e *Engine) AllowReturnTransforms() bool

AllowReturnTransforms reports if return type transforms are allowed.

Return transforms (like effect returns) are only applied during narrowing phase when full type information is available.

func (*Engine) CallQuery

func (e *Engine) CallQuery() core.TypeOps

CallQuery returns the type operations interface for call resolution.

func (*Engine) Context

func (e *Engine) Context() *db.QueryContext

Context returns the query context for type database operations.

func (*Engine) Entry

func (e *Engine) Entry() cfg.Point

Entry returns the CFG entry point for the function being analyzed.

func (*Engine) ExpandValues

func (e *Engine) ExpandValues(exprs []ast.Expr, needed int, p cfg.Point) []typ.Type

ExpandValues expands a list of expressions into the required number of types.

Handles multi-return expansion: the last expression in the list may contribute multiple values (e.g., function call returns). Earlier expressions contribute exactly one value each.

If fewer values are available than needed, pads with typ.Nil. If more values are available than needed, truncates to needed.

func (*Engine) ExpandValuesWithSpecTypes

func (e *Engine) ExpandValuesWithSpecTypes(exprs []ast.Expr, needed int, p cfg.Point, specTypes api.SpecTypes) []typ.Type

ExpandValuesWithSpecTypes expands values with overlay types for specific symbols.

SpecTypes provides override types for certain symbols during expansion, used when synthesizing in contexts where some types are known from specialization (e.g., generic instantiation).

func (*Engine) Field

func (e *Engine) Field(t typ.Type, name string) (typ.Type, bool)

Field looks up a field by name on a type.

For records/interfaces, looks up the named field. For maps, returns the value type. Returns the field type and whether found.

func (*Engine) FunctionType

func (e *Engine) FunctionType(fn *ast.FunctionExpr, sc *scope.State) *typ.Function

FunctionType synthesizes the function type from a function expression.

Combines declared parameter types, return type annotations, and inferred information to build a complete function type. The scope state provides context for resolving type references in annotations.

func (*Engine) InferIterVars

func (e *Engine) InferIterVars(exprs []ast.Expr, count int, p cfg.Point) []typ.Type

InferIterVars infers types for iterator variables from iterator expressions.

Given iterator expressions (generator, state, initial), infers the types for count loop variables. Handles standard iterator protocols:

  • pairs() returns key: any, value: any
  • ipairs() returns index: number, value: T
  • Custom iterators based on function signature

func (*Engine) InferIterVarsWithSpecTypes

func (e *Engine) InferIterVarsWithSpecTypes(exprs []ast.Expr, count int, p cfg.Point, specTypes api.SpecTypes) []typ.Type

InferIterVarsWithSpecTypes infers iterator variable types with symbol overrides.

func (*Engine) IsNarrowing

func (e *Engine) IsNarrowing() bool

IsNarrowing reports whether the engine operates in narrowing phase.

func (*Engine) Method

func (e *Engine) Method(t typ.Type, name string) (typ.Type, bool)

Method looks up a method by name on a type.

Searches the type's method table and any inherited methods from metatables or interfaces. Returns the method type and whether found.

func (*Engine) MultiTypeOf

func (e *Engine) MultiTypeOf(expr ast.Expr, p cfg.Point) []typ.Type

MultiTypeOf returns all types from a multi-valued expression.

Used for expressions that may return multiple values:

  • Function calls (returns all return values)
  • Vararg expressions (returns vararg element type)
  • Parenthesized expressions (single element slice)

Returns nil for non-multi-valued expressions.

func (*Engine) Narrow

func (e *Engine) Narrow() api.BaseSynth

Narrow returns a narrowing-capable synth if flow information is available and the engine is in PhaseNarrowing.

Returns nil if the engine was created without flow information or the phase is pre-flow. Returns self if flow information is present and phase is narrowing.

func (*Engine) Phase

func (e *Engine) Phase() api.Phase

Phase returns the current compilation phase.

func (*Engine) ResolveFieldAccess

func (e *Engine) ResolveFieldAccess(fullExpr *ast.AttrGetExpr, objType typ.Type, fieldName string, p cfg.Point) FieldAccessResult

ResolveFieldAccess resolves field or index access on a type.

Handles both named field access (t.field) and computed index access (t[key]). Returns the result type and whether the access is valid.

func (*Engine) ResolveFunctionSignature

func (e *Engine) ResolveFunctionSignature(fn *ast.FunctionExpr, sc *scope.State) *typ.Function

ResolveFunctionSignature resolves function type from annotations only.

Unlike FunctionType which may infer types, this only uses explicit type annotations. Returns nil if no annotations are present.

func (*Engine) ResolveReturnTypes

func (e *Engine) ResolveReturnTypes(types []ast.TypeExpr, sc *scope.State) []typ.Type

ResolveReturnTypes resolves a list of return type annotations.

func (*Engine) ResolveType

func (e *Engine) ResolveType(expr ast.TypeExpr, sc *scope.State) typ.Type

ResolveType resolves a type expression AST node to a concrete type.

Handles all type expression forms: named types, generics, functions, tables, unions, intersections, optionals, arrays, and maps.

func (*Engine) ResolveTypeDef

func (e *Engine) ResolveTypeDef(name string, typeExpr ast.TypeExpr, typeParams []ast.TypeParamExpr, sc *scope.State) typ.Type

ResolveTypeDef resolves a type definition with optional type parameters.

For generic types, creates a type constructor that can be instantiated with type arguments. For non-generic types, directly resolves the type.

func (*Engine) Scopes

func (e *Engine) Scopes() api.ScopeMap

Scopes returns the CFG point to scope state mapping.

func (*Engine) SynthExprAt

func (e *Engine) SynthExprAt(expr ast.Expr, p cfg.Point, sc *scope.State) typ.Type

SynthExprAt synthesizes an expression type at a point with explicit scope.

Allows synthesis with a different scope than the one mapped to the point, useful for synthesizing expressions in contexts where scope differs from the standard CFG mapping.

func (*Engine) SynthFunctionTypeWithExpected

func (e *Engine) SynthFunctionTypeWithExpected(fn *ast.FunctionExpr, sc *scope.State, expected *typ.Function) *typ.Function

SynthFunctionTypeWithExpected synthesizes function type with expected signature hint.

Uses the expected function type to infer parameter and return types for unannotated function literals. Common for callbacks and higher-order functions.

func (*Engine) SynthWithExpected

func (e *Engine) SynthWithExpected(expr ast.Expr, p cfg.Point, expected typ.Type) typ.Type

SynthWithExpected synthesizes with expected type, using flow information.

Combines flow-sensitive synthesis with expected type hints. Returns typ.Nil for nil expressions. Falls back to TypeOf if no expected type provided.

func (*Engine) TypeOf

func (e *Engine) TypeOf(expr ast.Expr, p cfg.Point) typ.Type

TypeOf returns the synthesized type of an expression at a CFG point.

In narrowing phase, queries the narrow cache first, then synthesizes using flow information and caches the result. In declared phase, delegates to the extract synthesizer without flow context.

Returns typ.Nil for nil expressions. Returns typ.Unknown if synthesis fails.

func (*Engine) TypeOfWithExpected

func (e *Engine) TypeOfWithExpected(expr ast.Expr, p cfg.Point, expected typ.Type) typ.Type

TypeOfWithExpected synthesizes an expression type with an expected type hint.

The expected type guides inference for polymorphic expressions like empty tables, generic function calls, and function literals without annotations. Does not enforce that the result matches expected - only uses it as a hint.

type FieldAccessResult

type FieldAccessResult struct {
	Type         typ.Type
	Found        bool
	SkipCheck    bool
	NotIndexable bool
}

FieldAccessResult contains the result of resolving a field or index access.

Used by type checking to determine whether a field access is valid and what type it produces. The result encodes several possible outcomes:

  • Found=true: Field exists, Type contains the field type
  • Found=false, SkipCheck=true: Cannot determine validity, skip error reporting
  • Found=false, SkipCheck=false: Field definitely missing, report error
  • NotIndexable=true: The base type does not support indexing at all

func ResolveFieldAccess

func ResolveFieldAccess(
	resolver FieldResolver,
	fullExpr *ast.AttrGetExpr,
	objType typ.Type,
	fieldName string,
	p cfg.Point,
) FieldAccessResult

ResolveFieldAccess resolves field or index access on an object type.

This function handles the complexity of Lua's dynamic field access:

For named field access (obj.field):

  • Records: Returns the declared field type
  • Interfaces: Returns the declared field type
  • Unions: Checks all variants have the field
  • Other types: Returns SkipCheck=true (dynamic access)

For computed index access (obj[expr]):

  • Maps: Returns the value type
  • Arrays: Returns the element type
  • Tuples: Returns the element type
  • Other types: Returns NotIndexable=true if not indexable

Special cases:

  • Placeholder types (unknown, pending): Always SkipCheck=true
  • IdentExpr keys: Treated as dynamic lookup, SkipCheck=true
  • Empty fieldName with computed key: Checks indexability only

If fullExpr is provided and resolver can type it directly, uses that type (for cached/known expressions). Otherwise, performs structural lookup.

type FieldResolver

type FieldResolver interface {
	TypeOf(expr ast.Expr, p cfg.Point) typ.Type
	Field(t typ.Type, name string) (typ.Type, bool)
}

FieldResolver provides the minimal interface needed for field access resolution.

Allows ResolveFieldAccess to be used with different synthesis implementations that can provide type lookup and field resolution. Both Engine and test mocks can implement this interface.

type LiteralSynth

type LiteralSynth interface {
	TypeOf(expr ast.Expr, p cfg.Point) typ.Type
	SynthFunctionTypeWithExpected(fn *ast.FunctionExpr, sc *scope.State, expected *typ.Function) *typ.Function
	Scopes() api.ScopeMap
	Entry() cfg.Point
}

LiteralSynth provides synthesis capabilities for function literal extraction.

Used during literal signature synthesis to resolve types of expressions and build function signatures from function expressions. Limited interface focused on the subset of Engine capabilities needed for literal processing.

type SimpleSynth

type SimpleSynth interface {
	TypeOf(expr ast.Expr, p cfg.Point) typ.Type
	CallQuery() core.TypeOps
	Context() *db.QueryContext
}

SimpleSynth is a minimal synthesis interface for simple type queries.

Provides the subset of synthesis capabilities needed by components that only need basic type lookup without full flow-sensitive narrowing. Used by hooks and checkers that perform localized type validation.

type Synth

type Synth interface {
	api.Synth
	ResolveFieldAccess(fullExpr *ast.AttrGetExpr, objType typ.Type, fieldName string, p cfg.Point) FieldAccessResult
}

Synth extends the base synthesis interface with field access resolution.

This interface is implemented by Engine and provides the full synthesis capabilities needed by the type checker, including:

  • All methods from api.Synth (TypeOf, MultiTypeOf, etc.)
  • Field access resolution for attribute get expressions

The ResolveFieldAccess method handles both named field access (obj.field) and computed index access (obj[expr]), determining validity and result type.

Directories

Path Synopsis
Package intercept handles special-case type synthesis interceptions.
Package intercept handles special-case type synthesis interceptions.
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.
extract
Package extract handles expression extraction from CFG for synthesis.
Package extract handles expression extraction from CFG for synthesis.
resolve
Package resolve performs final type resolution after synthesis.
Package resolve performs final type resolution after synthesis.
Package transform provides type transformation operations.
Package transform provides type transformation operations.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL