Skip to content

Commit d81a717

Browse files
committed
Fix a unification regression
1 parent cc6d869 commit d81a717

File tree

129 files changed

+453
-352
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

129 files changed

+453
-352
lines changed

src/check/Check.zig

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4316,6 +4316,110 @@ fn checkDeferredStaticDispatchConstraints(self: *Self, env: *Env) std.mem.Alloca
43164316
};
43174317

43184318
const result = try self.unify(local_def_var, constraint.fn_var, env);
4319+
4320+
if (false and std.mem.indexOf(u8, qualified_name_bytes, "I128.plus") != null) {
4321+
const resolved_local = self.types.resolveVar(local_def_var);
4322+
std.debug.print("\n=== DEBUG: I128.plus Method Signature ===\n", .{});
4323+
std.debug.print(" Qualified name: {s}\n", .{qualified_name_bytes});
4324+
std.debug.print(" local_def_var: {d}\n", .{@intFromEnum(local_def_var)});
4325+
std.debug.print(" Resolved var: {d}\n", .{@intFromEnum(resolved_local.var_)});
4326+
std.debug.print(" Content: {s}\n", .{@tagName(resolved_local.desc.content)});
4327+
if (resolved_local.desc.content == .structure) {
4328+
const struct_content = resolved_local.desc.content.structure;
4329+
if (struct_content == .fn_pure or struct_content == .fn_effectful or struct_content == .fn_unbound) {
4330+
const func = switch (struct_content) {
4331+
.fn_pure => |f| f,
4332+
.fn_effectful => |f| f,
4333+
.fn_unbound => |f| f,
4334+
else => unreachable,
4335+
};
4336+
const resolved_ret = self.types.resolveVar(func.ret);
4337+
std.debug.print(" Function type: {s}\n", .{@tagName(struct_content)});
4338+
std.debug.print(" Return type var: {d}\n", .{@intFromEnum(func.ret)});
4339+
std.debug.print(" Resolved return var: {d}\n", .{@intFromEnum(resolved_ret.var_)});
4340+
std.debug.print(" Return content: {s}\n", .{@tagName(resolved_ret.desc.content)});
4341+
if (resolved_ret.desc.content == .structure) {
4342+
std.debug.print(" Return structure: {s}\n", .{@tagName(resolved_ret.desc.content.structure)});
4343+
if (resolved_ret.desc.content.structure == .num) {
4344+
const num_val = resolved_ret.desc.content.structure.num;
4345+
std.debug.print(" Num variant: {s}\n", .{@tagName(num_val)});
4346+
if (num_val == .num_compact) {
4347+
const compact = num_val.num_compact;
4348+
std.debug.print(" Compact type: {s}\n", .{@tagName(compact)});
4349+
switch (compact) {
4350+
.int => |int_prec| std.debug.print(" Int precision: {s}\n", .{@tagName(int_prec)}),
4351+
.frac => |frac_prec| std.debug.print(" Frac precision: {s}\n", .{@tagName(frac_prec)}),
4352+
}
4353+
}
4354+
}
4355+
}
4356+
}
4357+
}
4358+
std.debug.print(" constraint.fn_var: {d}\n", .{@intFromEnum(constraint.fn_var)});
4359+
4360+
// Also check what constraint.fn_var looks like
4361+
const resolved_constraint_fn = self.types.resolveVar(constraint.fn_var);
4362+
std.debug.print(" Constraint fn type var: {d}\n", .{@intFromEnum(resolved_constraint_fn.var_)});
4363+
std.debug.print(" Constraint fn content: {s}\n", .{@tagName(resolved_constraint_fn.desc.content)});
4364+
if (resolved_constraint_fn.desc.content == .structure) {
4365+
std.debug.print(" Constraint structure: {s}\n", .{@tagName(resolved_constraint_fn.desc.content.structure)});
4366+
const struct_content = resolved_constraint_fn.desc.content.structure;
4367+
if (struct_content == .fn_pure or struct_content == .fn_effectful or struct_content == .fn_unbound) {
4368+
const constraint_func = switch (struct_content) {
4369+
.fn_pure => |f| f,
4370+
.fn_effectful => |f| f,
4371+
.fn_unbound => |f| f,
4372+
else => unreachable,
4373+
};
4374+
const resolved_constraint_ret = self.types.resolveVar(constraint_func.ret);
4375+
std.debug.print(" Constraint ret var: {d}\n", .{@intFromEnum(constraint_func.ret)});
4376+
std.debug.print(" Constraint ret resolved var: {d}\n", .{@intFromEnum(resolved_constraint_ret.var_)});
4377+
std.debug.print(" Constraint ret content: {s}\n", .{@tagName(resolved_constraint_ret.desc.content)});
4378+
if (resolved_constraint_ret.desc.content == .structure and resolved_constraint_ret.desc.content.structure == .num) {
4379+
std.debug.print(" Constraint ret num: {s}\n", .{@tagName(resolved_constraint_ret.desc.content.structure.num)});
4380+
}
4381+
}
4382+
}
4383+
4384+
std.debug.print(" About to unify local_def_var={d} with constraint.fn_var={d}\n", .{@intFromEnum(local_def_var), @intFromEnum(constraint.fn_var)});
4385+
std.debug.print("==========================================\n", .{});
4386+
}
4387+
4388+
// DEBUG: After unification, check what happened to the return types
4389+
if (false and std.mem.indexOf(u8, qualified_name_bytes, "I128.plus") != null) {
4390+
const resolved_local_after = self.types.resolveVar(local_def_var);
4391+
const resolved_constraint_after = self.types.resolveVar(constraint.fn_var);
4392+
std.debug.print("\n=== AFTER UNIFICATION ===\n", .{});
4393+
std.debug.print(" Unification result: {s}\n", .{@tagName(result)});
4394+
std.debug.print(" local_def_var: {d} -> {d}\n", .{@intFromEnum(local_def_var), @intFromEnum(resolved_local_after.var_)});
4395+
std.debug.print(" constraint.fn_var: {d} -> {d}\n", .{@intFromEnum(constraint.fn_var), @intFromEnum(resolved_constraint_after.var_)});
4396+
4397+
// Check if they unified to the same var
4398+
if (resolved_local_after.var_ == resolved_constraint_after.var_) {
4399+
std.debug.print(" -> Both now point to same var: {d}\n", .{@intFromEnum(resolved_local_after.var_)});
4400+
}
4401+
4402+
// Check the resolved function structure
4403+
if (resolved_constraint_after.desc.content == .structure) {
4404+
const struct_content = resolved_constraint_after.desc.content.structure;
4405+
std.debug.print(" Constraint structure after: {s}\n", .{@tagName(struct_content)});
4406+
if (struct_content == .fn_pure or struct_content == .fn_effectful or struct_content == .fn_unbound) {
4407+
const func_after = switch (struct_content) {
4408+
.fn_pure => |f| f,
4409+
.fn_effectful => |f| f,
4410+
.fn_unbound => |f| f,
4411+
else => unreachable,
4412+
};
4413+
const ret_after = self.types.resolveVar(func_after.ret);
4414+
std.debug.print(" Return var: {d} -> {d}\n", .{@intFromEnum(func_after.ret), @intFromEnum(ret_after.var_)});
4415+
std.debug.print(" Return content: {s}\n", .{@tagName(ret_after.desc.content)});
4416+
if (ret_after.desc.content == .structure and ret_after.desc.content.structure == .num) {
4417+
std.debug.print(" Return num: {s}\n", .{@tagName(ret_after.desc.content.structure.num)});
4418+
}
4419+
}
4420+
}
4421+
std.debug.print("=========================\n", .{});
4422+
}
43194423
if (result.isProblem()) {
43204424
try self.unifyWith(deferred_constraint.var_, .err, env);
43214425
try self.unifyWith(resolved_func.ret, .err, env);

src/check/unify.zig

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2024,6 +2024,23 @@ const Unifier = struct {
20242024
.int => return error.TypeMismatch, // int can't unify with frac_unbound
20252025
}
20262026
},
2027+
.num_unbound_if_builtin => |b_reqs| {
2028+
// Concrete type wins over num_unbound_if_builtin
2029+
// Similar to num_unbound case, but we need to check requirements
2030+
if (b_reqs.constraints.len() > 0) {
2031+
// Defer constraint checking
2032+
_ = self.scratch.deferred_constraints.append(self.scratch.gpa, DeferredConstraintCheck{
2033+
.var_ = vars.a.var_,
2034+
.constraints = b_reqs.constraints,
2035+
}) catch return Error.AllocatorError;
2036+
}
2037+
2038+
try self.unifyCompactAndUnboundNums(
2039+
vars,
2040+
a_num_compact,
2041+
b_reqs,
2042+
);
2043+
},
20272044
else => return error.TypeMismatch,
20282045
}
20292046
},

