Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
134 commits
Select commit Hold shift + click to select a range
d5d5cb8
Add type checking to Laurel resolution pass
keyboardDrummer-bot May 5, 2026
f65de03
Fix type checking: skip TCore types in assignability check
keyboardDrummer-bot May 5, 2026
7aaea81
Simplify assignment arity check to use valueTy directly
keyboardDrummer-bot May 5, 2026
179d16d
Add tests for type checking error diagnostics in resolution pass
keyboardDrummer-bot May 5, 2026
76ea8df
Add multi-output procedure in expression position check and test
keyboardDrummer-bot May 5, 2026
0a26f1d
Remove checkSingleValued; let type checks report multi-output errors …
keyboardDrummer-bot May 5, 2026
2acc758
Address review feedback: symmetric Eq/Neq errors, extract helper, con…
keyboardDrummer-bot May 6, 2026
e243dbd
add explanations about the typechecking routine added
leo-leesco May 15, 2026
8cb4ab2
bidirectional type checking
leo-leesco May 15, 2026
fbaa911
add resolution-only function
leo-leesco May 15, 2026
39c8dd5
document type system
leo-leesco May 15, 2026
14d27b4
ifthenelse type checking
leo-leesco May 15, 2026
d3750b4
document ifthenelse type checking
leo-leesco May 15, 2026
37e6c35
typechecking description refactor
leo-leesco May 15, 2026
60822bb
reformat typechecking section
leo-leesco May 15, 2026
7d24b64
concise explanations
leo-leesco May 15, 2026
2d85319
restore contexts in rules
leo-leesco May 15, 2026
55083a6
simplify presentation
leo-leesco May 15, 2026
1feda5b
add back in contexts
leo-leesco May 15, 2026
9bb2990
describe literals and easy rules (call, assert/assume…)
leo-leesco May 15, 2026
fea9f95
remove old helpers + mis-subtyping diagnostics
leo-leesco May 15, 2026
ce11f12
quantifier check for bool body
leo-leesco May 15, 2026
2207acc
remove redundant headings
leo-leesco May 15, 2026
4cbab19
class-rules : updates on same-typed fields + this in class context only
leo-leesco May 15, 2026
0a38a7c
references checks
leo-leesco May 15, 2026
3efef2f
pretty printers
leo-leesco May 15, 2026
59ce64d
better type mismatch diagnostics
leo-leesco May 15, 2026
538a687
check ifthenelse
leo-leesco May 15, 2026
a0862ee
type check returns
leo-leesco May 15, 2026
b58a578
type check procedures contracts
leo-leesco May 15, 2026
1a639d1
check untyped holes
leo-leesco May 15, 2026
5717c34
remove dangling reference
leo-leesco May 15, 2026
d8b8c7c
move subtyping/consistency rules in type definition
leo-leesco May 15, 2026
bbee6a7
inferholetypes flag already filled hole types when inconsistent
leo-leesco May 15, 2026
9e4c2f3
future roadmap
leo-leesco May 15, 2026
6fc8e36
fix multi value return destructuring
leo-leesco May 15, 2026
dbc220a
fix error messages to match current type mismatch reporting
leo-leesco May 15, 2026
4cb6de7
fix error reporting location
leo-leesco May 15, 2026
12c7a96
fix location error reporting
leo-leesco May 15, 2026
83fba02
Merge branch 'leo/1120-type-checking' into keyboardDrummer-bot/issue-…
leo-leesco May 15, 2026
9e353ac
fix field lookup
leo-leesco May 18, 2026
228559e
fix silent fail
leo-leesco May 18, 2026
3b3e598
thread typing context through type resolution/inheritance
leo-leesco May 18, 2026
2dffa2a
drop info
leo-leesco May 18, 2026
e69d58f
Merge branch 'leo/1120-type-checking' into keyboardDrummer-bot/issue-…
leo-leesco May 18, 2026
6fa3c22
fix typing doc direction
leo-leesco May 19, 2026
3440420
fix documentation : subtyping is implemented
leo-leesco May 19, 2026
8fc56ae
remove special treatment of TCore
leo-leesco May 19, 2026
d476f6b
fix TCore documentation
leo-leesco May 19, 2026
40236ce
uniform <=/=> and use latex rule presentation
leo-leesco May 19, 2026
c544b2b
extract typing rules out in helper functions for easier verso documen…
leo-leesco May 19, 2026
b0ffaf5
better if-then-else typing discipline
leo-leesco May 19, 2026
887c888
if then else type synthesis
leo-leesco May 19, 2026
8ad24ac
move test to appropriate location
leo-leesco May 19, 2026
4f7b6ba
very strict dereference comparison
leo-leesco May 19, 2026
59a8461
consistent references when comparing
leo-leesco May 19, 2026
8a950f8
fix assign by creating a checking rule
leo-leesco May 20, 2026
fd75ea5
documentation is moved to in-code docstrings
leo-leesco May 20, 2026
fbb5c81
namespace scoping to make code less verbose
leo-leesco May 20, 2026
2a3536a
Add type checking to Laurel resolution pass
keyboardDrummer-bot May 5, 2026
39b4f6c
Fix type checking: skip TCore types in assignability check
keyboardDrummer-bot May 5, 2026
bdae7eb
Simplify assignment arity check to use valueTy directly
keyboardDrummer-bot May 5, 2026
f5302f9
Add tests for type checking error diagnostics in resolution pass
keyboardDrummer-bot May 5, 2026
7d00fae
add explanations about the typechecking routine added
leo-leesco May 15, 2026
941f4ae
bidirectional type checking
leo-leesco May 15, 2026
be1d8de
add resolution-only function
leo-leesco May 15, 2026
83e449f
document type system
leo-leesco May 15, 2026
a7d90d6
ifthenelse type checking
leo-leesco May 15, 2026
0958f26
document ifthenelse type checking
leo-leesco May 15, 2026
2a513c7
typechecking description refactor
leo-leesco May 15, 2026
fcbe1fc
reformat typechecking section
leo-leesco May 15, 2026
084fa2d
concise explanations
leo-leesco May 15, 2026
294be7b
restore contexts in rules
leo-leesco May 15, 2026
06ce7b9
simplify presentation
leo-leesco May 15, 2026
835d8b5
add back in contexts
leo-leesco May 15, 2026
a7ae7bb
describe literals and easy rules (call, assert/assume…)
leo-leesco May 15, 2026
f3d657e
remove old helpers + mis-subtyping diagnostics
leo-leesco May 15, 2026
054a8ba
quantifier check for bool body
leo-leesco May 15, 2026
597c79e
remove redundant headings
leo-leesco May 15, 2026
3ea2431
class-rules : updates on same-typed fields + this in class context only
leo-leesco May 15, 2026
7872826
references checks
leo-leesco May 15, 2026
b212eb5
pretty printers
leo-leesco May 15, 2026
c51fc96
better type mismatch diagnostics
leo-leesco May 15, 2026
a70d171
check ifthenelse
leo-leesco May 15, 2026
453d293
type check returns
leo-leesco May 15, 2026
7bc098d
type check procedures contracts
leo-leesco May 15, 2026
c69210a
check untyped holes
leo-leesco May 15, 2026
e823cef
remove dangling reference
leo-leesco May 15, 2026
47ad5db
move subtyping/consistency rules in type definition
leo-leesco May 15, 2026
40e1572
inferholetypes flag already filled hole types when inconsistent
leo-leesco May 15, 2026
3897097
future roadmap
leo-leesco May 15, 2026
86b6d27
fix multi value return destructuring
leo-leesco May 15, 2026
18eb6c9
fix error messages to match current type mismatch reporting
leo-leesco May 15, 2026
151efeb
Add multi-output procedure in expression position check and test
keyboardDrummer-bot May 5, 2026
d7e2903
fix error reporting location
leo-leesco May 15, 2026
138993c
Remove checkSingleValued; let type checks report multi-output errors …
keyboardDrummer-bot May 5, 2026
4a4bbc6
fix location error reporting
leo-leesco May 15, 2026
d9caa37
Address review feedback: symmetric Eq/Neq errors, extract helper, con…
keyboardDrummer-bot May 6, 2026
65bc2b0
Merge branch 'leo/1120-type-checking' into keyboardDrummer-bot/issue-…
leo-leesco May 15, 2026
f2fea0a
thread typing context through type resolution/inheritance
leo-leesco May 18, 2026
f4fd6ff
fix field lookup
leo-leesco May 18, 2026
fb6fdd6
drop info
leo-leesco May 18, 2026
f5f57c1
fix silent fail
leo-leesco May 18, 2026
06ab507
Merge branch 'leo/1120-type-checking' into keyboardDrummer-bot/issue-…
leo-leesco May 18, 2026
c28cd1c
fix typing doc direction
leo-leesco May 19, 2026
cdfdda8
fix documentation : subtyping is implemented
leo-leesco May 19, 2026
fbb1de3
remove special treatment of TCore
leo-leesco May 19, 2026
a4674bd
fix TCore documentation
leo-leesco May 19, 2026
77d32f1
uniform <=/=> and use latex rule presentation
leo-leesco May 19, 2026
ccc2a98
extract typing rules out in helper functions for easier verso documen…
leo-leesco May 19, 2026
3d61b04
better if-then-else typing discipline
leo-leesco May 19, 2026
665e88f
if then else type synthesis
leo-leesco May 19, 2026
95763e2
move test to appropriate location
leo-leesco May 19, 2026
0341105
very strict dereference comparison
leo-leesco May 19, 2026
3fbb542
consistent references when comparing
leo-leesco May 19, 2026
2502825
fix assign by creating a checking rule
leo-leesco May 20, 2026
c9ce940
documentation is moved to in-code docstrings
leo-leesco May 20, 2026
d2de9da
namespace scoping to make code less verbose
leo-leesco May 20, 2026
8ceaf32
Merge branch 'leo/1120-type-checking' of github.com:strata-org/strata…
leo-leesco May 20, 2026
5826fff
cleanup rules presentation
leo-leesco May 20, 2026
1cde2a1
expand prose around Block typing rules
leo-leesco May 20, 2026
cc16c3e
remove synthesis rule for if-then-else
leo-leesco May 20, 2026
fbccb15
remove synthesis rule for blocks
leo-leesco May 20, 2026
7d3cdf6
move statement-shaped constructs to check-only
leo-leesco May 20, 2026
be18c88
move Old and ProveBy to check-only
leo-leesco May 20, 2026
4c23930
push assignment target types into RHS
leo-leesco May 20, 2026
971c39d
fix return type
leo-leesco May 20, 2026
f3da746
add [⇐] Assign entry to typing-rule index
leo-leesco May 20, 2026
5717a72
remove synthesis rule for assign
leo-leesco May 20, 2026
88e01cb
better docstrings
leo-leesco May 20, 2026
f0016e6
move holes to check-only, roll back InferHoleTypes
leo-leesco May 21, 2026
6f64515
Merge branch 'leo/1120-type-checking' into keyboardDrummer-bot/issue-…
leo-leesco May 21, 2026
778faf7
remove future structural changes entirely
leo-leesco May 21, 2026
b649e94
update rules
leo-leesco May 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
155 changes: 155 additions & 0 deletions Strata/Languages/Laurel/Laurel.lean
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,20 @@ inductive Operation : Type where
| StrConcat
deriving Repr

