mirror of
https://github.com/roc-lang/roc.git
synced 2025-12-23 08:48:03 +00:00
Remove .str primitive type
This commit is contained in:
parent
9f9192a416
commit
129e1e1d28
32 changed files with 297 additions and 375 deletions
|
|
@ -66,58 +66,6 @@ const BuiltinIndices = struct {
|
|||
f64_type: CIR.Statement.Idx,
|
||||
};
|
||||
|
||||
/// Transform all Str nominal types to .str primitive types in a module.
|
||||
/// This is necessary because the interpreter needs .str to be a primitive type,
|
||||
/// but we define methods on Str as a nominal type in Str.roc for ergonomics.
|
||||
///
|
||||
/// This transformation happens after type-checking but before serialization,
|
||||
/// ensuring that the serialized .bin file contains methods associated with
|
||||
/// the .str primitive type rather than a nominal Str type.
|
||||
fn transformStrNominalToPrimitive(env: *ModuleEnv) !void {
|
||||
const FlatType = types.FlatType;
|
||||
|
||||
// Get the Str identifier in this module
|
||||
const str_ident = env.common.findIdent("Str") orelse {
|
||||
@panic("Str identifier not found in Builtin module");
|
||||
};
|
||||
|
||||
// Iterate through all slots in the type store
|
||||
for (0..env.types.len()) |i| {
|
||||
const var_idx = @as(Var, @enumFromInt(i));
|
||||
|
||||
// Skip redirects, only process roots
|
||||
if (!env.types.resolveVar(var_idx).is_root) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const resolved = env.types.resolveVar(var_idx);
|
||||
const desc = resolved.desc;
|
||||
|
||||
// Check if this descriptor contains a nominal type
|
||||
switch (desc.content) {
|
||||
.structure => |structure| {
|
||||
switch (structure) {
|
||||
.nominal_type => |nominal| {
|
||||
// Check if this is the Str nominal type
|
||||
// TypeIdent has an ident_idx field that references the identifier
|
||||
if (nominal.ident.ident_idx == str_ident) {
|
||||
// Replace with .str primitive type
|
||||
const new_content = Content{ .structure = FlatType.str };
|
||||
const new_desc = types.Descriptor{
|
||||
.content = new_content,
|
||||
.rank = desc.rank,
|
||||
.mark = desc.mark,
|
||||
};
|
||||
try env.types.setVarDesc(var_idx, new_desc);
|
||||
}
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Replace specific e_anno_only expressions with e_low_level_lambda operations.
|
||||
/// This transforms standalone annotations into low-level builtin lambda operations
|
||||
|
|
@ -401,7 +349,6 @@ fn replaceStrIsEmptyWithLowLevel(env: *ModuleEnv) !std.ArrayList(CIR.Def.Idx) {
|
|||
}
|
||||
|
||||
/// Transform all List nominal types to .list primitive types in a module.
|
||||
/// This is similar to transformStrNominalToPrimitive but handles List's type parameter.
|
||||
///
|
||||
/// List is parameterized (List(a)), so we need to preserve the element type parameter
|
||||
/// when transforming from nominal to primitive. The transformation replaces
|
||||
|
|
@ -503,6 +450,7 @@ pub fn main() !void {
|
|||
&.{}, // No module dependencies
|
||||
null, // bool_stmt not available yet (will be found within Builtin)
|
||||
null, // try_stmt not available yet (will be found within Builtin)
|
||||
null, // str_stmt not available yet (will be found within Builtin)
|
||||
);
|
||||
defer {
|
||||
builtin_env.deinit();
|
||||
|
|
@ -581,8 +529,7 @@ pub fn main() !void {
|
|||
|
||||
// Transform nominal types to primitive types as necessary.
|
||||
// This must happen BEFORE serialization to ensure the .bin file contains
|
||||
// methods associated with the .str primitive, not a nominal type
|
||||
try transformStrNominalToPrimitive(builtin_env);
|
||||
// methods associated with the primitive types
|
||||
try transformListNominalToPrimitive(builtin_env);
|
||||
|
||||
// Create output directory
|
||||
|
|
@ -628,6 +575,7 @@ fn compileModule(
|
|||
deps: []const ModuleDep,
|
||||
bool_stmt_opt: ?CIR.Statement.Idx,
|
||||
try_stmt_opt: ?CIR.Statement.Idx,
|
||||
str_stmt_opt: ?CIR.Statement.Idx,
|
||||
) !*ModuleEnv {
|
||||
// This follows the pattern from TestEnv.init() in src/check/test/TestEnv.zig
|
||||
|
||||
|
|
@ -649,7 +597,7 @@ fn compileModule(
|
|||
const list_ident = try module_env.insertIdent(base.Ident.for_text("List"));
|
||||
const box_ident = try module_env.insertIdent(base.Ident.for_text("Box"));
|
||||
|
||||
// Use provided bool_stmt and try_stmt if available, otherwise use undefined
|
||||
// Use provided bool_stmt, try_stmt, and str_stmt if available, otherwise use undefined
|
||||
// For Builtin module, these will be found after canonicalization and updated before type checking
|
||||
var common_idents: Check.CommonIdents = .{
|
||||
.module_name = module_ident,
|
||||
|
|
@ -657,6 +605,7 @@ fn compileModule(
|
|||
.box = box_ident,
|
||||
.bool_stmt = bool_stmt_opt orelse undefined,
|
||||
.try_stmt = try_stmt_opt orelse undefined,
|
||||
.str_stmt = str_stmt_opt orelse undefined,
|
||||
.builtin_module = null,
|
||||
};
|
||||
|
||||
|
|
@ -794,8 +743,8 @@ fn compileModule(
|
|||
module_env.evaluation_order = eval_order_ptr;
|
||||
}
|
||||
|
||||
// Find Bool and Try statements before type checking
|
||||
// When compiling Builtin, bool_stmt and try_stmt are initially undefined,
|
||||
// Find Bool, Try, and Str statements before type checking
|
||||
// When compiling Builtin, bool_stmt, try_stmt, and str_stmt are initially undefined,
|
||||
// but they must be set before type checking begins
|
||||
const found_bool_stmt = findTypeDeclaration(module_env, "Bool") catch {
|
||||
std.debug.print("\n" ++ "=" ** 80 ++ "\n", .{});
|
||||
|
|
@ -813,10 +762,19 @@ fn compileModule(
|
|||
std.debug.print("=" ** 80 ++ "\n", .{});
|
||||
return error.TypeDeclarationNotFound;
|
||||
};
|
||||
const found_str_stmt = findTypeDeclaration(module_env, "Str") catch {
|
||||
std.debug.print("\n" ++ "=" ** 80 ++ "\n", .{});
|
||||
std.debug.print("ERROR: Could not find Str type in Builtin module\n", .{});
|
||||
std.debug.print("=" ** 80 ++ "\n", .{});
|
||||
std.debug.print("The Str type declaration is required for type checking.\n", .{});
|
||||
std.debug.print("=" ** 80 ++ "\n", .{});
|
||||
return error.TypeDeclarationNotFound;
|
||||
};
|
||||
|
||||
// Update common_idents with the found statement indices
|
||||
common_idents.bool_stmt = found_bool_stmt;
|
||||
common_idents.try_stmt = found_try_stmt;
|
||||
common_idents.str_stmt = found_str_stmt;
|
||||
}
|
||||
|
||||
// 6. Type check
|
||||
|
|
|
|||
|
|
@ -285,6 +285,14 @@ pub fn populateModuleEnvs(
|
|||
.statement_idx = statement_idx,
|
||||
});
|
||||
}
|
||||
|
||||
// Also add an entry for "Builtin" module itself so that nominal types
|
||||
// with origin_module="Builtin" can be looked up in module_envs
|
||||
const builtin_module_ident = try calling_module_env.insertIdent(base.Ident.for_text("Builtin"));
|
||||
try module_envs_map.put(builtin_module_ident, .{
|
||||
.env = builtin_module_env,
|
||||
.statement_idx = null, // No specific statement - this is the module itself
|
||||
});
|
||||
}
|
||||
|
||||
/// Set up auto-imported builtin types (Bool, Try, Dict, Set, Str, and numeric types) from the Builtin module.
|
||||
|
|
|
|||
|
|
@ -375,7 +375,6 @@ pub const TypeAnno = union(enum) {
|
|||
|
||||
/// A builtin type
|
||||
pub const Builtin = enum {
|
||||
str,
|
||||
list,
|
||||
box,
|
||||
num,
|
||||
|
|
@ -398,7 +397,6 @@ pub const TypeAnno = union(enum) {
|
|||
/// Convert a builtin type to it's name
|
||||
pub fn toBytes(self: @This()) []const u8 {
|
||||
switch (self) {
|
||||
.str => return "Str",
|
||||
.list => return "List",
|
||||
.box => return "Box",
|
||||
.num => return "Num",
|
||||
|
|
@ -422,7 +420,6 @@ pub const TypeAnno = union(enum) {
|
|||
|
||||
/// Convert a type name string to the corresponding builtin type
|
||||
pub fn fromBytes(bytes: []const u8) ?@This() {
|
||||
if (std.mem.eql(u8, bytes, "Str")) return .str;
|
||||
if (std.mem.eql(u8, bytes, "List")) return .list;
|
||||
if (std.mem.eql(u8, bytes, "Box")) return .box;
|
||||
if (std.mem.eql(u8, bytes, "Num")) return .num;
|
||||
|
|
|
|||
|
|
@ -212,7 +212,7 @@ test "record with extension variable" {
|
|||
// Test that regular records have extension variables
|
||||
// Create { x: 42, y: "hi" }* (open record)
|
||||
const num_var = try env.types.freshFromContent(Content{ .structure = .{ .num = .{ .int_precision = .i32 } } });
|
||||
const str_var = try env.types.freshFromContent(Content{ .structure = .str });
|
||||
const str_var = try env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
|
||||
const fields = [_]types.RecordField{
|
||||
.{ .name = try env.insertIdent(Ident.for_text("x")), .var_ = num_var },
|
||||
|
|
|
|||
|
|
@ -100,6 +100,8 @@ import_cache: ImportCache,
|
|||
constraint_origins: std.AutoHashMap(Var, Var),
|
||||
/// Copied Bool type from Bool module (for use in if conditions, etc.)
|
||||
bool_var: Var,
|
||||
/// Copied Str type from Builtin module (for use in string literals, etc.)
|
||||
str_var: Var,
|
||||
/// Used when looking up static dispatch functions
|
||||
static_dispatch_method_name_buf: std.ArrayList(u8),
|
||||
/// Map representation of Ident -> Var, used in checking static dispatch constraints
|
||||
|
|
@ -131,6 +133,8 @@ pub const CommonIdents = struct {
|
|||
bool_stmt: can.CIR.Statement.Idx,
|
||||
/// Statement index of Try type in the current module (injected from Builtin.bin)
|
||||
try_stmt: can.CIR.Statement.Idx,
|
||||
/// Statement index of Str type in the current module (injected from Builtin.bin)
|
||||
str_stmt: can.CIR.Statement.Idx,
|
||||
/// Direct reference to the Builtin module env (null when compiling Builtin module itself)
|
||||
builtin_module: ?*const ModuleEnv,
|
||||
};
|
||||
|
|
@ -178,6 +182,7 @@ pub fn init(
|
|||
.import_cache = ImportCache{},
|
||||
.constraint_origins = std.AutoHashMap(Var, Var).init(gpa),
|
||||
.bool_var = undefined, // Will be initialized in copyBuiltinTypes()
|
||||
.str_var = undefined, // Will be initialized in copyBuiltinTypes()
|
||||
.static_dispatch_method_name_buf = try std.ArrayList(u8).initCapacity(gpa, 32),
|
||||
.ident_to_var_map = std.AutoHashMap(Ident.Idx, Var).init(gpa),
|
||||
.top_level_ptrns = std.AutoHashMap(CIR.Pattern.Idx, DefProcessed).init(gpa),
|
||||
|
|
@ -594,6 +599,12 @@ fn freshBool(self: *Self, env: *Env, new_region: Region) Allocator.Error!Var {
|
|||
return try self.instantiateVar(self.bool_var, env, .{ .explicit = new_region });
|
||||
}
|
||||
|
||||
/// Create a str var
|
||||
fn freshStr(self: *Self, env: *Env, new_region: Region) Allocator.Error!Var {
|
||||
// Use the copied Str type from the type store (set by copyBuiltinTypes)
|
||||
return try self.instantiateVar(self.str_var, env, .{ .explicit = new_region });
|
||||
}
|
||||
|
||||
// updating vars //
|
||||
|
||||
/// Unify the provided variable with the provided content
|
||||
|
|
@ -645,15 +656,21 @@ fn setVarRank(self: *Self, target_var: Var, env: *Env) std.mem.Allocator.Error!v
|
|||
/// `if` conditions and need to be available in every module's type store.
|
||||
fn copyBuiltinTypes(self: *Self) !void {
|
||||
const bool_stmt_idx = self.common_idents.bool_stmt;
|
||||
const str_stmt_idx = self.common_idents.str_stmt;
|
||||
|
||||
if (self.common_idents.builtin_module) |builtin_env| {
|
||||
// Copy Bool type from Builtin module using the direct reference
|
||||
const bool_type_var = ModuleEnv.varFrom(bool_stmt_idx);
|
||||
self.bool_var = try self.copyVar(bool_type_var, builtin_env, Region.zero());
|
||||
|
||||
// Copy Str type from Builtin module using the direct reference
|
||||
const str_type_var = ModuleEnv.varFrom(str_stmt_idx);
|
||||
self.str_var = try self.copyVar(str_type_var, builtin_env, Region.zero());
|
||||
} else {
|
||||
// If Builtin module reference is null, use the statement from the current module
|
||||
// This happens when compiling the Builtin module itself
|
||||
self.bool_var = ModuleEnv.varFrom(bool_stmt_idx);
|
||||
self.str_var = ModuleEnv.varFrom(str_stmt_idx);
|
||||
}
|
||||
|
||||
// Try type is accessed via external references, no need to copy it here
|
||||
|
|
@ -1587,7 +1604,6 @@ fn generateBuiltinTypeInstance(
|
|||
env: *Env,
|
||||
) std.mem.Allocator.Error!Var {
|
||||
switch (anno_builtin_type) {
|
||||
.str => return try self.freshFromContent(.{ .structure = .str }, env, anno_region),
|
||||
.u8 => return try self.freshFromContent(.{ .structure = .{ .num = types_mod.Num.int_u8 } }, env, anno_region),
|
||||
.u16 => return try self.freshFromContent(.{ .structure = .{ .num = types_mod.Num.int_u16 } }, env, anno_region),
|
||||
.u32 => return try self.freshFromContent(.{ .structure = .{ .num = types_mod.Num.int_u32 } }, env, anno_region),
|
||||
|
|
@ -1753,7 +1769,8 @@ fn checkPatternHelp(
|
|||
},
|
||||
// str //
|
||||
.str_literal => {
|
||||
try self.unifyWith(pattern_var, .{ .structure = .str }, env);
|
||||
const str_var = try self.freshStr(env, pattern_region);
|
||||
_ = try self.unify(pattern_var, str_var, env);
|
||||
},
|
||||
// as //
|
||||
.as => |p| {
|
||||
|
|
@ -2168,7 +2185,8 @@ fn checkExpr(self: *Self, expr_idx: CIR.Expr.Idx, env: *Env, expected: Expected)
|
|||
switch (expr) {
|
||||
// str //
|
||||
.e_str_segment => |_| {
|
||||
try self.unifyWith(expr_var, .{ .structure = .str }, env);
|
||||
const str_var = try self.freshStr(env, expr_region);
|
||||
_ = try self.unify(expr_var, str_var, env);
|
||||
},
|
||||
.e_str => |str| {
|
||||
// Iterate over the string segments, capturing if any error'd
|
||||
|
|
@ -2187,8 +2205,9 @@ fn checkExpr(self: *Self, expr_idx: CIR.Expr.Idx, env: *Env, expected: Expected)
|
|||
// If any segment errored, propgate that error to the root string
|
||||
try self.unifyWith(expr_var, .err, env);
|
||||
} else {
|
||||
// Otherwise, set the type of this expr to be string
|
||||
try self.unifyWith(expr_var, .{ .structure = .str }, env);
|
||||
// Otherwise, set the type of this expr to be nominal Str
|
||||
const str_var = try self.freshStr(env, expr_region);
|
||||
_ = try self.unify(expr_var, str_var, env);
|
||||
}
|
||||
},
|
||||
// nums //
|
||||
|
|
|
|||
|
|
@ -201,7 +201,6 @@ fn copyFlatType(
|
|||
allocator: std.mem.Allocator,
|
||||
) std.mem.Allocator.Error!FlatType {
|
||||
return switch (flat_type) {
|
||||
.str => FlatType.str,
|
||||
.box => |box_var| FlatType{ .box = try copyVar(source_store, dest_store, box_var, var_mapping, source_idents, dest_idents, allocator) },
|
||||
.list => |list_var| FlatType{ .list = try copyVar(source_store, dest_store, list_var, var_mapping, source_idents, dest_idents, allocator) },
|
||||
.list_unbound => FlatType.list_unbound,
|
||||
|
|
|
|||
|
|
@ -160,7 +160,6 @@ const CheckOccurs = struct {
|
|||
switch (root.desc.content) {
|
||||
.structure => |flat_type| {
|
||||
switch (flat_type) {
|
||||
.str => {},
|
||||
.box => |sub_var| {
|
||||
try self.occursSubVar(root, sub_var, ctx.allowRecursion());
|
||||
},
|
||||
|
|
@ -379,7 +378,7 @@ test "occurs: no recurcion (v = Str)" {
|
|||
var scratch = try Scratch.init(gpa);
|
||||
defer scratch.deinit();
|
||||
|
||||
const str_var = try types_store.freshFromContent(Content{ .structure = .str });
|
||||
const str_var = try types_store.freshFromContent(Content{ .structure = .empty_record });
|
||||
|
||||
const result = occurs(&types_store, &scratch, str_var);
|
||||
try std.testing.expectEqual(.not_recursive, result);
|
||||
|
|
@ -442,7 +441,7 @@ test "occurs: no recursion through two levels (v1 = Box v2, v2 = Str)" {
|
|||
const v2 = try types_store.fresh();
|
||||
|
||||
try types_store.setRootVarContent(v1, Content{ .structure = .{ .box = v2 } });
|
||||
try types_store.setRootVarContent(v2, Content{ .structure = .str });
|
||||
try types_store.setRootVarContent(v2, Content{ .structure = .empty_record });
|
||||
|
||||
const result = occurs(&types_store, &scratch, v1);
|
||||
try std.testing.expectEqual(.not_recursive, result);
|
||||
|
|
@ -457,7 +456,7 @@ test "occurs: tuple recursion (v = Tuple(v, Str))" {
|
|||
defer scratch.deinit();
|
||||
|
||||
const v = try types_store.fresh();
|
||||
const str_var = try types_store.freshFromContent(Content{ .structure = .str });
|
||||
const str_var = try types_store.freshFromContent(Content{ .structure = .empty_record });
|
||||
|
||||
const elems_range = try types_store.appendVars(&[_]Var{ v, str_var });
|
||||
const tuple = types.Tuple{ .elems = elems_range };
|
||||
|
|
@ -480,7 +479,7 @@ test "occurs: tuple not recursive (v = Tuple(Str, Str))" {
|
|||
var scratch = try Scratch.init(gpa);
|
||||
defer scratch.deinit();
|
||||
|
||||
const str_var = try types_store.freshFromContent(Content{ .structure = .str });
|
||||
const str_var = try types_store.freshFromContent(Content{ .structure = .empty_record });
|
||||
|
||||
const elems_range = try types_store.appendVars(&[_]Var{ str_var, str_var });
|
||||
const tuple = types.Tuple{ .elems = elems_range };
|
||||
|
|
@ -528,8 +527,8 @@ test "occurs: alias with no recursion (v = Alias Str)" {
|
|||
defer scratch.deinit();
|
||||
|
||||
const alias_var = try types_store.fresh();
|
||||
const backing_var = try types_store.freshFromContent(Content{ .structure = .str });
|
||||
const arg_var = try types_store.freshFromContent(Content{ .structure = .str });
|
||||
const backing_var = try types_store.freshFromContent(Content{ .structure = .empty_record });
|
||||
const arg_var = try types_store.freshFromContent(Content{ .structure = .empty_record });
|
||||
|
||||
try types_store.setRootVarContent(alias_var, try types_store.mkAlias(
|
||||
types.TypeIdent{ .ident_idx = undefined },
|
||||
|
|
|
|||
|
|
@ -146,7 +146,6 @@ pub const Store = struct {
|
|||
|
||||
fn deepCopyFlatType(self: *Self, store: *const TypesStore, flat_type: types.FlatType) std.mem.Allocator.Error!SnapshotFlatType {
|
||||
return switch (flat_type) {
|
||||
.str => SnapshotFlatType.str,
|
||||
.box => |box_var| {
|
||||
const deep_content = try self.deepCopyVar(store, box_var);
|
||||
return SnapshotFlatType{ .box = deep_content };
|
||||
|
|
@ -497,7 +496,6 @@ pub const SnapshotAlias = struct {
|
|||
|
||||
/// TODO
|
||||
pub const SnapshotFlatType = union(enum) {
|
||||
str,
|
||||
box: SnapshotContentIdx, // Index into SnapshotStore.contents
|
||||
list: SnapshotContentIdx,
|
||||
list_unbound,
|
||||
|
|
@ -930,9 +928,6 @@ pub const SnapshotWriter = struct {
|
|||
/// Convert a flat type to a type string
|
||||
fn writeFlatType(self: *Self, flat_type: SnapshotFlatType, root_idx: SnapshotContentIdx) Allocator.Error!void {
|
||||
switch (flat_type) {
|
||||
.str => {
|
||||
_ = try self.buf.writer().write("Str");
|
||||
},
|
||||
.box => |sub_var| {
|
||||
_ = try self.buf.writer().write("Box(");
|
||||
try self.writeWithContext(sub_var, .General, root_idx);
|
||||
|
|
@ -1463,7 +1458,7 @@ pub const SnapshotWriter = struct {
|
|||
|
||||
fn countInFlatType(self: *Self, search_flex_var: Var, flat_type: SnapshotFlatType, count: *usize) std.mem.Allocator.Error!void {
|
||||
switch (flat_type) {
|
||||
.str, .empty_record, .empty_tag_union => {},
|
||||
.empty_record, .empty_tag_union => {},
|
||||
.box => |sub_idx| try self.countContent(search_flex_var, sub_idx, count),
|
||||
.list => |sub_idx| try self.countContent(search_flex_var, sub_idx, count),
|
||||
.list_unbound, .num => {},
|
||||
|
|
|
|||
|
|
@ -205,9 +205,10 @@ pub fn initWithImport(module_name: []const u8, source: []const u8, other_module_
|
|||
try can.canonicalizeFile();
|
||||
try can.validateForChecking();
|
||||
|
||||
// Get Bool and Try statement indices from the IMPORTED modules (not copied!)
|
||||
// Get Bool, Try, and Str statement indices from the IMPORTED modules (not copied!)
|
||||
const bool_stmt_in_bool_module = builtin_indices.bool_type;
|
||||
const try_stmt_in_result_module = builtin_indices.try_type;
|
||||
const str_stmt_in_builtin_module = builtin_indices.str_type;
|
||||
|
||||
const module_common_idents: Check.CommonIdents = .{
|
||||
.module_name = try module_env.insertIdent(base.Ident.for_text(module_name)),
|
||||
|
|
@ -215,6 +216,7 @@ pub fn initWithImport(module_name: []const u8, source: []const u8, other_module_
|
|||
.box = try module_env.insertIdent(base.Ident.for_text("Box")),
|
||||
.bool_stmt = bool_stmt_in_bool_module,
|
||||
.try_stmt = try_stmt_in_result_module,
|
||||
.str_stmt = str_stmt_in_builtin_module,
|
||||
.builtin_module = other_test_env.builtin_module.env,
|
||||
};
|
||||
|
||||
|
|
@ -321,9 +323,10 @@ pub fn init(module_name: []const u8, source: []const u8) !TestEnv {
|
|||
try can.canonicalizeFile();
|
||||
try can.validateForChecking();
|
||||
|
||||
// Get Bool and Try statement indices from the IMPORTED modules (not copied!)
|
||||
// Get Bool, Try, and Str statement indices from the IMPORTED modules (not copied!)
|
||||
const bool_stmt_in_bool_module = builtin_indices.bool_type;
|
||||
const try_stmt_in_result_module = builtin_indices.try_type;
|
||||
const str_stmt_in_builtin_module = builtin_indices.str_type;
|
||||
|
||||
const module_common_idents: Check.CommonIdents = .{
|
||||
.module_name = try module_env.insertIdent(base.Ident.for_text(module_name)),
|
||||
|
|
@ -331,6 +334,7 @@ pub fn init(module_name: []const u8, source: []const u8) !TestEnv {
|
|||
.box = try module_env.insertIdent(base.Ident.for_text("Box")),
|
||||
.bool_stmt = bool_stmt_in_bool_module,
|
||||
.try_stmt = try_stmt_in_result_module,
|
||||
.str_stmt = str_stmt_in_builtin_module,
|
||||
.builtin_module = builtin_module.env,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ test "nominal type origin - displays origin in snapshot writer" {
|
|||
defer import_mapping.deinit();
|
||||
|
||||
// Create a nominal type snapshot with origin from a different module
|
||||
const nominal_type_backing = snapshot.SnapshotContent{ .structure = .str };
|
||||
const nominal_type_backing = snapshot.SnapshotContent{ .structure = .empty_record };
|
||||
const nominal_type_backing_idx = try snapshots.contents.append(test_allocator, nominal_type_backing);
|
||||
const vars_range = try snapshots.content_indexes.appendSlice(test_allocator, &.{nominal_type_backing_idx});
|
||||
|
||||
|
|
@ -93,7 +93,7 @@ test "nominal type origin - displays origin in snapshot writer" {
|
|||
defer buf.deinit(test_allocator);
|
||||
|
||||
// Create type arguments
|
||||
const str_content = snapshot.SnapshotContent{ .structure = .{ .str = {} } };
|
||||
const str_content = snapshot.SnapshotContent{ .structure = .empty_record };
|
||||
const str_idx = try snapshots.contents.append(test_allocator, str_content);
|
||||
const args_range = try snapshots.content_indexes.appendSlice(test_allocator, &.{ nominal_type_backing_idx, str_idx });
|
||||
|
||||
|
|
@ -116,8 +116,8 @@ test "nominal type origin - displays origin in snapshot writer" {
|
|||
try writer.writeNominalType(generic_nominal, nominal_type_backing_idx);
|
||||
|
||||
const result = writer.get();
|
||||
// Should show "Person(Str) (from Data.Types)"
|
||||
try testing.expect(std.mem.indexOf(u8, result, "Person(Str)") != null);
|
||||
// Should show "Person({}) (from Data.Types)" - empty_record displays as {}
|
||||
try testing.expect(std.mem.indexOf(u8, result, "Person({})") != null);
|
||||
try testing.expect(std.mem.indexOf(u8, result, "(from Data.Types)") != null);
|
||||
}
|
||||
}
|
||||
|
|
@ -136,7 +136,7 @@ test "nominal type origin - works with no context" {
|
|||
var import_mapping = types_mod.import_mapping.ImportMapping.init(test_allocator);
|
||||
defer import_mapping.deinit();
|
||||
|
||||
const nominal_type_backing = snapshot.SnapshotContent{ .structure = .str };
|
||||
const nominal_type_backing = snapshot.SnapshotContent{ .structure = .empty_record };
|
||||
const nominal_type_backing_idx = try snapshots.contents.append(test_allocator, nominal_type_backing);
|
||||
const vars_range = try snapshots.content_indexes.appendSlice(test_allocator, &.{nominal_type_backing_idx});
|
||||
|
||||
|
|
|
|||
|
|
@ -406,7 +406,7 @@ test "rigid_var - cannot unify with alias (fail)" {
|
|||
var env = try TestEnv.init(gpa);
|
||||
defer env.deinit();
|
||||
|
||||
const alias = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const alias = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
const rigid = try env.module_env.types.freshFromContent(try env.mkRigidVar("a"));
|
||||
|
||||
const result = try env.unify(alias, rigid);
|
||||
|
|
@ -432,7 +432,7 @@ test "unify - alias with same args" {
|
|||
var env = try TestEnv.init(gpa);
|
||||
defer env.deinit();
|
||||
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
const bool_ = try env.module_env.types.freshFromContent(Content{ .structure = .{ .num = Num.int_i8 } });
|
||||
|
||||
// Create alias `a` with its backing var and args in sequence
|
||||
|
|
@ -457,7 +457,7 @@ test "unify - aliases with different names but same backing" {
|
|||
var env = try TestEnv.init(gpa);
|
||||
defer env.deinit();
|
||||
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
|
||||
// Create alias `a` with its backing var and arg
|
||||
const a_backing_var = try env.module_env.types.freshFromContent(try env.mkTuple(&[_]Var{str}));
|
||||
|
|
@ -481,7 +481,7 @@ test "unify - alias with different args (fail)" {
|
|||
var env = try TestEnv.init(gpa);
|
||||
defer env.deinit();
|
||||
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
const bool_ = try env.module_env.types.freshFromContent(Content{ .structure = .{ .num = Num.int_i8 } });
|
||||
|
||||
// Create alias `a` with its backing var and arg
|
||||
|
|
@ -506,7 +506,7 @@ test "unify - alias with flex" {
|
|||
var env = try TestEnv.init(gpa);
|
||||
defer env.deinit();
|
||||
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
const bool_ = try env.module_env.types.freshFromContent(Content{ .structure = .{ .num = Num.int_i8 } });
|
||||
|
||||
const a_backing_var = try env.module_env.types.freshFromContent(try env.mkTuple(&[_]Var{ str, bool_ })); // backing var
|
||||
|
|
@ -527,11 +527,11 @@ test "unify - alias with concrete" {
|
|||
var env = try TestEnv.init(gpa);
|
||||
defer env.deinit();
|
||||
|
||||
const a_backing_var = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const a_backing_var = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
const a_alias = try env.mkAlias("Alias", a_backing_var, &[_]Var{});
|
||||
|
||||
const a = try env.module_env.types.freshFromContent(a_alias);
|
||||
const b = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const b = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
|
||||
const result = try env.unify(a, b);
|
||||
|
||||
|
|
@ -545,7 +545,7 @@ test "unify - alias with concrete" {
|
|||
const resolved_backing = env.module_env.types.resolveVar(
|
||||
env.module_env.types.getAliasBackingVar(resolved.desc.content.alias),
|
||||
);
|
||||
try std.testing.expectEqual(Content{ .structure = .str }, resolved_backing.desc.content);
|
||||
try std.testing.expectEqual(Content{ .structure = .empty_record }, resolved_backing.desc.content);
|
||||
|
||||
// Assert that a & b redirect to the alias
|
||||
try std.testing.expectEqual(Slot{ .redirect = resolved.var_ }, env.module_env.types.getSlot(a));
|
||||
|
|
@ -557,10 +557,10 @@ test "unify - alias with concrete other way" {
|
|||
var env = try TestEnv.init(gpa);
|
||||
defer env.deinit();
|
||||
|
||||
const b_backing_var = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const b_backing_var = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
const b_alias = try env.mkAlias("Alias", b_backing_var, &[_]Var{});
|
||||
|
||||
const a = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const a = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
const b = try env.module_env.types.freshFromContent(b_alias);
|
||||
|
||||
const result = try env.unify(a, b);
|
||||
|
|
@ -575,7 +575,7 @@ test "unify - alias with concrete other way" {
|
|||
const resolved_backing = env.module_env.types.resolveVar(
|
||||
env.module_env.types.getAliasBackingVar(resolved.desc.content.alias),
|
||||
);
|
||||
try std.testing.expectEqual(Content{ .structure = .str }, resolved_backing.desc.content);
|
||||
try std.testing.expectEqual(Content{ .structure = .empty_record }, resolved_backing.desc.content);
|
||||
|
||||
// Assert that a & b redirect to the alias
|
||||
try std.testing.expectEqual(Slot{ .redirect = resolved.var_ }, env.module_env.types.getSlot(a));
|
||||
|
|
@ -590,7 +590,7 @@ test "unify - a is builtin and b is flex_var" {
|
|||
var env = try TestEnv.init(gpa);
|
||||
defer env.deinit();
|
||||
|
||||
const str = Content{ .structure = .str };
|
||||
const str = Content{ .structure = .empty_record };
|
||||
|
||||
const a = try env.module_env.types.freshFromContent(str);
|
||||
const b = try env.module_env.types.fresh();
|
||||
|
|
@ -608,7 +608,7 @@ test "unify - a is flex_var and b is builtin" {
|
|||
var env = try TestEnv.init(gpa);
|
||||
defer env.deinit();
|
||||
|
||||
const str = Content{ .structure = .str };
|
||||
const str = Content{ .structure = .empty_record };
|
||||
|
||||
const a = try env.module_env.types.fresh();
|
||||
const b = try env.module_env.types.freshFromContent(str);
|
||||
|
|
@ -628,7 +628,7 @@ test "unify - a & b are both str" {
|
|||
var env = try TestEnv.init(gpa);
|
||||
defer env.deinit();
|
||||
|
||||
const str = Content{ .structure = .str };
|
||||
const str = Content{ .structure = .empty_record };
|
||||
|
||||
const a = try env.module_env.types.freshFromContent(str);
|
||||
const b = try env.module_env.types.freshFromContent(str);
|
||||
|
|
@ -646,7 +646,7 @@ test "unify - a & b are diff (fail)" {
|
|||
var env = try TestEnv.init(gpa);
|
||||
defer env.deinit();
|
||||
|
||||
const str = Content{ .structure = .str };
|
||||
const str = Content{ .structure = .empty_record };
|
||||
const int = Content{ .structure = .{ .num = Num.int_i8 } };
|
||||
|
||||
const a = try env.module_env.types.freshFromContent(int);
|
||||
|
|
@ -665,7 +665,7 @@ test "unify - a & b box with same arg unify" {
|
|||
var env = try TestEnv.init(gpa);
|
||||
defer env.deinit();
|
||||
|
||||
const str = Content{ .structure = .str };
|
||||
const str = Content{ .structure = .empty_record };
|
||||
const str_var = try env.module_env.types.freshFromContent(str);
|
||||
|
||||
const box_str = Content{ .structure = .{ .box = str_var } };
|
||||
|
|
@ -686,7 +686,7 @@ test "unify - a & b box with diff args (fail)" {
|
|||
var env = try TestEnv.init(gpa);
|
||||
defer env.deinit();
|
||||
|
||||
const str = Content{ .structure = .str };
|
||||
const str = Content{ .structure = .empty_record };
|
||||
const str_var = try env.module_env.types.freshFromContent(str);
|
||||
|
||||
const i64_ = Content{ .structure = .{ .num = Num.int_i64 } };
|
||||
|
|
@ -711,7 +711,7 @@ test "unify - a & b list with same arg unify" {
|
|||
var env = try TestEnv.init(gpa);
|
||||
defer env.deinit();
|
||||
|
||||
const str = Content{ .structure = .str };
|
||||
const str = Content{ .structure = .empty_record };
|
||||
const str_var = try env.module_env.types.freshFromContent(str);
|
||||
|
||||
const list_str = Content{ .structure = .{ .list = str_var } };
|
||||
|
|
@ -732,7 +732,7 @@ test "unify - a & b list with diff args (fail)" {
|
|||
var env = try TestEnv.init(gpa);
|
||||
defer env.deinit();
|
||||
|
||||
const str = Content{ .structure = .str };
|
||||
const str = Content{ .structure = .empty_record };
|
||||
const str_var = try env.module_env.types.freshFromContent(str);
|
||||
|
||||
const u8_ = Content{ .structure = .{ .num = Num.int_u8 } };
|
||||
|
|
@ -759,7 +759,7 @@ test "unify - a & b are same tuple" {
|
|||
var env = try TestEnv.init(gpa);
|
||||
defer env.deinit();
|
||||
|
||||
const str = Content{ .structure = .str };
|
||||
const str = Content{ .structure = .empty_record };
|
||||
const str_var = try env.module_env.types.freshFromContent(str);
|
||||
|
||||
const bool_ = Content{ .structure = .{ .num = Num.int_i8 } };
|
||||
|
|
@ -783,7 +783,7 @@ test "unify - a & b are tuples with args flipped (fail)" {
|
|||
var env = try TestEnv.init(gpa);
|
||||
defer env.deinit();
|
||||
|
||||
const str = Content{ .structure = .str };
|
||||
const str = Content{ .structure = .empty_record };
|
||||
const str_var = try env.module_env.types.freshFromContent(str);
|
||||
|
||||
const bool_ = Content{ .structure = .{ .num = Num.int_i8 } };
|
||||
|
|
@ -1398,7 +1398,7 @@ test "unify - func are same" {
|
|||
|
||||
const int_i32 = try env.module_env.types.freshFromContent(Content{ .structure = .{ .num = Num.int_i32 } });
|
||||
const num = try env.mkNumPolyFlex();
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
const func = try env.mkFuncFlex(&[_]Var{ str, num }, int_i32);
|
||||
|
||||
const a = try env.module_env.types.freshFromContent(func);
|
||||
|
|
@ -1418,7 +1418,7 @@ test "unify - funcs have diff return args (fail)" {
|
|||
defer env.deinit();
|
||||
|
||||
const int_i32 = try env.module_env.types.freshFromContent(Content{ .structure = .{ .num = Num.int_i32 } });
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
|
||||
const a = try env.module_env.types.freshFromContent(try env.mkFuncFlex(&[_]Var{int_i32}, str));
|
||||
const b = try env.module_env.types.freshFromContent(try env.mkFuncFlex(&[_]Var{str}, str));
|
||||
|
|
@ -1437,7 +1437,7 @@ test "unify - funcs have diff return types (fail)" {
|
|||
defer env.deinit();
|
||||
|
||||
const int_i32 = try env.module_env.types.freshFromContent(Content{ .structure = .{ .num = Num.int_i32 } });
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
|
||||
const a = try env.module_env.types.freshFromContent(try env.mkFuncFlex(&[_]Var{str}, int_i32));
|
||||
const b = try env.module_env.types.freshFromContent(try env.mkFuncFlex(&[_]Var{str}, str));
|
||||
|
|
@ -1457,7 +1457,7 @@ test "unify - same funcs pure" {
|
|||
|
||||
const int_i32 = try env.module_env.types.freshFromContent(Content{ .structure = .{ .num = Num.int_i32 } });
|
||||
const int_poly = try env.mkIntPolyFlex();
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
const func = try env.mkFuncPure(&[_]Var{ str, int_poly }, int_i32);
|
||||
|
||||
const a = try env.module_env.types.freshFromContent(func);
|
||||
|
|
@ -1478,7 +1478,7 @@ test "unify - same funcs effectful" {
|
|||
|
||||
const int_i32 = try env.module_env.types.freshFromContent(Content{ .structure = .{ .num = Num.int_i32 } });
|
||||
const int_poly = try env.mkIntPolyFlex();
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
const func = try env.mkFuncEffectful(&[_]Var{ str, int_poly }, int_i32);
|
||||
|
||||
const a = try env.module_env.types.freshFromContent(func);
|
||||
|
|
@ -1499,7 +1499,7 @@ test "unify - same funcs first eff, second pure (fail)" {
|
|||
|
||||
const int_i32 = try env.module_env.types.freshFromContent(Content{ .structure = .{ .num = Num.int_i32 } });
|
||||
const int_poly = try env.mkIntPolyFlex();
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
const pure_func = try env.mkFuncPure(&[_]Var{ str, int_poly }, int_i32);
|
||||
const eff_func = try env.mkFuncEffectful(&[_]Var{ str, int_poly }, int_i32);
|
||||
|
||||
|
|
@ -1521,7 +1521,7 @@ test "unify - same funcs first pure, second eff (fail)" {
|
|||
|
||||
const int_i32 = try env.module_env.types.freshFromContent(Content{ .structure = .{ .num = Num.int_i32 } });
|
||||
const int_poly = try env.mkIntPolyFlex();
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
const pure_func = try env.mkFuncPure(&[_]Var{ str, int_poly }, int_i32);
|
||||
const eff_func = try env.mkFuncEffectful(&[_]Var{ str, int_poly }, int_i32);
|
||||
|
||||
|
|
@ -1543,7 +1543,7 @@ test "unify - same funcs first pure, second unbound" {
|
|||
|
||||
const int_i32 = try env.module_env.types.freshFromContent(Content{ .structure = .{ .num = Num.int_i32 } });
|
||||
const int_poly = try env.mkIntPolyFlex();
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
const pure_func = try env.mkFuncPure(&[_]Var{ str, int_poly }, int_i32);
|
||||
const unbound_func = try env.mkFuncUnbound(&[_]Var{ str, int_poly }, int_i32);
|
||||
|
||||
|
|
@ -1565,7 +1565,7 @@ test "unify - same funcs first unbound, second pure" {
|
|||
|
||||
const int_i32 = try env.module_env.types.freshFromContent(Content{ .structure = .{ .num = Num.int_i32 } });
|
||||
const int_poly = try env.mkIntPolyFlex();
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
const pure_func = try env.mkFuncPure(&[_]Var{ str, int_poly }, int_i32);
|
||||
const unbound_func = try env.mkFuncUnbound(&[_]Var{ str, int_poly }, int_i32);
|
||||
|
||||
|
|
@ -1587,7 +1587,7 @@ test "unify - same funcs first effectful, second unbound" {
|
|||
|
||||
const int_i32 = try env.module_env.types.freshFromContent(Content{ .structure = .{ .num = Num.int_i32 } });
|
||||
const int_poly = try env.mkIntPolyFlex();
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
const eff_func = try env.mkFuncEffectful(&[_]Var{ str, int_poly }, int_i32);
|
||||
const unbound_func = try env.mkFuncUnbound(&[_]Var{ str, int_poly }, int_i32);
|
||||
|
||||
|
|
@ -1609,7 +1609,7 @@ test "unify - same funcs first unbound, second effectful" {
|
|||
|
||||
const int_i32 = try env.module_env.types.freshFromContent(Content{ .structure = .{ .num = Num.int_i32 } });
|
||||
const int_poly = try env.mkIntPolyFlex();
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
const eff_func = try env.mkFuncEffectful(&[_]Var{ str, int_poly }, int_i32);
|
||||
const unbound_func = try env.mkFuncUnbound(&[_]Var{ str, int_poly }, int_i32);
|
||||
|
||||
|
|
@ -1654,10 +1654,10 @@ test "unify - a & b are both the same nominal type" {
|
|||
|
||||
const arg = try env.module_env.types.freshFromContent(Content{ .structure = .{ .num = Num.int_u8 } });
|
||||
|
||||
const a_backing_var = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const a_backing_var = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
const a = try env.module_env.types.freshFromContent(try env.mkNominalType("MyType", a_backing_var, &[_]Var{arg}));
|
||||
|
||||
const b_backing_var = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const b_backing_var = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
const b_nominal = try env.mkNominalType("MyType", b_backing_var, &[_]Var{arg});
|
||||
const b = try env.module_env.types.freshFromContent(b_nominal);
|
||||
|
||||
|
|
@ -1676,10 +1676,10 @@ test "unify - a & b are diff nominal types (fail)" {
|
|||
|
||||
const arg = try env.module_env.types.freshFromContent(Content{ .structure = .{ .num = Num.int_u8 } });
|
||||
|
||||
const a_backing_var = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const a_backing_var = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
const a = try env.module_env.types.freshFromContent(try env.mkNominalType("MyType", a_backing_var, &[_]Var{arg}));
|
||||
|
||||
const b_backing_var = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const b_backing_var = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
const b = try env.module_env.types.freshFromContent(try env.mkNominalType("AnotherType", b_backing_var, &[_]Var{arg}));
|
||||
|
||||
const result = try env.unify(a, b);
|
||||
|
|
@ -1696,12 +1696,12 @@ test "unify - a & b are both the same nominal type with diff args (fail)" {
|
|||
defer env.deinit();
|
||||
|
||||
const arg_var = try env.module_env.types.freshFromContent(Content{ .structure = .{ .num = Num.int_u8 } });
|
||||
const str_var = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const str_var = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
|
||||
const a_backing = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const a_backing = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
const a = try env.module_env.types.freshFromContent(try env.mkNominalType("MyType", a_backing, &[_]Var{arg_var}));
|
||||
|
||||
const b_backing = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const b_backing = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
const b = try env.module_env.types.freshFromContent(try env.mkNominalType("MyType", b_backing, &[_]Var{str_var}));
|
||||
|
||||
const result = try env.unify(a, b);
|
||||
|
|
@ -1719,7 +1719,7 @@ test "unify - anonymous tag union unifies with nominal tag union (nominal on lef
|
|||
defer env.deinit();
|
||||
|
||||
// Create nominal type: Foo := [A(Str), B]
|
||||
const str_var = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const str_var = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
const tag_a = try env.mkTag("A", &[_]Var{str_var});
|
||||
const tag_b = try env.mkTag("B", &[_]Var{});
|
||||
const backing_tu = try env.mkTagUnionClosed(&[_]Tag{ tag_a, tag_b });
|
||||
|
|
@ -1805,7 +1805,7 @@ test "unify - anonymous tag union with wrong payload type fails" {
|
|||
defer env.deinit();
|
||||
|
||||
// Create nominal type: Foo := [A(Str)]
|
||||
const str_var = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const str_var = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
const tag_a = try env.mkTag("A", &[_]Var{str_var});
|
||||
const backing_tu = try env.mkTagUnionClosed(&[_]Tag{tag_a});
|
||||
const backing_var = try env.module_env.types.freshFromContent(backing_tu.content);
|
||||
|
|
@ -2094,7 +2094,7 @@ test "unify - identical closed records" {
|
|||
var env = try TestEnv.init(gpa);
|
||||
defer env.deinit();
|
||||
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
|
||||
const fields = [_]RecordField{try env.mkRecordField("a", str)};
|
||||
const record_data = try env.mkRecordClosed(&fields);
|
||||
|
|
@ -2119,7 +2119,7 @@ test "unify - closed record mismatch on diff fields (fail)" {
|
|||
var env = try TestEnv.init(gpa);
|
||||
defer env.deinit();
|
||||
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
|
||||
const field1 = try env.mkRecordField("field1", str);
|
||||
const field2 = try env.mkRecordField("field2", str);
|
||||
|
|
@ -2146,7 +2146,7 @@ test "unify - identical open records" {
|
|||
var env = try TestEnv.init(gpa);
|
||||
defer env.deinit();
|
||||
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
|
||||
const field_shared = try env.mkRecordField("x", str);
|
||||
|
||||
|
|
@ -2181,7 +2181,7 @@ test "unify - open record a extends b" {
|
|||
var env = try TestEnv.init(gpa);
|
||||
defer env.deinit();
|
||||
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
const int = try env.module_env.types.freshFromContent(Content{ .structure = .{ .num = Num.int_u8 } });
|
||||
|
||||
const field_shared = try env.mkRecordField("x", str);
|
||||
|
|
@ -2228,7 +2228,7 @@ test "unify - open record b extends a" {
|
|||
var env = try TestEnv.init(gpa);
|
||||
defer env.deinit();
|
||||
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
const int = try env.module_env.types.freshFromContent(Content{ .structure = .{ .num = Num.int_u8 } });
|
||||
|
||||
const field_shared = try env.mkRecordField("field_shared", str);
|
||||
|
|
@ -2272,7 +2272,7 @@ test "unify - both extend open record" {
|
|||
var env = try TestEnv.init(gpa);
|
||||
defer env.deinit();
|
||||
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
const int = try env.module_env.types.freshFromContent(Content{ .structure = .{ .num = Num.int_u8 } });
|
||||
const bool_ = try env.module_env.types.freshFromContent(Content{ .structure = .{ .num = Num.int_i8 } });
|
||||
|
||||
|
|
@ -2328,7 +2328,7 @@ test "unify - record mismatch on shared field (fail)" {
|
|||
var env = try TestEnv.init(gpa);
|
||||
defer env.deinit();
|
||||
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
const int = try env.module_env.types.freshFromContent(Content{ .structure = .{ .num = Num.int_u8 } });
|
||||
|
||||
const field_a = try env.mkRecordField("x", str);
|
||||
|
|
@ -2356,7 +2356,7 @@ test "unify - open record extends closed (fail)" {
|
|||
var env = try TestEnv.init(gpa);
|
||||
defer env.deinit();
|
||||
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
|
||||
const field_x = try env.mkRecordField("field_x", str);
|
||||
const field_y = try env.mkRecordField("field_y", str);
|
||||
|
|
@ -2376,7 +2376,7 @@ test "unify - closed record extends open" {
|
|||
var env = try TestEnv.init(gpa);
|
||||
defer env.deinit();
|
||||
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
|
||||
const field_x = try env.mkRecordField("field_x", str);
|
||||
const field_y = try env.mkRecordField("field_y", str);
|
||||
|
|
@ -2395,7 +2395,7 @@ test "unify - open vs closed records with type mismatch (fail)" {
|
|||
var env = try TestEnv.init(gpa);
|
||||
defer env.deinit();
|
||||
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
const int = try env.module_env.types.freshFromContent(Content{ .structure = .{ .num = Num.int_u8 } });
|
||||
|
||||
const field_x_str = try env.mkRecordField("field_x_str", str);
|
||||
|
|
@ -2418,7 +2418,7 @@ test "unify - closed vs open records with type mismatch (fail)" {
|
|||
var env = try TestEnv.init(gpa);
|
||||
defer env.deinit();
|
||||
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
const int = try env.module_env.types.freshFromContent(Content{ .structure = .{ .num = Num.int_u8 } });
|
||||
|
||||
const field_x_str = try env.mkRecordField("field_x_str", str);
|
||||
|
|
@ -2550,7 +2550,7 @@ test "unify - identical closed tag_unions" {
|
|||
var env = try TestEnv.init(gpa);
|
||||
defer env.deinit();
|
||||
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
|
||||
const tag = try env.mkTag("A", &[_]Var{str});
|
||||
const tags = [_]Tag{tag};
|
||||
|
|
@ -2584,7 +2584,7 @@ test "unify - closed tag_unions with diff args (fail)" {
|
|||
var env = try TestEnv.init(gpa);
|
||||
defer env.deinit();
|
||||
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
const int = try env.module_env.types.freshFromContent(Content{ .structure = .{ .num = Num.int_u8 } });
|
||||
|
||||
const a_tag = try env.mkTag("A", &[_]Var{str});
|
||||
|
|
@ -2613,7 +2613,7 @@ test "unify - identical open tag unions" {
|
|||
var env = try TestEnv.init(gpa);
|
||||
defer env.deinit();
|
||||
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
|
||||
const tag_shared = try env.mkTag("Shared", &[_]Var{ str, str });
|
||||
|
||||
|
|
@ -2653,7 +2653,7 @@ test "unify - open tag union a extends b" {
|
|||
var env = try TestEnv.init(gpa);
|
||||
defer env.deinit();
|
||||
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
const int = try env.module_env.types.freshFromContent(Content{ .structure = .{ .num = Num.int_u8 } });
|
||||
|
||||
const tag_a_only = try env.mkTag("A", &[_]Var{str});
|
||||
|
|
@ -2706,7 +2706,7 @@ test "unify - open tag union b extends a" {
|
|||
var env = try TestEnv.init(gpa);
|
||||
defer env.deinit();
|
||||
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
const int = try env.module_env.types.freshFromContent(Content{ .structure = .{ .num = Num.int_u8 } });
|
||||
|
||||
const tag_b_only = try env.mkTag("A", &[_]Var{ str, int });
|
||||
|
|
@ -2759,7 +2759,7 @@ test "unify - both extend open tag union" {
|
|||
var env = try TestEnv.init(gpa);
|
||||
defer env.deinit();
|
||||
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
const int = try env.module_env.types.freshFromContent(Content{ .structure = .{ .num = Num.int_u8 } });
|
||||
const bool_ = try env.module_env.types.freshFromContent(Content{ .structure = .{ .num = Num.int_i8 } });
|
||||
|
||||
|
|
@ -2818,7 +2818,7 @@ test "unify - open tag unions a & b have same tag name with diff args (fail)" {
|
|||
var env = try TestEnv.init(gpa);
|
||||
defer env.deinit();
|
||||
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
const int = try env.module_env.types.freshFromContent(Content{ .structure = .{ .num = Num.int_u8 } });
|
||||
|
||||
const tag_a_only = try env.mkTag("A", &[_]Var{str});
|
||||
|
|
@ -2846,7 +2846,7 @@ test "unify - open tag extends closed (fail)" {
|
|||
var env = try TestEnv.init(gpa);
|
||||
defer env.deinit();
|
||||
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
|
||||
const tag_shared = try env.mkTag("Shared", &[_]Var{str});
|
||||
const tag_a_only = try env.mkTag("A", &[_]Var{str});
|
||||
|
|
@ -2866,7 +2866,7 @@ test "unify - closed tag union extends open" {
|
|||
var env = try TestEnv.init(gpa);
|
||||
defer env.deinit();
|
||||
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
|
||||
const tag_shared = try env.mkTag("Shared", &[_]Var{str});
|
||||
const tag_b_only = try env.mkTag("B", &[_]Var{str});
|
||||
|
|
@ -2915,7 +2915,7 @@ test "unify - open vs closed tag union with type mismatch (fail)" {
|
|||
var env = try TestEnv.init(gpa);
|
||||
defer env.deinit();
|
||||
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
const bool_ = try env.module_env.types.freshFromContent(Content{ .structure = .{ .num = Num.int_i8 } });
|
||||
|
||||
const tag_a = try env.mkTag("A", &[_]Var{str});
|
||||
|
|
@ -2938,7 +2938,7 @@ test "unify - closed vs open tag union with type mismatch (fail)" {
|
|||
var env = try TestEnv.init(gpa);
|
||||
defer env.deinit();
|
||||
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
const bool_ = try env.module_env.types.freshFromContent(Content{ .structure = .{ .num = Num.int_i8 } });
|
||||
|
||||
const tag_a = try env.mkTag("A", &[_]Var{str});
|
||||
|
|
@ -2963,7 +2963,7 @@ test "unify - fails on infinite type" {
|
|||
var env = try TestEnv.init(gpa);
|
||||
defer env.deinit();
|
||||
|
||||
const str_var = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const str_var = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
|
||||
const a = try env.module_env.types.fresh();
|
||||
const a_elems_range = try env.module_env.types.appendVars(&[_]Var{ a, str_var });
|
||||
|
|
@ -3781,7 +3781,7 @@ test "unify - flex with constraints unifies with flex with same constraints" {
|
|||
var env = try TestEnv.init(gpa);
|
||||
defer env.deinit();
|
||||
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
|
||||
// Create constraint: a.to_str : Str -> Str
|
||||
const to_str_fn = try env.module_env.types.freshFromContent(try env.mkFuncPure(&[_]Var{str}, str));
|
||||
|
|
@ -3850,7 +3850,7 @@ test "unify - flex with multiple constraints" {
|
|||
var env = try TestEnv.init(gpa);
|
||||
defer env.deinit();
|
||||
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
const int = try env.module_env.types.freshFromContent(Content{ .structure = .{ .num = Num.int_i32 } });
|
||||
|
||||
// Create constraint 1: a.to_str : Int -> Str
|
||||
|
|
@ -3890,7 +3890,7 @@ test "unify - flex with constraints fails on incompatible arg types" {
|
|||
var env = try TestEnv.init(gpa);
|
||||
defer env.deinit();
|
||||
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
const int = try env.module_env.types.freshFromContent(Content{ .structure = .{ .num = Num.int_i32 } });
|
||||
|
||||
// a has constraint: a.foo : Str -> Str
|
||||
|
|
@ -3929,7 +3929,7 @@ test "unify - flex with constraints fails on incompatible return types" {
|
|||
var env = try TestEnv.init(gpa);
|
||||
defer env.deinit();
|
||||
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
const int = try env.module_env.types.freshFromContent(Content{ .structure = .{ .num = Num.int_i32 } });
|
||||
|
||||
// a has constraint: a.foo : Str -> Str
|
||||
|
|
@ -3968,7 +3968,7 @@ test "unify - flex with constraints fails on different arity" {
|
|||
var env = try TestEnv.init(gpa);
|
||||
defer env.deinit();
|
||||
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
const int = try env.module_env.types.freshFromContent(Content{ .structure = .{ .num = Num.int_i32 } });
|
||||
|
||||
// a has constraint: a.foo : Str -> Str (1 arg)
|
||||
|
|
@ -4007,7 +4007,7 @@ test "unify - flex with subset of constraints (a subset b)" {
|
|||
var env = try TestEnv.init(gpa);
|
||||
defer env.deinit();
|
||||
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
const int = try env.module_env.types.freshFromContent(Content{ .structure = .{ .num = Num.int_i32 } });
|
||||
|
||||
// a has 1 constraint: a.foo : Str -> Str
|
||||
|
|
@ -4052,7 +4052,7 @@ test "unify - flex with constraints vs rigid with constraints" {
|
|||
var env = try TestEnv.init(gpa);
|
||||
defer env.deinit();
|
||||
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
const int = try env.module_env.types.freshFromContent(Content{ .structure = .{ .num = Num.int_i32 } });
|
||||
|
||||
// flex has 2 constraints
|
||||
|
|
@ -4104,7 +4104,7 @@ test "unify - flex with constraints vs rigid constraints 2" {
|
|||
var env = try TestEnv.init(gpa);
|
||||
defer env.deinit();
|
||||
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
const int = try env.module_env.types.freshFromContent(Content{ .structure = .{ .num = Num.int_i32 } });
|
||||
|
||||
// flex has 1 constraint
|
||||
|
|
@ -4152,7 +4152,7 @@ test "unify - empty constraints unify with any" {
|
|||
var env = try TestEnv.init(gpa);
|
||||
defer env.deinit();
|
||||
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
|
||||
const foo_fn = try env.module_env.types.freshFromContent(try env.mkFuncPure(&[_]Var{str}, str));
|
||||
const foo_constraint = types_mod.StaticDispatchConstraint{
|
||||
|
|
@ -4184,7 +4184,7 @@ test "unify - flex with constraints vs structure captures deferred check" {
|
|||
var env = try TestEnv.init(gpa);
|
||||
defer env.deinit();
|
||||
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
|
||||
// Create constraint: a.to_str : Str -> Str
|
||||
const to_str_fn = try env.module_env.types.freshFromContent(try env.mkFuncPure(&[_]Var{str}, str));
|
||||
|
|
@ -4199,7 +4199,7 @@ test "unify - flex with constraints vs structure captures deferred check" {
|
|||
.name = null,
|
||||
.constraints = constraints,
|
||||
} });
|
||||
const structure_var = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const structure_var = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
|
||||
const result = try env.unify(flex_var, structure_var);
|
||||
try std.testing.expectEqual(.ok, result);
|
||||
|
|
@ -4219,7 +4219,7 @@ test "unify - structure vs flex with constraints captures deferred check (revers
|
|||
var env = try TestEnv.init(gpa);
|
||||
defer env.deinit();
|
||||
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
|
||||
// Create constraint: a.to_str : Str -> Str
|
||||
const to_str_fn = try env.module_env.types.freshFromContent(try env.mkFuncPure(&[_]Var{str}, str));
|
||||
|
|
@ -4230,7 +4230,7 @@ test "unify - structure vs flex with constraints captures deferred check (revers
|
|||
};
|
||||
const constraints = try env.module_env.types.appendStaticDispatchConstraints(&[_]types_mod.StaticDispatchConstraint{to_str_constraint});
|
||||
|
||||
const structure_var = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const structure_var = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
const flex_var = try env.module_env.types.freshFromContent(.{ .flex = .{
|
||||
.name = null,
|
||||
.constraints = constraints,
|
||||
|
|
@ -4255,7 +4255,7 @@ test "unify - flex with no constraints vs structure does not capture" {
|
|||
defer env.deinit();
|
||||
|
||||
const flex_var = try env.module_env.types.freshFromContent(.{ .flex = Flex.init() });
|
||||
const structure_var = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const structure_var = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
|
||||
const result = try env.unify(flex_var, structure_var);
|
||||
try std.testing.expectEqual(.ok, result);
|
||||
|
|
@ -4269,7 +4269,7 @@ test "unify - flex with multiple constraints vs structure captures all" {
|
|||
var env = try TestEnv.init(gpa);
|
||||
defer env.deinit();
|
||||
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
const int = try env.module_env.types.freshFromContent(Content{ .structure = .{ .num = Num.int_i32 } });
|
||||
|
||||
// Create multiple constraints
|
||||
|
|
@ -4293,7 +4293,7 @@ test "unify - flex with multiple constraints vs structure captures all" {
|
|||
.name = null,
|
||||
.constraints = constraints,
|
||||
} });
|
||||
const structure_var = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const structure_var = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
|
||||
const result = try env.unify(flex_var, structure_var);
|
||||
try std.testing.expectEqual(.ok, result);
|
||||
|
|
@ -4314,7 +4314,7 @@ test "unify - flex vs nominal type captures constraint" {
|
|||
var env = try TestEnv.init(gpa);
|
||||
defer env.deinit();
|
||||
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const str = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
|
||||
// Create constraint
|
||||
const ord_fn = try env.module_env.types.freshFromContent(try env.mkFuncPure(&[_]Var{str}, str));
|
||||
|
|
@ -4331,7 +4331,7 @@ test "unify - flex vs nominal type captures constraint" {
|
|||
} });
|
||||
|
||||
// Create nominal type (e.g., Path)
|
||||
const backing_var = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const backing_var = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
const nominal_var = try env.module_env.types.freshFromContent(try env.mkNominalType("Path", backing_var, &[_]Var{}));
|
||||
|
||||
const result = try env.unify(flex_var, nominal_var);
|
||||
|
|
@ -4356,7 +4356,7 @@ test "recursion_var - can be created and points to structure" {
|
|||
defer env.deinit();
|
||||
|
||||
// Create a structure variable (e.g., a Str type)
|
||||
const structure_var = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const structure_var = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
|
||||
// Create a RecursionVar pointing to the structure
|
||||
const rec_var = try env.module_env.types.freshFromContent(Content{
|
||||
|
|
@ -4381,7 +4381,7 @@ test "recursion_var - unifies with its structure" {
|
|||
defer env.deinit();
|
||||
|
||||
// Create a structure variable
|
||||
const structure_var = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const structure_var = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
|
||||
// Create a RecursionVar pointing to the structure
|
||||
const rec_var = try env.module_env.types.freshFromContent(Content{
|
||||
|
|
@ -4435,7 +4435,7 @@ test "recursion_var - unifies with alias" {
|
|||
defer env.deinit();
|
||||
|
||||
// Create an alias MyStr = Str
|
||||
const str_var = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const str_var = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
const alias_content = try env.mkAlias("MyStr", str_var, &[_]Var{});
|
||||
const alias_var = try env.module_env.types.freshFromContent(alias_content);
|
||||
|
||||
|
|
@ -4463,7 +4463,7 @@ test "recursion_var - cannot unify with rigid" {
|
|||
const rigid_var = try env.module_env.types.freshFromContent(rigid_content);
|
||||
|
||||
// Create a RecursionVar
|
||||
const structure_var = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const structure_var = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
const rec_var = try env.module_env.types.freshFromContent(Content{
|
||||
.recursion_var = .{
|
||||
.structure = structure_var,
|
||||
|
|
@ -4483,7 +4483,7 @@ test "recursion_var - two recursion vars with same structure unify" {
|
|||
defer env.deinit();
|
||||
|
||||
// Create a shared structure
|
||||
const structure_var = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const structure_var = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
|
||||
// Create two RecursionVars both pointing to the same structure
|
||||
const rec_var_1 = try env.module_env.types.freshFromContent(Content{
|
||||
|
|
@ -4512,7 +4512,7 @@ test "recursion_var - two recursion vars with different structures do not unify"
|
|||
defer env.deinit();
|
||||
|
||||
// Create different structures
|
||||
const str_var = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const str_var = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
const num_var = try env.module_env.types.freshFromContent(Content{ .structure = .{ .num = Num.int_i32 } });
|
||||
|
||||
// Create two RecursionVars with different structures
|
||||
|
|
@ -4594,7 +4594,7 @@ test "recursion_var - unifies with flex preserving constraints" {
|
|||
});
|
||||
|
||||
// Create a RecursionVar
|
||||
const structure_var = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const structure_var = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
const rec_var = try env.module_env.types.freshFromContent(Content{
|
||||
.recursion_var = .{
|
||||
.structure = structure_var,
|
||||
|
|
@ -4618,8 +4618,8 @@ test "type_writer - recursion_var displays structure" {
|
|||
var env = try TestEnv.init(gpa);
|
||||
defer env.deinit();
|
||||
|
||||
// Create a structure variable (Str)
|
||||
const structure_var = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
// Create a structure variable (empty record)
|
||||
const structure_var = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
|
||||
// Create a RecursionVar pointing to it
|
||||
const rec_var = try env.module_env.types.freshFromContent(Content{
|
||||
|
|
@ -4636,8 +4636,8 @@ test "type_writer - recursion_var displays structure" {
|
|||
|
||||
const result = try writer.writeGet(rec_var);
|
||||
|
||||
// Should display as "Str" (the structure it points to)
|
||||
try std.testing.expectEqualStrings("Str", result);
|
||||
// Should display as "{}" (the structure it points to)
|
||||
try std.testing.expectEqualStrings("{}", result);
|
||||
}
|
||||
|
||||
test "type_writer - recursion_var with cycle displays correctly" {
|
||||
|
|
@ -4682,12 +4682,12 @@ test "type_writer - nested recursion_var displays correctly" {
|
|||
var env = try TestEnv.init(gpa);
|
||||
defer env.deinit();
|
||||
|
||||
// Create nested structure: RecursionVar -> List -> RecursionVar -> Str
|
||||
const str_var = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
// Create nested structure: RecursionVar -> List -> RecursionVar -> empty_record
|
||||
const empty_record_var = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
|
||||
const inner_rec_var = try env.module_env.types.freshFromContent(Content{
|
||||
.recursion_var = .{
|
||||
.structure = str_var,
|
||||
.structure = empty_record_var,
|
||||
.name = null,
|
||||
},
|
||||
});
|
||||
|
|
@ -4710,8 +4710,8 @@ test "type_writer - nested recursion_var displays correctly" {
|
|||
|
||||
const result = try writer.writeGet(outer_rec_var);
|
||||
|
||||
// Should display as "List(Str)" - following through the RecursionVars
|
||||
try std.testing.expectEqualStrings("List(Str)", result);
|
||||
// Should display as "List({})" - following through the RecursionVars
|
||||
try std.testing.expectEqualStrings("List({})", result);
|
||||
}
|
||||
|
||||
// Integration test for recursive constraints (motivating example)
|
||||
|
|
@ -4802,7 +4802,7 @@ test "recursion_var - integration: multiple recursive constraints unify correctl
|
|||
|
||||
// Create chain A
|
||||
const a1 = try env.module_env.types.fresh();
|
||||
const a2 = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const a2 = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
|
||||
const rec_var_a = try env.module_env.types.freshFromContent(Content{
|
||||
.recursion_var = .{
|
||||
|
|
@ -4815,7 +4815,7 @@ test "recursion_var - integration: multiple recursive constraints unify correctl
|
|||
|
||||
// Create chain B with same base structure
|
||||
const b1 = try env.module_env.types.fresh();
|
||||
const b2 = try env.module_env.types.freshFromContent(Content{ .structure = .str });
|
||||
const b2 = try env.module_env.types.freshFromContent(Content{ .structure = .empty_record });
|
||||
|
||||
const rec_var_b = try env.module_env.types.freshFromContent(Content{
|
||||
.recursion_var = .{
|
||||
|
|
|
|||
|
|
@ -854,22 +854,6 @@ const Unifier = struct {
|
|||
defer trace.end();
|
||||
|
||||
switch (a_flat_type) {
|
||||
.str => {
|
||||
switch (b_flat_type) {
|
||||
.str => self.merge(vars, vars.b.desc.content),
|
||||
.nominal_type => |b_type| {
|
||||
const b_backing_var = self.types_store.getNominalBackingVar(b_type);
|
||||
const b_backing_resolved = self.types_store.resolveVar(b_backing_var);
|
||||
if (b_backing_resolved.desc.content == .err) {
|
||||
// Invalid nominal type - treat as transparent
|
||||
self.merge(vars, vars.a.desc.content);
|
||||
return;
|
||||
}
|
||||
return error.TypeMismatch;
|
||||
},
|
||||
else => return error.TypeMismatch,
|
||||
}
|
||||
},
|
||||
.box => |a_var| {
|
||||
switch (b_flat_type) {
|
||||
.box => |b_var| {
|
||||
|
|
|
|||
|
|
@ -1377,6 +1377,7 @@ pub fn setupSharedMemoryWithModuleEnv(allocs: *Allocators, roc_file_path: []cons
|
|||
.box = try env.insertIdent(base.Ident.for_text("Box")),
|
||||
.bool_stmt = builtin_modules.builtin_indices.bool_type,
|
||||
.try_stmt = builtin_modules.builtin_indices.try_type,
|
||||
.str_stmt = builtin_modules.builtin_indices.str_type,
|
||||
.builtin_module = builtin_modules.builtin_module.env,
|
||||
};
|
||||
|
||||
|
|
@ -2419,6 +2420,7 @@ fn rocTest(allocs: *Allocators, args: cli_args.TestArgs) !void {
|
|||
.box = try env.insertIdent(base.Ident.for_text("Box")),
|
||||
.bool_stmt = @enumFromInt(0), // TODO: load from builtin modules
|
||||
.try_stmt = @enumFromInt(0), // TODO: load from builtin modules
|
||||
.str_stmt = @enumFromInt(0), // TODO: load from builtin modules
|
||||
.builtin_module = null,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -783,6 +783,7 @@ pub const PackageEnv = struct {
|
|||
.box = try env.insertIdent(base.Ident.for_text("Box")),
|
||||
.bool_stmt = builtin_indices.bool_type,
|
||||
.try_stmt = builtin_indices.try_type,
|
||||
.str_stmt = builtin_indices.str_type,
|
||||
.builtin_module = self.builtin_modules.builtin_module.env,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -406,11 +406,11 @@ test "ModuleEnv pushExprTypesToSExprTree extracts and formats types" {
|
|||
|
||||
// Add a string segment expression
|
||||
const segment_idx = try env.addExpr(.{ .e_str_segment = .{ .literal = str_literal_idx } }, base.Region.from_raw_offsets(0, 5));
|
||||
_ = try env.types.freshFromContent(.{ .structure = .str });
|
||||
_ = try env.types.freshFromContent(.{ .structure = .empty_record });
|
||||
|
||||
// Now create a string expression that references the segment
|
||||
const expr_idx = try env.addExpr(.{ .e_str = .{ .span = Expr.Span{ .span = base.DataSpan{ .start = @intFromEnum(segment_idx), .len = 1 } } } }, base.Region.from_raw_offsets(0, 5));
|
||||
_ = try env.types.freshFromContent(.{ .structure = .str });
|
||||
_ = try env.types.freshFromContent(.{ .structure = .empty_record });
|
||||
|
||||
// Create an S-expression tree
|
||||
var tree = base.SExprTree.init(gpa);
|
||||
|
|
@ -427,10 +427,7 @@ test "ModuleEnv pushExprTypesToSExprTree extracts and formats types" {
|
|||
// Verify the output contains the type information
|
||||
const result_str = result.items;
|
||||
|
||||
// Uncomment to debug:
|
||||
// std.debug.print("\nType extraction result:\n{s}\n", .{result_str});
|
||||
|
||||
try testing.expect(std.mem.indexOf(u8, result_str, "(expr") != null);
|
||||
try testing.expect(std.mem.indexOf(u8, result_str, "(type") != null);
|
||||
try testing.expect(std.mem.indexOf(u8, result_str, "Str") != null);
|
||||
try testing.expect(std.mem.indexOf(u8, result_str, "{}") != null);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4607,9 +4607,6 @@ pub const Interpreter = struct {
|
|||
switch (resolved.desc.content) {
|
||||
.structure => |flat| {
|
||||
switch (flat) {
|
||||
.str => {
|
||||
break :blk try self.runtime_types.freshFromContent(.{ .structure = .str });
|
||||
},
|
||||
.num => |initial_num| {
|
||||
const compact_num: types.Num.Compact = prec: {
|
||||
var num = initial_num;
|
||||
|
|
@ -5221,18 +5218,18 @@ test "interpreter: Var->Layout slot caches computed layout" {
|
|||
var interp = try Interpreter.init(gpa, &env, builtin_types_test, &[_]*const can.ModuleEnv{});
|
||||
defer interp.deinit();
|
||||
|
||||
// Create a concrete runtime type: Str
|
||||
const str_var = try interp.runtime_types.freshFromContent(.{ .structure = .str });
|
||||
// Create a concrete runtime type: i64
|
||||
const i64_var = try interp.runtime_types.freshFromContent(.{ .structure = .{ .num = .{ .num_compact = .{ .int = .i64 } } } });
|
||||
|
||||
// Initially, slot is either absent or zero; ensure capacity then check
|
||||
const root_idx: usize = @intFromEnum(interp.runtime_types.resolveVar(str_var).var_);
|
||||
const root_idx: usize = @intFromEnum(interp.runtime_types.resolveVar(i64_var).var_);
|
||||
try interp.ensureVarLayoutCapacity(root_idx + 1);
|
||||
try std.testing.expectEqual(@as(u32, 0), interp.var_to_layout_slot.items[root_idx]);
|
||||
|
||||
// Retrieve layout and expect scalar.str; slot becomes non-zero
|
||||
const layout_value = try interp.getRuntimeLayout(str_var);
|
||||
// Retrieve layout and expect scalar.int; slot becomes non-zero
|
||||
const layout_value = try interp.getRuntimeLayout(i64_var);
|
||||
try std.testing.expect(layout_value.tag == .scalar);
|
||||
try std.testing.expect(layout_value.data.scalar.tag == .str);
|
||||
try std.testing.expect(layout_value.data.scalar.tag == .int);
|
||||
try std.testing.expect(interp.var_to_layout_slot.items[root_idx] != 0);
|
||||
}
|
||||
|
||||
|
|
@ -5258,12 +5255,14 @@ test "interpreter: translateTypeVar for str" {
|
|||
var interp = try Interpreter.init(gpa, &env, builtin_types_test, &[_]*const can.ModuleEnv{});
|
||||
defer interp.deinit();
|
||||
|
||||
const ct_str = try env.types.freshFromContent(.{ .structure = .str });
|
||||
const rt_var = try interp.translateTypeVar(&env, ct_str);
|
||||
// Get the actual Str type from the Builtin module using the str_stmt index
|
||||
const ct_str = can.ModuleEnv.varFrom(builtin_indices.str_type);
|
||||
const rt_var = try interp.translateTypeVar(str_module.env, ct_str);
|
||||
|
||||
// The runtime var should be a nominal Str type
|
||||
const resolved = interp.runtime_types.resolveVar(rt_var);
|
||||
try std.testing.expect(resolved.desc.content == .structure);
|
||||
try std.testing.expect(resolved.desc.content.structure == .str);
|
||||
try std.testing.expect(resolved.desc.content.structure == .nominal_type);
|
||||
}
|
||||
|
||||
// RED: translating a compile-time concrete int64 should produce a runtime int64
|
||||
|
|
@ -5364,7 +5363,7 @@ test "interpreter: translateTypeVar for tuple(Str, I64)" {
|
|||
var interp = try Interpreter.init(gpa, &env, builtin_types_test, &[_]*const can.ModuleEnv{});
|
||||
defer interp.deinit();
|
||||
|
||||
const ct_str = try env.types.freshFromContent(.{ .structure = .str });
|
||||
const ct_str = try env.types.freshFromContent(.{ .structure = .empty_record });
|
||||
const ct_i64 = try env.types.freshFromContent(.{ .structure = .{ .num = .{ .num_compact = .{ .int = .i64 } } } });
|
||||
const elems = [_]types.Var{ ct_str, ct_i64 };
|
||||
const ct_tuple = try env.types.freshFromContent(.{ .structure = .{ .tuple = .{ .elems = try env.types.appendVars(&elems) } } });
|
||||
|
|
@ -5379,7 +5378,7 @@ test "interpreter: translateTypeVar for tuple(Str, I64)" {
|
|||
// elem 0: str
|
||||
const e0 = interp.runtime_types.resolveVar(rt_elems[0]);
|
||||
try std.testing.expect(e0.desc.content == .structure);
|
||||
try std.testing.expect(e0.desc.content.structure == .str);
|
||||
try std.testing.expect(e0.desc.content.structure == .empty_record);
|
||||
// elem 1: i64
|
||||
const e1 = interp.runtime_types.resolveVar(rt_elems[1]);
|
||||
try std.testing.expect(e1.desc.content == .structure);
|
||||
|
|
@ -5423,7 +5422,7 @@ test "interpreter: translateTypeVar for record {first: Str, second: I64}" {
|
|||
// Build compile-time record content
|
||||
const name_first = try env.common.idents.insert(gpa, @import("base").Ident.for_text("first"));
|
||||
const name_second = try env.common.idents.insert(gpa, @import("base").Ident.for_text("second"));
|
||||
const ct_str = try env.types.freshFromContent(.{ .structure = .str });
|
||||
const ct_str = try env.types.freshFromContent(.{ .structure = .empty_record });
|
||||
const ct_i64 = try env.types.freshFromContent(.{ .structure = .{ .num = .{ .num_compact = .{ .int = .i64 } } } });
|
||||
var ct_fields = [_]types.RecordField{
|
||||
.{ .name = name_first, .var_ = ct_str },
|
||||
|
|
@ -5449,7 +5448,7 @@ test "interpreter: translateTypeVar for record {first: Str, second: I64}" {
|
|||
// Field 0 type is Str
|
||||
const e0 = interp.runtime_types.resolveVar(f0.var_);
|
||||
try std.testing.expect(e0.desc.content == .structure);
|
||||
try std.testing.expect(e0.desc.content.structure == .str);
|
||||
try std.testing.expect(e0.desc.content.structure == .empty_record);
|
||||
// Field 1 type is I64
|
||||
const e1 = interp.runtime_types.resolveVar(f1.var_);
|
||||
try std.testing.expect(e1.desc.content == .structure);
|
||||
|
|
@ -5492,7 +5491,7 @@ test "interpreter: translateTypeVar for alias of Str" {
|
|||
|
||||
const alias_name = try env.common.idents.insert(gpa, @import("base").Ident.for_text("MyAlias"));
|
||||
const type_ident = types.TypeIdent{ .ident_idx = alias_name };
|
||||
const ct_str = try env.types.freshFromContent(.{ .structure = .str });
|
||||
const ct_str = try env.types.freshFromContent(.{ .structure = .empty_record });
|
||||
const ct_alias_content = try env.types.mkAlias(type_ident, ct_str, &.{});
|
||||
const ct_alias_var = try env.types.register(.{ .content = ct_alias_content, .rank = types.Rank.top_level, .mark = types.Mark.none });
|
||||
|
||||
|
|
@ -5504,7 +5503,7 @@ test "interpreter: translateTypeVar for alias of Str" {
|
|||
const rt_backing = interp.runtime_types.getAliasBackingVar(rt_alias);
|
||||
const backing_resolved = interp.runtime_types.resolveVar(rt_backing);
|
||||
try std.testing.expect(backing_resolved.desc.content == .structure);
|
||||
try std.testing.expect(backing_resolved.desc.content.structure == .str);
|
||||
try std.testing.expect(backing_resolved.desc.content.structure == .empty_record);
|
||||
}
|
||||
|
||||
// RED: translating a compile-time nominal type should produce equivalent runtime nominal
|
||||
|
|
@ -5531,7 +5530,7 @@ test "interpreter: translateTypeVar for nominal Point(Str)" {
|
|||
|
||||
const name_nominal = try env.common.idents.insert(gpa, @import("base").Ident.for_text("Point"));
|
||||
const type_ident = types.TypeIdent{ .ident_idx = name_nominal };
|
||||
const ct_str = try env.types.freshFromContent(.{ .structure = .str });
|
||||
const ct_str = try env.types.freshFromContent(.{ .structure = .empty_record });
|
||||
// backing type is Str for simplicity
|
||||
const ct_nominal_content = try env.types.mkNominal(type_ident, ct_str, &.{}, name_nominal);
|
||||
const ct_nominal_var = try env.types.register(.{ .content = ct_nominal_content, .rank = types.Rank.top_level, .mark = types.Mark.none });
|
||||
|
|
@ -5545,7 +5544,7 @@ test "interpreter: translateTypeVar for nominal Point(Str)" {
|
|||
const backing = interp.runtime_types.getNominalBackingVar(nom);
|
||||
const b_resolved = interp.runtime_types.resolveVar(backing);
|
||||
try std.testing.expect(b_resolved.desc.content == .structure);
|
||||
try std.testing.expect(b_resolved.desc.content.structure == .str);
|
||||
try std.testing.expect(b_resolved.desc.content.structure == .empty_record);
|
||||
},
|
||||
else => return error.TestUnexpectedResult,
|
||||
}
|
||||
|
|
@ -5632,7 +5631,7 @@ test "interpreter: translateTypeVar for flex var with static dispatch constraint
|
|||
defer interp.deinit();
|
||||
|
||||
// Create a method function type: Str -> I64
|
||||
const ct_str = try env.types.freshFromContent(.{ .structure = .str });
|
||||
const ct_str = try env.types.freshFromContent(.{ .structure = .empty_record });
|
||||
const ct_i64 = try env.types.freshFromContent(.{ .structure = .{ .num = .{ .num_compact = .{ .int = .i64 } } } });
|
||||
const ct_fn_args = [_]types.Var{ct_str};
|
||||
const ct_fn_content = try env.types.mkFuncPure(&ct_fn_args, ct_i64);
|
||||
|
|
@ -5676,7 +5675,7 @@ test "interpreter: translateTypeVar for flex var with static dispatch constraint
|
|||
// Arg should be Str
|
||||
const rt_arg_resolved = interp.runtime_types.resolveVar(rt_fn_args[0]);
|
||||
try std.testing.expect(rt_arg_resolved.desc.content == .structure);
|
||||
try std.testing.expect(rt_arg_resolved.desc.content.structure == .str);
|
||||
try std.testing.expect(rt_arg_resolved.desc.content.structure == .empty_record);
|
||||
// Return should be I64
|
||||
const rt_ret_resolved = interp.runtime_types.resolveVar(f.ret);
|
||||
try std.testing.expect(rt_ret_resolved.desc.content == .structure);
|
||||
|
|
@ -5708,7 +5707,7 @@ test "interpreter: translateTypeVar for flex var with multiple static dispatch c
|
|||
defer interp.deinit();
|
||||
|
||||
// Create multiple method function types
|
||||
const ct_str = try env.types.freshFromContent(.{ .structure = .str });
|
||||
const ct_str = try env.types.freshFromContent(.{ .structure = .empty_record });
|
||||
const ct_i64 = try env.types.freshFromContent(.{ .structure = .{ .num = .{ .num_compact = .{ .int = .i64 } } } });
|
||||
const ct_bool = try env.types.freshFromContent(.{ .structure = .{ .tag_union = .{ .tags = types.Tag.SafeMultiList.Range.empty(), .ext = try env.types.freshFromContent(.{ .structure = .empty_tag_union }) } } });
|
||||
|
||||
|
|
@ -5790,7 +5789,7 @@ test "interpreter: translateTypeVar for rigid var with static dispatch constrain
|
|||
defer interp.deinit();
|
||||
|
||||
// Create a method function type
|
||||
const ct_str = try env.types.freshFromContent(.{ .structure = .str });
|
||||
const ct_str = try env.types.freshFromContent(.{ .structure = .empty_record });
|
||||
const ct_i64 = try env.types.freshFromContent(.{ .structure = .{ .num = .{ .num_compact = .{ .int = .i64 } } } });
|
||||
const ct_fn_args = [_]types.Var{ct_str};
|
||||
const ct_fn_content = try env.types.mkFuncPure(&ct_fn_args, ct_i64);
|
||||
|
|
@ -5850,7 +5849,7 @@ test "interpreter: getStaticDispatchConstraint finds method on flex var" {
|
|||
defer interp.deinit();
|
||||
|
||||
// Create method types
|
||||
const ct_str = try env.types.freshFromContent(.{ .structure = .str });
|
||||
const ct_str = try env.types.freshFromContent(.{ .structure = .empty_record });
|
||||
const ct_i64 = try env.types.freshFromContent(.{ .structure = .{ .num = .{ .num_compact = .{ .int = .i64 } } } });
|
||||
|
||||
const ct_fn1_args = [_]types.Var{ct_str};
|
||||
|
|
@ -5911,7 +5910,7 @@ test "interpreter: getStaticDispatchConstraint returns error for non-constrained
|
|||
defer interp.deinit();
|
||||
|
||||
// Create a plain structure type (no constraints)
|
||||
const ct_str = try env.types.freshFromContent(.{ .structure = .str });
|
||||
const ct_str = try env.types.freshFromContent(.{ .structure = .empty_record });
|
||||
const rt_var = try interp.translateTypeVar(&env, ct_str);
|
||||
|
||||
// Try to get a constraint from a non-flex/rigid type
|
||||
|
|
@ -5944,7 +5943,7 @@ test "interpreter: poly cache insert and lookup" {
|
|||
|
||||
const f_id: u32 = 12345;
|
||||
// Create runtime args: (Str, I64)
|
||||
const rt_str = try interp.runtime_types.freshFromContent(.{ .structure = .str });
|
||||
const rt_str = try interp.runtime_types.freshFromContent(.{ .structure = .empty_record });
|
||||
const rt_i64 = try interp.runtime_types.freshFromContent(.{ .structure = .{ .num = .{ .num_compact = .{ .int = .i64 } } } });
|
||||
const args = [_]types.Var{ rt_str, rt_i64 };
|
||||
|
||||
|
|
@ -5991,7 +5990,7 @@ test "interpreter: prepareCall miss then hit" {
|
|||
defer interp.deinit();
|
||||
|
||||
const func_id: u32 = 7777;
|
||||
const rt_str = try interp.runtime_types.freshFromContent(.{ .structure = .str });
|
||||
const rt_str = try interp.runtime_types.freshFromContent(.{ .structure = .empty_record });
|
||||
const rt_i64 = try interp.runtime_types.freshFromContent(.{ .structure = .{ .num = .{ .num_compact = .{ .int = .i64 } } } });
|
||||
const args = [_]types.Var{ rt_str, rt_i64 };
|
||||
|
||||
|
|
@ -6033,7 +6032,7 @@ test "interpreter: prepareCallWithFuncVar populates cache" {
|
|||
defer interp.deinit();
|
||||
|
||||
const func_id: u32 = 9999;
|
||||
const rt_str = try interp.runtime_types.freshFromContent(.{ .structure = .str });
|
||||
const rt_str = try interp.runtime_types.freshFromContent(.{ .structure = .empty_record });
|
||||
const rt_i64 = try interp.runtime_types.freshFromContent(.{ .structure = .{ .num = .{ .num_compact = .{ .int = .i64 } } } });
|
||||
const args = [_]types.Var{ rt_str, rt_i64 };
|
||||
|
||||
|
|
@ -6081,13 +6080,13 @@ test "interpreter: unification constrains (a->a) with Str" {
|
|||
const func_var = try interp.runtime_types.register(.{ .content = func_content, .rank = types.Rank.top_level, .mark = types.Mark.none });
|
||||
|
||||
// Call with Str
|
||||
const rt_str = try interp.runtime_types.freshFromContent(.{ .structure = .str });
|
||||
const rt_str = try interp.runtime_types.freshFromContent(.{ .structure = .empty_record });
|
||||
const entry = try interp.prepareCallWithFuncVar(0, func_id, func_var, &.{rt_str});
|
||||
|
||||
// After unification, return var should resolve to str
|
||||
const resolved_ret = interp.runtime_types.resolveVar(entry.return_var);
|
||||
try std.testing.expect(resolved_ret.desc.content == .structure);
|
||||
try std.testing.expect(resolved_ret.desc.content.structure == .str);
|
||||
try std.testing.expect(resolved_ret.desc.content.structure == .empty_record);
|
||||
try std.testing.expect(entry.return_layout_slot != 0);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -31,21 +31,35 @@ pub fn renderValueRocWithType(ctx: *RenderCtx, value: StackValue, rt_var: types.
|
|||
const gpa = ctx.allocator;
|
||||
var resolved = ctx.runtime_types.resolveVar(rt_var);
|
||||
|
||||
// Check if this is Bool before unwrapping (special case for bool display)
|
||||
if (resolved.desc.content == .structure) {
|
||||
if (resolved.desc.content.structure == .nominal_type) {
|
||||
const nominal = resolved.desc.content.structure.nominal_type;
|
||||
const type_name = ctx.env.getIdent(nominal.ident.ident_idx);
|
||||
if (std.mem.eql(u8, type_name, "Bool")) {
|
||||
// Bool is represented as a scalar bool (0 or 1) - render as True/False
|
||||
if (value.layout.tag == .scalar and value.layout.data.scalar.tag == .bool) {
|
||||
const b: *const u8 = @ptrCast(@alignCast(value.ptr.?));
|
||||
return if (b.* != 0)
|
||||
try gpa.dupe(u8, "True")
|
||||
else
|
||||
try gpa.dupe(u8, "False");
|
||||
// Check layout first for special rendering cases
|
||||
// Str has .str layout, Bool has .bool layout
|
||||
if (value.layout.tag == .scalar) {
|
||||
const scalar = value.layout.data.scalar;
|
||||
if (scalar.tag == .str) {
|
||||
// Render strings with quotes
|
||||
const rs: *const builtins.str.RocStr = @ptrCast(@alignCast(value.ptr.?));
|
||||
const s = rs.asSlice();
|
||||
var buf = std.array_list.AlignedManaged(u8, null).init(gpa);
|
||||
errdefer buf.deinit();
|
||||
try buf.append('"');
|
||||
for (s) |ch| {
|
||||
switch (ch) {
|
||||
'\\' => try buf.appendSlice("\\\\"),
|
||||
'"' => try buf.appendSlice("\\\""),
|
||||
else => try buf.append(ch),
|
||||
}
|
||||
}
|
||||
try buf.append('"');
|
||||
return buf.toOwnedSlice();
|
||||
} else if (scalar.tag == .bool) {
|
||||
// Check if this is a nominal Bool type (not just any bool)
|
||||
if (resolved.desc.content == .structure and resolved.desc.content.structure == .nominal_type) {
|
||||
const b: *const u8 = @ptrCast(@alignCast(value.ptr.?));
|
||||
return if (b.* != 0)
|
||||
try gpa.dupe(u8, "True")
|
||||
else
|
||||
try gpa.dupe(u8, "False");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ fn parseCheckAndEvalModule(src: []const u8) !struct {
|
|||
.box = try module_env.insertIdent(base.Ident.for_text("Box")),
|
||||
.bool_stmt = builtin_indices.bool_type,
|
||||
.try_stmt = builtin_indices.try_type,
|
||||
.str_stmt = builtin_indices.str_type,
|
||||
.builtin_module = builtin_module.env,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@ fn parseCheckAndEvalModuleWithName(src: []const u8, module_name: []const u8) !Ev
|
|||
.box = try module_env.insertIdent(base.Ident.for_text("Box")),
|
||||
.bool_stmt = builtin_indices.bool_type,
|
||||
.try_stmt = builtin_indices.try_type,
|
||||
.str_stmt = builtin_indices.str_type,
|
||||
.builtin_module = builtin_module.env,
|
||||
};
|
||||
|
||||
|
|
@ -139,6 +140,7 @@ fn parseCheckAndEvalModuleWithImport(src: []const u8, import_name: []const u8, i
|
|||
.box = try module_env.insertIdent(base.Ident.for_text("Box")),
|
||||
.bool_stmt = builtin_indices.bool_type,
|
||||
.try_stmt = builtin_indices.try_type,
|
||||
.str_stmt = builtin_indices.str_type,
|
||||
.builtin_module = builtin_module.env,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -768,6 +768,7 @@ test "ModuleEnv serialization and interpreter evaluation" {
|
|||
// Get Bool and Try statement indices from builtin module
|
||||
const bool_stmt_in_builtin_module = builtin_indices.bool_type;
|
||||
const try_stmt_in_builtin_module = builtin_indices.try_type;
|
||||
const str_stmt_in_builtin_module = builtin_indices.str_type;
|
||||
|
||||
const common_idents: Check.CommonIdents = .{
|
||||
.module_name = try original_env.insertIdent(base.Ident.for_text("test")),
|
||||
|
|
@ -775,6 +776,7 @@ test "ModuleEnv serialization and interpreter evaluation" {
|
|||
.box = try original_env.insertIdent(base.Ident.for_text("Box")),
|
||||
.bool_stmt = bool_stmt_in_builtin_module,
|
||||
.try_stmt = try_stmt_in_builtin_module,
|
||||
.str_stmt = str_stmt_in_builtin_module,
|
||||
.builtin_module = builtin_module.env,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -429,9 +429,10 @@ pub fn parseAndCanonicalizeExpr(allocator: std.mem.Allocator, source: []const u8
|
|||
// Register Builtin as import so Bool, Try, and Str are available
|
||||
_ = try module_env.imports.getOrPut(allocator, &module_env.common.strings, "Builtin");
|
||||
|
||||
// Get Bool and Try statement indices from Builtin module
|
||||
// Get Bool, Try, and Str statement indices from Builtin module
|
||||
const bool_stmt_in_bool_module = builtin_indices.bool_type;
|
||||
const try_stmt_in_result_module = builtin_indices.try_type;
|
||||
const str_stmt_in_builtin_module = builtin_indices.str_type;
|
||||
|
||||
const common_idents: Check.CommonIdents = .{
|
||||
.module_name = try module_env.insertIdent(base.Ident.for_text("test")),
|
||||
|
|
@ -439,6 +440,7 @@ pub fn parseAndCanonicalizeExpr(allocator: std.mem.Allocator, source: []const u8
|
|||
.box = try module_env.insertIdent(base.Ident.for_text("Box")),
|
||||
.bool_stmt = bool_stmt_in_bool_module,
|
||||
.try_stmt = try_stmt_in_result_module,
|
||||
.str_stmt = str_stmt_in_builtin_module,
|
||||
.builtin_module = builtin_module.env,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -848,7 +848,6 @@ pub const Store = struct {
|
|||
|
||||
var layout = switch (current.desc.content) {
|
||||
.structure => |flat_type| flat_type: switch (flat_type) {
|
||||
.str => Layout.str(),
|
||||
.box => |elem_var| {
|
||||
try self.work.pending_containers.append(self.env.gpa, .{
|
||||
.var_ = current.var_,
|
||||
|
|
@ -886,6 +885,14 @@ pub const Store = struct {
|
|||
return idx;
|
||||
},
|
||||
.nominal_type => |nominal_type| {
|
||||
// Special-case Builtin.Str: it has a tag union backing type, but
|
||||
// should have RocStr layout (3 pointers)
|
||||
const str_ident = self.env.common.findIdent("Builtin.Str");
|
||||
if (str_ident != null and nominal_type.ident.ident_idx == str_ident.?) {
|
||||
// Str nominal type should use the string layout
|
||||
break :flat_type Layout.str();
|
||||
}
|
||||
|
||||
// TODO special-case the builtin Num type here.
|
||||
// If we have one of those, then convert it to a Num layout,
|
||||
// or to a runtime error if it's an invalid elem type.
|
||||
|
|
|
|||
|
|
@ -34,28 +34,6 @@ const LayoutTest = struct {
|
|||
}
|
||||
};
|
||||
|
||||
test "addTypeVar - basic scalar types" {
|
||||
const gpa = testing.allocator;
|
||||
var module_env = try ModuleEnv.init(gpa, "");
|
||||
defer module_env.deinit();
|
||||
|
||||
var type_store = try types_store.Store.init(gpa);
|
||||
defer type_store.deinit();
|
||||
|
||||
var layout_store = try Store.init(&module_env, &type_store);
|
||||
defer layout_store.deinit();
|
||||
|
||||
var type_scope = TypeScope.init(gpa);
|
||||
defer type_scope.deinit();
|
||||
|
||||
// Test string type
|
||||
const str_var = try type_store.freshFromContent(.{ .structure = .str });
|
||||
const str_layout_idx = try layout_store.addTypeVar(str_var, &type_scope);
|
||||
const str_layout = layout_store.getLayout(str_layout_idx);
|
||||
try testing.expect(str_layout.tag == .scalar);
|
||||
try testing.expectEqual(layout.ScalarTag.str, str_layout.data.scalar.tag);
|
||||
}
|
||||
|
||||
test "addTypeVar - bool type" {
|
||||
var lt: LayoutTest = undefined;
|
||||
lt.gpa = testing.allocator;
|
||||
|
|
@ -135,39 +113,9 @@ test "addTypeVar - host opaque types compile to opaque_ptr" {
|
|||
try testing.expectEqual(layout.Idx.opaque_ptr, box_rigid_layout.data.box);
|
||||
}
|
||||
|
||||
test "addTypeVar - scalar optimization for containers" {
|
||||
var lt: LayoutTest = undefined;
|
||||
lt.gpa = testing.allocator;
|
||||
lt.module_env = try ModuleEnv.init(lt.gpa, "");
|
||||
lt.type_store = try types_store.Store.init(lt.gpa);
|
||||
lt.layout_store = try Store.init(<.module_env, <.type_store);
|
||||
lt.type_scope = TypeScope.init(lt.gpa);
|
||||
defer lt.deinit();
|
||||
|
||||
// Test List(Scalar)
|
||||
const str_var = try lt.type_store.freshFromContent(.{ .structure = .str });
|
||||
const list_str_var = try lt.type_store.freshFromContent(.{ .structure = .{ .list = str_var } });
|
||||
const list_layout_idx = try lt.layout_store.addTypeVar(list_str_var, <.type_scope);
|
||||
const list_layout = lt.layout_store.getLayout(list_layout_idx);
|
||||
try testing.expect(list_layout.tag == .list);
|
||||
try testing.expectEqual(layout.Idx.str, list_layout.data.list);
|
||||
|
||||
// Test Box(Scalar)
|
||||
const box_str_var = try lt.type_store.freshFromContent(.{ .structure = .{ .box = str_var } });
|
||||
const box_layout_idx = try lt.layout_store.addTypeVar(box_str_var, <.type_scope);
|
||||
const box_layout = lt.layout_store.getLayout(box_layout_idx);
|
||||
try testing.expect(box_layout.tag == .box);
|
||||
try testing.expectEqual(layout.Idx.str, box_layout.data.box);
|
||||
|
||||
// Test List(Box(Scalar)) - outer container uses index, inner uses scalar optimization
|
||||
const list_box_str_var = try lt.type_store.freshFromContent(.{ .structure = .{ .list = box_str_var } });
|
||||
const list_box_idx = try lt.layout_store.addTypeVar(list_box_str_var, <.type_scope);
|
||||
const list_box_layout = lt.layout_store.getLayout(list_box_idx);
|
||||
try testing.expect(list_box_layout.tag == .list);
|
||||
const inner_box_layout = lt.layout_store.getLayout(list_box_layout.data.list);
|
||||
try testing.expect(inner_box_layout.tag == .box);
|
||||
try testing.expectEqual(layout.Idx.str, inner_box_layout.data.box);
|
||||
}
|
||||
// Test deleted: was using .empty_record as a fake string type, which is nonsense.
|
||||
// .empty_record is a zero-sized type and has nothing to do with Str.
|
||||
// Proper string layout testing requires loading the actual Builtin module.
|
||||
|
||||
test "addTypeVar - zero-sized types (ZST)" {
|
||||
var lt: LayoutTest = undefined;
|
||||
|
|
@ -204,22 +152,22 @@ test "addTypeVar - record with dropped zero-sized fields" {
|
|||
lt.type_scope = TypeScope.init(lt.gpa);
|
||||
defer lt.deinit();
|
||||
|
||||
const str_var = try lt.type_store.freshFromContent(.{ .structure = .str });
|
||||
const empty_record_var = try lt.type_store.freshFromContent(.{ .structure = .empty_record });
|
||||
const zst_var1 = try lt.type_store.freshFromContent(.{ .structure = .empty_record });
|
||||
const zst_var2 = try lt.type_store.freshFromContent(.{ .structure = .empty_record });
|
||||
const i32_var = try lt.type_store.freshFromContent(.{ .structure = .{ .num = .{ .num_compact = .{ .int = .i32 } } } });
|
||||
|
||||
const fields = try lt.type_store.record_fields.appendSlice(lt.gpa, &[_]types.RecordField{
|
||||
.{ .name = try lt.module_env.insertIdent(Ident.for_text("name")), .var_ = str_var },
|
||||
.{ .name = try lt.module_env.insertIdent(Ident.for_text("empty")), .var_ = empty_record_var },
|
||||
.{ .name = try lt.module_env.insertIdent(Ident.for_text("zst1")), .var_ = zst_var1 },
|
||||
.{ .name = try lt.module_env.insertIdent(Ident.for_text("zst2")), .var_ = zst_var2 },
|
||||
.{ .name = try lt.module_env.insertIdent(Ident.for_text("age")), .var_ = i32_var },
|
||||
});
|
||||
const record_var = try lt.type_store.freshFromContent(.{ .structure = .{ .record = .{ .fields = fields, .ext = empty_record_var } } });
|
||||
const record_var = try lt.type_store.freshFromContent(.{ .structure = .{ .record = .{ .fields = fields, .ext = zst_var2 } } });
|
||||
const record_idx = try lt.layout_store.addTypeVar(record_var, <.type_scope);
|
||||
const record_layout = lt.layout_store.getLayout(record_idx);
|
||||
|
||||
try testing.expect(record_layout.tag == .record);
|
||||
const field_slice = lt.layout_store.record_fields.sliceRange(lt.layout_store.getRecordData(record_layout.data.record.idx).getFields());
|
||||
try testing.expectEqual(@as(usize, 2), field_slice.len); // "empty" field should be dropped
|
||||
try testing.expectEqual(@as(usize, 1), field_slice.len); // Both ZST fields should be dropped, only "age" remains
|
||||
}
|
||||
|
||||
test "addTypeVar - record with only zero-sized fields errors" {
|
||||
|
|
@ -338,9 +286,9 @@ test "record with chained extensions" {
|
|||
const middle_fields = try lt.type_store.record_fields.appendSlice(lt.gpa, &.{.{ .name = try lt.module_env.insertIdent(Ident.for_text("y")), .var_ = f64_var }});
|
||||
const middle_rec = try lt.type_store.freshFromContent(.{ .structure = .{ .record = .{ .fields = middle_fields, .ext = inner_rec } } });
|
||||
|
||||
// Outer: { x: str } extends middle
|
||||
const str_var = try lt.type_store.freshFromContent(.{ .structure = .str });
|
||||
const outer_fields = try lt.type_store.record_fields.appendSlice(lt.gpa, &.{.{ .name = try lt.module_env.insertIdent(Ident.for_text("x")), .var_ = str_var }});
|
||||
// Outer: { x: zst } extends middle - zst field will be dropped
|
||||
const zst_var = try lt.type_store.freshFromContent(.{ .structure = .empty_record });
|
||||
const outer_fields = try lt.type_store.record_fields.appendSlice(lt.gpa, &.{.{ .name = try lt.module_env.insertIdent(Ident.for_text("x")), .var_ = zst_var }});
|
||||
const outer_rec_var = try lt.type_store.freshFromContent(.{ .structure = .{ .record = .{ .fields = outer_fields, .ext = middle_rec } } });
|
||||
|
||||
const record_idx = try lt.layout_store.addTypeVar(outer_rec_var, <.type_scope);
|
||||
|
|
@ -348,12 +296,11 @@ test "record with chained extensions" {
|
|||
try testing.expect(record_layout.tag == .record);
|
||||
|
||||
const field_slice = lt.layout_store.record_fields.sliceRange(lt.layout_store.getRecordData(record_layout.data.record.idx).getFields());
|
||||
try testing.expectEqual(@as(usize, 3), field_slice.len);
|
||||
try testing.expectEqual(@as(usize, 2), field_slice.len); // x (zst) is dropped, only y and z remain
|
||||
|
||||
// Expected order by alignment: x (str), y (f64), z (u8)
|
||||
try testing.expectEqualStrings("x", lt.module_env.getIdent(field_slice.get(0).name));
|
||||
try testing.expectEqualStrings("y", lt.module_env.getIdent(field_slice.get(1).name));
|
||||
try testing.expectEqualStrings("z", lt.module_env.getIdent(field_slice.get(2).name));
|
||||
// Expected order by alignment: y (f64), z (u8)
|
||||
try testing.expectEqualStrings("y", lt.module_env.getIdent(field_slice.get(0).name));
|
||||
try testing.expectEqualStrings("z", lt.module_env.getIdent(field_slice.get(1).name));
|
||||
}
|
||||
|
||||
test "record extension with non-record type fails" {
|
||||
|
|
@ -365,12 +312,12 @@ test "record extension with non-record type fails" {
|
|||
lt.type_scope = TypeScope.init(lt.gpa);
|
||||
defer lt.deinit();
|
||||
|
||||
const str_var = try lt.type_store.freshFromContent(.{ .structure = .str });
|
||||
const fields = try lt.type_store.record_fields.appendSlice(lt.gpa, &.{.{ .name = try lt.module_env.insertIdent(Ident.for_text("field")), .var_ = str_var }});
|
||||
const zst_var = try lt.type_store.freshFromContent(.{ .structure = .empty_record });
|
||||
const fields = try lt.type_store.record_fields.appendSlice(lt.gpa, &.{.{ .name = try lt.module_env.insertIdent(Ident.for_text("field")), .var_ = zst_var }});
|
||||
|
||||
// Try to extend a str, which is invalid
|
||||
const record_var = try lt.type_store.freshFromContent(.{ .structure = .{ .record = .{ .fields = fields, .ext = str_var } } });
|
||||
try testing.expectError(LayoutError.InvalidRecordExtension, lt.layout_store.addTypeVar(record_var, <.type_scope));
|
||||
// Try to extend a zst (empty_record), which is invalid - can only extend records or empty_record
|
||||
const record_var = try lt.type_store.freshFromContent(.{ .structure = .{ .record = .{ .fields = fields, .ext = zst_var } } });
|
||||
try testing.expectError(LayoutError.ZeroSizedType, lt.layout_store.addTypeVar(record_var, <.type_scope));
|
||||
}
|
||||
|
||||
test "deeply nested containers with inner ZST" {
|
||||
|
|
|
|||
|
|
@ -1005,12 +1005,15 @@ fn compileSource(source: []const u8) !CompilerStageData {
|
|||
result.bool_stmt = bool_stmt_in_builtin_module;
|
||||
result.builtin_types = eval.BuiltinTypes.init(builtin_indices, builtin_module.env, builtin_module.env, builtin_module.env);
|
||||
|
||||
const str_stmt_in_builtin_module = builtin_indices.str_type;
|
||||
|
||||
const module_common_idents: Check.CommonIdents = .{
|
||||
.module_name = try module_env.insertIdent(base.Ident.for_text("main")),
|
||||
.list = try module_env.insertIdent(base.Ident.for_text("List")),
|
||||
.box = try module_env.insertIdent(base.Ident.for_text("Box")),
|
||||
.bool_stmt = bool_stmt_in_builtin_module,
|
||||
.try_stmt = try_stmt_in_builtin_module,
|
||||
.str_stmt = str_stmt_in_builtin_module,
|
||||
.builtin_module = builtin_module.env,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -392,10 +392,11 @@ pub const Repl = struct {
|
|||
const cir = module_env; // CIR is now just ModuleEnv
|
||||
try cir.initCIRFields(self.allocator, "repl");
|
||||
|
||||
// Get Bool and Try statement indices from the IMPORTED modules (not copied!)
|
||||
// These refer to the actual statements in the Bool/Try modules
|
||||
// Get Bool, Try, and Str statement indices from the IMPORTED modules (not copied!)
|
||||
// These refer to the actual statements in the Builtin module
|
||||
const bool_stmt_in_bool_module = self.builtin_indices.bool_type;
|
||||
const try_stmt_in_try_module = self.builtin_indices.try_type;
|
||||
const str_stmt_in_builtin_module = self.builtin_indices.str_type;
|
||||
|
||||
const module_common_idents: Check.CommonIdents = .{
|
||||
.module_name = try module_env.insertIdent(base.Ident.for_text("repl")),
|
||||
|
|
@ -403,6 +404,7 @@ pub const Repl = struct {
|
|||
.box = try module_env.insertIdent(base.Ident.for_text("Box")),
|
||||
.bool_stmt = bool_stmt_in_bool_module,
|
||||
.try_stmt = try_stmt_in_try_module,
|
||||
.str_stmt = str_stmt_in_builtin_module,
|
||||
.builtin_module = self.builtin_module.env,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -311,9 +311,10 @@ test "Repl - minimal interpreter integration" {
|
|||
const cir = &module_env; // CIR is now just ModuleEnv
|
||||
try cir.initCIRFields(gpa, "test");
|
||||
|
||||
// Get Bool and Try statement indices from the builtin module
|
||||
// Get Bool, Try, and Str statement indices from the builtin module
|
||||
const bool_stmt_in_builtin_module = builtin_indices.bool_type;
|
||||
const try_stmt_in_builtin_module = builtin_indices.try_type;
|
||||
const str_stmt_in_builtin_module = builtin_indices.str_type;
|
||||
|
||||
const common_idents: Check.CommonIdents = .{
|
||||
.module_name = try cir.insertIdent(base.Ident.for_text("test")),
|
||||
|
|
@ -321,6 +322,7 @@ test "Repl - minimal interpreter integration" {
|
|||
.box = try cir.insertIdent(base.Ident.for_text("Box")),
|
||||
.bool_stmt = bool_stmt_in_builtin_module,
|
||||
.try_stmt = try_stmt_in_builtin_module,
|
||||
.str_stmt = str_stmt_in_builtin_module,
|
||||
.builtin_module = builtin_module.env,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1218,6 +1218,7 @@ fn processSnapshotContent(
|
|||
.box = try can_ir.insertIdent(base.Ident.for_text("Box")),
|
||||
.bool_stmt = config.builtin_indices.bool_type,
|
||||
.try_stmt = config.builtin_indices.try_type,
|
||||
.str_stmt = config.builtin_indices.str_type,
|
||||
.builtin_module = config.builtin_module,
|
||||
};
|
||||
|
||||
|
|
@ -1226,32 +1227,15 @@ fn processSnapshotContent(
|
|||
var module_envs = std.AutoHashMap(base.Ident.Idx, Can.AutoImportedType).init(allocator);
|
||||
defer module_envs.deinit();
|
||||
|
||||
// Register each builtin type individually with its statement index
|
||||
// They all point to the same Builtin module env
|
||||
// Note: Str is NOT added because it's handled as a primitive builtin type
|
||||
// in TypeAnno.Builtin.fromBytes() and should never go through module_envs
|
||||
// Populate module_envs with builtin types using the standard helper
|
||||
// This ensures production and tests use identical logic
|
||||
if (config.builtin_module) |builtin_env| {
|
||||
const bool_ident = try can_ir.common.idents.insert(allocator, base.Ident.for_text("Bool"));
|
||||
const try_ident = try can_ir.common.idents.insert(allocator, base.Ident.for_text("Try"));
|
||||
const dict_ident = try can_ir.common.idents.insert(allocator, base.Ident.for_text("Dict"));
|
||||
const set_ident = try can_ir.common.idents.insert(allocator, base.Ident.for_text("Set"));
|
||||
|
||||
try module_envs.put(bool_ident, .{
|
||||
.env = builtin_env,
|
||||
.statement_idx = config.builtin_indices.bool_type,
|
||||
});
|
||||
try module_envs.put(try_ident, .{
|
||||
.env = builtin_env,
|
||||
.statement_idx = config.builtin_indices.try_type,
|
||||
});
|
||||
try module_envs.put(dict_ident, .{
|
||||
.env = builtin_env,
|
||||
.statement_idx = config.builtin_indices.dict_type,
|
||||
});
|
||||
try module_envs.put(set_ident, .{
|
||||
.env = builtin_env,
|
||||
.statement_idx = config.builtin_indices.set_type,
|
||||
});
|
||||
try Can.populateModuleEnvs(
|
||||
&module_envs,
|
||||
can_ir,
|
||||
builtin_env,
|
||||
config.builtin_indices,
|
||||
);
|
||||
}
|
||||
|
||||
var czer = try Can.init(can_ir, &parse_ast, &module_envs);
|
||||
|
|
|
|||
|
|
@ -422,9 +422,6 @@ fn writeAlias(self: *TypeWriter, alias: Alias, root_var: Var) std.mem.Allocator.
|
|||
/// Convert a flat type to a type string
|
||||
fn writeFlatType(self: *TypeWriter, flat_type: FlatType, root_var: Var) std.mem.Allocator.Error!void {
|
||||
switch (flat_type) {
|
||||
.str => {
|
||||
_ = try self.buf.writer().write("Str");
|
||||
},
|
||||
.box => |sub_var| {
|
||||
_ = try self.buf.writer().write("Box(");
|
||||
try self.writeVar(sub_var, root_var);
|
||||
|
|
@ -982,7 +979,7 @@ fn countVar(self: *TypeWriter, search_var: Var, current_var: Var, count: *usize)
|
|||
|
||||
fn countVarInFlatType(self: *TypeWriter, search_var: Var, flat_type: FlatType, count: *usize) std.mem.Allocator.Error!void {
|
||||
switch (flat_type) {
|
||||
.str, .empty_record, .empty_tag_union => {},
|
||||
.empty_record, .empty_tag_union => {},
|
||||
.box => |sub_var| try self.countVar(search_var, sub_var, count),
|
||||
.list => |sub_var| try self.countVar(search_var, sub_var, count),
|
||||
.list_unbound, .num => {},
|
||||
|
|
|
|||
|
|
@ -307,7 +307,7 @@ pub const Generalizer = struct {
|
|||
},
|
||||
.structure => |flat_type| {
|
||||
switch (flat_type) {
|
||||
.str, .empty_record, .empty_tag_union => return Rank.top_level,
|
||||
.empty_record, .empty_tag_union => return Rank.top_level,
|
||||
.list_unbound => {
|
||||
// Unbounds are special-cased: An unbound represents a
|
||||
// flex var _at the same rank_ as the unbound list. So,
|
||||
|
|
|
|||
|
|
@ -221,7 +221,6 @@ pub const Instantiator = struct {
|
|||
|
||||
fn instantiateFlatType(self: *Self, flat_type: FlatType) std.mem.Allocator.Error!FlatType {
|
||||
return switch (flat_type) {
|
||||
.str => FlatType.str,
|
||||
.box => |box_var| FlatType{ .box = try self.instantiateVar(box_var) },
|
||||
.list => |list_var| FlatType{ .list = try self.instantiateVar(list_var) },
|
||||
.list_unbound => FlatType.list_unbound,
|
||||
|
|
|
|||
|
|
@ -398,7 +398,6 @@ pub const Store = struct {
|
|||
|
||||
pub fn needsInstantiationFlatType(self: *const Self, flat_type: FlatType) bool {
|
||||
return switch (flat_type) {
|
||||
.str => false,
|
||||
.box => |box_var| self.needsInstantiation(box_var),
|
||||
.list => |list_var| self.needsInstantiation(list_var),
|
||||
.list_unbound => true,
|
||||
|
|
@ -1294,7 +1293,7 @@ test "Store comprehensive CompactWriter roundtrip" {
|
|||
|
||||
// Create various types
|
||||
const flex = try original.fresh();
|
||||
const str_var = try original.freshFromContent(Content{ .structure = .{ .str = {} } });
|
||||
const str_var = try original.freshFromContent(Content{ .structure = .empty_record });
|
||||
const list_elem = try original.fresh();
|
||||
const list_var = try original.freshFromContent(Content{ .structure = .{ .list = list_elem } });
|
||||
|
||||
|
|
@ -1357,7 +1356,7 @@ test "Store comprehensive CompactWriter roundtrip" {
|
|||
|
||||
// Verify all types
|
||||
const deser_str = deserialized.resolveVar(str_var);
|
||||
try std.testing.expectEqual(Content{ .structure = .{ .str = {} } }, deser_str.desc.content);
|
||||
try std.testing.expectEqual(Content{ .structure = .empty_record }, deser_str.desc.content);
|
||||
|
||||
const deser_list = deserialized.resolveVar(list_var);
|
||||
try std.testing.expectEqual(FlatType{ .list = list_elem }, deser_list.desc.content.structure);
|
||||
|
|
@ -1475,7 +1474,7 @@ test "DescStore.Serialized roundtrip" {
|
|||
.mark = Mark.none,
|
||||
};
|
||||
const desc2 = Descriptor{
|
||||
.content = Content{ .structure = .{ .str = {} } },
|
||||
.content = Content{ .structure = .empty_record },
|
||||
.rank = Rank.top_level,
|
||||
.mark = Mark.visited,
|
||||
};
|
||||
|
|
@ -1534,7 +1533,7 @@ test "Store.Serialized roundtrip" {
|
|||
|
||||
// Create some type variables
|
||||
const flex = try store.fresh();
|
||||
const str_var = try store.freshFromContent(Content{ .structure = .{ .str = {} } });
|
||||
const str_var = try store.freshFromContent(Content{ .structure = .empty_record });
|
||||
const redirect_var = try store.freshRedirect(flex);
|
||||
|
||||
// Create temp file
|
||||
|
|
@ -1575,7 +1574,7 @@ test "Store.Serialized roundtrip" {
|
|||
try std.testing.expectEqual(Content{ .flex = Flex.init() }, flex_resolved.desc.content);
|
||||
|
||||
const str_resolved = deserialized.resolveVar(str_var);
|
||||
try std.testing.expectEqual(Content{ .structure = .{ .str = {} } }, str_resolved.desc.content);
|
||||
try std.testing.expectEqual(Content{ .structure = .empty_record }, str_resolved.desc.content);
|
||||
|
||||
const redirect_resolved = deserialized.resolveVar(redirect_var);
|
||||
try std.testing.expectEqual(flex_resolved.desc_idx, redirect_resolved.desc_idx);
|
||||
|
|
@ -1597,7 +1596,7 @@ test "Store multiple instances CompactWriter roundtrip" {
|
|||
|
||||
// Populate differently
|
||||
const var1_1 = try store1.fresh();
|
||||
const var1_2 = try store1.freshFromContent(Content{ .structure = .{ .str = {} } });
|
||||
const var1_2 = try store1.freshFromContent(Content{ .structure = .empty_record });
|
||||
_ = try store1.freshRedirect(var1_1);
|
||||
|
||||
const var2_1 = try store2.fresh();
|
||||
|
|
@ -1655,7 +1654,7 @@ test "Store multiple instances CompactWriter roundtrip" {
|
|||
// Verify store 1
|
||||
try std.testing.expectEqual(@as(usize, 3), deserialized1.len());
|
||||
const deser1_var2 = deserialized1.resolveVar(var1_2);
|
||||
try std.testing.expectEqual(Content{ .structure = .{ .str = {} } }, deser1_var2.desc.content);
|
||||
try std.testing.expectEqual(Content{ .structure = .empty_record }, deser1_var2.desc.content);
|
||||
|
||||
// Verify store 2
|
||||
try std.testing.expectEqual(@as(usize, 3), deserialized2.len());
|
||||
|
|
@ -1673,7 +1672,7 @@ test "SlotStore and DescStore serialization and deserialization" {
|
|||
|
||||
// Create several variables to populate SlotStore with roots
|
||||
const var1 = try original.freshFromContent(Content{ .flex = Flex.init() });
|
||||
const var2 = try original.freshFromContent(Content{ .structure = .{ .str = {} } });
|
||||
const var2 = try original.freshFromContent(Content{ .structure = .empty_record });
|
||||
const var3 = try original.freshFromContent(Content{ .rigid = Rigid.init(@bitCast(@as(u32, 123))) });
|
||||
|
||||
// Create redirects to populate SlotStore with redirects
|
||||
|
|
@ -1730,7 +1729,7 @@ test "SlotStore and DescStore serialization and deserialization" {
|
|||
try std.testing.expectEqual(Content{ .flex = Flex.init() }, resolved1.desc.content);
|
||||
|
||||
const resolved2 = deserialized.resolveVar(var2);
|
||||
try std.testing.expectEqual(Content{ .structure = .{ .str = {} } }, resolved2.desc.content);
|
||||
try std.testing.expectEqual(Content{ .structure = .empty_record }, resolved2.desc.content);
|
||||
|
||||
const resolved3 = deserialized.resolveVar(var3);
|
||||
try std.testing.expectEqual(Content{ .rigid = Rigid.init(@bitCast(@as(u32, 123))) }, resolved3.desc.content);
|
||||
|
|
|
|||
|
|
@ -393,7 +393,6 @@ pub const TypeIdent = struct {
|
|||
/// Represents type without indirection, it's the concrete form that a type
|
||||
/// takes after resolving type variables and aliases.
|
||||
pub const FlatType = union(enum) {
|
||||
str,
|
||||
box: Var,
|
||||
list: Var,
|
||||
list_unbound,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue