From 402922cd199c85d404ca9a508b16f7a653f5423c Mon Sep 17 00:00:00 2001 From: Jared Ramirez Date: Thu, 6 Nov 2025 11:35:54 -0500 Subject: [PATCH] Record update --- src/check/Check.zig | 29 ++- src/check/snapshot.zig | 125 ++++++++--- src/check/test/TestEnv.zig | 10 +- .../let_polymorphism_integration_test.zig | 2 +- src/check/test/type_checking_integration.zig | 33 ++- src/check/unify.zig | 48 ++++- src/types/TypeWriter.zig | 141 ++++++++---- src/types/mod.zig | 2 + src/types/types.zig | 26 +++ test/snapshots/comprehensive/Container.md | 4 +- test/snapshots/eval/tuple_bool.md | 2 +- test/snapshots/expr/boolean_tuple.md | 2 +- test/snapshots/expr/record_updater_basic.md | 4 +- test/snapshots/expr/record_updater_chained.md | 12 +- test/snapshots/expr/tag_simple.md | 2 +- test/snapshots/expr/tag_vs_function_calls.md | 2 +- test/snapshots/expr/tag_with_payload.md | 2 +- test/snapshots/expr/tuple.md | 2 +- test/snapshots/expr/tuple_simple_unbound.md | 2 +- test/snapshots/fuzz_crash/fuzz_crash_019.md | 8 +- test/snapshots/fuzz_crash/fuzz_crash_020.md | 12 +- test/snapshots/fuzz_crash/fuzz_crash_023.md | 4 +- test/snapshots/fuzz_crash/fuzz_crash_027.md | 4 +- test/snapshots/fuzz_crash/fuzz_crash_028.md | Bin 56024 -> 56010 bytes test/snapshots/fuzz_crash/fuzz_crash_047.md | 4 +- test/snapshots/fuzz_crash/fuzz_crash_049.md | Bin 119669 -> 119597 bytes ...if_then_else_simple_comments_formatting.md | 2 +- .../if_then_else/if_then_else_simple_tag.md | 2 +- test/snapshots/let_polymorphism_complex.md | 24 +-- test/snapshots/let_polymorphism_records.md | 200 +++++++++--------- .../empty_list_before_rest_pattern.md | 2 +- test/snapshots/match_expr/wrong_arrow.md | 2 +- test/snapshots/primitive/expr_tag.md | 4 +- test/snapshots/records/record_nested.md | 2 +- .../records/record_with_complex_types.md | 2 +- test/snapshots/static_dispatch/Container.md | 4 +- test/snapshots/syntax_grab_bag.md | 4 +- .../type_var_name_avoids_collision.md | 8 +- 38 files changed, 468 insertions(+), 270 deletions(-) diff --git a/src/check/Check.zig b/src/check/Check.zig index daf6f08ab7..0af2684ddb 100644 --- a/src/check/Check.zig +++ b/src/check/Check.zig @@ -2357,15 +2357,28 @@ fn checkExpr(self: *Self, expr_idx: CIR.Expr.Idx, env: *Env, expected: Expected) std.mem.sort(types_mod.RecordField, record_fields_scratch, self.cir.getIdentStore(), comptime types_mod.RecordField.sortByNameAsc); const record_fields_range = try self.types.appendRecordFields(record_fields_scratch); - try self.unifyWith(expr_var, .{ .structure = .{ - .record_unbound = record_fields_range, - } }, env); + // Check if this is a record update + if (e.ext) |record_being_updated_expr| { + // Create an unbound record with the provided fields + const ext_var = try self.fresh(env, expr_region); + try self.unifyWith(expr_var, .{ .structure = .{ + .record = .{ + .fields = record_fields_range, + .ext = ext_var, + }, + } }, env); - // Process the ext if it exists - if (e.ext) |ext_expr| { - does_fx = try self.checkExpr(ext_expr, env, .no_expectation) or does_fx; - const ext_var = ModuleEnv.varFrom(ext_expr); - _ = try self.unify(expr_var, ext_var, env); + does_fx = try self.checkExpr(record_being_updated_expr, env, .no_expectation) or does_fx; + const record_being_updated_var = ModuleEnv.varFrom(record_being_updated_expr); + + _ = try self.unify(record_being_updated_var, expr_var, env); + } else { + // Create an unbound record with the provided fields + const ext_var = try self.freshFromContent(.{ .structure = .empty_record }, env, expr_region); + try self.unifyWith(expr_var, .{ .structure = .{ .record = .{ + .fields = record_fields_range, + .ext = ext_var, + } } }, env); } }, .e_empty_record => { diff --git a/src/check/snapshot.zig b/src/check/snapshot.zig index 4dfd73043d..2996ddda50 100644 --- a/src/check/snapshot.zig +++ b/src/check/snapshot.zig @@ -592,6 +592,7 @@ pub const SnapshotWriter = struct { flex_var_names_map: std.AutoHashMap(Var, FlexVarNameRange), flex_var_names: std.array_list.Managed(u8), static_dispatch_constraints: std.array_list.Managed(SnapshotStaticDispatchConstraint), + scratch_record_fields: std.array_list.Managed(SnapshotRecordField), count_seen_idxs: std.array_list.Managed(SnapshotContentIdx), const FlexVarNameRange = struct { start: usize, end: usize }; @@ -610,6 +611,7 @@ pub const SnapshotWriter = struct { .flex_var_names_map = std.AutoHashMap(Var, FlexVarNameRange).init(gpa), .flex_var_names = std.array_list.Managed(u8).init(gpa), .static_dispatch_constraints = std.array_list.Managed(SnapshotStaticDispatchConstraint).init(gpa), + .scratch_record_fields = std.array_list.Managed(SnapshotRecordField).init(gpa), .count_seen_idxs = std.array_list.Managed(SnapshotContentIdx).init(gpa), }; } @@ -634,6 +636,7 @@ pub const SnapshotWriter = struct { .flex_var_names_map = std.AutoHashMap(Var, FlexVarNameRange).init(gpa), .flex_var_names = std.array_list.Managed(u8).init(gpa), .static_dispatch_constraints = std.array_list.Managed(SnapshotStaticDispatchConstraint).init(gpa), + .scratch_record_fields = std.array_list.Managed(SnapshotRecordField).init(gpa), .count_seen_idxs = try std.array_list.Managed(SnapshotContentIdx).init(gpa), }; } @@ -643,6 +646,7 @@ pub const SnapshotWriter = struct { self.flex_var_names_map.deinit(); self.flex_var_names.deinit(); self.static_dispatch_constraints.deinit(); + self.scratch_record_fields.deinit(); self.count_seen_idxs.deinit(); } @@ -653,6 +657,7 @@ pub const SnapshotWriter = struct { self.flex_var_names_map.clearRetainingCapacity(); self.flex_var_names.clearRetainingCapacity(); self.static_dispatch_constraints.clearRetainingCapacity(); + self.scratch_record_fields.clearRetainingCapacity(); self.count_seen_idxs.clearRetainingCapacity(); } @@ -1013,43 +1018,111 @@ pub const SnapshotWriter = struct { /// Write a record type fn writeRecord(self: *Self, record: SnapshotRecord, root_idx: SnapshotContentIdx) Allocator.Error!void { + const ext = try self.gatherRecordFields(record.fields, record.ext); + const gathered_fields = self.scratch_record_fields.items; + const num_fields = gathered_fields.len; + + // TODO: Sort gathered fields alphabetically + _ = try self.buf.writer().write("{ "); - const fields_slice = self.snapshots.record_fields.sliceRange(record.fields); + switch (ext) { + .flex => |flex| { + if (flex.payload.name) |ident_idx| { + _ = try self.buf.writer().write(".."); + _ = try self.buf.writer().write(self.idents.getText(ident_idx)); + if (num_fields > 0) _ = try self.buf.writer().write(", "); + } else if (flex.payload.constraints.len() > 0 or try self.countOccurrences(flex.payload.var_, root_idx) > 1) { + _ = try self.buf.writer().write(".."); + try self.writeFlexVarName(flex.payload.var_, flex.idx, .RecordExtension, root_idx); + if (num_fields > 0) _ = try self.buf.writer().write(", "); + } - if (fields_slice.len > 0) { - // Write first field - _ = try self.buf.writer().write(self.idents.getText(fields_slice.items(.name)[0])); - _ = try self.buf.writer().write(": "); - try self.writeWithContext(fields_slice.items(.content)[0], .RecordFieldContent, root_idx); + // Since don't recurse above, we must capture the static dispatch + // constraints directly + for (self.snapshots.sliceStaticDispatchConstraints(flex.payload.constraints)) |constraint| { + try self.appendStaticDispatchConstraint(constraint); + } + }, + .rigid => |rigid| { + _ = try self.buf.writer().write(".."); + _ = try self.buf.writer().write(self.idents.getText(rigid.name)); + if (num_fields > 0) _ = try self.buf.writer().write(", "); - // Write remaining fields - for (fields_slice.items(.name)[1..], fields_slice.items(.content)[1..]) |name, content| { - _ = try self.buf.writer().write(", "); - _ = try self.buf.writer().write(self.idents.getText(name)); - _ = try self.buf.writer().write(": "); - try self.writeWithContext(content, .RecordFieldContent, root_idx); - } + // Since don't recurse above, we must capture the static dispatch + // constraints directly + for (self.snapshots.sliceStaticDispatchConstraints(rigid.constraints)) |constraint| { + try self.appendStaticDispatchConstraint(constraint); + } + }, + .unbound, .invalid => {}, } - // Show extension variable if it's not empty - switch (self.snapshots.contents.get(record.ext).*) { - .structure => |flat_type| switch (flat_type) { - .empty_record => {}, // Don't show empty extension - else => { - if (fields_slice.len > 0) _ = try self.buf.writer().write(", "); - try self.writeWithContext(record.ext, .RecordExtension, root_idx); - }, - }, - else => { - if (fields_slice.len > 0) _ = try self.buf.writer().write(", "); - try self.writeWithContext(record.ext, .RecordExtension, root_idx); - }, + for (gathered_fields, 0..) |field, i| { + _ = try self.buf.writer().write(self.idents.getText(field.name)); + _ = try self.buf.writer().write(": "); + try self.writeWithContext(field.content, .RecordFieldContent, root_idx); + + if (i != gathered_fields.len - 1) _ = try self.buf.writer().write(", "); } _ = try self.buf.writer().write(" }"); } + /// Recursively unwrap all record fields + fn gatherRecordFields(self: *Self, fields: SnapshotRecordFieldSafeList.Range, ext_var: SnapshotContentIdx) std.mem.Allocator.Error!union(enum) { + flex: struct { idx: SnapshotContentIdx, payload: SnapshotFlex }, + rigid: SnapshotRigid, + unbound, + invalid, + } { + self.scratch_record_fields.clearRetainingCapacity(); + + const slice = self.snapshots.sliceRecordFields(fields); + try self.scratch_record_fields.ensureUnusedCapacity(fields.len()); + for (slice.items(.name), slice.items(.content)) |name, content| { + self.scratch_record_fields.appendAssumeCapacity(.{ .name = name, .content = content }); + } + + var ext = ext_var; + while (true) { + const resolved = self.snapshots.contents.get(ext); + switch (resolved.*) { + .flex => |flex| { + return .{ .flex = .{ .idx = ext, .payload = flex } }; + }, + .rigid => |rigid| { + return .{ .rigid = rigid }; + }, + .alias => |alias| { + ext = alias.backing; + }, + .structure => |flat_type| { + switch (flat_type) { + .record => |ext_record| { + const ext_slice = self.snapshots.sliceRecordFields(ext_record.fields); + try self.scratch_record_fields.ensureUnusedCapacity(ext_record.fields.len()); + for (ext_slice.items(.name), ext_slice.items(.content)) |name, content| { + self.scratch_record_fields.appendAssumeCapacity(.{ .name = name, .content = content }); + } + ext = ext_record.ext; + }, + .record_unbound => |ext_fields| { + const ext_slice = self.snapshots.sliceRecordFields(ext_fields); + try self.scratch_record_fields.ensureUnusedCapacity(ext_fields.len()); + for (ext_slice.items(.name), ext_slice.items(.content)) |name, content| { + self.scratch_record_fields.appendAssumeCapacity(.{ .name = name, .content = content }); + } + return .unbound; + }, + else => return .invalid, + } + }, + else => return .invalid, + } + } + } + /// Write record fields without extension fn writeRecordFields(self: *Self, fields: SnapshotRecordFieldSafeList.Range, root_idx: SnapshotContentIdx) Allocator.Error!void { if (fields.isEmpty()) { diff --git a/src/check/test/TestEnv.zig b/src/check/test/TestEnv.zig index 318e4b1006..dc710436ef 100644 --- a/src/check/test/TestEnv.zig +++ b/src/check/test/TestEnv.zig @@ -426,10 +426,8 @@ pub fn assertDefType(self: *TestEnv, target_def_name: []const u8, expected: []co .assign => |assign| { const def_name = idents.getText(assign.ident); if (std.mem.eql(u8, target_def_name, def_name)) { - try testing.expectEqualStrings( - expected, - try self.type_writer.writeGet(ModuleEnv.varFrom(def_idx)), - ); + try self.type_writer.write(ModuleEnv.varFrom(def_idx)); + try testing.expectEqualStrings(expected, self.type_writer.get()); return; } }, @@ -453,8 +451,10 @@ pub fn assertLastDefType(self: *TestEnv, expected: []const u8) !void { try testing.expect(self.module_env.all_defs.span.len > 0); const defs_slice = self.module_env.store.sliceDefs(self.module_env.all_defs); const last_def_idx = defs_slice[defs_slice.len - 1]; + const last_def_var = ModuleEnv.varFrom(last_def_idx); - try testing.expectEqualStrings(expected, try self.type_writer.writeGet(ModuleEnv.varFrom(last_def_idx))); + try self.type_writer.write(last_def_var); + try testing.expectEqualStrings(expected, self.type_writer.get()); } /// Get the inferred type descriptor of the last declaration diff --git a/src/check/test/let_polymorphism_integration_test.zig b/src/check/test/let_polymorphism_integration_test.zig index eadd812d61..be9a1006a4 100644 --- a/src/check/test/let_polymorphism_integration_test.zig +++ b/src/check/test/let_polymorphism_integration_test.zig @@ -113,7 +113,7 @@ test "polymorphic record constructor" { \\{ \\ make_pair = |x, y| { first: x, second: y } \\ pair1 = make_pair(1, "a") - \\ pair2 = make_pair("hello", 42) + \\ pair2 = make_pair(1512351, 42) \\ pair3 = make_pair(True, False) \\ { pair1, pair2, pair3 } \\} diff --git a/src/check/test/type_checking_integration.zig b/src/check/test/type_checking_integration.zig index 7d12559290..8f7ff29176 100644 --- a/src/check/test/type_checking_integration.zig +++ b/src/check/test/type_checking_integration.zig @@ -159,7 +159,7 @@ test "check type - tag" { const source = \\MyTag ; - try checkTypesExpr(source, .pass, "[MyTag]_others"); + try checkTypesExpr(source, .pass, "[MyTag]"); } test "check type - tag - args" { @@ -804,27 +804,44 @@ test "check type - record - access - not a record" { // record update -test "check type - record - update" { +test "check type - record - update 1" { const source = \\update_data = |container, new_value| { ..container, data: new_value } ; - try checkTypesModule(source, .{ .pass = .{ .def = "update_data" } }, "a, b -> { data: b, ..a }"); + try checkTypesModule( + source, + .{ .pass = .{ .def = "update_data" } }, + "{ ..a, data: b }, b -> { ..a, data: b }", + ); } test "check type - record - update 2" { const source = - \\update_data = |container, new_value| { ..container, data: new_value } + \\set_data = |container, new_value| { ..container, data: new_value } \\ - \\updated1 = update_data({ data: 10 }, 100) - \\updated2 = update_data({ data: 10, other: "hello" }, 100) - \\updated3 = update_data({ data: "hello", other: 20 }, "world") + \\updated1 = set_data({ data: 10 }, 100) # Updates field + \\updated2 = set_data({ data: 10, other: "hello" }, 100) # Updates with extra fields + \\updated3 = set_data({ data: "hello" }, "world") # Polymorphic \\ \\final = (updated1, updated2, updated3) ; try checkTypesModule( source, .{ .pass = .{ .def = "final" } }, - "({ data: Num(_size) }, { data: Num(_size2), other: Str }, { data: Str, other: Num(_size3) })", + "({ data: Num(_size) }, { data: Num(_size2), other: Str }, { data: Str })", + ); +} + +test "check type - record - update fail" { + const source = + \\set_data = |container, new_value| { ..container, data: new_value } + \\ + \\updated = set_data({ data: "hello" }, 10) + ; + try checkTypesModule( + source, + .fail, + "TYPE MISMATCH", ); } diff --git a/src/check/unify.zig b/src/check/unify.zig index 6782aadfdd..29e9149a97 100644 --- a/src/check/unify.zig +++ b/src/check/unify.zig @@ -2540,6 +2540,9 @@ const Unifier = struct { record_fields, ) catch return Error.AllocatorError; + // TODO: Below if we run into a record with the same field, then we + // prefer the leftmost field. But we should probably unify the dups + // then recursiv var ext = record_ext; while (true) { @@ -2561,20 +2564,43 @@ const Unifier = struct { .structure => |flat_type| { switch (flat_type) { .record => |ext_record| { - const next_range = self.scratch.copyGatherFieldsFromMultiList( - &self.types_store.record_fields, - ext_record.fields, - ) catch return Error.AllocatorError; - range.count += next_range.count; + const next_fields = self.types_store.record_fields.sliceRange(ext_record.fields); + const already_gathered = self.scratch.gathered_fields.sliceRange(range); + + for (next_fields.items(.name), next_fields.items(.var_)) |name, var_| { + // Check if field name already exists (O(n) but fields are small) + const already_exists = for (already_gathered) |existing| { + if (existing.name == name) break true; + } else false; + + // Only append if NOT already present (Map.union left-bias) + if (!already_exists) { + _ = self.scratch.gathered_fields.append( + self.scratch.gpa, + RecordField{ .name = name, .var_ = var_ }, + ) catch return Error.AllocatorError; + range.count += 1; + } + } ext = .{ .ext = ext_record.ext }; }, .record_unbound => |fields| { - const next_range = self.scratch.copyGatherFieldsFromMultiList( - &self.types_store.record_fields, - fields, - ) catch return Error.AllocatorError; - range.count += next_range.count; - // record_unbound has no extension, so we're done + const next_fields = self.types_store.record_fields.sliceRange(fields); + const already_gathered = self.scratch.gathered_fields.sliceRange(range); + + for (next_fields.items(.name), next_fields.items(.var_)) |name, var_| { + const already_exists = for (already_gathered) |existing| { + if (existing.name == name) break true; + } else false; + + if (!already_exists) { + _ = self.scratch.gathered_fields.append( + self.scratch.gpa, + RecordField{ .name = name, .var_ = var_ }, + ) catch return Error.AllocatorError; + range.count += 1; + } + } return .{ .ext = ext, .range = range }; }, .empty_record => { diff --git a/src/types/TypeWriter.zig b/src/types/TypeWriter.zig index bdcc77449b..7fbee24111 100644 --- a/src/types/TypeWriter.zig +++ b/src/types/TypeWriter.zig @@ -62,6 +62,7 @@ name_counters: std.EnumMap(TypeContext, u32), flex_var_names_map: std.AutoHashMap(Var, FlexVarNameRange), flex_var_names: std.array_list.Managed(u8), static_dispatch_constraints: std.array_list.Managed(types_mod.StaticDispatchConstraint), +scratch_record_fields: std.array_list.Managed(types_mod.RecordField), /// Mapping from fully-qualified type identifiers to their display names based on top-level imports. /// This allows error messages to show "Str" instead of "Builtin.Str" for auto-imported types, /// "Bar" instead of "Foo.Bar" for nested imports, and aliases like "Baz" instead of "Foo". @@ -88,6 +89,7 @@ pub fn initFromParts(gpa: std.mem.Allocator, types_store: *const TypesStore, ide .flex_var_names_map = std.AutoHashMap(Var, FlexVarNameRange).init(gpa), .flex_var_names = try std.array_list.Managed(u8).initCapacity(gpa, 32), .static_dispatch_constraints = try std.array_list.Managed(types_mod.StaticDispatchConstraint).initCapacity(gpa, 32), + .scratch_record_fields = try std.array_list.Managed(types_mod.RecordField).initCapacity(gpa, 32), .import_mapping = import_mapping, }; } @@ -100,6 +102,7 @@ pub fn deinit(self: *TypeWriter) void { self.flex_var_names_map.deinit(); self.flex_var_names.deinit(); self.static_dispatch_constraints.deinit(); + self.scratch_record_fields.deinit(); self.import_mapping.deinit(); } @@ -111,6 +114,7 @@ pub fn reset(self: *TypeWriter) void { self.flex_var_names_map.clearRetainingCapacity(); self.flex_var_names.clearRetainingCapacity(); self.static_dispatch_constraints.clearRetainingCapacity(); + self.scratch_record_fields.clearRetainingCapacity(); self.next_name_index = 0; self.name_counters = std.EnumMap(TypeContext, u32).init(.{}); @@ -507,6 +511,8 @@ fn writeNominalType(self: *TypeWriter, nominal_type: NominalType, root_var: Var) /// Write record fields without extension fn writeRecordFields(self: *TypeWriter, fields: RecordField.SafeMultiList.Range, root_var: Var) std.mem.Allocator.Error!void { + std.debug.print("\n----- writeRecordFields -----\n", .{}); + if (fields.isEmpty()) { _ = try self.buf.writer().write("{}"); return; @@ -555,68 +561,109 @@ fn writeFuncWithArrow(self: *TypeWriter, func: Func, arrow: []const u8, root_var /// Write a record type fn writeRecord(self: *TypeWriter, record: Record, root_var: Var) std.mem.Allocator.Error!void { - const fields = self.types.getRecordFieldsSlice(record.fields); + const ext = try self.gatherRecordFields(record.fields, record.ext); + const gathered_fields = self.scratch_record_fields.items; + const num_fields = gathered_fields.len; + + // TODO: Sort gathered fields alphabetically _ = try self.buf.writer().write("{ "); - for (fields.items(.name), fields.items(.var_), 0..) |field_name, field_var, i| { - if (i > 0) _ = try self.buf.writer().write(", "); - _ = try self.buf.writer().write(self.getIdent(field_name)); - _ = try self.buf.writer().write(": "); - try self.writeVarWithContext(field_var, .RecordFieldContent, root_var); - } - try self.writeRecordExtension(record.ext, fields.len, root_var); - - _ = try self.buf.writer().write(" }"); -} - -/// Helper to write record extension, handling nested records -fn writeRecordExtension(self: *TypeWriter, ext_var: Var, num_fields: usize, root_var: Var) std.mem.Allocator.Error!void { - const ext_resolved = self.types.resolveVar(ext_var); - switch (ext_resolved.desc.content) { - .structure => |flat_type| switch (flat_type) { - .empty_record => {}, // Don't show empty extension - .record => |ext_record| { - // Flatten nested record extensions - const ext_fields = self.types.getRecordFieldsSlice(ext_record.fields); - for (ext_fields.items(.name), ext_fields.items(.var_)) |field_name, field_var| { - _ = try self.buf.writer().write(", "); - _ = try self.buf.writer().write(self.getIdent(field_name)); - _ = try self.buf.writer().write(": "); - try self.writeVarWithContext(field_var, .RecordFieldContent, root_var); - } - // Recursively handle the extension's extension - try self.writeRecordExtension(ext_record.ext, num_fields + ext_fields.len, root_var); - }, - else => { - if (num_fields > 0) _ = try self.buf.writer().write(", "); - try self.writeVarWithContext(ext_var, .RecordExtension, root_var); - }, - }, + switch (ext) { .flex => |flex| { - if (num_fields > 0) _ = try self.buf.writer().write(", "); - _ = try self.buf.writer().write(".."); - try self.writeVarWithContext(ext_var, .RecordExtension, root_var); + if (flex.payload.name) |ident_idx| { + _ = try self.buf.writer().write(".."); + _ = try self.buf.writer().write(self.getIdent(ident_idx)); + if (num_fields > 0) _ = try self.buf.writer().write(", "); + } else if (flex.payload.constraints.len() > 0 or try self.countVarOccurrences(flex.var_, root_var) > 1) { + _ = try self.buf.writer().write(".."); + try self.writeFlexVarName(flex.var_, .RecordExtension, root_var); + if (num_fields > 0) _ = try self.buf.writer().write(", "); + } - for (self.types.sliceStaticDispatchConstraints(flex.constraints)) |constraint| { + // Since don't recurse above, we must capture the static dispatch + // constraints directly + for (self.types.sliceStaticDispatchConstraints(flex.payload.constraints)) |constraint| { try self.appendStaticDispatchConstraint(constraint); } }, .rigid => |rigid| { - // Show rigid vars with .. syntax - if (num_fields > 0) _ = try self.buf.writer().write(", "); _ = try self.buf.writer().write(".."); _ = try self.buf.writer().write(self.getIdent(rigid.name)); + if (num_fields > 0) _ = try self.buf.writer().write(", "); + // Since don't recurse above, we must capture the static dispatch + // constraints directly for (self.types.sliceStaticDispatchConstraints(rigid.constraints)) |constraint| { try self.appendStaticDispatchConstraint(constraint); } }, - else => { - // Show other types (aliases, errors, etc) - if (num_fields > 0) _ = try self.buf.writer().write(", "); - try self.writeVarWithContext(ext_var, .RecordExtension, root_var); - }, + .unbound, .invalid => {}, + } + + for (gathered_fields, 0..) |field, i| { + _ = try self.buf.writer().write(self.getIdent(field.name)); + _ = try self.buf.writer().write(": "); + try self.writeVarWithContext(field.var_, .RecordFieldContent, root_var); + + if (i != gathered_fields.len - 1) _ = try self.buf.writer().write(", "); + } + + _ = try self.buf.writer().write(" }"); +} + +/// Recursively unwrap all record fields +fn gatherRecordFields(self: *TypeWriter, fields: RecordField.SafeMultiList.Range, ext_var: Var) std.mem.Allocator.Error!union(enum) { + flex: struct { var_: Var, payload: types_mod.Flex }, + rigid: types_mod.Rigid, + unbound, + invalid, +} { + self.scratch_record_fields.clearRetainingCapacity(); + + const slice = self.types.getRecordFieldsSlice(fields); + try self.scratch_record_fields.ensureUnusedCapacity(fields.len()); + for (slice.items(.name), slice.items(.var_)) |name, var_| { + self.scratch_record_fields.appendAssumeCapacity(.{ .name = name, .var_ = var_ }); + } + + var ext = ext_var; + while (true) { + const resolved = self.types.resolveVar(ext); + + switch (resolved.desc.content) { + .flex => |flex| { + return .{ .flex = .{ .var_ = resolved.var_, .payload = flex } }; + }, + .rigid => |rigid| { + return .{ .rigid = rigid }; + }, + .alias => |alias| { + ext = self.types.getAliasBackingVar(alias); + }, + .structure => |flat_type| { + switch (flat_type) { + .record => |ext_record| { + const ext_slice = self.types.getRecordFieldsSlice(ext_record.fields); + try self.scratch_record_fields.ensureUnusedCapacity(ext_record.fields.len()); + for (ext_slice.items(.name), ext_slice.items(.var_)) |name, var_| { + self.scratch_record_fields.appendAssumeCapacity(.{ .name = name, .var_ = var_ }); + } + ext = ext_record.ext; + }, + .record_unbound => |ext_fields| { + const ext_slice = self.types.getRecordFieldsSlice(ext_fields); + try self.scratch_record_fields.ensureUnusedCapacity(ext_fields.len()); + for (ext_slice.items(.name), ext_slice.items(.var_)) |name, var_| { + self.scratch_record_fields.appendAssumeCapacity(.{ .name = name, .var_ = var_ }); + } + return .unbound; + }, + else => return .invalid, + } + }, + else => return .invalid, + } } } @@ -652,7 +699,7 @@ fn writeTagUnion(self: *TypeWriter, tag_union: TagUnion, root_var: Var) std.mem. .flex => |flex| { if (flex.name) |ident_idx| { _ = try self.buf.writer().write(self.getIdent(ident_idx)); - } else { + } else if (flex.constraints.len() > 0 or try self.countVarOccurrences(ext_resolved.var_, root_var) > 1) { try self.writeFlexVarName(tag_union.ext, .TagUnionExtension, root_var); } diff --git a/src/types/mod.zig b/src/types/mod.zig index ad0c76d65d..ad37c7c703 100644 --- a/src/types/mod.zig +++ b/src/types/mod.zig @@ -46,6 +46,8 @@ pub const ResolvedVarDescs = store.ResolvedVarDescs; pub const Store = store.Store; pub const DescStoreIdx = store.DescStoreIdx; +pub const Polarity = types.Polarity; + test { std.testing.refAllDecls(@import("test/test_rigid_instantiation.zig")); } diff --git a/src/types/types.zig b/src/types/types.zig index 6785017472..9caf401a68 100644 --- a/src/types/types.zig +++ b/src/types/types.zig @@ -223,6 +223,21 @@ pub const Content = union(enum) { else => return null, } } + + /// Unwrap a function (pure, eff, or unbound) and return it + pub fn unwrapFuncFull(content: Self) ?struct { func: Func, ext: enum { unbound, pure, effectful } } { + switch (content) { + .structure => |flat_type| { + switch (flat_type) { + .fn_pure => |func| return .{ .func = func, .ext = .pure }, + .fn_effectful => |func| return .{ .func = func, .ext = .effectful }, + .fn_unbound => |func| return .{ .func = func, .ext = .unbound }, + else => return null, + } + }, + else => return null, + } + } }; // flex // @@ -994,3 +1009,14 @@ pub const TwoStaticDispatchConstraints = struct { /// A safe multi list of tag union fields pub const SafeMultiList = MkSafeMultiList(@This()); }; + +/// Polarity of a type, or roughly, what side of an arrow it appears on. +pub const Polarity = enum { + /// A type that appears in negative/input position + neg, + /// A type that appears in positive/output position + pos, + + pub const lhs = Polarity.neg; + pub const rhs = Polarity.pos; +}; diff --git a/test/snapshots/comprehensive/Container.md b/test/snapshots/comprehensive/Container.md index 8eaed8ff0a..583eb7187c 100644 --- a/test/snapshots/comprehensive/Container.md +++ b/test/snapshots/comprehensive/Container.md @@ -1224,7 +1224,7 @@ main = { (patt (type "(a -> a), a -> a")) (patt (type "(a -> b) -> ((b -> c) -> (a -> c))")) (patt (type "a, c -> d where [a.map : a, (b -> c) -> d]")) - (patt (type "{ chained: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], final: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], id_results: (num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], Str, [True]_others), processed: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], transformed: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] }"))) + (patt (type "{ chained: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], final: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], id_results: (num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], Str, [True]), processed: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], transformed: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] }"))) (type_decls (nominal (type "Container(a)") (ty-header (name "Container") @@ -1238,5 +1238,5 @@ main = { (expr (type "(a -> a), a -> a")) (expr (type "(a -> b) -> ((b -> c) -> (a -> c))")) (expr (type "a, c -> d where [a.map : a, (b -> c) -> d]")) - (expr (type "{ chained: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], final: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], id_results: (num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], Str, [True]_others), processed: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], transformed: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] }")))) + (expr (type "{ chained: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], final: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], id_results: (num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], Str, [True]), processed: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], transformed: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] }")))) ~~~ diff --git a/test/snapshots/eval/tuple_bool.md b/test/snapshots/eval/tuple_bool.md index daf29d1a55..7e46d3d238 100644 --- a/test/snapshots/eval/tuple_bool.md +++ b/test/snapshots/eval/tuple_bool.md @@ -67,5 +67,5 @@ NO CHANGE ~~~ # TYPES ~~~clojure -(expr (type "([True]_others, [False]_others2, Bool, Bool, Bool, Bool, Bool, Bool)")) +(expr (type "([True], [False], Bool, Bool, Bool, Bool, Bool, Bool)")) ~~~ diff --git a/test/snapshots/expr/boolean_tuple.md b/test/snapshots/expr/boolean_tuple.md index 3512b4d3ba..c03133f0e3 100644 --- a/test/snapshots/expr/boolean_tuple.md +++ b/test/snapshots/expr/boolean_tuple.md @@ -35,5 +35,5 @@ NO CHANGE ~~~ # TYPES ~~~clojure -(expr (type "([True]_others, [False]_others2)")) +(expr (type "([True], [False])")) ~~~ diff --git a/test/snapshots/expr/record_updater_basic.md b/test/snapshots/expr/record_updater_basic.md index 6119e6ac20..980d8cabdd 100644 --- a/test/snapshots/expr/record_updater_basic.md +++ b/test/snapshots/expr/record_updater_basic.md @@ -70,8 +70,8 @@ NO CHANGE (inferred-types (defs (patt (type "{ age: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], name: Str }")) - (patt (type "{ age: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], name: Str, .._others }"))) + (patt (type "{ age: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], name: Str }"))) (expressions (expr (type "{ age: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], name: Str }")) - (expr (type "{ age: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], name: Str, .._others }")))) + (expr (type "{ age: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], name: Str }")))) ~~~ diff --git a/test/snapshots/expr/record_updater_chained.md b/test/snapshots/expr/record_updater_chained.md index d3790cefc5..39d28ca49c 100644 --- a/test/snapshots/expr/record_updater_chained.md +++ b/test/snapshots/expr/record_updater_chained.md @@ -120,12 +120,12 @@ NO CHANGE (inferred-types (defs (patt (type "{ age: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], city: Str, name: Str }")) - (patt (type "{ age: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], city: Str, name: Str, .._others }")) - (patt (type "{ city: Str, age: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], name: Str, .._others }")) - (patt (type "{ age: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], name: Str, city: Str, .._others }"))) + (patt (type "{ age: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], city: Str, name: Str }")) + (patt (type "{ city: Str, age: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], name: Str }")) + (patt (type "{ age: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], name: Str, city: Str }"))) (expressions (expr (type "{ age: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], city: Str, name: Str }")) - (expr (type "{ age: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], city: Str, name: Str, .._others }")) - (expr (type "{ city: Str, age: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], name: Str, .._others }")) - (expr (type "{ age: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], name: Str, city: Str, .._others }")))) + (expr (type "{ age: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], city: Str, name: Str }")) + (expr (type "{ city: Str, age: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], name: Str }")) + (expr (type "{ age: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], name: Str, city: Str }")))) ~~~ diff --git a/test/snapshots/expr/tag_simple.md b/test/snapshots/expr/tag_simple.md index 645d54bcf4..c582aabc33 100644 --- a/test/snapshots/expr/tag_simple.md +++ b/test/snapshots/expr/tag_simple.md @@ -30,5 +30,5 @@ NO CHANGE ~~~ # TYPES ~~~clojure -(expr (type "[MyTag]_others")) +(expr (type "[MyTag]")) ~~~ diff --git a/test/snapshots/expr/tag_vs_function_calls.md b/test/snapshots/expr/tag_vs_function_calls.md index b8d4e10852..45897c5453 100644 --- a/test/snapshots/expr/tag_vs_function_calls.md +++ b/test/snapshots/expr/tag_vs_function_calls.md @@ -164,5 +164,5 @@ EndOfFile, ~~~ # TYPES ~~~clojure -(expr (type "{ addOne: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] -> num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], errTag: [Err(Str)]_others, nested: [Some([Ok([Just(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])])]_others2)]_others3)]_others4, noneTag: [None]_others5, okTag: [Ok(Str)]_others6, result: Error, someTag: [Some(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])])]_others7, tagList: List([Some(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])])][None]_others8) }")) +(expr (type "{ addOne: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] -> num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], errTag: [Err(Str)], nested: [Some([Ok([Just(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])])])])], noneTag: [None], okTag: [Ok(Str)], result: Error, someTag: [Some(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])])], tagList: List([Some(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])])][None]) }")) ~~~ diff --git a/test/snapshots/expr/tag_with_payload.md b/test/snapshots/expr/tag_with_payload.md index c6a915276d..88d4232108 100644 --- a/test/snapshots/expr/tag_with_payload.md +++ b/test/snapshots/expr/tag_with_payload.md @@ -34,5 +34,5 @@ NO CHANGE ~~~ # TYPES ~~~clojure -(expr (type "[Some(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])])]_others")) +(expr (type "[Some(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])])]")) ~~~ diff --git a/test/snapshots/expr/tuple.md b/test/snapshots/expr/tuple.md index 8a284d6ea8..21ffbfb5d4 100644 --- a/test/snapshots/expr/tuple.md +++ b/test/snapshots/expr/tuple.md @@ -39,5 +39,5 @@ NO CHANGE ~~~ # TYPES ~~~clojure -(expr (type "(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], Str, [True]_others)")) +(expr (type "(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], Str, [True])")) ~~~ diff --git a/test/snapshots/expr/tuple_simple_unbound.md b/test/snapshots/expr/tuple_simple_unbound.md index 56f081c9be..4ca328a32a 100644 --- a/test/snapshots/expr/tuple_simple_unbound.md +++ b/test/snapshots/expr/tuple_simple_unbound.md @@ -39,5 +39,5 @@ NO CHANGE ~~~ # TYPES ~~~clojure -(expr (type "(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], Str, [True]_others)")) +(expr (type "(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], Str, [True])")) ~~~ diff --git a/test/snapshots/fuzz_crash/fuzz_crash_019.md b/test/snapshots/fuzz_crash/fuzz_crash_019.md index 8952a3b887..1ee98ff3fd 100644 --- a/test/snapshots/fuzz_crash/fuzz_crash_019.md +++ b/test/snapshots/fuzz_crash/fuzz_crash_019.md @@ -2001,9 +2001,9 @@ expect { (patt (type "Bool -> num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]")) (patt (type "Error")) (patt (type "Bool -> Error")) - (patt (type "[Blue]_others, [Tb]_others2 -> Error")) + (patt (type "[Blue], [Tb] -> Error")) (patt (type "Error")) - (patt (type "_arg -> [Stdo!(Error)]_others")) + (patt (type "_arg -> [Stdo!(Error)]")) (patt (type "{ }")) (patt (type "{}")) (patt (type "Error"))) @@ -2038,9 +2038,9 @@ expect { (expr (type "Bool -> num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]")) (expr (type "Error")) (expr (type "Bool -> Error")) - (expr (type "[Blue]_others, [Tb]_others2 -> Error")) + (expr (type "[Blue], [Tb] -> Error")) (expr (type "Error")) - (expr (type "_arg -> [Stdo!(Error)]_others")) + (expr (type "_arg -> [Stdo!(Error)]")) (expr (type "{ }")) (expr (type "{}")) (expr (type "Error")))) diff --git a/test/snapshots/fuzz_crash/fuzz_crash_020.md b/test/snapshots/fuzz_crash/fuzz_crash_020.md index 208b2d9f7e..eac9da3452 100644 --- a/test/snapshots/fuzz_crash/fuzz_crash_020.md +++ b/test/snapshots/fuzz_crash/fuzz_crash_020.md @@ -1979,10 +1979,10 @@ expect { (patt (type "()")) (patt (type "Bool -> num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]")) (patt (type "Error")) - (patt (type "[Rum]_others -> Error")) - (patt (type "[Blue]_others -> Error")) + (patt (type "[Rum] -> Error")) + (patt (type "[Blue] -> Error")) (patt (type "Error")) - (patt (type "_arg -> [Stdo!(Error)]_others")) + (patt (type "_arg -> [Stdo!(Error)]")) (patt (type "{ }")) (patt (type "{}")) (patt (type "Error"))) @@ -2016,10 +2016,10 @@ expect { (expr (type "()")) (expr (type "Bool -> num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]")) (expr (type "Error")) - (expr (type "[Rum]_others -> Error")) - (expr (type "[Blue]_others -> Error")) + (expr (type "[Rum] -> Error")) + (expr (type "[Blue] -> Error")) (expr (type "Error")) - (expr (type "_arg -> [Stdo!(Error)]_others")) + (expr (type "_arg -> [Stdo!(Error)]")) (expr (type "{ }")) (expr (type "{}")) (expr (type "Error")))) diff --git a/test/snapshots/fuzz_crash/fuzz_crash_023.md b/test/snapshots/fuzz_crash/fuzz_crash_023.md index 918a24a630..1709e431ba 100644 --- a/test/snapshots/fuzz_crash/fuzz_crash_023.md +++ b/test/snapshots/fuzz_crash/fuzz_crash_023.md @@ -2574,7 +2574,7 @@ expect { (defs (patt (type "Bool -> num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]")) (patt (type "Error -> Num(Int(Unsigned64))")) - (patt (type "[Red][Blue, Green]_others, _arg -> Error")) + (patt (type "[Red][Blue, Green], _arg -> Error")) (patt (type "Error")) (patt (type "List(Error) -> Error")) (patt (type "{}")) @@ -2621,7 +2621,7 @@ expect { (expressions (expr (type "Bool -> num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]")) (expr (type "Error -> Num(Int(Unsigned64))")) - (expr (type "[Red][Blue, Green]_others, _arg -> Error")) + (expr (type "[Red][Blue, Green], _arg -> Error")) (expr (type "Error")) (expr (type "List(Error) -> Error")) (expr (type "{}")) diff --git a/test/snapshots/fuzz_crash/fuzz_crash_027.md b/test/snapshots/fuzz_crash/fuzz_crash_027.md index 44ab315edc..987a01a06d 100644 --- a/test/snapshots/fuzz_crash/fuzz_crash_027.md +++ b/test/snapshots/fuzz_crash/fuzz_crash_027.md @@ -2252,7 +2252,7 @@ expect { (patt (type "(Error, Error)")) (patt (type "Bool -> num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]")) (patt (type "Error -> Num(Int(Unsigned64))")) - (patt (type "[Red, Blue]_others, _arg -> Error")) + (patt (type "[Red, Blue], _arg -> Error")) (patt (type "List(Error) -> Error")) (patt (type "{}")) (patt (type "Error"))) @@ -2289,7 +2289,7 @@ expect { (expr (type "(Error, Error)")) (expr (type "Bool -> num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]")) (expr (type "Error -> Num(Int(Unsigned64))")) - (expr (type "[Red, Blue]_others, _arg -> Error")) + (expr (type "[Red, Blue], _arg -> Error")) (expr (type "List(Error) -> Error")) (expr (type "{}")) (expr (type "Error")))) diff --git a/test/snapshots/fuzz_crash/fuzz_crash_028.md b/test/snapshots/fuzz_crash/fuzz_crash_028.md index 564b8d8700c2a514e5156a16897f165e749d4022..17510d1536995223a914c806de5fcf492eef7ab6 100644 GIT binary patch delta 40 tcmcbymHE_G<_$4dnRFB;ue++Sx%KKY77)Akmcr!LtFn_lZt(!w(Exuf5|;n~ delta 30 kcmX@LmHEb2<_$4dC%XlUZO*>BlLf?>Tysl!vfHf|0OWZNTL1t6 diff --git a/test/snapshots/fuzz_crash/fuzz_crash_047.md b/test/snapshots/fuzz_crash/fuzz_crash_047.md index 3babbcdb74..8ed50e4569 100644 --- a/test/snapshots/fuzz_crash/fuzz_crash_047.md +++ b/test/snapshots/fuzz_crash/fuzz_crash_047.md @@ -76,8 +76,8 @@ updated = { (inferred-types (defs (patt (type "{ age: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], name: Str }")) - (patt (type "{ age: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], name: Str, .._others }"))) + (patt (type "{ age: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], name: Str }"))) (expressions (expr (type "{ age: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], name: Str }")) - (expr (type "{ age: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], name: Str, .._others }")))) + (expr (type "{ age: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], name: Str }")))) ~~~ diff --git a/test/snapshots/fuzz_crash/fuzz_crash_049.md b/test/snapshots/fuzz_crash/fuzz_crash_049.md index 3707c1a4dd8cfbae8426fa82fafd0a41660f8ddc..536eb0b2ae615279c95ccad23addb1c5dd384ce0 100644 GIT binary patch delta 126 zcmeymjD77g_J%Etr;cyGc%1PdBa=eybRa```nr>hanmy|F-A}Se;UU9d66*#B(`Dt zjpK|w+l9_Bo@Scfco8Vgf1Ysyh>+Mm|2!ib*c706A>07=>5`Wjd8SXf2s9LA%66bB GJd6NQRyQjE delta 223 zcmZ3xjQ#5}_J%Etr;fA7=a*!p78OsIIm0MD{qJ$cj_LYm7&$;Jf$gm)7}*%%D!NZH z7Q;D;ml&g`-#*PKJbl4Q#yGg}sZ)#@98e>TrUMmin4WNok!Sn6(~PH?;Hoa3V_ZC4 y?lRDtH|H1yrUR{!p5A|+@iiw@r$X&?pc3Kf{TGmo2HSY*61p&ojoX2Wco+d)2VD~Y diff --git a/test/snapshots/if_then_else/if_then_else_simple_comments_formatting.md b/test/snapshots/if_then_else/if_then_else_simple_comments_formatting.md index d54c235ea0..b0f4df0b66 100644 --- a/test/snapshots/if_then_else/if_then_else_simple_comments_formatting.md +++ b/test/snapshots/if_then_else/if_then_else_simple_comments_formatting.md @@ -56,5 +56,5 @@ NO CHANGE ~~~ # TYPES ~~~clojure -(expr (type "[A, B]_others")) +(expr (type "[A, B]")) ~~~ diff --git a/test/snapshots/if_then_else/if_then_else_simple_tag.md b/test/snapshots/if_then_else/if_then_else_simple_tag.md index ed186b7d03..e12043b1ed 100644 --- a/test/snapshots/if_then_else/if_then_else_simple_tag.md +++ b/test/snapshots/if_then_else/if_then_else_simple_tag.md @@ -49,5 +49,5 @@ NO CHANGE ~~~ # TYPES ~~~clojure -(expr (type "[Ok(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]), Err(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])])]_others")) +(expr (type "[Ok(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]), Err(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])])]")) ~~~ diff --git a/test/snapshots/let_polymorphism_complex.md b/test/snapshots/let_polymorphism_complex.md index d1abf4e544..cfa89fb57b 100644 --- a/test/snapshots/let_polymorphism_complex.md +++ b/test/snapshots/let_polymorphism_complex.md @@ -1058,58 +1058,58 @@ main = |_| { (patt (type "num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]")) (patt (type "Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])])")) (patt (type "Str")) - (patt (type "[True]_others")) + (patt (type "[True]")) (patt (type "List(_elem)")) (patt (type "{}")) (patt (type "List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])])")) (patt (type "List(Str)")) - (patt (type "List([True, False]_others)")) + (patt (type "List([True, False])")) (patt (type "List(List(_elem))")) (patt (type "List(List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]))")) (patt (type "{ count: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], items: List(_elem) }")) (patt (type "{ count: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], items: List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]) }")) (patt (type "{ count: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], items: List(Str) }")) (patt (type "{ data: List(_elem), metadata: { description: Str, ratio: Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])]), version: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] } }")) - (patt (type "{ data: List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]), metadata: { description: Str, ratio: Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])]), version: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] }, name: Str }")) - (patt (type "{ data: List(Str), metadata: { description: Str, ratio: Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])]), version: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] }, name: Str }")) + (patt (type "{ data: List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]), metadata: { description: Str, ratio: Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])]), version: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] }, version: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] }")) + (patt (type "{ data: List(Str), metadata: { description: Str, ratio: Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])]), version: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] }, version: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] }")) (patt (type "a -> { value: a, wrapper: List(a) }")) (patt (type "{ value: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], wrapper: List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]) }")) (patt (type "{ value: Str, wrapper: List(Str) }")) (patt (type "{ value: Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])]), wrapper: List(Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])])) }")) - (patt (type "{ level1: { collection: List(_elem), level2: { items: List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]), level3: { data: List(_elem2), value: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] } } }, results: List({ data: List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]), tag: Str, .._others }) }")) + (patt (type "{ level1: { collection: List(_elem), level2: { items: List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]), level3: { data: List(_elem2), value: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] } } }, value: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] }")) (patt (type "num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]")) (patt (type "num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]")) (patt (type "List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])])")) (patt (type "{ base: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], derived: List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]) }")) - (patt (type "{ computations: { from_frac: Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])]), from_num: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], list_from_num: List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]) }, empty_lists: { in_list: List(List(_elem)), in_record: { data: List(_elem2) }, raw: List(_elem3) }, numbers: { float: Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])]), list: List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]), value: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] }, strings: { list: List(Str), value: Str } }")) + (patt (type "{ computations: { from_frac: Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])]), from_num: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], list_from_num: List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]) }, from_num: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], list_from_num: List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]), strings: { list: List(Str), value: Str } }")) (patt (type "_arg -> num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]"))) (expressions (expr (type "num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]")) (expr (type "Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])])")) (expr (type "Str")) - (expr (type "[True]_others")) + (expr (type "[True]")) (expr (type "List(_elem)")) (expr (type "{}")) (expr (type "List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])])")) (expr (type "List(Str)")) - (expr (type "List([True, False]_others)")) + (expr (type "List([True, False])")) (expr (type "List(List(_elem))")) (expr (type "List(List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]))")) (expr (type "{ count: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], items: List(_elem) }")) (expr (type "{ count: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], items: List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]) }")) (expr (type "{ count: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], items: List(Str) }")) (expr (type "{ data: List(_elem), metadata: { description: Str, ratio: Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])]), version: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] } }")) - (expr (type "{ data: List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]), metadata: { description: Str, ratio: Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])]), version: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] }, name: Str }")) - (expr (type "{ data: List(Str), metadata: { description: Str, ratio: Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])]), version: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] }, name: Str }")) + (expr (type "{ data: List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]), metadata: { description: Str, ratio: Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])]), version: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] }, version: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] }")) + (expr (type "{ data: List(Str), metadata: { description: Str, ratio: Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])]), version: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] }, version: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] }")) (expr (type "a -> { value: a, wrapper: List(a) }")) (expr (type "{ value: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], wrapper: List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]) }")) (expr (type "{ value: Str, wrapper: List(Str) }")) (expr (type "{ value: Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])]), wrapper: List(Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])])) }")) - (expr (type "{ level1: { collection: List(_elem), level2: { items: List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]), level3: { data: List(_elem2), value: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] } } }, results: List({ data: List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]), tag: Str, .._others }) }")) + (expr (type "{ level1: { collection: List(_elem), level2: { items: List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]), level3: { data: List(_elem2), value: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] } } }, value: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] }")) (expr (type "num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]")) (expr (type "num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]")) (expr (type "List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])])")) (expr (type "{ base: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], derived: List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]) }")) - (expr (type "{ computations: { from_frac: Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])]), from_num: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], list_from_num: List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]) }, empty_lists: { in_list: List(List(_elem)), in_record: { data: List(_elem2) }, raw: List(_elem3) }, numbers: { float: Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])]), list: List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]), value: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] }, strings: { list: List(Str), value: Str } }")) + (expr (type "{ computations: { from_frac: Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])]), from_num: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], list_from_num: List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]) }, from_num: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], list_from_num: List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]), strings: { list: List(Str), value: Str } }")) (expr (type "_arg -> num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]")))) ~~~ diff --git a/test/snapshots/let_polymorphism_records.md b/test/snapshots/let_polymorphism_records.md index db7f0fc9a7..57d8fe3040 100644 --- a/test/snapshots/let_polymorphism_records.md +++ b/test/snapshots/let_polymorphism_records.md @@ -22,14 +22,15 @@ int_container = make_container(num) str_container = make_container(str) list_container = make_container(my_empty_list) -# TODO # Polymorphic record update -update_data : a, b -> { data: b, ..a } +# TODO: 11/06/2025 currently record ext syntax is not parsable +# update_data : { ..a, data: b }, b -> { ..a, data: b } update_data = |container, new_value| { ..container, data: new_value } # Used with different record types -# updated_int = update_data(int_container, 100) -# updated_str = update_data(str_container, "world") +updated_int = update_data(int_container, 100) +updated_str = update_data(str_container, "world") +updated_mismatch = update_data(str_container, 99) # Function returning polymorphic record identity_record = |x| { value: x } @@ -40,62 +41,44 @@ str_record = identity_record("test") list_record = identity_record([1, 2, 3]) main = |_| { - # Access polymorphic fields - int_container.count + str_container.count + # Mismatch to snapshot infererd type of update_data + 1 + update_data + + # Access polymorphic fields + int_container.count + str_container.count } ~~~ # EXPECTED -UNEXPECTED TOKEN IN EXPRESSION - let_polymorphism_records.md:20:50:20:51 -UNRECOGNIZED SYNTAX - let_polymorphism_records.md:20:50:20:51 -UNUSED VARIABLE - let_polymorphism_records.md:20:52:20:67 -UNUSED VARIABLE - let_polymorphism_records.md:20:27:20:36 -UNUSED VALUE - let_polymorphism_records.md:20:40:20:49 +TYPE MISMATCH - let_polymorphism_records.md:26:47:26:49 +TYPE MISMATCH - let_polymorphism_records.md:38:6:38:17 # PROBLEMS -**UNEXPECTED TOKEN IN TYPE ANNOTATION** -The token **..** is not expected in a type annotation. -Type annotations should contain types like _Str_, _Num a_, or _List U64_. - -**let_polymorphism_records.md:20:34:20:36:** +**TYPE MISMATCH** +The second argument being passed to this function has the wrong type: +**let_polymorphism_records.md:26:47:26:49:** ```roc -update_data : a, b -> { data: b, ..a } +updated_mismatch = update_data(str_container, 99) ``` - ^^ + ^^ +This argument has the type: + _Num(_size)_ -**PARSE ERROR** -A parsing error occurred: `expected_arrow` -This is an unexpected parsing error. Please check your syntax. - -**let_polymorphism_records.md:20:31:20:32:** -```roc -update_data : a, b -> { data: b, ..a } -``` - ^ - - -**MALFORMED TYPE** -This type annotation is malformed or contains invalid syntax. - -**let_polymorphism_records.md:20:31:20:37:** -```roc -update_data : a, b -> { data: b, ..a } -``` - ^^^^^^ - +But `update_data` needs the second argument to be: + _Str_ **TYPE MISMATCH** This expression is used in an unexpected way: -**let_polymorphism_records.md:21:42:21:51:** +**let_polymorphism_records.md:38:6:38:17:** ```roc -update_data = |container, new_value| { ..container, data: new_value } + 1 + update_data ``` - ^^^^^^^^^ + ^^^^^^^^^^^ It has the type: - _a_ + _{ ..a, data: b }, b -> { ..a, data: b }_ But I expected it to be: - _{ data: b }_ + _Num(_size)_ # TOKENS ~~~zig @@ -109,13 +92,16 @@ LowerIdent,OpAssign,OpBar,LowerIdent,OpBar,OpenCurly,LowerIdent,OpColon,LowerIde LowerIdent,OpAssign,LowerIdent,NoSpaceOpenRound,LowerIdent,CloseRound, LowerIdent,OpAssign,LowerIdent,NoSpaceOpenRound,LowerIdent,CloseRound, LowerIdent,OpAssign,LowerIdent,NoSpaceOpenRound,LowerIdent,CloseRound, -LowerIdent,OpColon,LowerIdent,Comma,LowerIdent,OpArrow,OpenCurly,LowerIdent,OpColon,LowerIdent,Comma,DoubleDot,LowerIdent,CloseCurly, LowerIdent,OpAssign,OpBar,LowerIdent,Comma,LowerIdent,OpBar,OpenCurly,DoubleDot,LowerIdent,Comma,LowerIdent,OpColon,LowerIdent,CloseCurly, +LowerIdent,OpAssign,LowerIdent,NoSpaceOpenRound,LowerIdent,Comma,Int,CloseRound, +LowerIdent,OpAssign,LowerIdent,NoSpaceOpenRound,LowerIdent,Comma,StringStart,StringPart,StringEnd,CloseRound, +LowerIdent,OpAssign,LowerIdent,NoSpaceOpenRound,LowerIdent,Comma,Int,CloseRound, LowerIdent,OpAssign,OpBar,LowerIdent,OpBar,OpenCurly,LowerIdent,OpColon,LowerIdent,CloseCurly, LowerIdent,OpAssign,LowerIdent,NoSpaceOpenRound,Int,CloseRound, LowerIdent,OpAssign,LowerIdent,NoSpaceOpenRound,StringStart,StringPart,StringEnd,CloseRound, LowerIdent,OpAssign,LowerIdent,NoSpaceOpenRound,OpenSquare,Int,Comma,Int,Comma,Int,CloseSquare,CloseRound, LowerIdent,OpAssign,OpBar,Underscore,OpBar,OpenCurly, +Int,OpPlus,LowerIdent, LowerIdent,NoSpaceDotLowerIdent,OpPlus,LowerIdent,NoSpaceDotLowerIdent, CloseCurly, EndOfFile, @@ -178,13 +164,6 @@ EndOfFile, (e-apply (e-ident (raw "make_container")) (e-ident (raw "my_empty_list")))) - (s-type-anno (name "update_data") - (ty-fn - (ty-var (raw "a")) - (ty-var (raw "b")) - (ty-record - (anno-record-field (name "data") - (ty-malformed (tag "expected_arrow")))))) (s-decl (p-ident (raw "update_data")) (e-lambda @@ -196,6 +175,25 @@ EndOfFile, (e-ident (raw "container"))) (field (field "data") (e-ident (raw "new_value")))))) + (s-decl + (p-ident (raw "updated_int")) + (e-apply + (e-ident (raw "update_data")) + (e-ident (raw "int_container")) + (e-int (raw "100")))) + (s-decl + (p-ident (raw "updated_str")) + (e-apply + (e-ident (raw "update_data")) + (e-ident (raw "str_container")) + (e-string + (e-string-part (raw "world"))))) + (s-decl + (p-ident (raw "updated_mismatch")) + (e-apply + (e-ident (raw "update_data")) + (e-ident (raw "str_container")) + (e-int (raw "99")))) (s-decl (p-ident (raw "identity_record")) (e-lambda @@ -230,6 +228,9 @@ EndOfFile, (p-underscore)) (e-block (statements + (e-binop (op "+") + (e-int (raw "1")) + (e-ident (raw "update_data"))) (e-binop (op "+") (e-field-access (e-ident (raw "int_container")) @@ -240,44 +241,7 @@ EndOfFile, ~~~ # FORMATTED ~~~roc -app [main] { pf: platform "../basic-cli/platform.roc" } - -# Basic values for polymorphism testing -num = 42 -frac = 4.2 -str = "hello" -my_empty_list = [] -my_nonempty_list = [num, frac] - -# Record with polymorphic field -make_container = |value| { data: value, count: 1 } - -# Used with different types -int_container = make_container(num) -str_container = make_container(str) -list_container = make_container(my_empty_list) - -# TODO -# Polymorphic record update -update_data : a, b -> { data : } -update_data = |container, new_value| { ..container, data: new_value } - -# Used with different record types -# updated_int = update_data(int_container, 100) -# updated_str = update_data(str_container, "world") - -# Function returning polymorphic record -identity_record = |x| { value: x } - -# Used at different types -int_record = identity_record(42) -str_record = identity_record("test") -list_record = identity_record([1, 2, 3]) - -main = |_| { - # Access polymorphic fields - int_container.count + str_container.count -} +NO CHANGE ~~~ # CANONICALIZE ~~~clojure @@ -349,14 +313,32 @@ main = |_| { (fields (field (name "data") (e-lookup-local - (p-assign (ident "new_value"))))))) - (annotation - (ty-fn (effectful false) - (ty-rigid-var (name "a")) - (ty-rigid-var (name "b")) - (ty-record - (field (field "data") - (ty-malformed)))))) + (p-assign (ident "new_value")))))))) + (d-let + (p-assign (ident "updated_int")) + (e-call + (e-lookup-local + (p-assign (ident "update_data"))) + (e-lookup-local + (p-assign (ident "int_container"))) + (e-num (value "100")))) + (d-let + (p-assign (ident "updated_str")) + (e-call + (e-lookup-local + (p-assign (ident "update_data"))) + (e-lookup-local + (p-assign (ident "str_container"))) + (e-string + (e-literal (string "world"))))) + (d-let + (p-assign (ident "updated_mismatch")) + (e-call + (e-lookup-local + (p-assign (ident "update_data"))) + (e-lookup-local + (p-assign (ident "str_container"))) + (e-num (value "99")))) (d-let (p-assign (ident "identity_record")) (e-lambda @@ -394,12 +376,18 @@ main = |_| { (p-assign (ident "main")) (e-closure (captures + (capture (ident "update_data")) (capture (ident "int_container")) (capture (ident "str_container"))) (e-lambda (args (p-underscore)) (e-block + (s-expr + (e-binop (op "add") + (e-num (value "1")) + (e-lookup-local + (p-assign (ident "update_data"))))) (e-binop (op "add") (e-dot-access (field "count") (receiver @@ -419,12 +407,15 @@ main = |_| { (patt (type "Str")) (patt (type "List(_elem)")) (patt (type "List(Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])]))")) - (patt (type "c -> { count: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], data: c }")) + (patt (type "a -> { count: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], data: a }")) (patt (type "{ count: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], data: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] }")) (patt (type "{ count: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], data: Str }")) (patt (type "{ count: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], data: List(_elem) }")) - (patt (type "Error, b -> Error")) - (patt (type "c -> { value: c }")) + (patt (type "{ ..a, data: b }, b -> { ..a, data: b }")) + (patt (type "{ data: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], count: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] }")) + (patt (type "{ data: Str, count: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] }")) + (patt (type "Error")) + (patt (type "a -> { value: a }")) (patt (type "{ value: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] }")) (patt (type "{ value: Str }")) (patt (type "{ value: List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]) }")) @@ -435,12 +426,15 @@ main = |_| { (expr (type "Str")) (expr (type "List(_elem)")) (expr (type "List(Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])]))")) - (expr (type "c -> { count: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], data: c }")) + (expr (type "a -> { count: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], data: a }")) (expr (type "{ count: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], data: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] }")) (expr (type "{ count: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], data: Str }")) (expr (type "{ count: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], data: List(_elem) }")) - (expr (type "Error, b -> Error")) - (expr (type "c -> { value: c }")) + (expr (type "{ ..a, data: b }, b -> { ..a, data: b }")) + (expr (type "{ data: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], count: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] }")) + (expr (type "{ data: Str, count: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] }")) + (expr (type "Error")) + (expr (type "a -> { value: a }")) (expr (type "{ value: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] }")) (expr (type "{ value: Str }")) (expr (type "{ value: List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]) }")) diff --git a/test/snapshots/match_expr/empty_list_before_rest_pattern.md b/test/snapshots/match_expr/empty_list_before_rest_pattern.md index caab1f0d5d..3706e46493 100644 --- a/test/snapshots/match_expr/empty_list_before_rest_pattern.md +++ b/test/snapshots/match_expr/empty_list_before_rest_pattern.md @@ -88,5 +88,5 @@ match l { ~~~ # TYPES ~~~clojure -(expr (type "[Err([EmptyList]_others), Ok(_a)]_others2")) +(expr (type "[Err([EmptyList]), Ok(_a)]")) ~~~ diff --git a/test/snapshots/match_expr/wrong_arrow.md b/test/snapshots/match_expr/wrong_arrow.md index be9a56dd0e..8a76296ab4 100644 --- a/test/snapshots/match_expr/wrong_arrow.md +++ b/test/snapshots/match_expr/wrong_arrow.md @@ -110,5 +110,5 @@ match l { ~~~ # TYPES ~~~clojure -(expr (type "[Err([EmptyList]_others), Ok(_a)]_others2")) +(expr (type "[Err([EmptyList]), Ok(_a)]")) ~~~ diff --git a/test/snapshots/primitive/expr_tag.md b/test/snapshots/primitive/expr_tag.md index f8a9bcee3e..289daf7c17 100644 --- a/test/snapshots/primitive/expr_tag.md +++ b/test/snapshots/primitive/expr_tag.md @@ -40,7 +40,7 @@ NO CHANGE ~~~clojure (inferred-types (defs - (patt (type "[FortyTwo]_others"))) + (patt (type "[FortyTwo]"))) (expressions - (expr (type "[FortyTwo]_others")))) + (expr (type "[FortyTwo]")))) ~~~ diff --git a/test/snapshots/records/record_nested.md b/test/snapshots/records/record_nested.md index cd59d8faeb..aa3afa3993 100644 --- a/test/snapshots/records/record_nested.md +++ b/test/snapshots/records/record_nested.md @@ -137,5 +137,5 @@ EndOfFile, ~~~ # TYPES ~~~clojure -(expr (type "{ address: { city: Str, coordinates: { lat: Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])]), lng: Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])]) }, street: Str }, contact: { email: Str, phone: { home: Str, work: Str } }, person: { age: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], name: Str } }")) +(expr (type "{ address: { city: Str, coordinates: { lat: Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])]), lng: Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])]) }, street: Str }, lng: Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])]), street: Str }")) ~~~ diff --git a/test/snapshots/records/record_with_complex_types.md b/test/snapshots/records/record_with_complex_types.md index 33f2ffe6fc..f4431fdcff 100644 --- a/test/snapshots/records/record_with_complex_types.md +++ b/test/snapshots/records/record_with_complex_types.md @@ -234,5 +234,5 @@ EndOfFile, ~~~ # TYPES ~~~clojure -(expr (type "{ callback: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] -> num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], metadata: [Ok({ permissions: List([Read, Write, Admin]_others), tags: List(Str) })]_others2, name: Str, nested: { items: List([Some(Str)][None]_others3), result: [Success({ data: List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]), timestamp: Str })]_others4 }, preferences: { notifications: [Email(Str)]_others5, theme: [Dark]_others6 }, scores: List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]), status: [Active({ since: Str })]_others7 }")) +(expr (type "{ callback: num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])] -> num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])], metadata: [Ok({ permissions: List([Read, Write, Admin]), tags: List(Str) })], name: Str, nested: { items: List([Some(Str)][None]), result: [Success({ data: List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]), timestamp: Str })] }, preferences: { notifications: [Email(Str)], theme: [Dark] }, scores: List(num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]), status: [Active({ since: Str })] }")) ~~~ diff --git a/test/snapshots/static_dispatch/Container.md b/test/snapshots/static_dispatch/Container.md index f72fbe7c3d..11b6f4b63d 100644 --- a/test/snapshots/static_dispatch/Container.md +++ b/test/snapshots/static_dispatch/Container.md @@ -434,7 +434,7 @@ func = { (inferred-types (defs (patt (type "Container(a), (a -> b) -> Container(b)")) - (patt (type "[Value(c), Empty]_others, c -> c")) + (patt (type "[Value(c), Empty], c -> c")) (patt (type "Container(a), (a -> Container(b)) -> Container(b)")) (patt (type "num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]"))) (type_decls @@ -444,7 +444,7 @@ func = { (ty-rigid-var (name "a")))))) (expressions (expr (type "Container(a), (a -> b) -> Container(b)")) - (expr (type "[Value(c), Empty]_others, c -> c")) + (expr (type "[Value(c), Empty], c -> c")) (expr (type "Container(a), (a -> Container(b)) -> Container(b)")) (expr (type "num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]")))) ~~~ diff --git a/test/snapshots/syntax_grab_bag.md b/test/snapshots/syntax_grab_bag.md index fdec86a07a..885ba9f613 100644 --- a/test/snapshots/syntax_grab_bag.md +++ b/test/snapshots/syntax_grab_bag.md @@ -2460,7 +2460,7 @@ expect { (defs (patt (type "Bool -> num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]")) (patt (type "Error -> Num(Int(Unsigned64))")) - (patt (type "[Red][Blue, Green]_others, _arg -> Error")) + (patt (type "[Red][Blue, Green], _arg -> Error")) (patt (type "List(Error) -> Error")) (patt (type "{}")) (patt (type "Error"))) @@ -2506,7 +2506,7 @@ expect { (expressions (expr (type "Bool -> num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]")) (expr (type "Error -> Num(Int(Unsigned64))")) - (expr (type "[Red][Blue, Green]_others, _arg -> Error")) + (expr (type "[Red][Blue, Green], _arg -> Error")) (expr (type "List(Error) -> Error")) (expr (type "{}")) (expr (type "Error")))) diff --git a/test/snapshots/type_var_name_avoids_collision.md b/test/snapshots/type_var_name_avoids_collision.md index 2e3f39f346..23dbd4f693 100644 --- a/test/snapshots/type_var_name_avoids_collision.md +++ b/test/snapshots/type_var_name_avoids_collision.md @@ -588,8 +588,8 @@ main! = |_| { (patt (type "ac -> ac")) (patt (type "Str")) (patt (type "Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])])")) - (patt (type "[True]_others")) - (patt (type "[False]_others")) + (patt (type "[True]")) + (patt (type "[False]")) (patt (type "ac -> ac")) (patt (type "ac, ad -> (ac, ad)")) (patt (type "num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]")) @@ -623,8 +623,8 @@ main! = |_| { (expr (type "ac -> ac")) (expr (type "Str")) (expr (type "Num(num where [num.from_dec_digits : (List(U8), List(U8)) -> Try(num, [OutOfRange])])")) - (expr (type "[True]_others")) - (expr (type "[False]_others")) + (expr (type "[True]")) + (expr (type "[False]")) (expr (type "ac -> ac")) (expr (type "ac, ad -> (ac, ad)")) (expr (type "num where [num.from_int_digits : List(U8) -> Try(num, [OutOfRange])]"))