Documentation
¶
Overview ¶
Package typecheck holds unobin's semantic type model and the static type checker that runs after parsing. Type values carry no source position so the same value compares cleanly whether it came from a written declaration or was inferred from a literal.
The parser's lang.TypeExpr is the syntactic form of a written type declaration; convert it to a Type with FromLang before reasoning about it here.
Index ¶
- func Assignable(dst, src Type) bool
- func FieldKindLabel(kind string) string
- type EachBinding
- type FuncSig
- type Kind
- type LibraryConfigInfo
- type LookupLocalFn
- type LookupNodeFn
- type ObjectField
- type Scope
- type Type
- func Check(e lang.Expr, target Type, scope *Scope, errs *lang.ErrorList) Type
- func FromLang(t lang.TypeExpr) Type
- func Infer(e lang.Expr, target Type, scope *Scope, errs *lang.ErrorList) Type
- func MergeShallow(args []Type) Type
- func TBoolean() Type
- func TInteger() Type
- func TLibraryConfig(path, identity, digest string, fields []ObjectField) Type
- func TList(elem Type) Type
- func TMap(elem Type) Type
- func TNull() Type
- func TNumber() Type
- func TObject(fields []ObjectField) Type
- func TOpaque() Type
- func TOpenObject(fields []ObjectField) Type
- func TOptional(elem Type) Type
- func TString() Type
- func TTuple(elems []Type) Type
- func TUnion(members []Type) Type
- func TUnknown() Type
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Assignable ¶
Assignable reports whether a value of type src can flow into a slot declared with type dst. Either side being Unknown returns true so partial schema information fails open rather than producing spurious errors.
Rules in plain terms:
- opaque accepts anything, and an opaque source flows only into an opaque slot: the value passes through unread, and a typed slot is a promise to read. null is assignable only into a slot that includes null (an optional() wrapper or the null atom).
- integer widens into number but not the other way.
- optional(T) accepts T, null, or optional(T); a possibly-null source never flows into a slot that wants a value, so null handling is settled at compile and a null can only appear where the program wrote down what happens then.
- list/map/tuple compare element-wise.
- object types compare structurally: every required dst field must have a compatible src field; extra src fields are allowed (open against the source) and missing-optional dst fields are tolerated.
func FieldKindLabel ¶
FieldKindLabel returns the lowercase singular noun for a node kind ("resource", "data source", "action") used in error messages. Lives here so check_refs.go and the inferrer agree.
Types ¶
type EachBinding ¶
EachBinding is the type pair bound by an enclosing @for-each. Key is the index type (integer for lists, string for maps). Value is the iterated element type.
type FuncSig ¶ added in v0.6.0
FuncSig describes a library function to the inferrer: the fixed parameter types in order, the element type of a variadic tail (nil when the function is not variadic), and the result type. A function registered without declared types reads as all-Unknown, which checks nothing and infers nothing.
Infer, when set, computes the call's type from the argument types in place of Result. Arguments of such a call are inferred without a target so the hook sees their natural types: an object literal stays the precise object it spells, not the parameter type it flowed into.
type Kind ¶
type Kind int
Kind discriminates the Type variants. The zero value Unknown stands in for a type the walker could not determine; the checker skips compatibility comparisons involving Unknown so source the inferrer cannot reason about fails open rather than producing noise.
type LibraryConfigInfo ¶
LibraryConfigInfo is the nominal identity for a Go library config type.
type LookupLocalFn ¶
LookupLocalFn returns the inferred Type of a `locals:` entry by name. The boolean is false when no such local is declared; the inferrer then returns Unknown without an error (the reference checker reports an unknown local name).
type LookupNodeFn ¶
LookupNodeFn returns the output Type of a node by kind, alias, type, and name. The boolean is false when the node is not known; the inferrer then returns Unknown without an error (the existing reference checker has the responsibility to report unresolved node addresses).
type ObjectField ¶
ObjectField is one named field of an Object type. Optional is true when the field may be omitted. Defaulted is true when omission fills a non-null default, so reads use Type directly instead of optional(Type).
func InputsFromBlock ¶
func InputsFromBlock(decl *lang.ObjectLit) []ObjectField
InputsFromBlock walks an `inputs:` block object literal and returns each input's name to its semantic type. A nullable optional() input sets Optional, and a non-null default sets Defaulted.
type Scope ¶
type Scope struct {
Inputs []ObjectField
Each *EachBinding
LookupNode LookupNodeFn
LookupLocal LookupLocalFn
// LookupFunction resolves a library-qualified function to its
// signature so a call's arguments and result type-check. Nil, or a
// false return, leaves the call inferring Unknown; existence and
// argument count are the reference checker's to enforce.
LookupFunction func(library, name string) (FuncSig, bool)
// Bindings holds comprehension-bound names. They resolve as bare
// values and as dot-path roots ahead of var/resource/data/action.
// Names are distinct across nesting; validation rejects an inner
// comprehension that rebinds an enclosing name.
Bindings map[string]Type
// Narrowed overrides reference types inside a region guarded by a
// null test, keyed by the canonical dot path ("var.x.y"). A lookup
// takes the longest matching prefix and resumes traversal from the
// narrowed type. Sound because values never change between the
// test and the read.
Narrowed map[string]Type
// MissingAsNull mirrors the evaluation mode of the same name:
// navigating into a possibly-null value reads as null instead of
// failing, so the result of such a path is optional. Constraint
// expressions check in this mode; everything else is strict.
MissingAsNull bool
// Observe, when set, receives every inferred expression with its
// type. The residual-Unknown harness reads the stream to find
// positions the checker cannot type.
Observe func(e lang.Expr, t Type)
}
Scope carries the lexical information the inferrer needs to type an expression: local input declarations, an optional @each binding, and a callback that returns the output Type for a node address. LookupNode may be nil when the caller has no node table; the walker returns Unknown for any node reference in that case.
type Type ¶
type Type struct {
Kind Kind
Open bool
Elem *Type
Elems []Type
Fields []ObjectField
LibraryConfig *LibraryConfigInfo
}
Type is a structural type description. The Kind field discrim- inates which of the value-bearing fields is populated:
- List/Set/Map/Optional read Elem.
- Tuple reads Elems.
- Object reads Fields.
Pass Type around by value; the recursive children live on the pointer fields so a deeply nested type still copies in constant time at the top level.
Open applies to Object only: an open object may hold fields beyond the declared ones. Open changes what may be present, never what may be read - dotting into an undeclared field stays an error.
func Check ¶
Check infers the type of e and verifies it is assignable to the declared target. Returns the inferred type and appends a mismatch diagnostic to errs when the types are incompatible. An array or object literal matching a container target is enforced element by element inside Infer, so Check's own Assignable comparison skips exactly those pairings and the same mistake is not reported twice; everything else, references and calls included, is compared here.
func FromLang ¶
FromLang turns a parsed lang.TypeExpr into a semantic Type. Returns TUnknown when the input is nil or names a constructor the converter does not understand; the checker treats Unknown as a silent skip.
func Infer ¶
Infer walks e and returns its inferred type. The target steers how ambiguous literals decide between list/tuple and how object literals match against a declared type; pass TUnknown when no target is in effect. Errors found during inference are appended to errs; the return value is best-effort and may be Unknown when nothing useful can be determined.
func MergeShallow ¶
MergeShallow computes the type of a left-to-right shallow merge of objects. When every argument is a known object, optional-wrapped or null allowed, the result is the merged object type: a required field of a definitely-present later argument replaces an earlier field outright, while a field that may be absent at runtime (an optional field, or any field of a possibly-null argument) joins what it may replace. An argument that is a map, opaque, or unknown can hold keys the checker cannot see, so the whole result is Unknown. No arguments merge to the empty object.
func TLibraryConfig ¶
func TLibraryConfig(path, identity, digest string, fields []ObjectField) Type
func TObject ¶
func TObject(fields []ObjectField) Type
func TOpenObject ¶ added in v0.6.0
func TOpenObject(fields []ObjectField) Type
func (Type) ContainsUnknown ¶ added in v0.6.0
ContainsUnknown reports whether t or any type nested inside it is Unknown. IsKnown looks only through Optional; this walks every element, tuple member, and object field.
func (Type) Equal ¶
Equal reports whether two types describe the same thing. It is recursive and order-sensitive for tuples; object fields compare by name regardless of declaration order so two object types that declare the same fields in different orders match.
func (Type) Field ¶
func (t Type) Field(name string) (ObjectField, bool)
Field returns the named field of an Object or LibraryConfig type, ok=false when the field is absent or the type has no fields.
func (Type) IsKnown ¶
IsKnown returns false when the Type is Unknown or wraps Unknown through an Optional. The checker uses this to bail out of comparisons it cannot reason about.
func (Type) String ¶
String renders the type in the unobin type vocabulary the operator would have written: `list(string)`, `optional(integer)`, `object({ a: string b: integer })`. Object field order follows the order the type was constructed in so error messages stay stable with respect to the source.