src/eval/interpreter.zig

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4593,20 +4593,7 @@ pub const Interpreter = struct {
45934593
/// Get layout directly from compile-time type var (no translation needed)
45944594
/// Uses the cache for the CURRENT module (self.env), which allows cross-module jumps to work.
45954595
pub fn getLayoutFromCompileVar(self: *Interpreter, compile_var: types.Var) !layout.Layout {
4596-
std.debug.print("\n=== DEBUG: getLayoutFromCompileVar called ===\n", .{});
4597-
std.debug.print(" compile_var: {d}\n", .{@intFromEnum(compile_var)});
4598-
std.debug.print(" current module env: {*}\n", .{self.env});
4599-
std.debug.print(" runtime_layout_store.env: {*}\n", .{self.runtime_layout_store.env});
4600-
std.debug.print(" runtime_layout_store.types_store: {*}\n", .{self.runtime_layout_store.types_store});
4601-
std.debug.print(" &self.env.types: {*}\n", .{&self.env.types});
4602-
std.debug.print(" MATCH: {}\n", .{self.runtime_layout_store.types_store == &self.env.types});
46034596
const resolved = self.env.types.resolveVar(compile_var);
4604-
std.debug.print(" resolved to var: {d}, content: {s}\n", .{@intFromEnum(resolved.var_), @tagName(resolved.desc.content)});
4605-
if (resolved.desc.content == .err) {
4606-
std.debug.print(" ❌ ERROR: Type var resolved to .err!\n", .{});
4607-
std.debug.print(" This means var {d} contains .err in THIS module's type store\n", .{@intFromEnum(resolved.var_)});
4608-
std.debug.print(" So the question is: WHY does the type store contain .err for var {d}?\n", .{@intFromEnum(resolved.var_)});
4609-
}
46104597

46114598
// Get the cache for the CURRENT module (self.env changes as we jump between modules!)
46124599
const cache = try self.getOrCreateCacheForModule(self.env);

src/eval/test/helpers.zig

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -500,17 +500,7 @@ pub fn parseAndCanonicalizeExpr(allocator: std.mem.Allocator, source: []const u8
500500
checker.* = try Check.init(allocator, &module_env.types, module_env, &imported_envs, &module_envs_map, &module_env.store.regions, common_idents);
501501

502502
// Type check the expression
503-
_ = try checker.checkExprRepl(canonical_expr_idx);
504-
505-
// DEBUG: Check if there are any diagnostics after type checking
506-
const diagnostics = module_env.store.sliceDiagnostics(module_env.diagnostics);
507-
if (diagnostics.len > 0) {
508-
std.debug.print("\n⚠️ Type checking produced {d} diagnostic(s)!\n", .{diagnostics.len});
509-
for (diagnostics, 0..) |diag_idx, i| {
510-
const diag = module_env.store.getDiagnostic(diag_idx);
511-
std.debug.print(" Diagnostic {d}: {s}\n", .{i, @tagName(diag)});
512-
}
513-
}
503+
try checker.checkExprRepl(canonical_expr_idx);
514504

515505
const builtin_types = BuiltinTypes.init(builtin_indices, builtin_module.env, builtin_module.env, builtin_module.env, builtin_module.env);
516506
return .{

src/layout/store.zig

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1449,7 +1449,10 @@ pub const Store = struct {
14491449
current = self.types_store.resolveVar(rec_var.structure);
14501450
continue;
14511451
},
1452-
.err => {
1452+
.err => |err_info| {
1453+
std.debug.print("\n=== ERROR TYPE ENCOUNTERED IN LAYOUT ===\n", .{});
1454+
std.debug.print(" Type var: {d}\n", .{@intFromEnum(current.var_)});
1455+
std.debug.print(" Error info: {any}\n", .{err_info});
14531456
return LayoutError.TypeContainedMismatch;
14541457
},
14551458
};

test/snapshots/binops.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,5 +177,5 @@ EndOfFile,
177177
~~~
178178
# TYPES
179179
~~~clojure
180-
(expr (type "(_size, _size2, _size3, _size4, _size5, Bool, Bool, Bool, Bool, Bool, Bool, _size6, Bool, Bool, _field) where [_a.from_int_digits : _arg -> _ret, _b.from_dec_digits : _arg -> _ret]"))
180+
(expr (type "(_size, _size2, _size3, _size4, _size5, Bool, Bool, Bool, Bool, Bool, Bool, _size6, Bool, Bool, _field) where [_a.from_int_digits : _arg -> _ret]"))
181181
~~~

test/snapshots/can_basic_scoping.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -149,11 +149,11 @@ outerFunc = |_| {
149149
~~~clojure
150150
(inferred-types
151151
(defs
152-
(patt (type "_size where [_a.from_int_digits : _arg -> _ret, _b.from_dec_digits : _arg -> _ret]"))
153-
(patt (type "_size where [_a.from_int_digits : _arg -> _ret, _b.from_dec_digits : _arg -> _ret]"))
154-
(patt (type "_arg -> _size where [_a.from_int_digits : _arg -> _ret, _b.from_dec_digits : _arg -> _ret]")))
152+
(patt (type "_size where [_a.from_int_digits : _arg -> _ret]"))
153+
(patt (type "_size where [_a.from_int_digits : _arg -> _ret]"))
154+
(patt (type "_arg -> _size where [_a.from_int_digits : _arg -> _ret]")))
155155
(expressions
156-
(expr (type "_size where [_a.from_int_digits : _arg -> _ret, _b.from_dec_digits : _arg -> _ret]"))
157-
(expr (type "_size where [_a.from_int_digits : _arg -> _ret, _b.from_dec_digits : _arg -> _ret]"))
158-
(expr (type "_arg -> _size where [_a.from_int_digits : _arg -> _ret, _b.from_dec_digits : _arg -> _ret]"))))
156+
(expr (type "_size where [_a.from_int_digits : _arg -> _ret]"))
157+
(expr (type "_size where [_a.from_int_digits : _arg -> _ret]"))
158+
(expr (type "_arg -> _size where [_a.from_int_digits : _arg -> _ret]"))))
159159
~~~

test/snapshots/can_hex_integer.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ NO CHANGE
4040
~~~clojure
4141
(inferred-types
4242
(defs
43-
(patt (type "_size where [_a.from_int_digits : _arg -> _ret, _b.from_dec_digits : _arg -> _ret]")))
43+
(patt (type "_size where [_a.from_int_digits : _arg -> _ret]")))
4444
(expressions
45-
(expr (type "_size where [_a.from_int_digits : _arg -> _ret, _b.from_dec_digits : _arg -> _ret]"))))
45+
(expr (type "_size where [_a.from_int_digits : _arg -> _ret]"))))
4646
~~~