instance : ToString Operation where
toString
| .Eq => "==" | .Neq => "!="
| .And => "&&" | .Or => "||"
| .Not => "!" | .Implies => "==>"
| .AndThen => "&&!" | .OrElse => "||!"
| .Neg => "-" | .Add => "+"
| .Sub => "-" | .Mul => "*"
| .Div => "/" | .Mod => "%"
| .DivT => "/t" | .ModT => "%t"
| .Lt => "<" | .Leq => "<="
| .Gt => ">" | .Geq => ">="
| .StrConcat => "++"

/--
A wrapper that pairs a value with source-level metadata such as source
locations and annotations. All Laurel AST nodes are wrapped in
Expand Down Expand Up @@ -334,6 +348,40 @@ inductive ContractType where
| Reads | Modifies | Precondition | PostCondition
end

/-- A short user-facing name for the construct, used in diagnostic messages. -/
def StmtExpr.constrName : StmtExpr → String
| .IfThenElse .. => "if"
| .Block .. => "block"
| .While .. => "while"
| .Exit .. => "exit"
| .Return .. => "return"
| .LiteralInt .. => "integer literal"
| .LiteralBool .. => "boolean literal"
| .LiteralString .. => "string literal"
| .LiteralDecimal .. => "decimal literal"
| .Var .. => "variable"
| .Assign .. => ":="
| .PureFieldUpdate .. => "field update"
| .StaticCall .. => "call"
| .PrimitiveOp op _ => toString op
| .New .. => "new"
| .This => "this"
| .ReferenceEquals .. => "reference equality"
| .AsType .. => "as"
| .IsType .. => "is"
| .InstanceCall .. => "method call"
| .Quantifier .. => "quantifier"
| .Assigned .. => "assigned"
| .Old .. => "old"
| .Fresh .. => "fresh"
| .Assert .. => "assert"
| .Assume .. => "assume"
| .ProveBy .. => "by"
| .ContractOf .. => "contractOf"
| .Abstract => "abstract"
| .All => "all"
| .Hole .. => "hole"

