ir: dedicated TypeId.unresolved sentinel; kill inferred_type => .s64
An unannotated param resolving to a plausible .s64 was the classic silent-default trap (root of the 2.5 multi-param-closure bug). Replace it with a dedicated TypeId.unresolved at slot 0, so a zero-initialised or forgotten TypeId trips the sentinel instead of masquerading as a real type. - types.zig: TypeId.unresolved = 0 (void moves to 17); TypeInfo.unresolved; sizeOf/toLLVMType @panic on it (codegen tripwire); hash/eql/printer cover it. - type_bridge: inferred_type => .unresolved (was .s64). - resolveParamType: emit "parameter 'x' has no type annotation" for a genuinely-unannotated value param (comptime/variadic/pack params exempt -- they resolve via per-call substitution). - lowerLambda: resolve unannotated params from the target closure signature; otherwise emit "cannot infer type of lambda parameter". - CLAUDE.md: .void documented as an UNACCEPTABLE failed-type sentinel (it conflates with a real, heavily-checked type); prescribe a distinct .unresolved-style value + codegen tripwire. Snapshot churn: one .ir (ffi-objc-call-06) -- the runtime type-name table and typeof match arms renumber by the new builtin slot; program output unchanged.
This commit is contained in:
@@ -7101,13 +7101,22 @@ pub const Lowering = struct {
|
||||
// User params follow the ctx (optional) + env slots in `params`.
|
||||
const user_param_base: usize = (if (lambda_wants_ctx) @as(usize, 1) else 0) + 1;
|
||||
for (lam.params, 0..) |p, pi| {
|
||||
var pty = self.resolveParamType(&p);
|
||||
// Infer param type from target closure type if no annotation
|
||||
if (p.type_expr.data == .inferred_type and target_closure_params != null) {
|
||||
if (pi < target_closure_params.?.len) {
|
||||
pty = target_closure_params.?[pi];
|
||||
const pty: TypeId = blk: {
|
||||
// Unannotated lambda params take their type positionally from
|
||||
// the target `Closure(T0, …)` signature. Resolve them here so
|
||||
// `resolveParamType` (which would diagnose a missing annotation)
|
||||
// is only called for params that carry one.
|
||||
if (p.type_expr.data == .inferred_type) {
|
||||
if (target_closure_params != null and pi < target_closure_params.?.len) {
|
||||
break :blk target_closure_params.?[pi];
|
||||
}
|
||||
if (self.diagnostics) |d| {
|
||||
d.addFmt(.err, p.type_expr.span, "cannot infer type of lambda parameter '{s}'; annotate it or use the lambda where a closure type is expected", .{p.name});
|
||||
}
|
||||
break :blk .unresolved;
|
||||
}
|
||||
}
|
||||
break :blk self.resolveParamType(&p);
|
||||
};
|
||||
params.append(self.alloc, .{
|
||||
.name = self.module.types.internString(p.name),
|
||||
.ty = pty,
|
||||
@@ -10743,6 +10752,18 @@ pub const Lowering = struct {
|
||||
}
|
||||
|
||||
fn resolveParamType(self: *Lowering, p: *const ast.Param) TypeId {
|
||||
// A plain value param with no annotation can only be typed from
|
||||
// context (a lambda's target closure signature). When `resolveParamType`
|
||||
// is reached for one, there is no such context — so it's a genuine
|
||||
// "missing annotation" error, not an 8-byte-int guess. (Comptime/
|
||||
// variadic pack params also carry `inferred_type` but get their types
|
||||
// from per-call substitution, so they're exempt here.)
|
||||
if (p.type_expr.data == .inferred_type and !p.is_comptime and !p.is_variadic and !p.is_pack) {
|
||||
if (self.diagnostics) |d| {
|
||||
d.addFmt(.err, p.type_expr.span, "parameter '{s}' has no type annotation", .{p.name});
|
||||
}
|
||||
return .unresolved;
|
||||
}
|
||||
const declared_ty = self.resolveTypeWithBindings(p.type_expr);
|
||||
if (p.is_variadic) {
|
||||
// Two surface forms:
|
||||
|
||||
Reference in New Issue
Block a user