From 5157dd99981cc272eb684a04abc6e5a0205d6949 Mon Sep 17 00:00:00 2001 From: rvcas Date: Wed, 18 Feb 2026 17:42:28 -0500 Subject: [PATCH 1/3] feat: add multiple integer types (i8, i16, i32, u8, u16, u32, u64) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add IntWidth enum with 8 variants and change Type::Int to Type::Int(IntWidth). Integer literals are now polymorphic — they get a fresh type variable constrained to integer types, with the concrete type determined by context and defaulting to i64. Cross-type arithmetic (e.g. i32 + i64) is a type error. Unsigned negation is rejected. Wasm codegen maps i64/u64 to ValType::I64 and all smaller types to ValType::I32 with appropriate truncation/sign-extension. Component ABI store functions are filled in for all widths. Overflow checking is generalized per type. Signed-off-by: rvcas --- docs/errors/E0044.md | 42 ++ docs/language-spec.md | 60 ++- starstream-compiler/src/docs.rs | 2 +- starstream-compiler/src/parser/primitives.rs | 2 +- starstream-compiler/src/typecheck/builtins.rs | 6 +- starstream-compiler/src/typecheck/env.rs | 2 +- starstream-compiler/src/typecheck/errors.rs | 15 +- .../src/typecheck/exhaustiveness.rs | 6 +- starstream-compiler/src/typecheck/infer.rs | 432 ++++++++++++++---- ...__typecheck__tests__binary_add_traces.snap | 20 +- ...check__tests__call_expression_chained.snap | 5 +- ...check__tests__call_expression_no_args.snap | 6 +- ...echeck__tests__call_expression_traces.snap | 9 +- ...ypecheck__tests__enum_match_inference.snap | 6 +- ...k__tests__enum_struct_pattern_punning.snap | 1 - ..._tests__exhaustive_match_all_variants.snap | 5 +- ..._exhaustive_match_with_struct_payload.snap | 1 - ...tests__exhaustive_match_with_wildcard.snap | 16 +- ...r__typecheck__tests__if_branch_traces.snap | 26 +- ..._typecheck__tests__let_binding_traces.snap | 20 +- ..._tests__match_bool_literal_exhaustive.snap | 16 +- ...ests__match_int_literal_with_wildcard.snap | 22 +- ...ck__tests__option_explicit_annotation.snap | 6 +- ...check__tests__option_pattern_matching.snap | 50 +- ...check__tests__option_some_constructor.snap | 6 +- ...__tests__reports_let_annotation_error.snap | 7 +- ..._typecheck__tests__reports_type_error.snap | 4 +- ...ck__tests__result_explicit_annotation.snap | 8 +- ...pecheck__tests__result_ok_constructor.snap | 6 +- ...check__tests__result_pattern_matching.snap | 34 +- ...__tests__struct_field_order_preserved.snap | 20 +- ...sts__struct_literals_and_field_access.snap | 11 +- ...__typecheck__tests__while_loop_traces.snap | 26 +- starstream-interpreter/src/lib.rs | 18 +- starstream-language-server/src/document.rs | 2 +- starstream-to-wasm/src/component_abi.rs | 27 +- starstream-to-wasm/src/decision_tree.rs | 4 +- starstream-to-wasm/src/lib.rs | 237 ++++++++-- .../tests/snapshots/inputs@add.star.snap | 25 +- .../snapshots/inputs@blockheight.star.snap | 44 +- .../tests/snapshots/inputs@enum.star.snap | 36 +- .../snapshots/inputs@enum_simple.star.snap | 10 +- .../snapshots/inputs@if_elseif_else.star.snap | 18 +- .../snapshots/inputs@if_expression.star.snap | 40 +- .../inputs@match_expression.star.snap | 263 +++++++---- .../inputs@multiple_functions.star.snap | 37 +- .../snapshots/inputs@option_result.star.snap | 314 +++++++++---- .../inputs@struct_definition.star.snap | 16 +- .../snapshots/inputs@struct_param.star.snap | 57 ++- .../snapshots/inputs@utxo_storage.star.snap | 29 +- .../snapshots/inputs@utxo_storage_2.star.snap | 76 ++- .../snapshots/inputs@while_simple.star.snap | 50 +- starstream-types/src/ast.rs | 2 +- starstream-types/src/types.rs | 94 +++- 54 files changed, 1623 insertions(+), 674 deletions(-) create mode 100644 docs/errors/E0044.md diff --git a/docs/errors/E0044.md b/docs/errors/E0044.md new file mode 100644 index 00000000..3f683e3e --- /dev/null +++ b/docs/errors/E0044.md @@ -0,0 +1,42 @@ +# E0044: Integer Literal Out of Range + +An integer literal does not fit in the type it was resolved to. + +## Example + +```starstream +fn main() { + let x: i8 = 300; // Error: integer literal `300` does not fit in type `i8` +} +``` + +```starstream +fn main() { + let y: u8 = -1; // Error: integer literal `-1` does not fit in type `u8` +} +``` + +## How to fix + +Either use a literal value that fits within the target type's range, or use a wider type: + +```starstream +fn main() { + let x: i8 = 100; // OK: 100 fits in i8 (-128..127) + let y: i16 = 300; // OK: 300 fits in i16 (-32768..32767) + let z: u8 = 255; // OK: 255 fits in u8 (0..255) +} +``` + +## Integer type ranges + +| Type | Min | Max | +| ----- | -------------------------- | ------------------------- | +| `i8` | -128 | 127 | +| `i16` | -32768 | 32767 | +| `i32` | -2147483648 | 2147483647 | +| `i64` | -9223372036854775808 | 9223372036854775807 | +| `u8` | 0 | 255 | +| `u16` | 0 | 65535 | +| `u32` | 0 | 4294967295 | +| `u64` | 0 | 18446744073709551615 | diff --git a/docs/language-spec.md b/docs/language-spec.md index 4e4d4081..8c5afa1f 100644 --- a/docs/language-spec.md +++ b/docs/language-spec.md @@ -372,7 +372,7 @@ Calling an effectful or runtime function without the appropriate keyword is a ty ## Expression semantics -- Integer literals work in the obvious way. +- Integer literals are polymorphic: an unadorned numeric literal like `42` adopts the integer type determined by context (e.g. a type annotation or function parameter type). When no context constrains the type, the literal defaults to `i64`. A compile-time error is emitted if the literal value does not fit in the resolved type (e.g. `let x: i8 = 300` is an error). - Boolean literals work in the obvious way. - Struct literals `TypeName { field: expr, ... }` evaluate each field expression once and produce a record value. Field names must be unique; order is irrelevant. - Enum constructors use `TypeName::Variant` with a previously declared enum name. Tuple-style payloads evaluate left-to-right and are stored without reordering. @@ -385,12 +385,26 @@ Calling an effectful or runtime function without the appropriate keyword is a ty - `runtime expr` wraps a runtime function call. Runtime functions access runtime-only information (e.g., block height) and must be explicitly marked at the call site. Using `runtime` on a non-runtime call is a type error. - Variable names refer to a `let` declaration earlier in the current scope or one of its parents, but not child scopes. -- Arithmetic operators: `+`, `-`, `*`, `/`, `%` work over integers in the usual - way. - - We assume wrapping signed 64-bit two's complement integers. - - `/` and `%` are floored. `%` has the same sign as the divisor. -- Unary `-` applies to integers. Unary `!` applies to booleans. -- Comparison operators: `==`, `!=`, `<`, `>`, `<=`, `>=` accept (integer, integer) or (boolean, boolean) and +- Arithmetic operators: `+`, `-`, `*`, `/`, `%` work over integers of the same type. + - Both operands must have the same integer type; cross-type arithmetic (e.g. `i32 + i64`) is a type error. + - The supported integer types and their ranges are: + + | Type | Signed | Bits | Min | Max | + | ----- | ------ | ---- | -------------------------- | ------------------------- | + | `i8` | yes | 8 | -128 | 127 | + | `i16` | yes | 16 | -32768 | 32767 | + | `i32` | yes | 32 | -2147483648 | 2147483647 | + | `i64` | yes | 64 | -9223372036854775808 | 9223372036854775807 | + | `u8` | no | 8 | 0 | 255 | + | `u16` | no | 16 | 0 | 65535 | + | `u32` | no | 32 | 0 | 4294967295 | + | `u64` | no | 64 | 0 | 18446744073709551615 | + + - Integer overflow and underflow is checked at runtime and traps. + - `/` and `%` are floored for signed types. `%` has the same sign as the divisor. + - For unsigned types, `/` and `%` are standard unsigned division and remainder. +- Unary `-` applies to signed integers only. Negating an unsigned integer is a type error. Unary `!` applies to booleans. +- Comparison operators: `==`, `!=`, `<`, `>`, `<=`, `>=` accept (integer, integer) of the same type or (boolean, boolean) and produce booleans. - The boolean operators `!`, `&&`, `||` accept booleans and produce booleans. @@ -399,29 +413,31 @@ Calling an effectful or runtime function without the appropriate keyword is a ty | Syntax rule | Type rule | Value rule | | --------------------------- | ------------------------------------------------------------------------- | ------------------------- | -| integer_literal | $\dfrac{}{Γ ⊢ integer\ literal : i64}$ | Integer literal | +| integer_literal | $\dfrac{}{Γ ⊢ integer\ literal : Int}$ where $Int$ is inferred from context, defaulting to $i64$ | Polymorphic integer literal | | boolean_literal | $\dfrac{}{Γ ⊢ boolean\ literal : bool}$ | Boolean literal | | identifier | $\dfrac{ident : T ∈ Γ}{Γ ⊢ ident : T}$ | Refers to `let` in scope | | (expression) | $\dfrac{Γ ⊢ e : T}{Γ ⊢ (e) : T}$ | Identity | | !expression | $\dfrac{Γ ⊢ e : bool}{Γ ⊢\ !e : bool}$ | Boolean inverse | -| -expression | $\dfrac{Γ ⊢ e : i64}{Γ ⊢ -e : i64}$ | Integer negation | -| expression \* expression | $\dfrac{Γ ⊢ lhs : i64 ∧ Γ ⊢ rhs : i64}{Γ ⊢ lhs * rhs : i64}$ | Integer multiplication | -| expression / expression | $\dfrac{Γ ⊢ lhs : i64 ∧ Γ ⊢ rhs : i64}{Γ ⊢ lhs / rhs : i64}$ | Integer floored division | -| expression % expression | $\dfrac{Γ ⊢ lhs : i64 ∧ Γ ⊢ rhs : i64}{Γ ⊢ lhs\ \%\ rhs : i64}$ | Integer floored remainder | -| expression + expression | $\dfrac{Γ ⊢ lhs : i64 ∧ Γ ⊢ rhs : i64}{Γ ⊢ lhs + rhs : i64}$ | Integer addition | -| expression - expression | $\dfrac{Γ ⊢ lhs : i64 ∧ Γ ⊢ rhs : i64}{Γ ⊢ lhs - rhs : i64}$ | Integer subtraction | -| expression < expression | $\dfrac{Γ ⊢ lhs : i64 ∧ Γ ⊢ rhs : i64}{Γ ⊢ lhs < rhs : bool}$ | Integer less-than | +| -expression | $\dfrac{Γ ⊢ e : Int,\ Int\ signed}{Γ ⊢ -e : Int}$ | Signed integer negation | +| expression \* expression | $\dfrac{Γ ⊢ lhs : Int ∧ Γ ⊢ rhs : Int}{Γ ⊢ lhs * rhs : Int}$ | Integer multiplication | +| expression / expression | $\dfrac{Γ ⊢ lhs : Int ∧ Γ ⊢ rhs : Int}{Γ ⊢ lhs / rhs : Int}$ | Integer division | +| expression % expression | $\dfrac{Γ ⊢ lhs : Int ∧ Γ ⊢ rhs : Int}{Γ ⊢ lhs\ \%\ rhs : Int}$ | Integer remainder | +| expression + expression | $\dfrac{Γ ⊢ lhs : Int ∧ Γ ⊢ rhs : Int}{Γ ⊢ lhs + rhs : Int}$ | Integer addition | +| expression - expression | $\dfrac{Γ ⊢ lhs : Int ∧ Γ ⊢ rhs : Int}{Γ ⊢ lhs - rhs : Int}$ | Integer subtraction | +| expression < expression | $\dfrac{Γ ⊢ lhs : Int ∧ Γ ⊢ rhs : Int}{Γ ⊢ lhs < rhs : bool}$ | Integer less-than | | | $\dfrac{Γ ⊢ lhs : bool ∧ Γ ⊢ rhs : bool}{Γ ⊢ lhs < rhs : bool}$ | See [truth tables] | -| expression <= expression | $\dfrac{Γ ⊢ lhs : i64 ∧ Γ ⊢ rhs : i64}{Γ ⊢ lhs <= rhs : bool}$ | Integer less-or-equal | +| expression <= expression | $\dfrac{Γ ⊢ lhs : Int ∧ Γ ⊢ rhs : Int}{Γ ⊢ lhs <= rhs : bool}$ | Integer less-or-equal | | | $\dfrac{Γ ⊢ lhs : bool ∧ Γ ⊢ rhs : bool}{Γ ⊢ lhs <= rhs : bool}$ | See [truth tables] | -| expression > expression | $\dfrac{Γ ⊢ lhs : i64 ∧ Γ ⊢ rhs : i64}{Γ ⊢ lhs > rhs : bool}$ | Integer greater-than | +| expression > expression | $\dfrac{Γ ⊢ lhs : Int ∧ Γ ⊢ rhs : Int}{Γ ⊢ lhs > rhs : bool}$ | Integer greater-than | | | $\dfrac{Γ ⊢ lhs : bool ∧ Γ ⊢ rhs : bool}{Γ ⊢ lhs > rhs : bool}$ | See [truth tables] | -| expression >= expression | $\dfrac{Γ ⊢ lhs : i64 ∧ Γ ⊢ rhs : i64}{Γ ⊢ lhs >= rhs : bool}$ | Integer greater-or-equal | +| expression >= expression | $\dfrac{Γ ⊢ lhs : Int ∧ Γ ⊢ rhs : Int}{Γ ⊢ lhs >= rhs : bool}$ | Integer greater-or-equal | | | $\dfrac{Γ ⊢ lhs : bool ∧ Γ ⊢ rhs : bool}{Γ ⊢ lhs >= rhs : bool}$ | See [truth tables] | -| expression == expression | $\dfrac{Γ ⊢ lhs : i64 ∧ Γ ⊢ rhs : i64}{Γ ⊢ lhs == rhs : bool}$ | Integer equality | +| expression == expression | $\dfrac{Γ ⊢ lhs : Int ∧ Γ ⊢ rhs : Int}{Γ ⊢ lhs == rhs : bool}$ | Integer equality | | | $\dfrac{Γ ⊢ lhs : bool ∧ Γ ⊢ rhs : bool}{Γ ⊢ lhs == rhs : bool}$ | See [truth tables] | -| expression != expression | $\dfrac{Γ ⊢ lhs : i64 ∧ Γ ⊢ rhs : i64}{Γ ⊢ lhs \text{ != } rhs : bool}$ | Integer nonequality | +| expression != expression | $\dfrac{Γ ⊢ lhs : Int ∧ Γ ⊢ rhs : Int}{Γ ⊢ lhs \text{ != } rhs : bool}$ | Integer nonequality | | | $\dfrac{Γ ⊢ lhs : bool ∧ Γ ⊢ rhs : bool}{Γ ⊢ lhs \text{ != } rhs : bool}$ | See [truth tables] | + +In the rules above, $Int$ stands for any single integer type from `{i8, i16, i32, i64, u8, u16, u32, u64}`. Both operands of a binary operator must have the **same** integer type. | expression && expression | $\dfrac{Γ ⊢ lhs : bool ∧ Γ ⊢ rhs : bool}{Γ ⊢ lhs\ \&\&\ rhs : bool}$ | Short-circuiting AND | | expression \|\| expression | $\dfrac{Γ ⊢ lhs : bool ∧ Γ ⊢ rhs : bool}{Γ ⊢ lhs\ \|\|\ rhs : bool}$ | Short-circuiting OR | | f(e₁, ..., eₙ) | $\dfrac{f : (T_1, ..., T_n) → R ∧ Γ ⊢ e_i : T_i}{Γ ⊢ f(e_1, ..., e_n) : R}$ | Function call | @@ -436,7 +452,7 @@ Calling an effectful or runtime function without the appropriate keyword is a ty ### Overflow and underflow -Integer overflow and underflow wraps. +Integer overflow and underflow is checked at runtime and causes a trap (runtime error). ### Floored division and remainder @@ -472,7 +488,7 @@ The remainder always has the sign of the right-hand side. - Blocks introduce a new child scope for `let` statements. - `let` statements add a new variable binding to the current scope and give it an initial value based on its expression. - - Variables are integers. + - Variables may be integers (`i8`, `i16`, `i32`, `i64`, `u8`, `u16`, `u32`, `u64`), booleans, structs, or enums. - Assignment statements look up a variable in the stack of scopes and change its current value to the result of evaluating the right-hand side. # Not Yet Implemented diff --git a/starstream-compiler/src/docs.rs b/starstream-compiler/src/docs.rs index cd316d76..84e4134d 100644 --- a/starstream-compiler/src/docs.rs +++ b/starstream-compiler/src/docs.rs @@ -55,7 +55,7 @@ pub enum TypeKind { impl From<&Type> for TypeRef { fn from(ty: &Type) -> Self { match ty { - Type::Int => TypeRef::Primitive("i64".to_string()), + Type::Int(w) => TypeRef::Primitive(w.display_name().to_string()), Type::Bool => TypeRef::Primitive("bool".to_string()), Type::Unit => TypeRef::Primitive("()".to_string()), Type::Var(id) => TypeRef::Primitive(id.as_str()), diff --git a/starstream-compiler/src/parser/primitives.rs b/starstream-compiler/src/parser/primitives.rs index 46daac9e..3627dcf0 100644 --- a/starstream-compiler/src/parser/primitives.rs +++ b/starstream-compiler/src/parser/primitives.rs @@ -26,7 +26,7 @@ pub fn identifier<'a>() -> impl Parser<'a, &'a str, Identifier, Extra<'a>> { pub fn integer_literal<'a>() -> impl Parser<'a, &'a str, Literal, Extra<'a>> + Clone { text::int(10) .map(|digits: &str| { - let value = digits.parse::().expect("integer literal"); + let value = digits.parse::().expect("integer literal"); Literal::Integer(value) }) .boxed() diff --git a/starstream-compiler/src/typecheck/builtins.rs b/starstream-compiler/src/typecheck/builtins.rs index 7e69fdbd..52c258f4 100644 --- a/starstream-compiler/src/typecheck/builtins.rs +++ b/starstream-compiler/src/typecheck/builtins.rs @@ -95,7 +95,7 @@ impl BuiltinRegistry { "blockHeight".to_string(), BuiltinFunction { params: vec![], - return_type: Type::Int, + return_type: Type::int(), effect: EffectKind::Runtime, }, ); @@ -105,7 +105,7 @@ impl BuiltinRegistry { "currentSlot".to_string(), BuiltinFunction { params: vec![], - return_type: Type::Int, + return_type: Type::int(), effect: EffectKind::Runtime, }, ); @@ -129,7 +129,7 @@ mod tests { .expect("blockHeight should exist"); assert_eq!(func.params, vec![]); - assert_eq!(func.return_type, Type::Int); + assert_eq!(func.return_type, Type::int()); assert_eq!(func.effect, EffectKind::Runtime); } diff --git a/starstream-compiler/src/typecheck/env.rs b/starstream-compiler/src/typecheck/env.rs index f39c6f6a..3899653e 100644 --- a/starstream-compiler/src/typecheck/env.rs +++ b/starstream-compiler/src/typecheck/env.rs @@ -116,7 +116,7 @@ fn free_type_vars_type(ty: &Type, out: &mut HashSet) { } } } - Type::Int | Type::Bool | Type::Unit => {} + Type::Int(_) | Type::Bool | Type::Unit => {} } } diff --git a/starstream-compiler/src/typecheck/errors.rs b/starstream-compiler/src/typecheck/errors.rs index ee7f8a44..7fdf2487 100644 --- a/starstream-compiler/src/typecheck/errors.rs +++ b/starstream-compiler/src/typecheck/errors.rs @@ -313,6 +313,11 @@ pub enum TypeErrorKind { }, /// A function was declared with a return type in a position where it shouldn't have one. ReturnTypeNotAllowed, + /// An integer literal is out of range for its resolved type. + LiteralOutOfRange { + value: i128, + ty: Type, + }, } impl TypeErrorKind { @@ -361,7 +366,8 @@ impl TypeErrorKind { TypeErrorKind::RuntimeRequiresRuntime => "E0040", TypeErrorKind::RuntimeWithoutKeyword { .. } => "E0041", TypeErrorKind::WrongGenericArity { .. } => "E0042", - TypeErrorKind::ReturnTypeNotAllowed { .. } => "E0043", + TypeErrorKind::ReturnTypeNotAllowed => "E0043", + TypeErrorKind::LiteralOutOfRange { .. } => "E0044", } } } @@ -686,6 +692,13 @@ impl fmt::Display for TypeErrorKind { TypeErrorKind::ReturnTypeNotAllowed => { write!(f, "return type not allowed on this function") } + TypeErrorKind::LiteralOutOfRange { value, ty } => { + write!( + f, + "integer literal `{value}` does not fit in type `{}`", + ty.to_compact_string() + ) + } } } } diff --git a/starstream-compiler/src/typecheck/exhaustiveness.rs b/starstream-compiler/src/typecheck/exhaustiveness.rs index f1ef3fa0..35261f97 100644 --- a/starstream-compiler/src/typecheck/exhaustiveness.rs +++ b/starstream-compiler/src/typecheck/exhaustiveness.rs @@ -35,7 +35,7 @@ pub enum SimplePat { /// Simplified literal for exhaustiveness checking #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub enum SimpleLiteral { - Int(i64), + Int(i128), Bool(bool), Unit, } @@ -79,7 +79,7 @@ impl CtorSet { // For primitive types (Int, Unit) and type variables, there are no constructors. // A wildcard pattern always covers these types completely. // We represent this as an empty constructor set. - Type::Int | Type::Unit | Type::Var(_) => Some(Self::infinite()), + Type::Int(_) | Type::Unit | Type::Var(_) => Some(Self::infinite()), Type::Function { .. } | Type::Tuple(_) => Some(Self::infinite()), } } @@ -834,7 +834,7 @@ mod tests { if arity == 0 { EnumVariantType::unit(vname) } else { - EnumVariantType::tuple(vname, vec![Type::Int; arity]) + EnumVariantType::tuple(vname, vec![Type::int(); arity]) } }) .collect(), diff --git a/starstream-compiler/src/typecheck/infer.rs b/starstream-compiler/src/typecheck/infer.rs index fb20d228..ae59054f 100644 --- a/starstream-compiler/src/typecheck/infer.rs +++ b/starstream-compiler/src/typecheck/infer.rs @@ -6,8 +6,9 @@ use std::{ }; use starstream_types::{ - AbiDef, AbiPart, EffectKind, EventDef, GenericTypeDef, Scheme, Span, Spanned, Type, TypeParam, - TypeVarId, TypedUtxoDef, TypedUtxoGlobal, TypedUtxoPart, UtxoDef, UtxoGlobal, UtxoPart, + AbiDef, AbiPart, EffectKind, EventDef, GenericTypeDef, IntWidth, Scheme, Span, Spanned, Type, + TypeParam, TypeVarId, TypedUtxoDef, TypedUtxoGlobal, TypedUtxoPart, UtxoDef, UtxoGlobal, + UtxoPart, ast::{ BinaryOp, Block, Definition, EnumConstructorPayload, EnumDef, EnumPatternPayload, EnumVariantPayload, Expr, FunctionDef, Identifier, ImportDef, ImportItems, Literal, @@ -189,6 +190,12 @@ pub fn typecheck_program( return Err(errors); } + // Default any unresolved integer type variables to i64. + inferencer.default_int_vars(); + + // Check range validity of integer literals against their resolved types. + inferencer.check_int_literal_ranges()?; + let mut typed_program = TypedProgram::new(typed_definitions); inferencer.apply_substitutions_program(&mut typed_program); @@ -213,6 +220,10 @@ struct Inferencer { capture_traces: bool, next_type_var: u32, subst: HashMap, + /// Type variables constrained to integer types (from polymorphic integer literals). + int_vars: HashSet, + /// Tracks the literal value associated with each integer type variable for range checking. + int_literal_values: HashMap, types: TypeRegistry, functions: FunctionRegistry, events: EventRegistry, @@ -322,6 +333,8 @@ impl Inferencer { capture_traces, next_type_var: 0, subst: HashMap::new(), + int_vars: HashSet::new(), + int_literal_values: HashMap::new(), types: TypeRegistry::new(), functions: FunctionRegistry::new(), events: EventRegistry::new(), @@ -1218,7 +1231,7 @@ impl Inferencer { Pattern::Literal { value, span } => { // Literal patterns must match the expected type let literal_ty = match value { - Literal::Integer(_) => Type::Int, + Literal::Integer(_) => Type::int(), Literal::Boolean(_) => Type::Bool, Literal::Unit => Type::Unit, }; @@ -1228,7 +1241,7 @@ impl Inferencer { value_span, *span, TypeErrorKind::GeneralMismatch { - expected: self.apply(&expected_ty), + expected: self.apply_for_display(&expected_ty), found: literal_ty.clone(), }, )?; @@ -1247,7 +1260,7 @@ impl Inferencer { enum_name.span.unwrap_or_else(dummy_span), TypeErrorKind::PatternEnumMismatch { enum_name: enum_name.name.clone(), - found: self.apply(&expected_ty), + found: self.apply_for_display(&expected_ty), }, )?; let mut traces = vec![unify_trace]; @@ -1620,8 +1633,8 @@ impl Inferencer { value.span, TypeErrorKind::AssignmentMismatch { name: name.name.clone(), - expected: self.apply(&expected_type), - found: self.apply(&value_type), + expected: self.apply_for_display(&expected_type), + found: self.apply_for_display(&value_type), }, )?; value_type = new_value_type; @@ -1689,8 +1702,8 @@ impl Inferencer { target.span.unwrap_or(value.span), TypeErrorKind::AssignmentMismatch { name: target.name.clone(), - expected: self.apply(&expected_type), - found: self.apply(&actual_type), + expected: self.apply_for_display(&expected_type), + found: self.apply_for_display(&actual_type), }, )?; @@ -1723,8 +1736,8 @@ impl Inferencer { expr.span, ctx.return_span, TypeErrorKind::ReturnMismatch { - expected: self.apply(&ctx.expected_return), - found: self.apply(&actual_type), + expected: self.apply_for_display(&ctx.expected_return), + found: self.apply_for_display(&actual_type), }, )?; ctx.saw_return = true; @@ -1745,8 +1758,8 @@ impl Inferencer { ctx.return_span, ctx.return_span, TypeErrorKind::ReturnMismatch { - expected: self.apply(&ctx.expected_return), - found: self.apply(&unit), + expected: self.apply_for_display(&ctx.expected_return), + found: self.apply_for_display(&unit), }, )?; ctx.saw_return = true; @@ -1832,8 +1845,8 @@ impl Inferencer { expr.span, ctx.return_span, TypeErrorKind::ReturnMismatch { - expected: self.apply(&ctx.expected_return), - found: self.apply(&actual), + expected: self.apply_for_display(&ctx.expected_return), + found: self.apply_for_display(&actual), }, )?; children.push(unify_trace); @@ -1867,11 +1880,17 @@ impl Inferencer { match &expr.node { Expr::Literal(lit) => { let (ty, kind, rule) = match lit { - Literal::Integer(value) => ( - Type::int(), - TypedExprKind::Literal(Literal::Integer(*value)), - "T-Int", - ), + Literal::Integer(value) => { + let ty = self.fresh_int_var(); + if let Type::Var(id) = &ty { + self.int_literal_values.insert(*id, (*value, expr.span)); + } + ( + ty, + TypedExprKind::Literal(Literal::Integer(*value)), + "T-Int", + ) + } Literal::Boolean(value) => ( Type::bool(), TypedExprKind::Literal(Literal::Boolean(*value)), @@ -1929,17 +1948,51 @@ impl Inferencer { Expr::Unary { op, expr: inner } => { let (typed_inner, inner_trace) = self.infer_expr(env, inner, ctx)?; let check = match op { - UnaryOp::Negate => self.require_is( - &typed_inner.node.ty, - Type::int(), - inner.span, - inner.span, - TypeErrorKind::UnaryMismatch { - op: *op, - expected: Type::int(), - found: self.apply(&typed_inner.node.ty), - }, - )?, + UnaryOp::Negate => { + let inner_ty = self.apply(&typed_inner.node.ty); + match &inner_ty { + Type::Int(w) if w.is_signed() => { + // Concrete signed int type — OK + let subject = + self.maybe_string(|| self.format_type(&inner_ty).to_string()); + let result = self.maybe_string(|| "ok (signed int)".to_string()); + self.make_trace("Check-Negate", None, subject, result, Vec::new) + } + Type::Int(w) => { + // Unsigned int — error + return Err(TypeError::new( + TypeErrorKind::UnaryMismatch { + op: *op, + expected: Type::int(), + found: Type::Int(*w), + }, + inner.span, + ) + .with_primary_message(format!( + "cannot negate unsigned type `{}`", + w.display_name() + ))); + } + Type::Var(id) if self.int_vars.contains(id) => { + // Int-constrained var (polymorphic literal) — allow for now, + // signedness will be checked when the type resolves + let subject = + self.maybe_string(|| self.format_type(&inner_ty).to_string()); + let result = self.maybe_string(|| "ok (int var)".to_string()); + self.make_trace("Check-Negate", None, subject, result, Vec::new) + } + _ => { + return Err(TypeError::new( + TypeErrorKind::UnaryMismatch { + op: *op, + expected: Type::int(), + found: inner_ty, + }, + inner.span, + )); + } + } + } UnaryOp::Not => self.require_is( &typed_inner.node.ty, Type::bool(), @@ -1948,7 +2001,7 @@ impl Inferencer { TypeErrorKind::UnaryMismatch { op: *op, expected: Type::bool(), - found: self.apply(&typed_inner.node.ty), + found: self.apply_for_display(&typed_inner.node.ty), }, )?, }; @@ -1995,8 +2048,7 @@ impl Inferencer { | BinaryOp::Multiply | BinaryOp::Divide | BinaryOp::Remainder => { - let both_int = - matches!(&left_ty, Type::Int) && matches!(&right_ty, Type::Int); + let both_int = self.is_int_like(&left_ty) && self.is_int_like(&right_ty); if !both_int { let left_repr = self.format_type(&left_ty); let right_repr = self.format_type(&right_ty); @@ -2012,29 +2064,20 @@ impl Inferencer { .with_secondary(right_label_span, format!("has type `{right_repr}`"))); } - children.push(self.require_is( - &typed_left.node.ty, - Type::int(), - left_label_span, + // Unify left and right to ensure same int width + let (unified_ty, unify_trace) = self.unify( + left_ty.clone(), + right_ty.clone(), left_label_span, - TypeErrorKind::BinaryOperandMismatch { - op: *op, - left: self.apply(&typed_left.node.ty), - right: self.apply(&typed_right.node.ty), - }, - )?); - children.push(self.require_is( - &typed_right.node.ty, - Type::int(), - right_label_span, right_label_span, TypeErrorKind::BinaryOperandMismatch { op: *op, - left: self.apply(&typed_left.node.ty), - right: self.apply(&typed_right.node.ty), + left: self.apply_for_display(&typed_left.node.ty), + right: self.apply_for_display(&typed_right.node.ty), }, - )?); - Type::int() + )?; + children.push(unify_trace); + unified_ty } BinaryOp::Less | BinaryOp::LessEqual @@ -2086,8 +2129,8 @@ impl Inferencer { left_label_span, TypeErrorKind::BinaryOperandMismatch { op: *op, - left: self.apply(&typed_left.node.ty), - right: self.apply(&typed_right.node.ty), + left: self.apply_for_display(&typed_left.node.ty), + right: self.apply_for_display(&typed_right.node.ty), }, )?); children.push(self.require_is( @@ -2097,8 +2140,8 @@ impl Inferencer { right_label_span, TypeErrorKind::BinaryOperandMismatch { op: *op, - left: self.apply(&typed_left.node.ty), - right: self.apply(&typed_right.node.ty), + left: self.apply_for_display(&typed_left.node.ty), + right: self.apply_for_display(&typed_right.node.ty), }, )?); Type::bool() @@ -2206,7 +2249,7 @@ impl Inferencer { field.name.span.unwrap_or(field.value.span), TypeErrorKind::GeneralMismatch { expected: expected_ty, - found: self.apply(&actual_ty), + found: self.apply_for_display(&actual_ty), }, )?; children.push(value_trace); @@ -2250,7 +2293,7 @@ impl Inferencer { } Expr::FieldAccess { target, field } => { let (typed_target, target_trace) = self.infer_expr(env, target, ctx)?; - let target_ty = self.apply(&typed_target.node.ty); + let target_ty = self.apply_for_display(&typed_target.node.ty); let field_ty = match target_ty.clone() { Type::Record(record) => record .fields @@ -2393,7 +2436,7 @@ impl Inferencer { arg.span, TypeErrorKind::ArgumentTypeMismatch { expected: expected_ty.clone(), - found: self.apply(&actual_ty), + found: self.apply_for_display(&actual_ty), position: index + 1, param_span: None, }, @@ -2531,7 +2574,7 @@ impl Inferencer { variant.span.unwrap_or(expr.span), TypeErrorKind::GeneralMismatch { expected: expected_ty.clone(), - found: self.apply(&actual_ty), + found: self.apply_for_display(&actual_ty), }, )?; children.push(value_trace); @@ -2590,7 +2633,7 @@ impl Inferencer { field.name.span.unwrap_or(field.value.span), TypeErrorKind::GeneralMismatch { expected: expected_field.ty.clone(), - found: self.apply(&actual_ty), + found: self.apply_for_display(&actual_ty), }, )?; children.push(value_trace); @@ -2706,7 +2749,7 @@ impl Inferencer { expr.span, TypeErrorKind::GeneralMismatch { expected: current, - found: self.apply(&then_ty), + found: self.apply_for_display(&then_ty), }, )?; children.push(unify_trace); @@ -2742,7 +2785,7 @@ impl Inferencer { expr.span, TypeErrorKind::GeneralMismatch { expected: current, - found: self.apply(&else_ty), + found: self.apply_for_display(&else_ty), }, )?; children.push(unify_trace); @@ -2856,7 +2899,7 @@ impl Inferencer { } Expr::Call { callee, args } => { let (typed_callee, callee_trace) = self.infer_expr(env, callee, ctx)?; - let callee_ty = self.apply(&typed_callee.node.ty); + let callee_ty = self.apply_for_display(&typed_callee.node.ty); let callee_name = if let TypedExprKind::Identifier(Identifier { name, .. }) = &typed_callee.node.kind @@ -2956,7 +2999,7 @@ impl Inferencer { arg.span, TypeErrorKind::ArgumentTypeMismatch { expected: expected_ty.clone(), - found: self.apply(&actual_ty), + found: self.apply_for_display(&actual_ty), position: index + 1, param_span, }, @@ -3029,7 +3072,7 @@ impl Inferencer { TypeErrorKind::EventArgumentTypeMismatch { event_name: event_name.clone(), expected: expected_ty.clone(), - found: self.apply(&actual_ty), + found: self.apply_for_display(&actual_ty), position: index + 1, param_span, }, @@ -3211,7 +3254,14 @@ impl Inferencer { } match annotation.name.name.as_str() { + "i8" => Ok(Type::int_of(IntWidth::I8)), + "i16" => Ok(Type::int_of(IntWidth::I16)), + "i32" => Ok(Type::int_of(IntWidth::I32)), "i64" => Ok(Type::int()), + "u8" => Ok(Type::int_of(IntWidth::U8)), + "u16" => Ok(Type::int_of(IntWidth::U16)), + "u32" => Ok(Type::int_of(IntWidth::U32)), + "u64" => Ok(Type::int_of(IntWidth::U64)), "bool" => Ok(Type::bool()), "()" => Ok(Type::unit()), "_" => Ok(self.fresh_var()), @@ -3242,7 +3292,7 @@ impl Inferencer { span: Span, context: ConditionContext, ) -> Result { - let applied = self.apply(ty); + let applied = self.apply_for_display(ty); if matches!(&applied, Type::Bool) { let subject = self.maybe_string(|| self.format_type(&applied)); Ok(self.make_trace( @@ -3285,19 +3335,33 @@ impl Inferencer { right: &Spanned, right_span: Span, ) -> Result { - let left_ty = self.apply(&left.node.ty); - let right_ty = self.apply(&right.node.ty); + let left_ty = self.apply_for_display(&left.node.ty); + let right_ty = self.apply_for_display(&right.node.ty); + + let both_int = self.is_int_like(&left_ty) && self.is_int_like(&right_ty); - if matches!(&left_ty, Type::Int) && matches!(&right_ty, Type::Int) { + if both_int { + // Unify to ensure same int width + let (unified_ty, unify_trace) = self.unify( + left_ty.clone(), + right_ty.clone(), + left_span, + right_span, + TypeErrorKind::BinaryOperandMismatch { + op: *op, + left: left_ty.clone(), + right: right_ty.clone(), + }, + )?; let subject = self.maybe_string(|| { format!( "{} vs {}", self.format_type(&left_ty), - self.format_type(&right_ty) + self.format_type(&unified_ty) ) }); let result = self.maybe_string(|| "ok (int)".to_string()); - Ok(self.make_trace("Check-Compare", None, subject, result, Vec::new)) + Ok(self.make_trace("Check-Compare", None, subject, result, || vec![unify_trace])) } else if matches!(&left_ty, Type::Bool) && matches!(&right_ty, Type::Bool) { let subject = self.maybe_string(|| { format!( @@ -3334,12 +3398,34 @@ impl Inferencer { right: &Spanned, right_span: Span, ) -> Result { - let left_ty = self.apply(&left.node.ty); - let right_ty = self.apply(&right.node.ty); + let left_ty = self.apply_for_display(&left.node.ty); + let right_ty = self.apply_for_display(&right.node.ty); - if (matches!(&left_ty, Type::Int) && matches!(&right_ty, Type::Int)) - || (matches!(&left_ty, Type::Bool) && matches!(&right_ty, Type::Bool)) - { + let both_int = self.is_int_like(&left_ty) && self.is_int_like(&right_ty); + + if both_int { + // Unify to ensure same int width + let (unified_ty, unify_trace) = self.unify( + left_ty.clone(), + right_ty.clone(), + left_span, + right_span, + TypeErrorKind::BinaryOperandMismatch { + op: *op, + left: left_ty.clone(), + right: right_ty.clone(), + }, + )?; + let subject = self.maybe_string(|| { + format!( + "{} vs {}", + self.format_type(&left_ty), + self.format_type(&unified_ty) + ) + }); + let result = self.maybe_string(|| "ok".to_string()); + Ok(self.make_trace("Check-Eq", None, subject, result, || vec![unify_trace])) + } else if matches!(&left_ty, Type::Bool) && matches!(&right_ty, Type::Bool) { let subject = self.maybe_string(|| { format!( "{} vs {}", @@ -3366,6 +3452,77 @@ impl Inferencer { } } + /// Normalize a type for use in user-facing error messages. + /// + /// Like [`apply`], but also defaults unresolved int-constrained type + /// variables to `i64` so that error messages show a concrete type name + /// instead of an internal type variable like `t3`. + fn apply_for_display(&self, ty: &Type) -> Type { + match ty { + Type::Var(id) => match self.subst.get(id) { + Some(ty) => self.apply_for_display(ty), + None if self.int_vars.contains(id) => Type::int(), + None => Type::Var(*id), + }, + Type::Function { + params, + result, + effect, + } => Type::Function { + params: params.iter().map(|t| self.apply_for_display(t)).collect(), + result: Box::new(self.apply_for_display(result)), + effect: *effect, + }, + Type::Tuple(items) => { + Type::Tuple(items.iter().map(|t| self.apply_for_display(t)).collect()) + } + Type::Record(record) => Type::Record(RecordType { + name: record.name.clone(), + fields: record + .fields + .iter() + .map(|field| TypeRecordField { + name: field.name.clone(), + ty: self.apply_for_display(&field.ty), + }) + .collect(), + }), + Type::Enum(enum_type) => Type::Enum(EnumType { + name: enum_type.name.clone(), + type_args: enum_type + .type_args + .iter() + .map(|t| self.apply_for_display(t)) + .collect(), + variants: enum_type + .variants + .iter() + .map(|variant| TypeEnumVariant { + name: variant.name.clone(), + kind: match &variant.kind { + TypeEnumVariantKind::Unit => TypeEnumVariantKind::Unit, + TypeEnumVariantKind::Tuple(payload) => TypeEnumVariantKind::Tuple( + payload.iter().map(|ty| self.apply_for_display(ty)).collect(), + ), + TypeEnumVariantKind::Struct(fields) => TypeEnumVariantKind::Struct( + fields + .iter() + .map(|field| { + TypeRecordField::new( + field.name.clone(), + self.apply_for_display(&field.ty), + ) + }) + .collect(), + ), + }, + }) + .collect(), + }), + _ => ty.clone(), + } + } + /// Fully normalize a type by applying the current substitution set. fn apply(&self, ty: &Type) -> Type { match ty { @@ -3426,7 +3583,7 @@ impl Inferencer { .map(|ty| self.apply(ty)) .collect(), }), - Type::Int => Type::Int, + Type::Int(w) => Type::Int(*w), Type::Bool => Type::Bool, Type::Unit => Type::Unit, } @@ -3573,7 +3730,9 @@ impl Inferencer { let applied = self.apply(ty); let mut ty_free = free_type_vars_type(&applied); let env_free = env.free_type_vars(); - ty_free.retain(|var| !env_free.contains(var)); + // Don't quantify int-constrained vars — they should stay monomorphic + // so that all uses share the same int type variable. + ty_free.retain(|var| !env_free.contains(var) && !self.int_vars.contains(var)); let mut vars: Vec<_> = ty_free.into_iter().collect(); vars.sort(); Scheme { vars, ty: applied } @@ -3599,6 +3758,48 @@ impl Inferencer { Type::Var(self.fresh_var_id()) } + /// Create a fresh type variable constrained to integer types. + /// Used for polymorphic integer literals. + fn fresh_int_var(&mut self) -> Type { + let id = self.fresh_var_id(); + self.int_vars.insert(id); + Type::Var(id) + } + + /// Default any unresolved integer type variables to `i64`. + fn default_int_vars(&mut self) { + for &id in &self.int_vars { + let resolved = self.apply(&Type::Var(id)); + if matches!(resolved, Type::Var(_)) { + self.subst.insert(id, Type::int()); + } + } + } + + /// Check that all integer literals fit within the range of their resolved type. + fn check_int_literal_ranges(&self) -> Result<(), Vec> { + let mut errors = Vec::new(); + for (&id, &(value, span)) in &self.int_literal_values { + let resolved = self.apply(&Type::Var(id)); + if let Type::Int(w) = resolved + && !w.fits(value) + { + errors.push(TypeError::new( + TypeErrorKind::LiteralOutOfRange { + value, + ty: Type::Int(w), + }, + span, + )); + } + } + if errors.is_empty() { + Ok(()) + } else { + Err(errors) + } + } + /// Render the current environment snapshot into a deterministic string. fn format_env(&self, env: &TypeEnv) -> String { let snapshot = env.snapshot(); @@ -3746,7 +3947,9 @@ impl Inferencer { right: Type, ) -> Result<(Type, Vec, &'static str), ()> { match (left.clone(), right.clone()) { - (Type::Int, Type::Int) => Ok((Type::Int, Vec::new(), "Unify-Const")), + (Type::Int(w1), Type::Int(w2)) if w1 == w2 => { + Ok((Type::Int(w1), Vec::new(), "Unify-Const")) + } (Type::Bool, Type::Bool) => Ok((Type::Bool, Vec::new(), "Unify-Const")), (Type::Unit, Type::Unit) => Ok((Type::Unit, Vec::new(), "Unify-Const")), (Type::Tuple(ls), Type::Tuple(rs)) if ls.len() == rs.len() => { @@ -3842,6 +4045,22 @@ impl Inferencer { if occurs_in(id, &ty, &self.subst) { return Err(()); } + // If this var is int-constrained, verify the target is an int type + // or propagate the constraint to another var. + if self.int_vars.contains(&id) { + match &ty { + Type::Int(_) => {} // OK + Type::Var(other_id) => { + // Propagate int constraint to the other var + self.int_vars.insert(*other_id); + // Also propagate literal value tracking if present + if let Some(val) = self.int_literal_values.get(&id).copied() { + self.int_literal_values.entry(*other_id).or_insert(val); + } + } + _ => return Err(()), + } + } self.subst.insert(id, ty.clone()); Ok((ty, Vec::new(), "Unify-Var")) } @@ -3852,6 +4071,20 @@ impl Inferencer { if occurs_in(id, &ty, &self.subst) { return Err(()); } + // If this var is int-constrained, verify the target is an int type + // or propagate the constraint to another var. + if self.int_vars.contains(&id) { + match &ty { + Type::Int(_) => {} // OK + Type::Var(other_id) => { + self.int_vars.insert(*other_id); + if let Some(val) = self.int_literal_values.get(&id).copied() { + self.int_literal_values.entry(*other_id).or_insert(val); + } + } + _ => return Err(()), + } + } self.subst.insert(id, ty.clone()); Ok((ty, Vec::new(), "Unify-Var")) } @@ -3879,7 +4112,9 @@ impl Inferencer { }; let (result_ty, children, rule) = match (left.clone(), right.clone()) { - (Type::Int, Type::Int) => (Type::Int, Vec::new(), "Unify-Const"), + (Type::Int(w1), Type::Int(w2)) if w1 == w2 => { + (Type::Int(w1), Vec::new(), "Unify-Const") + } (Type::Bool, Type::Bool) => (Type::Bool, Vec::new(), "Unify-Const"), (Type::Unit, Type::Unit) => (Type::Unit, Vec::new(), "Unify-Const"), (Type::Tuple(ls), Type::Tuple(rs)) => { @@ -4097,6 +4332,15 @@ impl Inferencer { Ok((result_ty, tree)) } + /// Returns `true` if `ty` is either a concrete `Type::Int(_)` or an int-constrained type variable. + fn is_int_like(&self, ty: &Type) -> bool { + match ty { + Type::Int(_) => true, + Type::Var(id) => self.int_vars.contains(id), + _ => false, + } + } + fn bind( &mut self, var: TypeVarId, @@ -4114,6 +4358,26 @@ impl Inferencer { .with_secondary(other_span, "would create an infinite type")); } + // If this var is int-constrained, verify the target is an int type + // or propagate the constraint to another var. + if self.int_vars.contains(&var) { + match &ty { + Type::Int(_) => {} // OK — concrete int type + Type::Var(other_id) => { + // Propagate int constraint to the other var + self.int_vars.insert(*other_id); + // Also propagate literal value tracking if present + if let Some(val) = self.int_literal_values.get(&var).copied() { + self.int_literal_values.entry(*other_id).or_insert(val); + } + } + _ => { + return Err(TypeError::new(kind, var_span) + .with_secondary(other_span, "expected an integer type")); + } + } + } + self.subst.insert(var, ty); Ok(()) } @@ -4195,7 +4459,7 @@ fn substitute_type(ty: &Type, mapping: &HashMap) -> Type { .map(|ty| substitute_type(ty, mapping)) .collect(), }), - Type::Int => Type::Int, + Type::Int(w) => Type::Int(*w), Type::Bool => Type::Bool, Type::Unit => Type::Unit, } @@ -4234,7 +4498,7 @@ fn occurs_in(var: TypeVarId, ty: &Type, subst: &HashMap) -> boo fields.iter().any(|field| occurs_in(var, &field.ty, subst)) } }), - Type::Int | Type::Bool | Type::Unit => false, + Type::Int(_) | Type::Bool | Type::Unit => false, } } @@ -4284,7 +4548,7 @@ fn collect_free_type_vars(ty: &Type, set: &mut HashSet) { } } } - Type::Int | Type::Bool | Type::Unit => {} + Type::Int(_) | Type::Bool | Type::Unit => {} } } diff --git a/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__binary_add_traces.snap b/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__binary_add_traces.snap index c8c6d135..3660bb1c 100644 --- a/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__binary_add_traces.snap +++ b/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__binary_add_traces.snap @@ -1,16 +1,14 @@ --- source: starstream-compiler/src/typecheck/tests.rs -assertion_line: 102 description: "Source:\n\nfn test() {\n let left = 1;\n let right = 2;\n let total = left + right;\n}\n" --- T-Fn: test => () - T-Let: {} ⊢ let left = 1; ⇒ i64 - T-Int: {} ⊢ 1 ⇒ i64 - T-Let: {left: i64} ⊢ let right = 2; ⇒ i64 - T-Int: {left: i64} ⊢ 2 ⇒ i64 - T-Let: {left: i64, right: i64} ⊢ let total = left + right; ⇒ i64 - T-Bin-Add: {left: i64, right: i64} ⊢ left + right ⇒ i64 - T-Var: {left: i64, right: i64} ⊢ left ⇒ i64 - T-Var: {left: i64, right: i64} ⊢ right ⇒ i64 - Unify-Const: i64 ~ i64 => {} - Unify-Const: i64 ~ i64 => {} + T-Let: {} ⊢ let left = 1; ⇒ t3 + T-Int: {} ⊢ 1 ⇒ t3 + T-Let: {left: t3} ⊢ let right = 2; ⇒ t4 + T-Int: {left: t3} ⊢ 2 ⇒ t4 + T-Let: {left: t3, right: t4} ⊢ let total = left + right; ⇒ t4 + T-Bin-Add: {left: t3, right: t4} ⊢ left + right ⇒ t4 + T-Var: {left: t3, right: t4} ⊢ left ⇒ t3 + T-Var: {left: t3, right: t4} ⊢ right ⇒ t4 + Unify-Var: t3 ~ t4 => {t4/t3} diff --git a/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__call_expression_chained.snap b/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__call_expression_chained.snap index 5fabbbcb..08d287fb 100644 --- a/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__call_expression_chained.snap +++ b/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__call_expression_chained.snap @@ -8,7 +8,6 @@ T-Fn: double => i64 T-Var: {n: i64} ⊢ n ⇒ i64 T-Var: {n: i64} ⊢ n ⇒ i64 Unify-Const: i64 ~ i64 => {} - Unify-Const: i64 ~ i64 => {} Unify-Const: i64 ~ i64 => {} T-Fn: test => () @@ -17,6 +16,6 @@ T-Fn: test => () T-Var: {} ⊢ double ⇒ fn(i64) -> i64 T-Call: {} ⊢ double(5) ⇒ i64 T-Var: {} ⊢ double ⇒ fn(i64) -> i64 - T-Int: {} ⊢ 5 ⇒ i64 - Unify-Const: i64 ~ i64 => {} + T-Int: {} ⊢ 5 ⇒ t3 + Unify-Var: t3 ~ i64 => {i64/t3} Unify-Const: i64 ~ i64 => {} diff --git a/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__call_expression_no_args.snap b/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__call_expression_no_args.snap index b528925c..7674f96c 100644 --- a/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__call_expression_no_args.snap +++ b/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__call_expression_no_args.snap @@ -3,9 +3,9 @@ source: starstream-compiler/src/typecheck/tests.rs description: "Source:\n\nfn greet() -> i64 {\n 42\n}\n\nfn test() {\n let value = greet();\n}\n" --- T-Fn: greet => i64 - T-ReturnTail: 42 => i64 - T-Int: {} ⊢ 42 ⇒ i64 - Unify-Const: i64 ~ i64 => {} + T-ReturnTail: 42 => t3 + T-Int: {} ⊢ 42 ⇒ t3 + Unify-Var: t3 ~ i64 => {i64/t3} T-Fn: test => () T-Let: {} ⊢ let value = greet(); ⇒ i64 diff --git a/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__call_expression_traces.snap b/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__call_expression_traces.snap index ece3b2d3..764eab45 100644 --- a/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__call_expression_traces.snap +++ b/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__call_expression_traces.snap @@ -8,14 +8,13 @@ T-Fn: add => i64 T-Var: {a: i64, b: i64} ⊢ a ⇒ i64 T-Var: {a: i64, b: i64} ⊢ b ⇒ i64 Unify-Const: i64 ~ i64 => {} - Unify-Const: i64 ~ i64 => {} Unify-Const: i64 ~ i64 => {} T-Fn: test => () T-Let: {} ⊢ let result = add(1, 2); ⇒ i64 T-Call: {} ⊢ add(1, 2) ⇒ i64 T-Var: {} ⊢ add ⇒ fn(i64, i64) -> i64 - T-Int: {} ⊢ 1 ⇒ i64 - Unify-Const: i64 ~ i64 => {} - T-Int: {} ⊢ 2 ⇒ i64 - Unify-Const: i64 ~ i64 => {} + T-Int: {} ⊢ 1 ⇒ t3 + Unify-Var: t3 ~ i64 => {i64/t3} + T-Int: {} ⊢ 2 ⇒ t4 + Unify-Var: t4 ~ i64 => {i64/t4} diff --git a/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__enum_match_inference.snap b/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__enum_match_inference.snap index 2564f853..5783bb0a 100644 --- a/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__enum_match_inference.snap +++ b/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__enum_match_inference.snap @@ -28,11 +28,11 @@ T-Fn: respond => i64 }} ⊢ msg ⇒ Message Unify-Enum: Message ~ Message => {} Unify-Const: i64 ~ i64 => {} - T-Tail: 0 => i64 + T-Tail: 0 => t3 T-Int: {msg: enum Message { Ping, Pong(i64), -}} ⊢ 0 ⇒ i64 +}} ⊢ 0 ⇒ t3 Unify-Enum: Message ~ Message => {} Unify-Const: i64 ~ i64 => {} T-Tail: value => i64 @@ -40,5 +40,5 @@ T-Fn: respond => i64 Ping, Pong(i64), }, value: i64} ⊢ value ⇒ i64 - Unify-Const: i64 ~ i64 => i64 + Unify-Var: i64 ~ t3 => i64 Unify-Const: i64 ~ i64 => {} diff --git a/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__enum_struct_pattern_punning.snap b/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__enum_struct_pattern_punning.snap index d392ef2a..85a96726 100644 --- a/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__enum_struct_pattern_punning.snap +++ b/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__enum_struct_pattern_punning.snap @@ -73,6 +73,5 @@ T-Fn: add => i64 Pong { x: i64 }, }, x: i64} ⊢ a ⇒ Point Unify-Const: i64 ~ i64 => {} - Unify-Const: i64 ~ i64 => {} Unify-Const: i64 ~ i64 => i64 Unify-Const: i64 ~ i64 => {} diff --git a/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__exhaustive_match_all_variants.snap b/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__exhaustive_match_all_variants.snap index 1e44e104..c140b319 100644 --- a/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__exhaustive_match_all_variants.snap +++ b/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__exhaustive_match_all_variants.snap @@ -45,12 +45,11 @@ T-Fn: test => i64 T-Int: {code: t6, r: enum Result { Ok(i64), Err(i64), -}} ⊢ 0 ⇒ i64 +}} ⊢ 0 ⇒ t7 T-Var: {code: t6, r: enum Result { Ok(i64), Err(i64), }} ⊢ code ⇒ t6 - Unify-Const: i64 ~ i64 => {} - Unify-Const: i64 ~ i64 => {} + Unify-Var: t7 ~ i64 => {i64/t7} Unify-Const: i64 ~ i64 => i64 Unify-Const: i64 ~ i64 => {} diff --git a/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__exhaustive_match_with_struct_payload.snap b/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__exhaustive_match_with_struct_payload.snap index 90fd7310..7d9b7298 100644 --- a/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__exhaustive_match_with_struct_payload.snap +++ b/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__exhaustive_match_with_struct_payload.snap @@ -53,6 +53,5 @@ T-Fn: test => i64 Rectangle { width: i64, height: i64 }, }, width: i64} ⊢ height ⇒ i64 Unify-Const: i64 ~ i64 => {} - Unify-Const: i64 ~ i64 => {} Unify-Const: i64 ~ i64 => i64 Unify-Const: i64 ~ i64 => {} diff --git a/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__exhaustive_match_with_wildcard.snap b/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__exhaustive_match_with_wildcard.snap index 5fb4c82b..ba921a84 100644 --- a/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__exhaustive_match_with_wildcard.snap +++ b/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__exhaustive_match_with_wildcard.snap @@ -10,7 +10,7 @@ T-Fn: test => i64 _ => { 1 }, -} => i64 +} => t3 T-Match: {c: enum Color { Red, Green, @@ -22,24 +22,24 @@ T-Fn: test => i64 _ => { 1 }, -} ⇒ i64 +} ⇒ t3 T-Var: {c: enum Color { Red, Green, Blue, }} ⊢ c ⇒ Color Unify-Enum: Color ~ Color => {} - T-Tail: 0 => i64 + T-Tail: 0 => t3 T-Int: {c: enum Color { Red, Green, Blue, -}} ⊢ 0 ⇒ i64 - T-Tail: 1 => i64 +}} ⊢ 0 ⇒ t3 + T-Tail: 1 => t4 T-Int: {c: enum Color { Red, Green, Blue, -}} ⊢ 1 ⇒ i64 - Unify-Const: i64 ~ i64 => i64 - Unify-Const: i64 ~ i64 => {} +}} ⊢ 1 ⇒ t4 + Unify-Var: t4 ~ t3 => t3 + Unify-Var: t3 ~ i64 => {i64/t3} diff --git a/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__if_branch_traces.snap b/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__if_branch_traces.snap index 3b93d639..902371a3 100644 --- a/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__if_branch_traces.snap +++ b/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__if_branch_traces.snap @@ -3,25 +3,25 @@ source: starstream-compiler/src/typecheck/tests.rs description: "Source:\n\nfn test() {\n let mut score = 10;\n if (score > 5) {\n score = score + 1;\n }\n}\n" --- T-Fn: test => () - T-Let: {} ⊢ let mut score = 10; ⇒ i64 - T-Int: {} ⊢ 10 ⇒ i64 + T-Let: {} ⊢ let mut score = 10; ⇒ t3 + T-Int: {} ⊢ 10 ⇒ t3 T-ReturnTail: if (score > 5) { score = score + 1; } => () - T-If: {score: i64} ⊢ if (score > 5) { + T-If: {score: t3} ⊢ if (score > 5) { score = score + 1; } ⇒ () - T-Bin-Gt: {score: i64} ⊢ score > 5 ⇒ bool - T-Var: {score: i64} ⊢ score ⇒ i64 - T-Int: {score: i64} ⊢ 5 ⇒ i64 + T-Bin-Gt: {score: t3} ⊢ score > 5 ⇒ bool + T-Var: {score: t3} ⊢ score ⇒ t3 + T-Int: {score: t3} ⊢ 5 ⇒ t4 Check-Compare: i64 vs i64 => ok (int) - Check-Bool: bool => ok - T-Assign: {score: i64} ⊢ score = score + 1; ⇒ i64 - T-Bin-Add: {score: i64} ⊢ score + 1 ⇒ i64 - T-Var: {score: i64} ⊢ score ⇒ i64 - T-Int: {score: i64} ⊢ 1 ⇒ i64 - Unify-Const: i64 ~ i64 => {} Unify-Const: i64 ~ i64 => {} - Unify-Const: i64 ~ i64 => {} + Check-Bool: bool => ok + T-Assign: {score: t3} ⊢ score = score + 1; ⇒ t3 + T-Bin-Add: {score: t3} ⊢ score + 1 ⇒ t5 + T-Var: {score: t3} ⊢ score ⇒ t3 + T-Int: {score: t3} ⊢ 1 ⇒ t5 + Unify-Var: t3 ~ t5 => {t5/t3} + Unify-Var: t5 ~ t5 => {} Unify-Const: () ~ () => {} Unify-Const: () ~ () => {} diff --git a/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__let_binding_traces.snap b/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__let_binding_traces.snap index a956c95d..e9006246 100644 --- a/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__let_binding_traces.snap +++ b/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__let_binding_traces.snap @@ -3,14 +3,14 @@ source: starstream-compiler/src/typecheck/tests.rs description: "Source:\n\nfn test() {\n let answer = 42;\n let foo: i64 = 10;\n let bar: bool = false;\n let baz: _ = 2;\n}\n" --- T-Fn: test => () - T-Let: {} ⊢ let answer = 42; ⇒ i64 - T-Int: {} ⊢ 42 ⇒ i64 - T-Let: {answer: i64} ⊢ let foo: i64 = 10; ⇒ i64 - T-Int: {answer: i64} ⊢ 10 ⇒ i64 - Unify-Const: i64 ~ i64 => {} - T-Let: {answer: i64, foo: i64} ⊢ let bar: bool = false; ⇒ bool - T-Bool: {answer: i64, foo: i64} ⊢ false ⇒ bool + T-Let: {} ⊢ let answer = 42; ⇒ t3 + T-Int: {} ⊢ 42 ⇒ t3 + T-Let: {answer: t3} ⊢ let foo: i64 = 10; ⇒ i64 + T-Int: {answer: t3} ⊢ 10 ⇒ t4 + Unify-Var: i64 ~ t4 => {i64/t4} + T-Let: {answer: t3, foo: i64} ⊢ let bar: bool = false; ⇒ bool + T-Bool: {answer: t3, foo: i64} ⊢ false ⇒ bool Unify-Const: bool ~ bool => {} - T-Let: {answer: i64, bar: bool, foo: i64} ⊢ let baz: _ = 2; ⇒ i64 - T-Int: {answer: i64, bar: bool, foo: i64} ⊢ 2 ⇒ i64 - Unify-Var: t3 ~ i64 => {i64/t3} + T-Let: {answer: t3, bar: bool, foo: i64} ⊢ let baz: _ = 2; ⇒ t5 + T-Int: {answer: t3, bar: bool, foo: i64} ⊢ 2 ⇒ t5 + Unify-Var: t6 ~ t5 => {t5/t6} diff --git a/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__match_bool_literal_exhaustive.snap b/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__match_bool_literal_exhaustive.snap index a3d2e255..8a948a5f 100644 --- a/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__match_bool_literal_exhaustive.snap +++ b/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__match_bool_literal_exhaustive.snap @@ -10,7 +10,7 @@ T-Fn: test => i64 false => { 0 }, -} => i64 +} => t3 T-Match: {b: bool} ⊢ match b { true => { 1 @@ -18,13 +18,13 @@ T-Fn: test => i64 false => { 0 }, -} ⇒ i64 +} ⇒ t3 T-Var: {b: bool} ⊢ b ⇒ bool Unify-Const: bool ~ bool => {} - T-Tail: 1 => i64 - T-Int: {b: bool} ⊢ 1 ⇒ i64 + T-Tail: 1 => t3 + T-Int: {b: bool} ⊢ 1 ⇒ t3 Unify-Const: bool ~ bool => {} - T-Tail: 0 => i64 - T-Int: {b: bool} ⊢ 0 ⇒ i64 - Unify-Const: i64 ~ i64 => i64 - Unify-Const: i64 ~ i64 => {} + T-Tail: 0 => t4 + T-Int: {b: bool} ⊢ 0 ⇒ t4 + Unify-Var: t4 ~ t3 => t3 + Unify-Var: t3 ~ i64 => {i64/t3} diff --git a/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__match_int_literal_with_wildcard.snap b/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__match_int_literal_with_wildcard.snap index 6fc4c213..1b3b5f3c 100644 --- a/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__match_int_literal_with_wildcard.snap +++ b/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__match_int_literal_with_wildcard.snap @@ -13,7 +13,7 @@ T-Fn: test => i64 _ => { 0 }, -} => i64 +} => t3 T-Match: {n: i64} ⊢ match n { 0 => { 100 @@ -24,16 +24,16 @@ T-Fn: test => i64 _ => { 0 }, -} ⇒ i64 +} ⇒ t3 T-Var: {n: i64} ⊢ n ⇒ i64 Unify-Const: i64 ~ i64 => {} - T-Tail: 100 => i64 - T-Int: {n: i64} ⊢ 100 ⇒ i64 + T-Tail: 100 => t3 + T-Int: {n: i64} ⊢ 100 ⇒ t3 Unify-Const: i64 ~ i64 => {} - T-Tail: 200 => i64 - T-Int: {n: i64} ⊢ 200 ⇒ i64 - Unify-Const: i64 ~ i64 => i64 - T-Tail: 0 => i64 - T-Int: {n: i64} ⊢ 0 ⇒ i64 - Unify-Const: i64 ~ i64 => i64 - Unify-Const: i64 ~ i64 => {} + T-Tail: 200 => t4 + T-Int: {n: i64} ⊢ 200 ⇒ t4 + Unify-Var: t4 ~ t3 => t3 + T-Tail: 0 => t5 + T-Int: {n: i64} ⊢ 0 ⇒ t5 + Unify-Var: t5 ~ t3 => t3 + Unify-Var: t3 ~ i64 => {i64/t3} diff --git a/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__option_explicit_annotation.snap b/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__option_explicit_annotation.snap index cdaa508a..6a5a7111 100644 --- a/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__option_explicit_annotation.snap +++ b/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__option_explicit_annotation.snap @@ -5,7 +5,7 @@ description: "Source:\n\nfn test() {\n let x: Option = Option::Some(42); T-Fn: test => () T-Let: {} ⊢ let x: Option = Option::Some(42); ⇒ Option T-EnumCtor: {} ⊢ Option::Some(42) ⇒ Option - T-Int: {} ⊢ 42 ⇒ i64 + T-Int: {} ⊢ 42 ⇒ t4 + Unify-Var: t4 ~ t3 => {t3/t4} + Unify-Enum: Option ~ Option => {i64/t3} Unify-Var: i64 ~ t3 => {i64/t3} - Unify-Enum: Option ~ Option => {} - Unify-Const: i64 ~ i64 => {} diff --git a/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__option_pattern_matching.snap b/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__option_pattern_matching.snap index 4042c11a..b169fb57 100644 --- a/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__option_pattern_matching.snap +++ b/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__option_pattern_matching.snap @@ -3,12 +3,12 @@ source: starstream-compiler/src/typecheck/tests.rs description: "Source:\n\nfn test() {\n let x = Option::Some(42);\n let y = match x {\n Option::Some(v) => {\n v\n },\n Option::None => {\n 0\n },\n };\n}\n" --- T-Fn: test => () - T-Let: {} ⊢ let x = Option::Some(42); ⇒ Option + T-Let: {} ⊢ let x = Option::Some(42); ⇒ Option T-EnumCtor: {} ⊢ Option::Some(42) ⇒ Option - T-Int: {} ⊢ 42 ⇒ i64 - Unify-Var: i64 ~ t3 => {i64/t3} - T-Let: {x: enum Option { - Some(i64), + T-Int: {} ⊢ 42 ⇒ t4 + Unify-Var: t4 ~ t3 => {t3/t4} + T-Let: {x: enum Option { + Some(t3), None, }} ⊢ let y = match x { Option::Some(v) => { @@ -17,9 +17,9 @@ T-Fn: test => () Option::None => { 0 }, -}; ⇒ i64 - T-Match: {x: enum Option { - Some(i64), +}; ⇒ t6 + T-Match: {x: enum Option { + Some(t3), None, }} ⊢ match x { Option::Some(v) => { @@ -28,23 +28,23 @@ T-Fn: test => () Option::None => { 0 }, -} ⇒ i64 - T-Var: {x: enum Option { - Some(i64), +} ⇒ t6 + T-Var: {x: enum Option { + Some(t3), None, -}} ⊢ x ⇒ Option - Unify-Enum: Option ~ Option => {i64/t4} - Unify-Var: i64 ~ t4 => {i64/t4} - T-Tail: v => t4 - T-Var: {v: t4, x: enum Option { - Some(i64), +}} ⊢ x ⇒ Option + Unify-Enum: Option ~ Option => {t5/t3} + Unify-Var: t3 ~ t5 => {t5/t3} + T-Tail: v => t5 + T-Var: {v: t5, x: enum Option { + Some(t3), None, -}} ⊢ v ⇒ t4 - Unify-Enum: Option ~ Option => {i64/t5} - Unify-Var: i64 ~ t5 => {i64/t5} - T-Tail: 0 => i64 - T-Int: {x: enum Option { - Some(i64), +}} ⊢ v ⇒ t5 + Unify-Enum: Option ~ Option => {t6/t5} + Unify-Var: t5 ~ t6 => {t6/t5} + T-Tail: 0 => t7 + T-Int: {x: enum Option { + Some(t3), None, -}} ⊢ 0 ⇒ i64 - Unify-Const: i64 ~ i64 => i64 +}} ⊢ 0 ⇒ t7 + Unify-Var: t7 ~ t6 => t6 diff --git a/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__option_some_constructor.snap b/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__option_some_constructor.snap index cd42b08c..07bdd937 100644 --- a/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__option_some_constructor.snap +++ b/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__option_some_constructor.snap @@ -3,7 +3,7 @@ source: starstream-compiler/src/typecheck/tests.rs description: "Source:\n\nfn test() {\n let x = Option::Some(42);\n}\n" --- T-Fn: test => () - T-Let: {} ⊢ let x = Option::Some(42); ⇒ Option + T-Let: {} ⊢ let x = Option::Some(42); ⇒ Option T-EnumCtor: {} ⊢ Option::Some(42) ⇒ Option - T-Int: {} ⊢ 42 ⇒ i64 - Unify-Var: i64 ~ t3 => {i64/t3} + T-Int: {} ⊢ 42 ⇒ t4 + Unify-Var: t4 ~ t3 => {t3/t4} diff --git a/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__reports_let_annotation_error.snap b/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__reports_let_annotation_error.snap index d94daade..1e944fb8 100644 --- a/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__reports_let_annotation_error.snap +++ b/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__reports_let_annotation_error.snap @@ -5,11 +5,10 @@ description: "Source:\n\nfn test() {\n let wrong: bool = 1;\n}\n" E0003 (https://starstream.nightstream.dev/errors/E0003) x cannot assign value of type `i64` to variable `wrong` of type `bool` - ,-[test.star:2:9] + ,-[test.star:2:23] 1 | fn test() { 2 | let wrong: bool = 1; - : ^^|^^ | - : | `-- has type `i64` - : `-- has type `bool` + : ^^|^^ ^ + : `-- expected an integer type 3 | } `---- diff --git a/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__reports_type_error.snap b/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__reports_type_error.snap index 80638ec5..a9ebd739 100644 --- a/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__reports_type_error.snap +++ b/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__reports_type_error.snap @@ -4,12 +4,12 @@ description: "Source:\n\nfn test() {\n let flag = true;\n let x = flag + 1 --- E0006 (https://starstream.nightstream.dev/errors/E0006) - x binary `+` operands must match; found `bool` and `i64` + x binary `+` operands must match; found `bool` and `t3` ,-[test.star:3:13] 2 | let flag = true; 3 | let x = flag + 1; : ^^|^ | - : | `-- has type `i64` + : | `-- has type `t3` : `-- has type `bool` 4 | } `---- diff --git a/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__result_explicit_annotation.snap b/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__result_explicit_annotation.snap index d117037e..abb275e7 100644 --- a/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__result_explicit_annotation.snap +++ b/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__result_explicit_annotation.snap @@ -5,8 +5,8 @@ description: "Source:\n\nfn test() {\n let x: Result = Result::Ok( T-Fn: test => () T-Let: {} ⊢ let x: Result = Result::Ok(42); ⇒ Result T-EnumCtor: {} ⊢ Result::Ok(42) ⇒ Result - T-Int: {} ⊢ 42 ⇒ i64 - Unify-Var: i64 ~ t3 => {i64/t3} - Unify-Enum: Result ~ Result => {bool/t4} + T-Int: {} ⊢ 42 ⇒ t5 + Unify-Var: t5 ~ t3 => {t3/t5} + Unify-Enum: Result ~ Result => {bool/t4, i64/t3} Unify-Var: bool ~ t4 => {bool/t4} - Unify-Const: i64 ~ i64 => {} + Unify-Var: i64 ~ t3 => {i64/t3} diff --git a/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__result_ok_constructor.snap b/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__result_ok_constructor.snap index b11d8378..5794d587 100644 --- a/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__result_ok_constructor.snap +++ b/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__result_ok_constructor.snap @@ -3,7 +3,7 @@ source: starstream-compiler/src/typecheck/tests.rs description: "Source:\n\nfn test() {\n let x = Result::Ok(42);\n}\n" --- T-Fn: test => () - T-Let: {} ⊢ let x = Result::Ok(42); ⇒ Result + T-Let: {} ⊢ let x = Result::Ok(42); ⇒ Result T-EnumCtor: {} ⊢ Result::Ok(42) ⇒ Result - T-Int: {} ⊢ 42 ⇒ i64 - Unify-Var: i64 ~ t3 => {i64/t3} + T-Int: {} ⊢ 42 ⇒ t5 + Unify-Var: t5 ~ t3 => {t3/t5} diff --git a/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__result_pattern_matching.snap b/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__result_pattern_matching.snap index ca07a184..871466be 100644 --- a/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__result_pattern_matching.snap +++ b/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__result_pattern_matching.snap @@ -5,11 +5,11 @@ description: "Source:\n\nfn test() {\n let x: Result = Result::Ok( T-Fn: test => () T-Let: {} ⊢ let x: Result = Result::Ok(42); ⇒ Result T-EnumCtor: {} ⊢ Result::Ok(42) ⇒ Result - T-Int: {} ⊢ 42 ⇒ i64 - Unify-Var: i64 ~ t3 => {i64/t3} - Unify-Enum: Result ~ Result => {bool/t4} + T-Int: {} ⊢ 42 ⇒ t5 + Unify-Var: t5 ~ t3 => {t3/t5} + Unify-Enum: Result ~ Result => {bool/t4, i64/t3} Unify-Var: bool ~ t4 => {bool/t4} - Unify-Const: i64 ~ i64 => {} + Unify-Var: i64 ~ t3 => {i64/t3} T-Let: {x: enum Result { Err(bool), Ok(i64), @@ -36,20 +36,20 @@ T-Fn: test => () Err(bool), Ok(i64), }} ⊢ x ⇒ Result - Unify-Enum: Result ~ Result => {bool/t6, i64/t5} - Unify-Var: bool ~ t6 => {bool/t6} - Unify-Var: i64 ~ t5 => {i64/t5} - T-Tail: v => t5 - T-Var: {v: t5, x: enum Result { + Unify-Enum: Result ~ Result => {bool/t7, i64/t6} + Unify-Var: bool ~ t7 => {bool/t7} + Unify-Var: i64 ~ t6 => {i64/t6} + T-Tail: v => t6 + T-Var: {v: t6, x: enum Result { Err(bool), Ok(i64), -}} ⊢ v ⇒ t5 - Unify-Enum: Result ~ Result => {bool/t8, i64/t7} - Unify-Var: bool ~ t8 => {bool/t8} - Unify-Var: i64 ~ t7 => {i64/t7} - T-Tail: 0 => i64 - T-Int: {e: t8, x: enum Result { +}} ⊢ v ⇒ t6 + Unify-Enum: Result ~ Result => {bool/t9, i64/t8} + Unify-Var: bool ~ t9 => {bool/t9} + Unify-Var: i64 ~ t8 => {i64/t8} + T-Tail: 0 => t10 + T-Int: {e: t9, x: enum Result { Err(bool), Ok(i64), -}} ⊢ 0 ⇒ i64 - Unify-Const: i64 ~ i64 => i64 +}} ⊢ 0 ⇒ t10 + Unify-Var: t10 ~ i64 => i64 diff --git a/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__struct_field_order_preserved.snap b/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__struct_field_order_preserved.snap index 72eb5cd1..8da42e4c 100644 --- a/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__struct_field_order_preserved.snap +++ b/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__struct_field_order_preserved.snap @@ -13,12 +13,12 @@ T-Fn: test => () age: 2, email: 3, } ⇒ User - T-Int: {} ⊢ 1 ⇒ i64 - Unify-Const: i64 ~ i64 => {} - T-Int: {} ⊢ 2 ⇒ i64 - Unify-Const: i64 ~ i64 => {} - T-Int: {} ⊢ 3 ⇒ i64 - Unify-Const: i64 ~ i64 => {} + T-Int: {} ⊢ 1 ⇒ t3 + Unify-Var: t3 ~ i64 => {i64/t3} + T-Int: {} ⊢ 2 ⇒ t4 + Unify-Var: t4 ~ i64 => {i64/t4} + T-Int: {} ⊢ 3 ⇒ t5 + Unify-Var: t5 ~ i64 => {i64/t5} T-Let: {u: struct User { name: i64, age: i64, @@ -39,11 +39,11 @@ T-Fn: test => () name: i64, age: i64, email: i64, -}} ⊢ 1 ⇒ i64 - Unify-Const: i64 ~ i64 => {} +}} ⊢ 1 ⇒ t6 + Unify-Var: t6 ~ i64 => {i64/t6} T-Int: {u: struct User { name: i64, age: i64, email: i64, -}} ⊢ 2 ⇒ i64 - Unify-Const: i64 ~ i64 => {} +}} ⊢ 2 ⇒ t7 + Unify-Var: t7 ~ i64 => {i64/t7} diff --git a/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__struct_literals_and_field_access.snap b/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__struct_literals_and_field_access.snap index f837bcb7..48eeedc9 100644 --- a/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__struct_literals_and_field_access.snap +++ b/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__struct_literals_and_field_access.snap @@ -32,9 +32,8 @@ T-Fn: total => i64 T-Int: {point: struct Point { x: i64, y: i64, -}} ⊢ 1 ⇒ i64 - Unify-Const: i64 ~ i64 => {} - Unify-Const: i64 ~ i64 => {} +}} ⊢ 1 ⇒ t3 + Unify-Var: i64 ~ t3 => {i64/t3} Unify-Const: i64 ~ i64 => {} T-Bin-Add: {point: struct Point { x: i64, @@ -51,9 +50,8 @@ T-Fn: total => i64 T-Int: {point: struct Point { x: i64, y: i64, -}} ⊢ 1 ⇒ i64 - Unify-Const: i64 ~ i64 => {} - Unify-Const: i64 ~ i64 => {} +}} ⊢ 1 ⇒ t4 + Unify-Var: i64 ~ t4 => {i64/t4} Unify-Const: i64 ~ i64 => {} T-ReturnTail: translated.x + translated.y => i64 T-Bin-Add: {point: struct Point { @@ -92,5 +90,4 @@ T-Fn: total => i64 y: i64, }} ⊢ translated ⇒ Point Unify-Const: i64 ~ i64 => {} - Unify-Const: i64 ~ i64 => {} Unify-Const: i64 ~ i64 => {} diff --git a/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__while_loop_traces.snap b/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__while_loop_traces.snap index 79d8cc60..332f63e3 100644 --- a/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__while_loop_traces.snap +++ b/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__while_loop_traces.snap @@ -3,20 +3,20 @@ source: starstream-compiler/src/typecheck/tests.rs description: "Source:\n\nfn test() {\n let mut counter = 0;\n while (counter < 3) {\n counter = counter + 1;\n }\n}\n" --- T-Fn: test => () - T-Let: {} ⊢ let mut counter = 0; ⇒ i64 - T-Int: {} ⊢ 0 ⇒ i64 - T-While: {counter: i64} ⊢ while (counter < 3) { + T-Let: {} ⊢ let mut counter = 0; ⇒ t3 + T-Int: {} ⊢ 0 ⇒ t3 + T-While: {counter: t3} ⊢ while (counter < 3) { counter = counter + 1; } ⇒ () - T-Bin-Lt: {counter: i64} ⊢ counter < 3 ⇒ bool - T-Var: {counter: i64} ⊢ counter ⇒ i64 - T-Int: {counter: i64} ⊢ 3 ⇒ i64 + T-Bin-Lt: {counter: t3} ⊢ counter < 3 ⇒ bool + T-Var: {counter: t3} ⊢ counter ⇒ t3 + T-Int: {counter: t3} ⊢ 3 ⇒ t4 Check-Compare: i64 vs i64 => ok (int) - Check-Bool: bool => ok - T-Assign: {counter: i64} ⊢ counter = counter + 1; ⇒ i64 - T-Bin-Add: {counter: i64} ⊢ counter + 1 ⇒ i64 - T-Var: {counter: i64} ⊢ counter ⇒ i64 - T-Int: {counter: i64} ⊢ 1 ⇒ i64 - Unify-Const: i64 ~ i64 => {} Unify-Const: i64 ~ i64 => {} - Unify-Const: i64 ~ i64 => {} + Check-Bool: bool => ok + T-Assign: {counter: t3} ⊢ counter = counter + 1; ⇒ t3 + T-Bin-Add: {counter: t3} ⊢ counter + 1 ⇒ t5 + T-Var: {counter: t3} ⊢ counter ⇒ t3 + T-Int: {counter: t3} ⊢ 1 ⇒ t5 + Unify-Var: t3 ~ t5 => {t5/t3} + Unify-Var: t5 ~ t5 => {} diff --git a/starstream-interpreter/src/lib.rs b/starstream-interpreter/src/lib.rs index 1ddc66a0..df028f52 100644 --- a/starstream-interpreter/src/lib.rs +++ b/starstream-interpreter/src/lib.rs @@ -80,7 +80,7 @@ pub fn eval(expr: &TypedExpr, locals: &Locals) -> ControlFlow { // Identifiers TypedExprKind::Identifier(Identifier { name, .. }) => locals.get(name), // Literals - TypedExprKind::Literal(Literal::Integer(i)) => Value::Number(*i), + TypedExprKind::Literal(Literal::Integer(i)) => Value::Number(*i as i64), TypedExprKind::Literal(Literal::Boolean(b)) => Value::Boolean(*b), TypedExprKind::Literal(Literal::Unit) => Value::Unit, // Arithmetic operators @@ -215,15 +215,15 @@ fn eval_math() { assert_eq!( eval( &TypedExpr::new( - Type::Int, + Type::int(), TypedExprKind::Binary { op: BinaryOp::Add, left: Box::new(Spanned::none(TypedExpr::new( - Type::Int, + Type::int(), TypedExprKind::Literal(Literal::Integer(17)) ))), right: Box::new(Spanned::none(TypedExpr::new( - Type::Int, + Type::int(), TypedExprKind::Literal(Literal::Integer(33)) ))), }, @@ -244,22 +244,22 @@ fn eval_locals() { mutable: true, name: foo.clone(), value: Spanned::none(TypedExpr::new( - Type::Int, + Type::int(), TypedExprKind::Literal(Literal::Integer(6)), )), }, TypedStatement::Assignment { target: foo.clone(), value: Spanned::none(TypedExpr::new( - Type::Int, + Type::int(), TypedExprKind::Binary { op: BinaryOp::Multiply, left: Box::new(Spanned::none(TypedExpr::new( - Type::Int, + Type::int(), TypedExprKind::Identifier(foo.clone()), ))), right: Box::new(Spanned::none(TypedExpr::new( - Type::Int, + Type::int(), TypedExprKind::Literal(Literal::Integer(3)), ))), }, @@ -268,7 +268,7 @@ fn eval_locals() { TypedStatement::Assignment { target: bar.clone(), value: Spanned::none(TypedExpr::new( - Type::Int, + Type::int(), TypedExprKind::Identifier(foo.clone()), )), }, diff --git a/starstream-language-server/src/document.rs b/starstream-language-server/src/document.rs index a12be501..73753043 100644 --- a/starstream-language-server/src/document.rs +++ b/starstream-language-server/src/document.rs @@ -1579,7 +1579,7 @@ impl DocumentState { let label = def.ty.display_with_params(&def.param_name_map()); self.add_hover_label_with_doc(span, label, doc); } else { - let ty = self.enum_types.get(name).or_else(|| fallback_ty).cloned(); + let ty = self.enum_types.get(name).or(fallback_ty).cloned(); if let Some(ty) = ty { self.add_hover_span_with_doc(span, &ty, doc); } diff --git a/starstream-to-wasm/src/component_abi.rs b/starstream-to-wasm/src/component_abi.rs index f60310d2..fe072a8b 100644 --- a/starstream-to-wasm/src/component_abi.rs +++ b/starstream-to-wasm/src/component_abi.rs @@ -211,18 +211,31 @@ impl ComponentAbiType { i.i32_store8(mem_arg); })); } - ComponentAbiType::S8 => todo!(), - ComponentAbiType::U8 => todo!(), - ComponentAbiType::S16 => todo!(), - ComponentAbiType::U16 => todo!(), - ComponentAbiType::S32 => todo!(), - ComponentAbiType::U32 => todo!(), + ComponentAbiType::S8 | ComponentAbiType::U8 => { + out.push(Box::new(move |mut i| { + i.i32_store8(mem_arg); + })); + } + ComponentAbiType::S16 | ComponentAbiType::U16 => { + out.push(Box::new(move |mut i| { + i.i32_store16(mem_arg); + })); + } + ComponentAbiType::S32 | ComponentAbiType::U32 => { + out.push(Box::new(move |mut i| { + i.i32_store(mem_arg); + })); + } ComponentAbiType::S64 => { out.push(Box::new(move |mut i: InstructionSink<'_>| { i.i64_store(mem_arg); })); } - ComponentAbiType::U64 => todo!(), + ComponentAbiType::U64 => { + out.push(Box::new(move |mut i: InstructionSink<'_>| { + i.i64_store(mem_arg); + })); + } ComponentAbiType::F32 => todo!(), ComponentAbiType::F64 => todo!(), ComponentAbiType::Char => todo!(), diff --git a/starstream-to-wasm/src/decision_tree.rs b/starstream-to-wasm/src/decision_tree.rs index 4bad5d2c..068ee0c4 100644 --- a/starstream-to-wasm/src/decision_tree.rs +++ b/starstream-to-wasm/src/decision_tree.rs @@ -28,7 +28,7 @@ pub enum Ctor { EnumVariant { variant_index: usize }, BoolTrue, BoolFalse, - IntLiteral(i64), + IntLiteral(i128), Struct, Unit, } @@ -331,7 +331,7 @@ fn is_complete_signature(ctors: &[Ctor], ty: &Type) -> bool { } Type::Record(_) => true, Type::Unit => true, - Type::Int => false, + Type::Int(_) => false, _ => false, } } diff --git a/starstream-to-wasm/src/lib.rs b/starstream-to-wasm/src/lib.rs index 512da240..3b367eb7 100644 --- a/starstream-to-wasm/src/lib.rs +++ b/starstream-to-wasm/src/lib.rs @@ -572,6 +572,33 @@ impl Compiler { idx } + /// Emit truncation/masking after an i32 operation for sub-32-bit types. + fn emit_truncate(&self, func: &mut Function, w: IntWidth) { + match w { + IntWidth::I8 => { + func.instructions().i32_const(24); + func.instructions().i32_shl(); + func.instructions().i32_const(24); + func.instructions().i32_shr_s(); + } + IntWidth::I16 => { + func.instructions().i32_const(16); + func.instructions().i32_shl(); + func.instructions().i32_const(16); + func.instructions().i32_shr_s(); + } + IntWidth::U8 => { + func.instructions().i32_const(0xFF); + func.instructions().i32_and(); + } + IntWidth::U16 => { + func.instructions().i32_const(0xFFFF); + func.instructions().i32_and(); + } + _ => {} // i32, u32, i64, u64 don't need truncation + } + } + // ------------------------------------------------------------------------ // Component table management @@ -725,7 +752,16 @@ impl Compiler { let cat = match ty { Type::Var(_) => todo!(), - Type::Int => ComponentAbiType::S64, + Type::Int(w) => match w { + IntWidth::I8 => ComponentAbiType::S8, + IntWidth::I16 => ComponentAbiType::S16, + IntWidth::I32 => ComponentAbiType::S32, + IntWidth::I64 => ComponentAbiType::S64, + IntWidth::U8 => ComponentAbiType::U8, + IntWidth::U16 => ComponentAbiType::U16, + IntWidth::U32 => ComponentAbiType::U32, + IntWidth::U64 => ComponentAbiType::U64, + }, Type::Bool => ComponentAbiType::Bool, Type::Unit => { return None; @@ -835,7 +871,13 @@ impl Compiler { match ty { Type::Unit => {} Type::Bool => dest.push(ValType::I32), - Type::Int => dest.push(ValType::I64), + Type::Int(w) => { + if w.is_64bit() { + dest.push(ValType::I64); + } else { + dest.push(ValType::I32); + } + } Type::Tuple(items) => { // flatten_record for each in items { @@ -968,7 +1010,7 @@ impl Compiler { match ty { Type::Unit => 0, Type::Bool => 1, - Type::Int => 1, + Type::Int(_) => 1, Type::Tuple(items) => items.iter().map(|t| self.star_count_core_types(t)).sum(), Type::Record(record) => record .fields @@ -1498,8 +1540,15 @@ impl Compiler { } // Literals TypedExprKind::Literal(Literal::Integer(i)) => { - func.instructions().i64_const(*i); - assert_eq!(expr.ty, Type::Int); + match &expr.ty { + Type::Int(w) if w.is_64bit() => { + func.instructions().i64_const(*i as i64); + } + Type::Int(_) => { + func.instructions().i32_const(*i as i32); + } + _ => {} + } Ok(()) } TypedExprKind::Literal(Literal::Boolean(b)) => { @@ -1522,14 +1571,18 @@ impl Compiler { lhs?; rhs?; match (&left.node.ty, &right.node.ty) { - (Type::Int, Type::Int) => { - if self.options.check_overflows { - let checked_sum = self.get_or_create_i64_add_checked(); - func.instructions().call(checked_sum); + (Type::Int(w), Type::Int(_)) => { + if w.is_64bit() { + if self.options.check_overflows { + let checked_sum = self.get_or_create_i64_add_checked(); + func.instructions().call(checked_sum); + } else { + func.instructions().i64_add(); + } } else { - func.instructions().i64_add(); + func.instructions().i32_add(); + self.emit_truncate(func, *w); } - Ok(()) } (lhs, rhs) => Err(self.todo(format!("Add({lhs:?}, {rhs:?})"))), @@ -1545,14 +1598,18 @@ impl Compiler { lhs?; rhs?; match (&left.node.ty, &right.node.ty) { - (Type::Int, Type::Int) => { - if self.options.check_overflows { - let checked_sub = self.get_or_create_i64_sub_checked(); - func.instructions().call(checked_sub); + (Type::Int(w), Type::Int(_)) => { + if w.is_64bit() { + if self.options.check_overflows { + let checked_sub = self.get_or_create_i64_sub_checked(); + func.instructions().call(checked_sub); + } else { + func.instructions().i64_sub(); + } } else { - func.instructions().i64_sub(); + func.instructions().i32_sub(); + self.emit_truncate(func, *w); } - Ok(()) } (lhs, rhs) => Err(self.todo(format!("Subtract({lhs:?}, {rhs:?})"))), @@ -1568,14 +1625,18 @@ impl Compiler { lhs?; rhs?; match (&left.node.ty, &right.node.ty) { - (Type::Int, Type::Int) => { - if self.options.check_overflows { - let checked_mul = self.get_or_create_i64_mul_checked(); - func.instructions().call(checked_mul); + (Type::Int(w), Type::Int(_)) => { + if w.is_64bit() { + if self.options.check_overflows { + let checked_mul = self.get_or_create_i64_mul_checked(); + func.instructions().call(checked_mul); + } else { + func.instructions().i64_mul(); + } } else { - func.instructions().i64_mul(); + func.instructions().i32_mul(); + self.emit_truncate(func, *w); } - Ok(()) } (lhs, rhs) => Err(self.todo(format!("Multiply({lhs:?}, {rhs:?})"))), @@ -1591,8 +1652,18 @@ impl Compiler { lhs?; rhs?; match (&left.node.ty, &right.node.ty) { - (Type::Int, Type::Int) => { - func.instructions().i64_div_s(); + (Type::Int(w), Type::Int(_)) => { + if w.is_64bit() { + if w.is_signed() { + func.instructions().i64_div_s(); + } else { + func.instructions().i64_div_u(); + } + } else if w.is_signed() { + func.instructions().i32_div_s(); + } else { + func.instructions().i32_div_u(); + } Ok(()) } (lhs, rhs) => Err(self.todo(format!("Divide({lhs:?}, {rhs:?})"))), @@ -1608,8 +1679,18 @@ impl Compiler { lhs?; rhs?; match (&left.node.ty, &right.node.ty) { - (Type::Int, Type::Int) => { - func.instructions().i64_rem_s(); + (Type::Int(w), Type::Int(_)) => { + if w.is_64bit() { + if w.is_signed() { + func.instructions().i64_rem_s(); + } else { + func.instructions().i64_rem_u(); + } + } else if w.is_signed() { + func.instructions().i32_rem_s(); + } else { + func.instructions().i32_rem_u(); + } Ok(()) } (lhs, rhs) => Err(self.todo(format!("Remainder({lhs:?}, {rhs:?})"))), @@ -1620,12 +1701,18 @@ impl Compiler { expr: inner, } => { // `-x` compiles to `0 - x`. - func.instructions().i64_const(0); - self.visit_expr_stack(func, locals, inner.span, &inner.node)?; match &inner.node.ty { - Type::Int => { + Type::Int(w) if w.is_64bit() => { + func.instructions().i64_const(0); + self.visit_expr_stack(func, locals, inner.span, &inner.node)?; func.instructions().i64_sub(); - assert_eq!(expr.ty, Type::Int); + Ok(()) + } + Type::Int(w) => { + func.instructions().i32_const(0); + self.visit_expr_stack(func, locals, inner.span, &inner.node)?; + func.instructions().i32_sub(); + self.emit_truncate(func, *w); Ok(()) } lhs => Err(self.todo(format!("Negate({lhs:?})"))), @@ -1656,8 +1743,12 @@ impl Compiler { lhs?; rhs?; match (&left.node.ty, &right.node.ty) { - (Type::Int, Type::Int) => { - func.instructions().i64_eq(); + (Type::Int(w), Type::Int(_)) => { + if w.is_64bit() { + func.instructions().i64_eq(); + } else { + func.instructions().i32_eq(); + } assert_eq!(expr.ty, Type::Bool); Ok(()) } @@ -1679,8 +1770,12 @@ impl Compiler { lhs?; rhs?; match (&left.node.ty, &right.node.ty) { - (Type::Int, Type::Int) => { - func.instructions().i64_ne(); + (Type::Int(w), Type::Int(_)) => { + if w.is_64bit() { + func.instructions().i64_ne(); + } else { + func.instructions().i32_ne(); + } assert_eq!(expr.ty, Type::Bool); Ok(()) } @@ -1702,8 +1797,18 @@ impl Compiler { lhs?; rhs?; match (&left.node.ty, &right.node.ty) { - (Type::Int, Type::Int) => { - func.instructions().i64_lt_s(); + (Type::Int(w), Type::Int(_)) => { + if w.is_64bit() { + if w.is_signed() { + func.instructions().i64_lt_s(); + } else { + func.instructions().i64_lt_u(); + } + } else if w.is_signed() { + func.instructions().i32_lt_s(); + } else { + func.instructions().i32_lt_u(); + } assert_eq!(expr.ty, Type::Bool); Ok(()) } @@ -1725,8 +1830,18 @@ impl Compiler { lhs?; rhs?; match (&left.node.ty, &right.node.ty) { - (Type::Int, Type::Int) => { - func.instructions().i64_gt_s(); + (Type::Int(w), Type::Int(_)) => { + if w.is_64bit() { + if w.is_signed() { + func.instructions().i64_gt_s(); + } else { + func.instructions().i64_gt_u(); + } + } else if w.is_signed() { + func.instructions().i32_gt_s(); + } else { + func.instructions().i32_gt_u(); + } assert_eq!(expr.ty, Type::Bool); Ok(()) } @@ -1748,8 +1863,18 @@ impl Compiler { lhs?; rhs?; match (&left.node.ty, &right.node.ty) { - (Type::Int, Type::Int) => { - func.instructions().i64_le_s(); + (Type::Int(w), Type::Int(_)) => { + if w.is_64bit() { + if w.is_signed() { + func.instructions().i64_le_s(); + } else { + func.instructions().i64_le_u(); + } + } else if w.is_signed() { + func.instructions().i32_le_s(); + } else { + func.instructions().i32_le_u(); + } assert_eq!(expr.ty, Type::Bool); Ok(()) } @@ -1771,8 +1896,18 @@ impl Compiler { lhs?; rhs?; match (&left.node.ty, &right.node.ty) { - (Type::Int, Type::Int) => { - func.instructions().i64_ge_s(); + (Type::Int(w), Type::Int(_)) => { + if w.is_64bit() { + if w.is_signed() { + func.instructions().i64_ge_s(); + } else { + func.instructions().i64_ge_u(); + } + } else if w.is_signed() { + func.instructions().i32_ge_s(); + } else { + func.instructions().i32_ge_u(); + } assert_eq!(expr.ty, Type::Bool); Ok(()) } @@ -1781,7 +1916,7 @@ impl Compiler { assert_eq!(expr.ty, Type::Bool); Ok(()) } - (lhs, rhs) => Err(self.todo(format!("Greater({lhs:?}, {rhs:?})"))), + (lhs, rhs) => Err(self.todo(format!("GreaterEqual({lhs:?}, {rhs:?})"))), } } // Short-circuiting operators @@ -2414,7 +2549,8 @@ impl Compiler { }, ); } - Type::Int => { + Type::Int(w) => { + let is_64bit = w.is_64bit(); self.emit_simple_switch( func, locals, @@ -2428,11 +2564,16 @@ impl Compiler { result_base, result_count, outer_depth, - |func_inst, ctor, base| { + move |func_inst, ctor, base| { if let Ctor::IntLiteral(n) = ctor { func_inst.local_get(base); - func_inst.i64_const(*n); - func_inst.i64_ne(); + if is_64bit { + func_inst.i64_const(*n as i64); + func_inst.i64_ne(); + } else { + func_inst.i32_const(*n as i32); + func_inst.i32_ne(); + } func_inst.br_if(0); } }, diff --git a/starstream-to-wasm/tests/snapshots/inputs@add.star.snap b/starstream-to-wasm/tests/snapshots/inputs@add.star.snap index 276a7476..1b132458 100644 --- a/starstream-to-wasm/tests/snapshots/inputs@add.star.snap +++ b/starstream-to-wasm/tests/snapshots/inputs@add.star.snap @@ -113,7 +113,6 @@ T-Fn: add => i64 T-Var: {x: i64, y: i64} ⊢ x ⇒ i64 T-Var: {x: i64, y: i64} ⊢ y ⇒ i64 Unify-Const: i64 ~ i64 => {} - Unify-Const: i64 ~ i64 => {} Unify-Const: i64 ~ i64 => {} ==== Typed AST ==== @@ -138,7 +137,9 @@ TypedProgram { 14..15, ), }, - ty: Int, + ty: Int( + I64, + ), }, TypedFunctionParam { name: Identifier { @@ -147,22 +148,30 @@ TypedProgram { 22..23, ), }, - ty: Int, + ty: Int( + I64, + ), }, ], - return_type: Int, + return_type: Int( + I64, + ), effect: Pure, body: TypedBlock { statements: [], tail_expression: Some( Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: Binary { op: Add, left: Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: Identifier( Identifier { name: "x", @@ -176,7 +185,9 @@ TypedProgram { }, right: Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: Identifier( Identifier { name: "y", diff --git a/starstream-to-wasm/tests/snapshots/inputs@blockheight.star.snap b/starstream-to-wasm/tests/snapshots/inputs@blockheight.star.snap index 184da8f4..c6b767ab 100644 --- a/starstream-to-wasm/tests/snapshots/inputs@blockheight.star.snap +++ b/starstream-to-wasm/tests/snapshots/inputs@blockheight.star.snap @@ -232,7 +232,9 @@ TypedProgram { }, ty: Function { params: [], - result: Int, + result: Int( + I64, + ), effect: Runtime, }, }, @@ -274,24 +276,32 @@ TypedProgram { ), }, params: [], - return_type: Int, + return_type: Int( + I64, + ), effect: Pure, body: TypedBlock { statements: [ Expression( Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: Runtime { expr: Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: Call { callee: Spanned { node: TypedExpr { ty: Function { params: [], - result: Int, + result: Int( + I64, + ), effect: Runtime, }, kind: Identifier( @@ -319,17 +329,23 @@ TypedProgram { tail_expression: Some( Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: Runtime { expr: Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: Call { callee: Spanned { node: TypedExpr { ty: Function { params: [], - result: Int, + result: Int( + I64, + ), effect: Runtime, }, kind: Identifier( @@ -368,20 +384,26 @@ TypedProgram { ), }, params: [], - return_type: Int, + return_type: Int( + I64, + ), effect: Pure, body: TypedBlock { statements: [], tail_expression: Some( Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: Call { callee: Spanned { node: TypedExpr { ty: Function { params: [], - result: Int, + result: Int( + I64, + ), effect: Pure, }, kind: Identifier( diff --git a/starstream-to-wasm/tests/snapshots/inputs@enum.star.snap b/starstream-to-wasm/tests/snapshots/inputs@enum.star.snap index 69294a5a..be68dc66 100644 --- a/starstream-to-wasm/tests/snapshots/inputs@enum.star.snap +++ b/starstream-to-wasm/tests/snapshots/inputs@enum.star.snap @@ -279,8 +279,8 @@ T-Fn: get_baz => () T-Call: {} ⊢ accepts_foo(Foo::Baz(2)) ⇒ () T-Var: {} ⊢ accepts_foo ⇒ fn(Foo) -> () T-EnumCtor: {} ⊢ Foo::Baz(2) ⇒ Foo - T-Int: {} ⊢ 2 ⇒ i64 - Unify-Const: i64 ~ i64 => {} + T-Int: {} ⊢ 2 ⇒ t3 + Unify-Var: t3 ~ i64 => {i64/t3} Unify-Enum: Foo ~ Foo => {} Unify-Const: bool ~ bool => {} Unify-Const: i64 ~ i64 => {} @@ -319,7 +319,9 @@ TypedProgram { }, payload: Tuple( [ - Int, + Int( + I64, + ), ], ), }, @@ -340,7 +342,9 @@ TypedProgram { name: "Baz", kind: Tuple( [ - Int, + Int( + I64, + ), ], ), }, @@ -383,7 +387,9 @@ TypedProgram { name: "Baz", kind: Tuple( [ - Int, + Int( + I64, + ), ], ), }, @@ -442,7 +448,9 @@ TypedProgram { name: "Baz", kind: Tuple( [ - Int, + Int( + I64, + ), ], ), }, @@ -484,7 +492,9 @@ TypedProgram { name: "Baz", kind: Tuple( [ - Int, + Int( + I64, + ), ], ), }, @@ -576,7 +586,9 @@ TypedProgram { name: "Baz", kind: Tuple( [ - Int, + Int( + I64, + ), ], ), }, @@ -618,7 +630,9 @@ TypedProgram { name: "Baz", kind: Tuple( [ - Int, + Int( + I64, + ), ], ), }, @@ -643,7 +657,9 @@ TypedProgram { [ Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: Literal( Integer( 2, diff --git a/starstream-to-wasm/tests/snapshots/inputs@enum_simple.star.snap b/starstream-to-wasm/tests/snapshots/inputs@enum_simple.star.snap index 259d9e66..0c5b1295 100644 --- a/starstream-to-wasm/tests/snapshots/inputs@enum_simple.star.snap +++ b/starstream-to-wasm/tests/snapshots/inputs@enum_simple.star.snap @@ -1,6 +1,6 @@ --- source: starstream-to-wasm/tests/snapshots.rs -input_file: starstream-to-wasm/tests/inputs/utxo_definition.star +input_file: starstream-to-wasm/tests/inputs/enum_simple.star --- ==== AST ==== Program { @@ -88,7 +88,9 @@ TypedProgram { }, payload: Tuple( [ - Int, + Int( + I64, + ), ], ), }, @@ -105,7 +107,9 @@ TypedProgram { name: "Pong", kind: Tuple( [ - Int, + Int( + I64, + ), ], ), }, diff --git a/starstream-to-wasm/tests/snapshots/inputs@if_elseif_else.star.snap b/starstream-to-wasm/tests/snapshots/inputs@if_elseif_else.star.snap index 7abe574a..ffa38980 100644 --- a/starstream-to-wasm/tests/snapshots/inputs@if_elseif_else.star.snap +++ b/starstream-to-wasm/tests/snapshots/inputs@if_elseif_else.star.snap @@ -138,14 +138,14 @@ T-Fn: main => () T-Bool: {} ⊢ false ⇒ bool Check-Bool: bool => ok T-Expr: {} ⊢ 1; ⇒ () - T-Int: {} ⊢ 1 ⇒ i64 + T-Int: {} ⊢ 1 ⇒ t3 T-Bool: {} ⊢ true ⇒ bool Check-Bool: bool => ok T-Expr: {} ⊢ 2; ⇒ () - T-Int: {} ⊢ 2 ⇒ i64 + T-Int: {} ⊢ 2 ⇒ t4 Unify-Const: () ~ () => {} T-Expr: {} ⊢ 3; ⇒ () - T-Int: {} ⊢ 3 ⇒ i64 + T-Int: {} ⊢ 3 ⇒ t5 Unify-Const: () ~ () => {} Unify-Const: () ~ () => {} @@ -191,7 +191,9 @@ TypedProgram { Expression( Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: Literal( Integer( 1, @@ -222,7 +224,9 @@ TypedProgram { Expression( Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: Literal( Integer( 2, @@ -243,7 +247,9 @@ TypedProgram { Expression( Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: Literal( Integer( 3, diff --git a/starstream-to-wasm/tests/snapshots/inputs@if_expression.star.snap b/starstream-to-wasm/tests/snapshots/inputs@if_expression.star.snap index ee5fd621..aed0762a 100644 --- a/starstream-to-wasm/tests/snapshots/inputs@if_expression.star.snap +++ b/starstream-to-wasm/tests/snapshots/inputs@if_expression.star.snap @@ -122,7 +122,7 @@ T-Fn: main => i64 2 } else { 3 -} => i64 +} => t5 T-If: {} ⊢ if (false) { 1 } else if (true) { @@ -132,17 +132,17 @@ T-Fn: main => i64 } ⇒ () T-Bool: {} ⊢ false ⇒ bool Check-Bool: bool => ok - T-Tail: 1 => i64 - T-Int: {} ⊢ 1 ⇒ i64 + T-Tail: 1 => t3 + T-Int: {} ⊢ 1 ⇒ t3 T-Bool: {} ⊢ true ⇒ bool Check-Bool: bool => ok - T-Tail: 2 => i64 - T-Int: {} ⊢ 2 ⇒ i64 - Unify-Const: i64 ~ i64 => {} - T-Tail: 3 => i64 - T-Int: {} ⊢ 3 ⇒ i64 - Unify-Const: i64 ~ i64 => {} - Unify-Const: i64 ~ i64 => {} + T-Tail: 2 => t4 + T-Int: {} ⊢ 2 ⇒ t4 + Unify-Var: t3 ~ t4 => {t4/t3} + T-Tail: 3 => t5 + T-Int: {} ⊢ 3 ⇒ t5 + Unify-Var: t4 ~ t5 => {t5/t4} + Unify-Var: t5 ~ i64 => {i64/t5} ==== Typed AST ==== TypedProgram { @@ -159,14 +159,18 @@ TypedProgram { ), }, params: [], - return_type: Int, + return_type: Int( + I64, + ), effect: Pure, body: TypedBlock { statements: [], tail_expression: Some( Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: If { branches: [ ( @@ -186,7 +190,9 @@ TypedProgram { tail_expression: Some( Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: Literal( Integer( 1, @@ -215,7 +221,9 @@ TypedProgram { tail_expression: Some( Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: Literal( Integer( 2, @@ -234,7 +242,9 @@ TypedProgram { tail_expression: Some( Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: Literal( Integer( 3, diff --git a/starstream-to-wasm/tests/snapshots/inputs@match_expression.star.snap b/starstream-to-wasm/tests/snapshots/inputs@match_expression.star.snap index 80dbed68..c76e994d 100644 --- a/starstream-to-wasm/tests/snapshots/inputs@match_expression.star.snap +++ b/starstream-to-wasm/tests/snapshots/inputs@match_expression.star.snap @@ -1061,13 +1061,13 @@ T-Fn: match_foo => i64 Unify-Enum: Thing ~ Thing => {} Unify-Record: Ctx ~ Ctx => {} Unify-Const: i64 ~ i64 => {} - T-Tail: 0 => i64 + T-Tail: 0 => t3 T-Int: {b: bool, foo: enum Foo { Bar(bool), Baz(i64), Thing(Thing), -}} ⊢ 0 ⇒ i64 - Unify-Const: i64 ~ i64 => i64 +}} ⊢ 0 ⇒ t3 + Unify-Var: t3 ~ i64 => i64 Unify-Enum: Foo ~ Foo => {} Unify-Const: bool ~ bool => {} Unify-Const: i64 ~ i64 => {} @@ -1081,13 +1081,13 @@ T-Fn: match_foo => i64 Thing(Thing), }, n: i64} ⊢ n ⇒ i64 Unify-Const: i64 ~ i64 => i64 - T-Tail: 2 => i64 + T-Tail: 2 => t4 T-Int: {foo: enum Foo { Bar(bool), Baz(i64), Thing(Thing), -}} ⊢ 2 ⇒ i64 - Unify-Const: i64 ~ i64 => i64 +}} ⊢ 2 ⇒ t4 + Unify-Var: t4 ~ i64 => i64 Unify-Const: i64 ~ i64 => {} T-Fn: match_with_wildcard => i64 @@ -1098,7 +1098,7 @@ T-Fn: match_with_wildcard => i64 _ => { 0 }, -} => i64 +} => t5 T-Match: {foo: enum Foo { Bar(bool), Baz(i64), @@ -1110,7 +1110,7 @@ T-Fn: match_with_wildcard => i64 _ => { 0 }, -} ⇒ i64 +} ⇒ t5 T-Var: {foo: enum Foo { Bar(bool), Baz(i64), @@ -1122,20 +1122,20 @@ T-Fn: match_with_wildcard => i64 Unify-Enum: Thing ~ Thing => {} Unify-Record: Ctx ~ Ctx => {} Unify-Const: i64 ~ i64 => {} - T-Tail: 1 => i64 + T-Tail: 1 => t5 T-Int: {b: bool, foo: enum Foo { Bar(bool), Baz(i64), Thing(Thing), -}} ⊢ 1 ⇒ i64 - T-Tail: 0 => i64 +}} ⊢ 1 ⇒ t5 + T-Tail: 0 => t6 T-Int: {foo: enum Foo { Bar(bool), Baz(i64), Thing(Thing), -}} ⊢ 0 ⇒ i64 - Unify-Const: i64 ~ i64 => i64 - Unify-Const: i64 ~ i64 => {} +}} ⊢ 0 ⇒ t6 + Unify-Var: t6 ~ t5 => t5 + Unify-Var: t5 ~ i64 => {i64/t5} T-Fn: match_literal => i64 T-ReturnTail: match x { @@ -1145,7 +1145,7 @@ T-Fn: match_literal => i64 _ => { 0 }, -} => i64 +} => t7 T-Match: {x: i64} ⊢ match x { 42 => { 1 @@ -1153,15 +1153,15 @@ T-Fn: match_literal => i64 _ => { 0 }, -} ⇒ i64 +} ⇒ t7 T-Var: {x: i64} ⊢ x ⇒ i64 Unify-Const: i64 ~ i64 => {} - T-Tail: 1 => i64 - T-Int: {x: i64} ⊢ 1 ⇒ i64 - T-Tail: 0 => i64 - T-Int: {x: i64} ⊢ 0 ⇒ i64 - Unify-Const: i64 ~ i64 => i64 - Unify-Const: i64 ~ i64 => {} + T-Tail: 1 => t7 + T-Int: {x: i64} ⊢ 1 ⇒ t7 + T-Tail: 0 => t8 + T-Int: {x: i64} ⊢ 0 ⇒ t8 + Unify-Var: t8 ~ t7 => t7 + Unify-Var: t7 ~ i64 => {i64/t7} T-Fn: match_point => i64 @@ -1205,13 +1205,12 @@ T-Fn: match_point => i64 Point { x: i64, y: i64 }, }, x: i64, y: i64} ⊢ y ⇒ i64 Unify-Const: i64 ~ i64 => {} - Unify-Const: i64 ~ i64 => {} - T-Tail: 0 => i64 + T-Tail: 0 => t9 T-Int: {msg: enum Message { Ping, Point { x: i64, y: i64 }, -}} ⊢ 0 ⇒ i64 - Unify-Const: i64 ~ i64 => i64 +}} ⊢ 0 ⇒ t9 + Unify-Var: t9 ~ i64 => i64 Unify-Const: i64 ~ i64 => {} T-Fn: get_bar => () @@ -1247,7 +1246,9 @@ TypedProgram { 17..18, ), }, - ty: Int, + ty: Int( + I64, + ), }, ], ty: Record( @@ -1256,7 +1257,9 @@ TypedProgram { fields: [ RecordFieldType { name: "x", - ty: Int, + ty: Int( + I64, + ), }, ], }, @@ -1287,7 +1290,9 @@ TypedProgram { fields: [ RecordFieldType { name: "x", - ty: Int, + ty: Int( + I64, + ), }, ], }, @@ -1319,7 +1324,9 @@ TypedProgram { fields: [ RecordFieldType { name: "x", - ty: Int, + ty: Int( + I64, + ), }, ], }, @@ -1368,7 +1375,9 @@ TypedProgram { }, payload: Tuple( [ - Int, + Int( + I64, + ), ], ), }, @@ -1395,7 +1404,9 @@ TypedProgram { fields: [ RecordFieldType { name: "x", - ty: Int, + ty: Int( + I64, + ), }, ], }, @@ -1431,7 +1442,9 @@ TypedProgram { name: "Baz", kind: Tuple( [ - Int, + Int( + I64, + ), ], ), }, @@ -1453,7 +1466,9 @@ TypedProgram { fields: [ RecordFieldType { name: "x", - ty: Int, + ty: Int( + I64, + ), }, ], }, @@ -1511,7 +1526,9 @@ TypedProgram { name: "Baz", kind: Tuple( [ - Int, + Int( + I64, + ), ], ), }, @@ -1533,7 +1550,9 @@ TypedProgram { fields: [ RecordFieldType { name: "x", - ty: Int, + ty: Int( + I64, + ), }, ], }, @@ -1558,14 +1577,18 @@ TypedProgram { ), }, ], - return_type: Int, + return_type: Int( + I64, + ), effect: Pure, body: TypedBlock { statements: [], tail_expression: Some( Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: Match { scrutinee: Spanned { node: TypedExpr { @@ -1585,7 +1608,9 @@ TypedProgram { name: "Baz", kind: Tuple( [ - Int, + Int( + I64, + ), ], ), }, @@ -1607,7 +1632,9 @@ TypedProgram { fields: [ RecordFieldType { name: "x", - ty: Int, + ty: Int( + I64, + ), }, ], }, @@ -1692,7 +1719,9 @@ TypedProgram { tail_expression: Some( Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: FieldAccess { target: Spanned { node: TypedExpr { @@ -1702,7 +1731,9 @@ TypedProgram { fields: [ RecordFieldType { name: "x", - ty: Int, + ty: Int( + I64, + ), }, ], }, @@ -1763,7 +1794,9 @@ TypedProgram { tail_expression: Some( Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: Literal( Integer( 0, @@ -1807,7 +1840,9 @@ TypedProgram { tail_expression: Some( Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: Identifier( Identifier { name: "n", @@ -1829,7 +1864,9 @@ TypedProgram { tail_expression: Some( Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: Literal( Integer( 2, @@ -1883,7 +1920,9 @@ TypedProgram { name: "Baz", kind: Tuple( [ - Int, + Int( + I64, + ), ], ), }, @@ -1905,7 +1944,9 @@ TypedProgram { fields: [ RecordFieldType { name: "x", - ty: Int, + ty: Int( + I64, + ), }, ], }, @@ -1930,14 +1971,18 @@ TypedProgram { ), }, ], - return_type: Int, + return_type: Int( + I64, + ), effect: Pure, body: TypedBlock { statements: [], tail_expression: Some( Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: Match { scrutinee: Spanned { node: TypedExpr { @@ -1957,7 +2002,9 @@ TypedProgram { name: "Baz", kind: Tuple( [ - Int, + Int( + I64, + ), ], ), }, @@ -1979,7 +2026,9 @@ TypedProgram { fields: [ RecordFieldType { name: "x", - ty: Int, + ty: Int( + I64, + ), }, ], }, @@ -2046,7 +2095,9 @@ TypedProgram { tail_expression: Some( Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: Literal( Integer( 1, @@ -2065,7 +2116,9 @@ TypedProgram { tail_expression: Some( Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: Literal( Integer( 0, @@ -2103,21 +2156,29 @@ TypedProgram { 569..570, ), }, - ty: Int, + ty: Int( + I64, + ), }, ], - return_type: Int, + return_type: Int( + I64, + ), effect: Pure, body: TypedBlock { statements: [], tail_expression: Some( Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: Match { scrutinee: Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: Identifier( Identifier { name: "x", @@ -2141,7 +2202,9 @@ TypedProgram { tail_expression: Some( Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: Literal( Integer( 1, @@ -2160,7 +2223,9 @@ TypedProgram { tail_expression: Some( Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: Literal( Integer( 0, @@ -2215,7 +2280,9 @@ TypedProgram { 727..728, ), }, - ty: Int, + ty: Int( + I64, + ), }, TypedStructField { name: Identifier { @@ -2224,7 +2291,9 @@ TypedProgram { 735..736, ), }, - ty: Int, + ty: Int( + I64, + ), }, ], ), @@ -2244,11 +2313,15 @@ TypedProgram { [ RecordFieldType { name: "x", - ty: Int, + ty: Int( + I64, + ), }, RecordFieldType { name: "y", - ty: Int, + ty: Int( + I64, + ), }, ], ), @@ -2290,11 +2363,15 @@ TypedProgram { [ RecordFieldType { name: "x", - ty: Int, + ty: Int( + I64, + ), }, RecordFieldType { name: "y", - ty: Int, + ty: Int( + I64, + ), }, ], ), @@ -2305,14 +2382,18 @@ TypedProgram { ), }, ], - return_type: Int, + return_type: Int( + I64, + ), effect: Pure, body: TypedBlock { statements: [], tail_expression: Some( Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: Match { scrutinee: Spanned { node: TypedExpr { @@ -2330,11 +2411,15 @@ TypedProgram { [ RecordFieldType { name: "x", - ty: Int, + ty: Int( + I64, + ), }, RecordFieldType { name: "y", - ty: Int, + ty: Int( + I64, + ), }, ], ), @@ -2411,12 +2496,16 @@ TypedProgram { tail_expression: Some( Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: Binary { op: Add, left: Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: Identifier( Identifier { name: "x", @@ -2430,7 +2519,9 @@ TypedProgram { }, right: Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: Identifier( Identifier { name: "y", @@ -2456,7 +2547,9 @@ TypedProgram { tail_expression: Some( Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: Literal( Integer( 0, @@ -2496,7 +2589,9 @@ TypedProgram { Expression( Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: Call { callee: Spanned { node: TypedExpr { @@ -2518,7 +2613,9 @@ TypedProgram { name: "Baz", kind: Tuple( [ - Int, + Int( + I64, + ), ], ), }, @@ -2540,7 +2637,9 @@ TypedProgram { fields: [ RecordFieldType { name: "x", - ty: Int, + ty: Int( + I64, + ), }, ], }, @@ -2564,7 +2663,9 @@ TypedProgram { }, ), ], - result: Int, + result: Int( + I64, + ), effect: Pure, }, kind: Identifier( @@ -2597,7 +2698,9 @@ TypedProgram { name: "Baz", kind: Tuple( [ - Int, + Int( + I64, + ), ], ), }, @@ -2619,7 +2722,9 @@ TypedProgram { fields: [ RecordFieldType { name: "x", - ty: Int, + ty: Int( + I64, + ), }, ], }, diff --git a/starstream-to-wasm/tests/snapshots/inputs@multiple_functions.star.snap b/starstream-to-wasm/tests/snapshots/inputs@multiple_functions.star.snap index 49177188..6450027a 100644 --- a/starstream-to-wasm/tests/snapshots/inputs@multiple_functions.star.snap +++ b/starstream-to-wasm/tests/snapshots/inputs@multiple_functions.star.snap @@ -202,15 +202,14 @@ T-Fn: main => () Unify-Const: () ~ () => {} T-Fn: compute => i64 - T-Let: {} ⊢ let base = 41; ⇒ i64 - T-Int: {} ⊢ 41 ⇒ i64 - T-ReturnTail: base + 1 => i64 - T-Bin-Add: {base: i64} ⊢ base + 1 ⇒ i64 - T-Var: {base: i64} ⊢ base ⇒ i64 - T-Int: {base: i64} ⊢ 1 ⇒ i64 - Unify-Const: i64 ~ i64 => {} - Unify-Const: i64 ~ i64 => {} - Unify-Const: i64 ~ i64 => {} + T-Let: {} ⊢ let base = 41; ⇒ t3 + T-Int: {} ⊢ 41 ⇒ t3 + T-ReturnTail: base + 1 => t4 + T-Bin-Add: {base: t3} ⊢ base + 1 ⇒ t4 + T-Var: {base: t3} ⊢ base ⇒ t3 + T-Int: {base: t3} ⊢ 1 ⇒ t4 + Unify-Var: t3 ~ t4 => {t4/t3} + Unify-Var: t4 ~ i64 => {i64/t4} ==== Typed AST ==== TypedProgram { @@ -309,7 +308,9 @@ TypedProgram { ), }, params: [], - return_type: Int, + return_type: Int( + I64, + ), effect: Pure, body: TypedBlock { statements: [ @@ -323,7 +324,9 @@ TypedProgram { }, value: Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: Literal( Integer( 41, @@ -337,12 +340,16 @@ TypedProgram { tail_expression: Some( Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: Binary { op: Add, left: Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: Identifier( Identifier { name: "base", @@ -356,7 +363,9 @@ TypedProgram { }, right: Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: Literal( Integer( 1, diff --git a/starstream-to-wasm/tests/snapshots/inputs@option_result.star.snap b/starstream-to-wasm/tests/snapshots/inputs@option_result.star.snap index e17eea0c..e14d69e7 100644 --- a/starstream-to-wasm/tests/snapshots/inputs@option_result.star.snap +++ b/starstream-to-wasm/tests/snapshots/inputs@option_result.star.snap @@ -837,16 +837,16 @@ Program { T-Fn: make_some => Option T-ReturnTail: Option::Some(42) => Option T-EnumCtor: {} ⊢ Option::Some(42) ⇒ Option - T-Int: {} ⊢ 42 ⇒ i64 - Unify-Var: i64 ~ t3 => {i64/t3} - Unify-Enum: Option ~ Option => {} - Unify-Const: i64 ~ i64 => {} + T-Int: {} ⊢ 42 ⇒ t4 + Unify-Var: t4 ~ t3 => {t3/t4} + Unify-Enum: Option ~ Option => {i64/t3} + Unify-Var: t3 ~ i64 => {i64/t3} T-Fn: make_none => Option - T-ReturnTail: Option::None => Option - T-EnumCtor: {} ⊢ Option::None ⇒ Option - Unify-Enum: Option ~ Option => {i64/t4} - Unify-Var: t4 ~ i64 => {i64/t4} + T-ReturnTail: Option::None => Option + T-EnumCtor: {} ⊢ Option::None ⇒ Option + Unify-Enum: Option ~ Option => {i64/t5} + Unify-Var: t5 ~ i64 => {i64/t5} T-Fn: unwrap_option => i64 T-ReturnTail: match opt { @@ -872,40 +872,40 @@ T-Fn: unwrap_option => i64 Some(i64), None, }} ⊢ opt ⇒ Option - Unify-Enum: Option ~ Option => {i64/t5} - Unify-Var: i64 ~ t5 => {i64/t5} - T-Tail: v => t5 + Unify-Enum: Option ~ Option => {i64/t6} + Unify-Var: i64 ~ t6 => {i64/t6} + T-Tail: v => t6 T-Var: {opt: enum Option { Some(i64), None, -}, v: t5} ⊢ v ⇒ t5 - Unify-Enum: Option ~ Option => {i64/t6} - Unify-Var: i64 ~ t6 => {i64/t6} - T-Tail: 0 => i64 +}, v: t6} ⊢ v ⇒ t6 + Unify-Enum: Option ~ Option => {i64/t7} + Unify-Var: i64 ~ t7 => {i64/t7} + T-Tail: 0 => t8 T-Int: {opt: enum Option { Some(i64), None, -}} ⊢ 0 ⇒ i64 - Unify-Const: i64 ~ i64 => i64 +}} ⊢ 0 ⇒ t8 + Unify-Var: t8 ~ i64 => i64 Unify-Const: i64 ~ i64 => {} T-Fn: make_ok => Result - T-ReturnTail: Result::Ok(42) => Result - T-EnumCtor: {} ⊢ Result::Ok(42) ⇒ Result - T-Int: {} ⊢ 42 ⇒ i64 - Unify-Var: i64 ~ t7 => {i64/t7} - Unify-Enum: Result ~ Result => {bool/t8} - Unify-Var: t8 ~ bool => {bool/t8} - Unify-Const: i64 ~ i64 => {} + T-ReturnTail: Result::Ok(42) => Result + T-EnumCtor: {} ⊢ Result::Ok(42) ⇒ Result + T-Int: {} ⊢ 42 ⇒ t11 + Unify-Var: t11 ~ t9 => {t9/t11} + Unify-Enum: Result ~ Result => {bool/t10, i64/t9} + Unify-Var: t10 ~ bool => {bool/t10} + Unify-Var: t9 ~ i64 => {i64/t9} T-Fn: make_err => Result - T-ReturnTail: Result::Err(false) => Result - T-EnumCtor: {} ⊢ Result::Err(false) ⇒ Result + T-ReturnTail: Result::Err(false) => Result + T-EnumCtor: {} ⊢ Result::Err(false) ⇒ Result T-Bool: {} ⊢ false ⇒ bool - Unify-Var: bool ~ t10 => {bool/t10} - Unify-Enum: Result ~ Result => {i64/t9} + Unify-Var: bool ~ t13 => {bool/t13} + Unify-Enum: Result ~ Result => {i64/t12} Unify-Const: bool ~ bool => {} - Unify-Var: t9 ~ i64 => {i64/t9} + Unify-Var: t12 ~ i64 => {i64/t12} T-Fn: unwrap_result => i64 T-ReturnTail: match res { @@ -931,23 +931,23 @@ T-Fn: unwrap_result => i64 Ok(i64), Err(bool), }} ⊢ res ⇒ Result - Unify-Enum: Result ~ Result => {bool/t12, i64/t11} - Unify-Var: bool ~ t12 => {bool/t12} - Unify-Var: i64 ~ t11 => {i64/t11} - T-Tail: v => t11 + Unify-Enum: Result ~ Result => {bool/t15, i64/t14} + Unify-Var: bool ~ t15 => {bool/t15} + Unify-Var: i64 ~ t14 => {i64/t14} + T-Tail: v => t14 T-Var: {res: enum Result { Ok(i64), Err(bool), -}, v: t11} ⊢ v ⇒ t11 - Unify-Enum: Result ~ Result => {bool/t14, i64/t13} - Unify-Var: bool ~ t14 => {bool/t14} - Unify-Var: i64 ~ t13 => {i64/t13} - T-Tail: 0 => i64 - T-Int: {e: t14, res: enum Result { +}, v: t14} ⊢ v ⇒ t14 + Unify-Enum: Result ~ Result => {bool/t17, i64/t16} + Unify-Var: bool ~ t17 => {bool/t17} + Unify-Var: i64 ~ t16 => {i64/t16} + T-Tail: 0 => t18 + T-Int: {e: t17, res: enum Result { Ok(i64), Err(bool), -}} ⊢ 0 ⇒ i64 - Unify-Const: i64 ~ i64 => i64 +}} ⊢ 0 ⇒ t18 + Unify-Var: t18 ~ i64 => i64 Unify-Const: i64 ~ i64 => {} @@ -1039,7 +1039,9 @@ TypedProgram { name: "Some", kind: Tuple( [ - Int, + Int( + I64, + ), ], ), }, @@ -1049,7 +1051,9 @@ TypedProgram { }, ], type_args: [ - Int, + Int( + I64, + ), ], }, ), @@ -1067,7 +1071,9 @@ TypedProgram { name: "Some", kind: Tuple( [ - Int, + Int( + I64, + ), ], ), }, @@ -1077,7 +1083,9 @@ TypedProgram { }, ], type_args: [ - Int, + Int( + I64, + ), ], }, ), @@ -1098,7 +1106,9 @@ TypedProgram { [ Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: Literal( Integer( 42, @@ -1135,7 +1145,9 @@ TypedProgram { name: "Some", kind: Tuple( [ - Int, + Int( + I64, + ), ], ), }, @@ -1145,7 +1157,9 @@ TypedProgram { }, ], type_args: [ - Int, + Int( + I64, + ), ], }, ), @@ -1163,7 +1177,9 @@ TypedProgram { name: "Some", kind: Tuple( [ - Int, + Int( + I64, + ), ], ), }, @@ -1173,7 +1189,9 @@ TypedProgram { }, ], type_args: [ - Int, + Int( + I64, + ), ], }, ), @@ -1224,7 +1242,9 @@ TypedProgram { name: "Some", kind: Tuple( [ - Int, + Int( + I64, + ), ], ), }, @@ -1234,20 +1254,26 @@ TypedProgram { }, ], type_args: [ - Int, + Int( + I64, + ), ], }, ), }, ], - return_type: Int, + return_type: Int( + I64, + ), effect: Pure, body: TypedBlock { statements: [], tail_expression: Some( Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: Match { scrutinee: Spanned { node: TypedExpr { @@ -1259,7 +1285,9 @@ TypedProgram { name: "Some", kind: Tuple( [ - Int, + Int( + I64, + ), ], ), }, @@ -1269,7 +1297,9 @@ TypedProgram { }, ], type_args: [ - Int, + Int( + I64, + ), ], }, ), @@ -1317,7 +1347,9 @@ TypedProgram { tail_expression: Some( Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: Identifier( Identifier { name: "v", @@ -1353,7 +1385,9 @@ TypedProgram { tail_expression: Some( Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: Literal( Integer( 0, @@ -1392,7 +1426,9 @@ TypedProgram { name: "Ok", kind: Tuple( [ - Int, + Int( + I64, + ), ], ), }, @@ -1406,7 +1442,9 @@ TypedProgram { }, ], type_args: [ - Int, + Int( + I64, + ), Bool, ], }, @@ -1425,7 +1463,9 @@ TypedProgram { name: "Ok", kind: Tuple( [ - Int, + Int( + I64, + ), ], ), }, @@ -1439,7 +1479,9 @@ TypedProgram { }, ], type_args: [ - Int, + Int( + I64, + ), Bool, ], }, @@ -1461,7 +1503,9 @@ TypedProgram { [ Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: Literal( Integer( 42, @@ -1498,7 +1542,9 @@ TypedProgram { name: "Ok", kind: Tuple( [ - Int, + Int( + I64, + ), ], ), }, @@ -1512,7 +1558,9 @@ TypedProgram { }, ], type_args: [ - Int, + Int( + I64, + ), Bool, ], }, @@ -1531,7 +1579,9 @@ TypedProgram { name: "Ok", kind: Tuple( [ - Int, + Int( + I64, + ), ], ), }, @@ -1545,7 +1595,9 @@ TypedProgram { }, ], type_args: [ - Int, + Int( + I64, + ), Bool, ], }, @@ -1611,7 +1663,9 @@ TypedProgram { name: "Ok", kind: Tuple( [ - Int, + Int( + I64, + ), ], ), }, @@ -1625,21 +1679,27 @@ TypedProgram { }, ], type_args: [ - Int, + Int( + I64, + ), Bool, ], }, ), }, ], - return_type: Int, + return_type: Int( + I64, + ), effect: Pure, body: TypedBlock { statements: [], tail_expression: Some( Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: Match { scrutinee: Spanned { node: TypedExpr { @@ -1651,7 +1711,9 @@ TypedProgram { name: "Ok", kind: Tuple( [ - Int, + Int( + I64, + ), ], ), }, @@ -1665,7 +1727,9 @@ TypedProgram { }, ], type_args: [ - Int, + Int( + I64, + ), Bool, ], }, @@ -1714,7 +1778,9 @@ TypedProgram { tail_expression: Some( Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: Identifier( Identifier { name: "v", @@ -1761,7 +1827,9 @@ TypedProgram { tail_expression: Some( Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: Literal( Integer( 0, @@ -1806,7 +1874,9 @@ TypedProgram { name: "Some", kind: Tuple( [ - Int, + Int( + I64, + ), ], ), }, @@ -1816,7 +1886,9 @@ TypedProgram { }, ], type_args: [ - Int, + Int( + I64, + ), ], }, ), @@ -1836,7 +1908,9 @@ TypedProgram { name: "Some", kind: Tuple( [ - Int, + Int( + I64, + ), ], ), }, @@ -1846,7 +1920,9 @@ TypedProgram { }, ], type_args: [ - Int, + Int( + I64, + ), ], }, ), @@ -1889,7 +1965,9 @@ TypedProgram { name: "Some", kind: Tuple( [ - Int, + Int( + I64, + ), ], ), }, @@ -1899,7 +1977,9 @@ TypedProgram { }, ], type_args: [ - Int, + Int( + I64, + ), ], }, ), @@ -1923,7 +2003,9 @@ TypedProgram { name: "Ok", kind: Tuple( [ - Int, + Int( + I64, + ), ], ), }, @@ -1937,7 +2019,9 @@ TypedProgram { }, ], type_args: [ - Int, + Int( + I64, + ), Bool, ], }, @@ -1951,7 +2035,9 @@ TypedProgram { Expression( Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: Call { callee: Spanned { node: TypedExpr { @@ -1965,7 +2051,9 @@ TypedProgram { name: "Some", kind: Tuple( [ - Int, + Int( + I64, + ), ], ), }, @@ -1975,12 +2063,16 @@ TypedProgram { }, ], type_args: [ - Int, + Int( + I64, + ), ], }, ), ], - result: Int, + result: Int( + I64, + ), effect: Pure, }, kind: Identifier( @@ -2005,7 +2097,9 @@ TypedProgram { name: "Some", kind: Tuple( [ - Int, + Int( + I64, + ), ], ), }, @@ -2015,7 +2109,9 @@ TypedProgram { }, ], type_args: [ - Int, + Int( + I64, + ), ], }, ), @@ -2032,7 +2128,9 @@ TypedProgram { name: "Some", kind: Tuple( [ - Int, + Int( + I64, + ), ], ), }, @@ -2042,7 +2140,9 @@ TypedProgram { }, ], type_args: [ - Int, + Int( + I64, + ), ], }, ), @@ -2073,7 +2173,9 @@ TypedProgram { Expression( Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: Call { callee: Spanned { node: TypedExpr { @@ -2087,7 +2189,9 @@ TypedProgram { name: "Ok", kind: Tuple( [ - Int, + Int( + I64, + ), ], ), }, @@ -2101,13 +2205,17 @@ TypedProgram { }, ], type_args: [ - Int, + Int( + I64, + ), Bool, ], }, ), ], - result: Int, + result: Int( + I64, + ), effect: Pure, }, kind: Identifier( @@ -2132,7 +2240,9 @@ TypedProgram { name: "Ok", kind: Tuple( [ - Int, + Int( + I64, + ), ], ), }, @@ -2146,7 +2256,9 @@ TypedProgram { }, ], type_args: [ - Int, + Int( + I64, + ), Bool, ], }, @@ -2164,7 +2276,9 @@ TypedProgram { name: "Ok", kind: Tuple( [ - Int, + Int( + I64, + ), ], ), }, @@ -2178,7 +2292,9 @@ TypedProgram { }, ], type_args: [ - Int, + Int( + I64, + ), Bool, ], }, diff --git a/starstream-to-wasm/tests/snapshots/inputs@struct_definition.star.snap b/starstream-to-wasm/tests/snapshots/inputs@struct_definition.star.snap index a63a044f..b0b658aa 100644 --- a/starstream-to-wasm/tests/snapshots/inputs@struct_definition.star.snap +++ b/starstream-to-wasm/tests/snapshots/inputs@struct_definition.star.snap @@ -81,7 +81,9 @@ TypedProgram { 19..20, ), }, - ty: Int, + ty: Int( + I64, + ), }, TypedStructField { name: Identifier { @@ -90,7 +92,9 @@ TypedProgram { 31..32, ), }, - ty: Int, + ty: Int( + I64, + ), }, ], ty: Record( @@ -99,11 +103,15 @@ TypedProgram { fields: [ RecordFieldType { name: "x", - ty: Int, + ty: Int( + I64, + ), }, RecordFieldType { name: "y", - ty: Int, + ty: Int( + I64, + ), }, ], }, diff --git a/starstream-to-wasm/tests/snapshots/inputs@struct_param.star.snap b/starstream-to-wasm/tests/snapshots/inputs@struct_param.star.snap index acf47474..6409151a 100644 --- a/starstream-to-wasm/tests/snapshots/inputs@struct_param.star.snap +++ b/starstream-to-wasm/tests/snapshots/inputs@struct_param.star.snap @@ -187,7 +187,6 @@ T-Fn: total_value => i64 price: i64, }} ⊢ token ⇒ Token Unify-Const: i64 ~ i64 => {} - Unify-Const: i64 ~ i64 => {} Unify-Const: i64 ~ i64 => {} ==== Typed AST ==== @@ -209,7 +208,9 @@ TypedProgram { 19..25, ), }, - ty: Int, + ty: Int( + I64, + ), }, TypedStructField { name: Identifier { @@ -218,7 +219,9 @@ TypedProgram { 36..41, ), }, - ty: Int, + ty: Int( + I64, + ), }, ], ty: Record( @@ -227,11 +230,15 @@ TypedProgram { fields: [ RecordFieldType { name: "amount", - ty: Int, + ty: Int( + I64, + ), }, RecordFieldType { name: "price", - ty: Int, + ty: Int( + I64, + ), }, ], }, @@ -263,30 +270,40 @@ TypedProgram { fields: [ RecordFieldType { name: "amount", - ty: Int, + ty: Int( + I64, + ), }, RecordFieldType { name: "price", - ty: Int, + ty: Int( + I64, + ), }, ], }, ), }, ], - return_type: Int, + return_type: Int( + I64, + ), effect: Pure, body: TypedBlock { statements: [], tail_expression: Some( Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: Binary { op: Multiply, left: Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: FieldAccess { target: Spanned { node: TypedExpr { @@ -296,11 +313,15 @@ TypedProgram { fields: [ RecordFieldType { name: "amount", - ty: Int, + ty: Int( + I64, + ), }, RecordFieldType { name: "price", - ty: Int, + ty: Int( + I64, + ), }, ], }, @@ -328,7 +349,9 @@ TypedProgram { }, right: Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: FieldAccess { target: Spanned { node: TypedExpr { @@ -338,11 +361,15 @@ TypedProgram { fields: [ RecordFieldType { name: "amount", - ty: Int, + ty: Int( + I64, + ), }, RecordFieldType { name: "price", - ty: Int, + ty: Int( + I64, + ), }, ], }, diff --git a/starstream-to-wasm/tests/snapshots/inputs@utxo_storage.star.snap b/starstream-to-wasm/tests/snapshots/inputs@utxo_storage.star.snap index 46776bd8..7c0f7ad6 100644 --- a/starstream-to-wasm/tests/snapshots/inputs@utxo_storage.star.snap +++ b/starstream-to-wasm/tests/snapshots/inputs@utxo_storage.star.snap @@ -134,9 +134,8 @@ T-Fn: increment => i64 T-Assign: {counter: i64} ⊢ counter = counter + 1; ⇒ i64 T-Bin-Add: {counter: i64} ⊢ counter + 1 ⇒ i64 T-Var: {counter: i64} ⊢ counter ⇒ i64 - T-Int: {counter: i64} ⊢ 1 ⇒ i64 - Unify-Const: i64 ~ i64 => {} - Unify-Const: i64 ~ i64 => {} + T-Int: {counter: i64} ⊢ 1 ⇒ t3 + Unify-Var: i64 ~ t3 => {i64/t3} Unify-Const: i64 ~ i64 => {} T-ReturnTail: counter => i64 T-Var: {counter: i64} ⊢ counter ⇒ i64 @@ -163,7 +162,9 @@ TypedProgram { 44..51, ), }, - ty: Int, + ty: Int( + I64, + ), }, ], ), @@ -182,7 +183,9 @@ TypedProgram { ), }, params: [], - return_type: Int, + return_type: Int( + I64, + ), effect: Pure, body: TypedBlock { statements: [ @@ -195,12 +198,16 @@ TypedProgram { }, value: Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: Binary { op: Add, left: Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: Identifier( Identifier { name: "counter", @@ -214,7 +221,9 @@ TypedProgram { }, right: Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: Literal( Integer( 1, @@ -232,7 +241,9 @@ TypedProgram { tail_expression: Some( Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: Identifier( Identifier { name: "counter", diff --git a/starstream-to-wasm/tests/snapshots/inputs@utxo_storage_2.star.snap b/starstream-to-wasm/tests/snapshots/inputs@utxo_storage_2.star.snap index 6e68fa07..4a0dc075 100644 --- a/starstream-to-wasm/tests/snapshots/inputs@utxo_storage_2.star.snap +++ b/starstream-to-wasm/tests/snapshots/inputs@utxo_storage_2.star.snap @@ -267,32 +267,28 @@ T-Fn: increment => i64 T-Assign: {counter: i64, multiplier: i64} ⊢ counter = counter + 1; ⇒ i64 T-Bin-Add: {counter: i64, multiplier: i64} ⊢ counter + 1 ⇒ i64 T-Var: {counter: i64, multiplier: i64} ⊢ counter ⇒ i64 - T-Int: {counter: i64, multiplier: i64} ⊢ 1 ⇒ i64 - Unify-Const: i64 ~ i64 => {} - Unify-Const: i64 ~ i64 => {} + T-Int: {counter: i64, multiplier: i64} ⊢ 1 ⇒ t3 + Unify-Var: i64 ~ t3 => {i64/t3} Unify-Const: i64 ~ i64 => {} T-ReturnTail: counter * multiplier => i64 T-Bin-Mul: {counter: i64, multiplier: i64} ⊢ counter * multiplier ⇒ i64 T-Var: {counter: i64, multiplier: i64} ⊢ counter ⇒ i64 T-Var: {counter: i64, multiplier: i64} ⊢ multiplier ⇒ i64 Unify-Const: i64 ~ i64 => {} - Unify-Const: i64 ~ i64 => {} Unify-Const: i64 ~ i64 => {} T-Fn: increment_mult => i64 T-Assign: {counter: i64, multiplier: i64} ⊢ multiplier = multiplier + 1; ⇒ i64 T-Bin-Add: {counter: i64, multiplier: i64} ⊢ multiplier + 1 ⇒ i64 T-Var: {counter: i64, multiplier: i64} ⊢ multiplier ⇒ i64 - T-Int: {counter: i64, multiplier: i64} ⊢ 1 ⇒ i64 - Unify-Const: i64 ~ i64 => {} - Unify-Const: i64 ~ i64 => {} + T-Int: {counter: i64, multiplier: i64} ⊢ 1 ⇒ t4 + Unify-Var: i64 ~ t4 => {i64/t4} Unify-Const: i64 ~ i64 => {} T-ReturnTail: counter * multiplier => i64 T-Bin-Mul: {counter: i64, multiplier: i64} ⊢ counter * multiplier ⇒ i64 T-Var: {counter: i64, multiplier: i64} ⊢ counter ⇒ i64 T-Var: {counter: i64, multiplier: i64} ⊢ multiplier ⇒ i64 Unify-Const: i64 ~ i64 => {} - Unify-Const: i64 ~ i64 => {} Unify-Const: i64 ~ i64 => {} ==== Typed AST ==== @@ -316,7 +312,9 @@ TypedProgram { 44..51, ), }, - ty: Int, + ty: Int( + I64, + ), }, TypedUtxoGlobal { name: Identifier { @@ -325,7 +323,9 @@ TypedProgram { 74..84, ), }, - ty: Int, + ty: Int( + I64, + ), }, ], ), @@ -344,7 +344,9 @@ TypedProgram { ), }, params: [], - return_type: Int, + return_type: Int( + I64, + ), effect: Pure, body: TypedBlock { statements: [ @@ -357,12 +359,16 @@ TypedProgram { }, value: Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: Binary { op: Add, left: Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: Identifier( Identifier { name: "counter", @@ -376,7 +382,9 @@ TypedProgram { }, right: Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: Literal( Integer( 1, @@ -394,12 +402,16 @@ TypedProgram { tail_expression: Some( Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: Binary { op: Multiply, left: Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: Identifier( Identifier { name: "counter", @@ -413,7 +425,9 @@ TypedProgram { }, right: Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: Identifier( Identifier { name: "multiplier", @@ -445,7 +459,9 @@ TypedProgram { ), }, params: [], - return_type: Int, + return_type: Int( + I64, + ), effect: Pure, body: TypedBlock { statements: [ @@ -458,12 +474,16 @@ TypedProgram { }, value: Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: Binary { op: Add, left: Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: Identifier( Identifier { name: "multiplier", @@ -477,7 +497,9 @@ TypedProgram { }, right: Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: Literal( Integer( 1, @@ -495,12 +517,16 @@ TypedProgram { tail_expression: Some( Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: Binary { op: Multiply, left: Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: Identifier( Identifier { name: "counter", @@ -514,7 +540,9 @@ TypedProgram { }, right: Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: Identifier( Identifier { name: "multiplier", diff --git a/starstream-to-wasm/tests/snapshots/inputs@while_simple.star.snap b/starstream-to-wasm/tests/snapshots/inputs@while_simple.star.snap index 09492417..e4c689a2 100644 --- a/starstream-to-wasm/tests/snapshots/inputs@while_simple.star.snap +++ b/starstream-to-wasm/tests/snapshots/inputs@while_simple.star.snap @@ -128,23 +128,23 @@ Program { ==== Inference trace ==== T-Fn: main => () - T-Let: {} ⊢ let mut foo = 0; ⇒ i64 - T-Int: {} ⊢ 0 ⇒ i64 - T-While: {foo: i64} ⊢ while (foo < 10) { + T-Let: {} ⊢ let mut foo = 0; ⇒ t3 + T-Int: {} ⊢ 0 ⇒ t3 + T-While: {foo: t3} ⊢ while (foo < 10) { foo = foo + 1; } ⇒ () - T-Bin-Lt: {foo: i64} ⊢ foo < 10 ⇒ bool - T-Var: {foo: i64} ⊢ foo ⇒ i64 - T-Int: {foo: i64} ⊢ 10 ⇒ i64 + T-Bin-Lt: {foo: t3} ⊢ foo < 10 ⇒ bool + T-Var: {foo: t3} ⊢ foo ⇒ t3 + T-Int: {foo: t3} ⊢ 10 ⇒ t4 Check-Compare: i64 vs i64 => ok (int) - Check-Bool: bool => ok - T-Assign: {foo: i64} ⊢ foo = foo + 1; ⇒ i64 - T-Bin-Add: {foo: i64} ⊢ foo + 1 ⇒ i64 - T-Var: {foo: i64} ⊢ foo ⇒ i64 - T-Int: {foo: i64} ⊢ 1 ⇒ i64 - Unify-Const: i64 ~ i64 => {} Unify-Const: i64 ~ i64 => {} - Unify-Const: i64 ~ i64 => {} + Check-Bool: bool => ok + T-Assign: {foo: t3} ⊢ foo = foo + 1; ⇒ t3 + T-Bin-Add: {foo: t3} ⊢ foo + 1 ⇒ t5 + T-Var: {foo: t3} ⊢ foo ⇒ t3 + T-Int: {foo: t3} ⊢ 1 ⇒ t5 + Unify-Var: t3 ~ t5 => {t5/t3} + Unify-Var: t5 ~ t5 => {} ==== Typed AST ==== TypedProgram { @@ -175,7 +175,9 @@ TypedProgram { }, value: Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: Literal( Integer( 0, @@ -193,7 +195,9 @@ TypedProgram { op: Less, left: Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: Identifier( Identifier { name: "foo", @@ -207,7 +211,9 @@ TypedProgram { }, right: Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: Literal( Integer( 10, @@ -231,12 +237,16 @@ TypedProgram { }, value: Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: Binary { op: Add, left: Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: Identifier( Identifier { name: "foo", @@ -250,7 +260,9 @@ TypedProgram { }, right: Spanned { node: TypedExpr { - ty: Int, + ty: Int( + I64, + ), kind: Literal( Integer( 1, diff --git a/starstream-types/src/ast.rs b/starstream-types/src/ast.rs index b9a7db53..0bc2e0a7 100644 --- a/starstream-types/src/ast.rs +++ b/starstream-types/src/ast.rs @@ -378,7 +378,7 @@ pub enum Expr { #[derive(Clone, Debug, Serialize, PartialEq)] pub enum Literal { - Integer(i64), + Integer(i128), Boolean(bool), Unit, } diff --git a/starstream-types/src/types.rs b/starstream-types/src/types.rs index 653e0f2c..dc228f5b 100644 --- a/starstream-types/src/types.rs +++ b/starstream-types/src/types.rs @@ -11,6 +11,88 @@ use std::fmt; const TYPE_FORMAT_WIDTH: usize = 80; +/// Integer width and signedness. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub enum IntWidth { + I8, + I16, + I32, + I64, + U8, + U16, + U32, + U64, +} + +impl IntWidth { + pub fn is_signed(self) -> bool { + matches!( + self, + IntWidth::I8 | IntWidth::I16 | IntWidth::I32 | IntWidth::I64 + ) + } + + pub fn bit_width(self) -> u32 { + match self { + IntWidth::I8 | IntWidth::U8 => 8, + IntWidth::I16 | IntWidth::U16 => 16, + IntWidth::I32 | IntWidth::U32 => 32, + IntWidth::I64 | IntWidth::U64 => 64, + } + } + + pub fn display_name(self) -> &'static str { + match self { + IntWidth::I8 => "i8", + IntWidth::I16 => "i16", + IntWidth::I32 => "i32", + IntWidth::I64 => "i64", + IntWidth::U8 => "u8", + IntWidth::U16 => "u16", + IntWidth::U32 => "u32", + IntWidth::U64 => "u64", + } + } + + pub fn min_value(self) -> i128 { + match self { + IntWidth::I8 => i8::MIN as i128, + IntWidth::I16 => i16::MIN as i128, + IntWidth::I32 => i32::MIN as i128, + IntWidth::I64 => i64::MIN as i128, + IntWidth::U8 | IntWidth::U16 | IntWidth::U32 | IntWidth::U64 => 0, + } + } + + pub fn max_value(self) -> i128 { + match self { + IntWidth::I8 => i8::MAX as i128, + IntWidth::I16 => i16::MAX as i128, + IntWidth::I32 => i32::MAX as i128, + IntWidth::I64 => i64::MAX as i128, + IntWidth::U8 => u8::MAX as i128, + IntWidth::U16 => u16::MAX as i128, + IntWidth::U32 => u32::MAX as i128, + IntWidth::U64 => u64::MAX as i128, + } + } + + pub fn fits(self, value: i128) -> bool { + value >= self.min_value() && value <= self.max_value() + } + + pub fn is_64bit(self) -> bool { + matches!(self, IntWidth::I64 | IntWidth::U64) + } + + pub fn is_sub32(self) -> bool { + matches!( + self, + IntWidth::I8 | IntWidth::I16 | IntWidth::U8 | IntWidth::U16 + ) + } +} + /// Effect kind for functions - tracks whether a function performs side effects. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)] pub enum EffectKind { @@ -71,8 +153,8 @@ impl GenericTypeDef { pub enum Type { /// An unknown type represented by a type variable. Var(TypeVarId), - /// 64-bit signed integer. - Int, + /// Integer type with width and signedness. + Int(IntWidth), /// Boolean. Bool, /// Unit `()` value used for statement expressions and other places that @@ -106,7 +188,11 @@ impl Type { } pub fn int() -> Self { - Type::Int + Type::Int(IntWidth::I64) + } + + pub fn int_of(w: IntWidth) -> Self { + Type::Int(w) } /// Canonical record type helper. @@ -183,7 +269,7 @@ impl Type { fn to_doc(&self, mode: TypeDocMode, params: &HashMap) -> RcDoc<'static, ()> { match self { Type::Var(id) => RcDoc::text(params.get(id).cloned().unwrap_or_else(|| id.as_str())), - Type::Int => RcDoc::text("i64"), + Type::Int(w) => RcDoc::text(w.display_name()), Type::Bool => RcDoc::text("bool"), Type::Unit => RcDoc::text("()"), Type::Function { From 67a6ea528faa06353096d37dbf12235fcd638244 Mon Sep 17 00:00:00 2001 From: rvcas Date: Thu, 19 Feb 2026 11:36:13 -0500 Subject: [PATCH 2/3] chore: fmt Signed-off-by: rvcas --- starstream-compiler/src/typecheck/infer.rs | 85 ++++++++++++------- ...r__typecheck__tests__if_branch_traces.snap | 6 +- ..._typecheck__tests__reports_type_error.snap | 4 +- ...__typecheck__tests__while_loop_traces.snap | 6 +- .../snapshots/inputs@while_simple.star.snap | 6 +- 5 files changed, 67 insertions(+), 40 deletions(-) diff --git a/starstream-compiler/src/typecheck/infer.rs b/starstream-compiler/src/typecheck/infer.rs index ae59054f..dc669b3b 100644 --- a/starstream-compiler/src/typecheck/infer.rs +++ b/starstream-compiler/src/typecheck/infer.rs @@ -2050,18 +2050,28 @@ impl Inferencer { | BinaryOp::Remainder => { let both_int = self.is_int_like(&left_ty) && self.is_int_like(&right_ty); if !both_int { - let left_repr = self.format_type(&left_ty); - let right_repr = self.format_type(&right_ty); + let left_display = self.apply_for_display(&typed_left.node.ty); + let right_display = + self.apply_for_display(&typed_right.node.ty); return Err(TypeError::new( TypeErrorKind::BinaryOperandMismatch { op: *op, - left: left_ty.clone(), - right: right_ty.clone(), + left: left_display.clone(), + right: right_display.clone(), }, left_label_span, ) - .with_primary_message(format!("has type `{left_repr}`")) - .with_secondary(right_label_span, format!("has type `{right_repr}`"))); + .with_primary_message(format!( + "has type `{}`", + left_display.to_compact_string() + )) + .with_secondary( + right_label_span, + format!( + "has type `{}`", + right_display.to_compact_string() + ), + )); } // Unify left and right to ensure same int width @@ -2108,18 +2118,28 @@ impl Inferencer { let both_bool = matches!(&left_ty, Type::Bool) && matches!(&right_ty, Type::Bool); if !both_bool { - let left_repr = self.format_type(&left_ty); - let right_repr = self.format_type(&right_ty); + let left_display = self.apply_for_display(&typed_left.node.ty); + let right_display = + self.apply_for_display(&typed_right.node.ty); return Err(TypeError::new( TypeErrorKind::BinaryOperandMismatch { op: *op, - left: left_ty.clone(), - right: right_ty.clone(), + left: left_display.clone(), + right: right_display.clone(), }, left_label_span, ) - .with_primary_message(format!("has type `{left_repr}`")) - .with_secondary(right_label_span, format!("has type `{right_repr}`"))); + .with_primary_message(format!( + "has type `{}`", + left_display.to_compact_string() + )) + .with_secondary( + right_label_span, + format!( + "has type `{}`", + right_display.to_compact_string() + ), + )); } children.push(self.require_is( @@ -3335,8 +3355,8 @@ impl Inferencer { right: &Spanned, right_span: Span, ) -> Result { - let left_ty = self.apply_for_display(&left.node.ty); - let right_ty = self.apply_for_display(&right.node.ty); + let left_ty = self.apply(&left.node.ty); + let right_ty = self.apply(&right.node.ty); let both_int = self.is_int_like(&left_ty) && self.is_int_like(&right_ty); @@ -3349,8 +3369,8 @@ impl Inferencer { right_span, TypeErrorKind::BinaryOperandMismatch { op: *op, - left: left_ty.clone(), - right: right_ty.clone(), + left: self.apply_for_display(&left.node.ty), + right: self.apply_for_display(&right.node.ty), }, )?; let subject = self.maybe_string(|| { @@ -3373,18 +3393,20 @@ impl Inferencer { let result = self.maybe_string(|| "ok (bool)".to_string()); Ok(self.make_trace("Check-Compare", None, subject, result, Vec::new)) } else { + let left_display = self.apply_for_display(&left.node.ty); + let right_display = self.apply_for_display(&right.node.ty); Err(TypeError::new( TypeErrorKind::BinaryOperandMismatch { op: *op, - left: left_ty.clone(), - right: right_ty.clone(), + left: left_display.clone(), + right: right_display.clone(), }, left_span, ) - .with_primary_message(format!("has type `{}`", left_ty.to_compact_string())) + .with_primary_message(format!("has type `{}`", left_display.to_compact_string())) .with_secondary( right_span, - format!("has type `{}`", right_ty.to_compact_string()), + format!("has type `{}`", right_display.to_compact_string()), )) } } @@ -3398,8 +3420,8 @@ impl Inferencer { right: &Spanned, right_span: Span, ) -> Result { - let left_ty = self.apply_for_display(&left.node.ty); - let right_ty = self.apply_for_display(&right.node.ty); + let left_ty = self.apply(&left.node.ty); + let right_ty = self.apply(&right.node.ty); let both_int = self.is_int_like(&left_ty) && self.is_int_like(&right_ty); @@ -3412,8 +3434,8 @@ impl Inferencer { right_span, TypeErrorKind::BinaryOperandMismatch { op: *op, - left: left_ty.clone(), - right: right_ty.clone(), + left: self.apply_for_display(&left.node.ty), + right: self.apply_for_display(&right.node.ty), }, )?; let subject = self.maybe_string(|| { @@ -3436,18 +3458,20 @@ impl Inferencer { let result = self.maybe_string(|| "ok".to_string()); Ok(self.make_trace("Check-Eq", None, subject, result, Vec::new)) } else { + let left_display = self.apply_for_display(&left.node.ty); + let right_display = self.apply_for_display(&right.node.ty); Err(TypeError::new( TypeErrorKind::BinaryOperandMismatch { op: *op, - left: left_ty.clone(), - right: right_ty.clone(), + left: left_display.clone(), + right: right_display.clone(), }, left_span, ) - .with_primary_message(format!("has type `{}`", left_ty.to_compact_string())) + .with_primary_message(format!("has type `{}`", left_display.to_compact_string())) .with_secondary( right_span, - format!("has type `{}`", right_ty.to_compact_string()), + format!("has type `{}`", right_display.to_compact_string()), )) } } @@ -3502,7 +3526,10 @@ impl Inferencer { kind: match &variant.kind { TypeEnumVariantKind::Unit => TypeEnumVariantKind::Unit, TypeEnumVariantKind::Tuple(payload) => TypeEnumVariantKind::Tuple( - payload.iter().map(|ty| self.apply_for_display(ty)).collect(), + payload + .iter() + .map(|ty| self.apply_for_display(ty)) + .collect(), ), TypeEnumVariantKind::Struct(fields) => TypeEnumVariantKind::Struct( fields diff --git a/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__if_branch_traces.snap b/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__if_branch_traces.snap index 902371a3..d3dfc206 100644 --- a/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__if_branch_traces.snap +++ b/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__if_branch_traces.snap @@ -14,14 +14,14 @@ T-Fn: test => () T-Bin-Gt: {score: t3} ⊢ score > 5 ⇒ bool T-Var: {score: t3} ⊢ score ⇒ t3 T-Int: {score: t3} ⊢ 5 ⇒ t4 - Check-Compare: i64 vs i64 => ok (int) - Unify-Const: i64 ~ i64 => {} + Check-Compare: t3 vs t4 => ok (int) + Unify-Var: t3 ~ t4 => {t4/t3} Check-Bool: bool => ok T-Assign: {score: t3} ⊢ score = score + 1; ⇒ t3 T-Bin-Add: {score: t3} ⊢ score + 1 ⇒ t5 T-Var: {score: t3} ⊢ score ⇒ t3 T-Int: {score: t3} ⊢ 1 ⇒ t5 - Unify-Var: t3 ~ t5 => {t5/t3} + Unify-Var: t4 ~ t5 => {t5/t4} Unify-Var: t5 ~ t5 => {} Unify-Const: () ~ () => {} Unify-Const: () ~ () => {} diff --git a/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__reports_type_error.snap b/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__reports_type_error.snap index a9ebd739..80638ec5 100644 --- a/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__reports_type_error.snap +++ b/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__reports_type_error.snap @@ -4,12 +4,12 @@ description: "Source:\n\nfn test() {\n let flag = true;\n let x = flag + 1 --- E0006 (https://starstream.nightstream.dev/errors/E0006) - x binary `+` operands must match; found `bool` and `t3` + x binary `+` operands must match; found `bool` and `i64` ,-[test.star:3:13] 2 | let flag = true; 3 | let x = flag + 1; : ^^|^ | - : | `-- has type `t3` + : | `-- has type `i64` : `-- has type `bool` 4 | } `---- diff --git a/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__while_loop_traces.snap b/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__while_loop_traces.snap index 332f63e3..42137b9f 100644 --- a/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__while_loop_traces.snap +++ b/starstream-compiler/src/typecheck/snapshots/starstream_compiler__typecheck__tests__while_loop_traces.snap @@ -11,12 +11,12 @@ T-Fn: test => () T-Bin-Lt: {counter: t3} ⊢ counter < 3 ⇒ bool T-Var: {counter: t3} ⊢ counter ⇒ t3 T-Int: {counter: t3} ⊢ 3 ⇒ t4 - Check-Compare: i64 vs i64 => ok (int) - Unify-Const: i64 ~ i64 => {} + Check-Compare: t3 vs t4 => ok (int) + Unify-Var: t3 ~ t4 => {t4/t3} Check-Bool: bool => ok T-Assign: {counter: t3} ⊢ counter = counter + 1; ⇒ t3 T-Bin-Add: {counter: t3} ⊢ counter + 1 ⇒ t5 T-Var: {counter: t3} ⊢ counter ⇒ t3 T-Int: {counter: t3} ⊢ 1 ⇒ t5 - Unify-Var: t3 ~ t5 => {t5/t3} + Unify-Var: t4 ~ t5 => {t5/t4} Unify-Var: t5 ~ t5 => {} diff --git a/starstream-to-wasm/tests/snapshots/inputs@while_simple.star.snap b/starstream-to-wasm/tests/snapshots/inputs@while_simple.star.snap index e4c689a2..cc2b349b 100644 --- a/starstream-to-wasm/tests/snapshots/inputs@while_simple.star.snap +++ b/starstream-to-wasm/tests/snapshots/inputs@while_simple.star.snap @@ -136,14 +136,14 @@ T-Fn: main => () T-Bin-Lt: {foo: t3} ⊢ foo < 10 ⇒ bool T-Var: {foo: t3} ⊢ foo ⇒ t3 T-Int: {foo: t3} ⊢ 10 ⇒ t4 - Check-Compare: i64 vs i64 => ok (int) - Unify-Const: i64 ~ i64 => {} + Check-Compare: t3 vs t4 => ok (int) + Unify-Var: t3 ~ t4 => {t4/t3} Check-Bool: bool => ok T-Assign: {foo: t3} ⊢ foo = foo + 1; ⇒ t3 T-Bin-Add: {foo: t3} ⊢ foo + 1 ⇒ t5 T-Var: {foo: t3} ⊢ foo ⇒ t3 T-Int: {foo: t3} ⊢ 1 ⇒ t5 - Unify-Var: t3 ~ t5 => {t5/t3} + Unify-Var: t4 ~ t5 => {t5/t4} Unify-Var: t5 ~ t5 => {} ==== Typed AST ==== From 15c08c897db9953316bed4c73e1c59bbc2ecf43c Mon Sep 17 00:00:00 2001 From: rvcas Date: Fri, 20 Feb 2026 11:49:52 -0500 Subject: [PATCH 3/3] chore: fmt Signed-off-by: rvcas --- starstream-compiler/src/typecheck/infer.rs | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/starstream-compiler/src/typecheck/infer.rs b/starstream-compiler/src/typecheck/infer.rs index dc669b3b..a365d675 100644 --- a/starstream-compiler/src/typecheck/infer.rs +++ b/starstream-compiler/src/typecheck/infer.rs @@ -2051,8 +2051,7 @@ impl Inferencer { let both_int = self.is_int_like(&left_ty) && self.is_int_like(&right_ty); if !both_int { let left_display = self.apply_for_display(&typed_left.node.ty); - let right_display = - self.apply_for_display(&typed_right.node.ty); + let right_display = self.apply_for_display(&typed_right.node.ty); return Err(TypeError::new( TypeErrorKind::BinaryOperandMismatch { op: *op, @@ -2067,10 +2066,7 @@ impl Inferencer { )) .with_secondary( right_label_span, - format!( - "has type `{}`", - right_display.to_compact_string() - ), + format!("has type `{}`", right_display.to_compact_string()), )); } @@ -2119,8 +2115,7 @@ impl Inferencer { matches!(&left_ty, Type::Bool) && matches!(&right_ty, Type::Bool); if !both_bool { let left_display = self.apply_for_display(&typed_left.node.ty); - let right_display = - self.apply_for_display(&typed_right.node.ty); + let right_display = self.apply_for_display(&typed_right.node.ty); return Err(TypeError::new( TypeErrorKind::BinaryOperandMismatch { op: *op, @@ -2135,10 +2130,7 @@ impl Inferencer { )) .with_secondary( right_label_span, - format!( - "has type `{}`", - right_display.to_compact_string() - ), + format!("has type `{}`", right_display.to_compact_string()), )); }