@[expose] abbrev HighTypeMd := AstNode HighType
@[expose] abbrev StmtExprMd := AstNode StmtExpr
@[expose] abbrev VariableMd := AstNode Variable
Expand Down Expand Up @@ -420,6 +468,7 @@ def highEq (a : HighTypeMd) (b : HighTypeMd) : Bool := match _a: a.val, _b: b.va
| HighType.TSet t1, HighType.TSet t2 => highEq t1 t2
| HighType.TMap k1 v1, HighType.TMap k2 v2 => highEq k1 k2 && highEq v1 v2
| HighType.UserDefined r1, HighType.UserDefined r2 => r1.text == r2.text
| HighType.TCore s1, HighType.TCore s2 => s1 == s2
| HighType.Applied b1 args1, HighType.Applied b2 args2 =>
highEq b1 b2 && args1.length == args2.length && (args1.attach.zip args2 |>.all (fun (a1, a2) => highEq a1.1 a2))
| HighType.Pure b1, HighType.Pure b2 => highEq b1 b2
Expand All @@ -441,6 +490,99 @@ instance : BEq HighTypeMd where

deriving instance BEq for HighType

/-- Lookup tables threaded through subtyping/consistency checks. Built from
the program's `TypeDefinition`s by the resolution pass:
- `unfoldMap` maps an alias or constrained type's name to the type it
unwraps to (alias target / constrained base). Followed transitively to
reach a non-alias, non-constrained type.
- `extendingMap` maps a composite type's name to the *direct* parents in
its `extending` list. Walked transitively for the subtype check. -/
structure TypeContext where
unfoldMap : Std.HashMap String HighTypeMd := {}
extendingMap : Std.HashMap String (List String) := {}
deriving Inhabited