test/snapshots/can_two_decls.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,9 @@ NO CHANGE
6464
~~~clojure
6565
(inferred-types
6666
(defs
67-
(patt (type "_size where [_c.from_int_digits : _arg -> _ret, _d.from_dec_digits : _arg -> _ret]"))
68-
(patt (type "_size where [_c.from_int_digits : _arg -> _ret, _d.from_dec_digits : _arg -> _ret]")))
67+
(patt (type "_size where [_c.from_int_digits : _arg -> _ret]"))
68+
(patt (type "_size where [_c.from_int_digits : _arg -> _ret]")))
6969
(expressions
70-
(expr (type "_size where [_c.from_int_digits : _arg -> _ret, _d.from_dec_digits : _arg -> _ret]"))
71-
(expr (type "_size where [_c.from_int_digits : _arg -> _ret, _d.from_dec_digits : _arg -> _ret]"))))
70+
(expr (type "_size where [_c.from_int_digits : _arg -> _ret]"))
71+
(expr (type "_size where [_c.from_int_digits : _arg -> _ret]"))))
7272
~~~

test/snapshots/can_var_scoping_invalid_top_level.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ topLevelVar_ = 0
5353
~~~clojure
5454
(inferred-types
5555
(defs
56-
(patt (type "_size where [_a.from_int_digits : _arg -> _ret, _b.from_dec_digits : _arg -> _ret]")))
56+
(patt (type "_size where [_a.from_int_digits : _arg -> _ret]")))
5757
(expressions
58-
(expr (type "_size where [_a.from_int_digits : _arg -> _ret, _b.from_dec_digits : _arg -> _ret]"))))
58+
(expr (type "_size where [_a.from_int_digits : _arg -> _ret]"))))
5959
~~~

0 commit comments

Comments
 (0)