diff --git a/src/snapshot_tool/main.zig b/src/snapshot_tool/main.zig index 143bd7e360..3ba6dea758 100644 --- a/src/snapshot_tool/main.zig +++ b/src/snapshot_tool/main.zig @@ -2421,6 +2421,102 @@ fn generateTypesSection(output: *DualOutput, can_ir: *ModuleEnv, maybe_expr_idx: try output.end_section(); } +/// Get the defaulted (monomorphized) type string for an expression. +/// This defaults: +/// - Flex vars with from_numeral constraint → Dec +/// - Tag unions matching [True, False] or [True, .._] or [False, .._] → Bool +/// - Recursively defaults element types in containers (List, Tuple) +fn getDefaultedTypeString(allocator: std.mem.Allocator, can_ir: *ModuleEnv, type_var: types.Var) ![]const u8 { + const resolved = can_ir.types.resolveVar(type_var); + + switch (resolved.desc.content) { + .flex => |flex| { + // Check if this flex var has a from_numeral constraint + const constraints = can_ir.types.sliceStaticDispatchConstraints(flex.constraints); + for (constraints) |constraint| { + if (constraint.origin == .from_numeral) { + return allocator.dupe(u8, "Dec"); + } + } + // No numeral constraint - fall through to TypeWriter + }, + .structure => |flat_type| { + switch (flat_type) { + .tag_union => |tag_union| { + // Check if this matches Bool pattern: [True, False] or open variant + const tag_slice = can_ir.types.getTagsSlice(tag_union.tags); + var has_true = false; + var has_false = false; + var only_bool_tags = true; + + for (tag_slice.items(.name)) |tag_name| { + const name_str = can_ir.getIdent(tag_name); + if (std.mem.eql(u8, name_str, "True")) { + has_true = true; + } else if (std.mem.eql(u8, name_str, "False")) { + has_false = true; + } else { + only_bool_tags = false; + } + } + + // Check extension - if open with _others, still could be Bool + const is_open = switch (can_ir.types.resolveVar(tag_union.ext).desc.content) { + .flex => true, + else => false, + }; + + if (only_bool_tags and (has_true or has_false) and (is_open or (has_true and has_false))) { + return allocator.dupe(u8, "Bool"); + } + }, + .tuple => |tuple| { + // Recursively default tuple element types + var result = std.array_list.Managed(u8).init(allocator); + errdefer result.deinit(); + + try result.append('('); + const elem_vars = can_ir.types.sliceVars(tuple.elems); + for (elem_vars, 0..) |elem_var, i| { + if (i > 0) try result.appendSlice(", "); + const elem_type = try getDefaultedTypeString(allocator, can_ir, elem_var); + try result.appendSlice(elem_type); + } + try result.append(')'); + return result.toOwnedSlice(); + }, + .nominal_type => |nominal| { + // Check if this is List and default the element type + const type_name = can_ir.getIdent(nominal.ident.ident_idx); + if (std.mem.eql(u8, type_name, "List")) { + var args_iter = can_ir.types.iterNominalArgs(nominal); + if (args_iter.next()) |elem_var| { + const elem_type = try getDefaultedTypeString(allocator, can_ir, elem_var); + var result = std.array_list.Managed(u8).init(allocator); + errdefer result.deinit(); + try result.appendSlice("List("); + try result.appendSlice(elem_type); + try result.append(')'); + return result.toOwnedSlice(); + } + } + // Fall through for other nominal types + }, + else => {}, + } + }, + else => {}, + } + + // Fall back to TypeWriter for other cases + var type_writer = try can_ir.initTypeWriter(); + defer type_writer.deinit(); + try type_writer.write(type_var); + + // Copy the result since type_writer will be deinitialized + return allocator.dupe(u8, type_writer.get()); +} + /// Generate MONO section for mono tests - emits monomorphized Roc code with type annotation fn generateMonoSection(output: *DualOutput, can_ir: *ModuleEnv, maybe_expr_idx: ?CIR.Expr.Idx) !void { const expr_idx = maybe_expr_idx orelse return; @@ -2430,12 +2526,10 @@ fn generateMonoSection(output: *DualOutput, can_ir: *ModuleEnv, maybe_expr_idx: defer emitter.deinit(); try emitter.emitExpr(expr_idx); - // Get the type of the expression + // Get the defaulted (monomorphized) type of the expression const expr_var = ModuleEnv.varFrom(expr_idx); - var type_writer = try can_ir.initTypeWriter(); - defer type_writer.deinit(); - try type_writer.write(expr_var); - const type_str = type_writer.get(); + const type_str = try getDefaultedTypeString(output.gpa, can_ir, expr_var); + defer output.gpa.free(type_str); try output.begin_section("MONO"); try output.begin_code_block("roc"); diff --git a/test/snapshots/mono_arithmetic.md b/test/snapshots/mono_arithmetic.md index ee3ebc24e6..36ff8d698c 100644 --- a/test/snapshots/mono_arithmetic.md +++ b/test/snapshots/mono_arithmetic.md @@ -9,7 +9,7 @@ type=mono ~~~ # MONO ~~~roc -(1 + 2) : a where [a.from_numeral : Numeral -> Try(a, [InvalidNumeral(Str)])] +(1 + 2) : Dec ~~~ # FORMATTED ~~~roc diff --git a/test/snapshots/mono_block_with_let.md b/test/snapshots/mono_block_with_let.md index 97178b49c9..62f705e402 100644 --- a/test/snapshots/mono_block_with_let.md +++ b/test/snapshots/mono_block_with_let.md @@ -15,7 +15,7 @@ type=mono { x = 42 x -} : a where [a.from_numeral : Numeral -> Try(a, [InvalidNumeral(Str)])] +} : Dec ~~~ # FORMATTED ~~~roc diff --git a/test/snapshots/mono_boolean_true.md b/test/snapshots/mono_boolean_true.md index 5ae0ceab81..065b17c72a 100644 --- a/test/snapshots/mono_boolean_true.md +++ b/test/snapshots/mono_boolean_true.md @@ -9,7 +9,7 @@ True ~~~ # MONO ~~~roc -True : [True, .._others] +True : Bool ~~~ # FORMATTED ~~~roc diff --git a/test/snapshots/mono_identity_applied.md b/test/snapshots/mono_identity_applied.md index 3e1c483ea9..f9c4b739e1 100644 --- a/test/snapshots/mono_identity_applied.md +++ b/test/snapshots/mono_identity_applied.md @@ -15,7 +15,7 @@ type=mono { identity = |x| x identity(42) -} : a where [a.from_numeral : Numeral -> Try(a, [InvalidNumeral(Str)])] +} : Dec ~~~ # FORMATTED ~~~roc diff --git a/test/snapshots/mono_identity_lambda.md b/test/snapshots/mono_identity_lambda.md deleted file mode 100644 index bcf98326c2..0000000000 --- a/test/snapshots/mono_identity_lambda.md +++ /dev/null @@ -1,45 +0,0 @@ -# META -~~~ini -description=Mono test: identity lambda -type=mono -~~~ -# SOURCE -~~~roc -|x| x -~~~ -# MONO -~~~roc -|x| x -~~~ -# FORMATTED -~~~roc -NO CHANGE -~~~ -# EXPECTED -NIL -# PROBLEMS -NIL -# TOKENS -~~~zig -OpBar,LowerIdent,OpBar,LowerIdent, -EndOfFile, -~~~ -# PARSE -~~~clojure -(e-lambda - (args - (p-ident (raw "x"))) - (e-ident (raw "x"))) -~~~ -# CANONICALIZE -~~~clojure -(e-lambda - (args - (p-assign (ident "x"))) - (e-lookup-local - (p-assign (ident "x")))) -~~~ -# TYPES -~~~clojure -(expr (type "a -> a")) -~~~ diff --git a/test/snapshots/mono_if_expression.md b/test/snapshots/mono_if_expression.md index 3c0f50e40f..75f5e32dde 100644 --- a/test/snapshots/mono_if_expression.md +++ b/test/snapshots/mono_if_expression.md @@ -9,7 +9,7 @@ if True 1 else 2 ~~~ # MONO ~~~roc -if True 1 else 2 : a where [a.from_numeral : Numeral -> Try(a, [InvalidNumeral(Str)])] +if True 1 else 2 : Dec ~~~ # FORMATTED ~~~roc diff --git a/test/snapshots/mono_integer_literal.md b/test/snapshots/mono_integer_literal.md index ae06b3d56e..487f86f35c 100644 --- a/test/snapshots/mono_integer_literal.md +++ b/test/snapshots/mono_integer_literal.md @@ -9,7 +9,7 @@ type=mono ~~~ # MONO ~~~roc -42 : a where [a.from_numeral : Numeral -> Try(a, [InvalidNumeral(Str)])] +42 : Dec ~~~ # FORMATTED ~~~roc diff --git a/test/snapshots/mono_list_with_elements.md b/test/snapshots/mono_list_with_elements.md index 1eb88e36cc..a95567980d 100644 --- a/test/snapshots/mono_list_with_elements.md +++ b/test/snapshots/mono_list_with_elements.md @@ -9,7 +9,7 @@ type=mono ~~~ # MONO ~~~roc -[1, 2, 3] : List(a) where [a.from_numeral : Numeral -> Try(a, [InvalidNumeral(Str)])] +[1, 2, 3] : List(Dec) ~~~ # FORMATTED ~~~roc diff --git a/test/snapshots/mono_tuple.md b/test/snapshots/mono_tuple.md index fa2d905623..052c1eb72f 100644 --- a/test/snapshots/mono_tuple.md +++ b/test/snapshots/mono_tuple.md @@ -9,7 +9,7 @@ type=mono ~~~ # MONO ~~~roc -(1, 2) : (a, b) where [a.from_numeral : Numeral -> Try(a, [InvalidNumeral(Str)]), b.from_numeral : Numeral -> Try(b, [InvalidNumeral(Str)])] +(1, 2) : (Dec, Dec) ~~~ # FORMATTED ~~~roc