/-- Unfold aliases and constrained types to their underlying type.
Composites and primitives are returned unchanged. A `visited` set guards
against cycles in the alias/constrained graph (already cycle-checked
elsewhere, but keeps `unfold` safe to call independently). -/
partial def TypeContext.unfold (ctx : TypeContext) (ty : HighTypeMd)
(visited : Std.HashSet String := {}) : HighTypeMd :=
match ty.val with
| .UserDefined name =>
if visited.contains name.text then ty
else match ctx.unfoldMap.get? name.text with
| some target => ctx.unfold target (visited.insert name.text)
| none => ty
| _ => ty

/-- All ancestors of a composite type (including itself), reachable via
repeated `extending` lookups. The `fuel` cap is the number of distinct
type names ever registered, bounding the BFS even with malformed input. -/
partial def TypeContext.ancestors (ctx : TypeContext) (name : String) : Std.HashSet String :=
let rec go (acc : Std.HashSet String) (frontier : List String) : Std.HashSet String :=
match frontier with
| [] => acc
| n :: rest =>
if acc.contains n then go acc rest
else
let acc' := acc.insert n
let parents := (ctx.extendingMap.get? n).getD []
go acc' (parents ++ rest)
go {} [name]

/-- Pure subtyping `<:`. Walks the `extending` chain for `CompositeType`
(via `TypeContext.ancestors`), unfolds `TypeAlias` to its target, and
unwraps `ConstrainedType` to its base (both via `TypeContext.unfold`),
then falls back to structural equality via `highEq`.

Used together with `isConsistent` to form `isConsistentSubtype`, which
is what the bidirectional checker invokes at every check-mode boundary
(rule `[⇐] Sub`). -/
def isSubtype (ctx : TypeContext) (sub sup : HighTypeMd) : Bool :=
let sub' := ctx.unfold sub
let sup' := ctx.unfold sup
match sub'.val, sup'.val with
| .UserDefined subName, .UserDefined supName =>
-- After unfolding, both sides are composites (or unresolved). A composite
-- is a subtype of any type in its extending chain.
(ctx.ancestors subName.text).contains supName.text || highEq sub' sup'
| _, _ => highEq sub' sup'

/-- Consistency `~` (Siek–Taha): the symmetric gradual relation. `Unknown`
is the dynamic type and is consistent with everything; otherwise
structural equality after unfolding aliases / constrained types.

Used directly by `[⇒] Op-Eq`, where the operand types must be mutually
consistent (no subtype direction is privileged), and as one half of
`isConsistentSubtype`. -/
def isConsistent (ctx : TypeContext) (a b : HighTypeMd) : Bool :=
let a' := ctx.unfold a
let b' := ctx.unfold b
match a'.val, b'.val with
| .Unknown, _ | _, .Unknown => true
| _, _ => highEq a' b'

/-- Consistent subtyping: `∃ R. sub ~ R ∧ R <: sup`. For our flat lattice
this collapses to `sub ~ sup ∨ sub <: sup` — the standard collapse.

Used by rule `[⇐] Sub` (and every bespoke check rule). That single
choice is what makes the system *gradual*: an expression of type
`Unknown` (a hole, an unresolved name, a `Hole _ none`) flows freely
into any typed slot, and any expression flows freely into a slot of
type `Unknown`. Strict checking is applied between fully-known types
only.

A previous iteration was synth-only with two *bivariantly-compatible*
wildcards: `Unknown` and `UserDefined`. The `UserDefined` carve-out was
load-bearing: no assignment, call argument, or comparison involving a
user type was ever rejected. The bidirectional design retires that
carve-out — user-defined types are now a regular participant in `<:`,
with `isSubtype` walking inheritance chains and unwrapping aliases
and constrained types to deliver real checking on user-defined code. -/
def isConsistentSubtype (ctx : TypeContext) (sub sup : HighTypeMd) : Bool :=
isConsistent ctx sub sup || isSubtype ctx sub sup

def HighType.isBool : HighType → Bool
| TBool => true
| _ => false
Expand Down Expand Up @@ -578,6 +720,19 @@ def TypeDefinition.name : TypeDefinition → Identifier
| .Datatype ty => ty.name
| .Alias ty => ty.name

/-- Build a `TypeContext` from a list of `TypeDefinition`s.
Aliases populate `unfoldMap` with their target; constrained types populate
it with their base; composites populate `extendingMap` with their direct
parents. Datatypes contribute nothing — they're nominal and irreducible. -/
def TypeContext.ofTypes (types : List TypeDefinition) : TypeContext :=
types.foldl (init := {}) fun ctx td =>
match td with
| .Alias ta => { ctx with unfoldMap := ctx.unfoldMap.insert ta.name.text ta.target }
| .Constrained ct => { ctx with unfoldMap := ctx.unfoldMap.insert ct.name.text ct.base }
| .Composite c =>
{ ctx with extendingMap := ctx.extendingMap.insert c.name.text (c.extending.map (·.text)) }
| .Datatype _ => ctx

structure Constant where
name : Identifier
type : HighTypeMd
Expand Down
Loading
Loading