mirror of
https://codeberg.org/ziglang/zig.git
synced 2026-04-26 13:01:34 +03:00
compiler: make dependency loop errors good
This commit is contained in:
@@ -243,12 +243,14 @@ fn renderErrorMessage(
|
||||
}
|
||||
try t.setColor(.reset);
|
||||
if (src.data.source_line != 0 and options.include_source_line) {
|
||||
try w.splatByteAll(' ', indent);
|
||||
const line = eb.nullTerminatedString(src.data.source_line);
|
||||
for (line) |b| switch (b) {
|
||||
'\t' => try w.writeByte(' '),
|
||||
else => try w.writeByte(b),
|
||||
};
|
||||
try w.writeByte('\n');
|
||||
try w.splatByteAll(' ', indent);
|
||||
// TODO basic unicode code point monospace width
|
||||
const before_caret = src.data.span_main - src.data.span_start;
|
||||
// -1 since span.main includes the caret
|
||||
@@ -267,11 +269,13 @@ fn renderErrorMessage(
|
||||
if (src.data.reference_trace_len > 0 and options.include_reference_trace) {
|
||||
try t.setColor(.reset);
|
||||
try t.setColor(.dim);
|
||||
try w.splatByteAll(' ', indent);
|
||||
try w.print("referenced by:\n", .{});
|
||||
var ref_index = src.end;
|
||||
for (0..src.data.reference_trace_len) |_| {
|
||||
const ref_trace = eb.extraData(ReferenceTrace, ref_index);
|
||||
ref_index = ref_trace.end;
|
||||
try w.splatByteAll(' ', indent);
|
||||
if (ref_trace.data.src_loc != .none) {
|
||||
const ref_src = eb.getSourceLocation(ref_trace.data.src_loc);
|
||||
try w.print(" {s}: {s}:{d}:{d}\n", .{
|
||||
|
||||
+14
-101
@@ -4189,6 +4189,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) error{OutOfMemory}!ErrorBundle {
|
||||
}
|
||||
}
|
||||
}
|
||||
try zcu.addDependencyLoopErrors(&bundle);
|
||||
for (zcu.failed_codegen.values()) |error_msg| {
|
||||
try addModuleErrorMsg(zcu, &bundle, error_msg.*, false);
|
||||
}
|
||||
@@ -4208,7 +4209,7 @@ pub fn getAllErrorsAlloc(comp: *Compilation) error{OutOfMemory}!ErrorBundle {
|
||||
.notes_len = 1,
|
||||
});
|
||||
const notes_start = try bundle.reserveNotes(1);
|
||||
bundle.extra.items[notes_start] = @intFromEnum(try bundle.addErrorMessage(.{
|
||||
bundle.extra.items[notes_start] = @intFromEnum(bundle.addErrorMessageAssumeCapacity(.{
|
||||
.msg = try bundle.printString("use '--error-limit {d}' to increase limit", .{
|
||||
actual_error_count,
|
||||
}),
|
||||
@@ -4230,10 +4231,10 @@ pub fn getAllErrorsAlloc(comp: *Compilation) error{OutOfMemory}!ErrorBundle {
|
||||
.notes_len = 2,
|
||||
});
|
||||
const notes_start = try bundle.reserveNotes(2);
|
||||
bundle.extra.items[notes_start + 0] = @intFromEnum(try bundle.addErrorMessage(.{
|
||||
bundle.extra.items[notes_start + 0] = @intFromEnum(bundle.addErrorMessageAssumeCapacity(.{
|
||||
.msg = try bundle.addString("run 'zig libc -h' to learn about libc installations"),
|
||||
}));
|
||||
bundle.extra.items[notes_start + 1] = @intFromEnum(try bundle.addErrorMessage(.{
|
||||
bundle.extra.items[notes_start + 1] = @intFromEnum(bundle.addErrorMessageAssumeCapacity(.{
|
||||
.msg = try bundle.addString("run 'zig targets' to see the targets for which zig can always provide libc"),
|
||||
}));
|
||||
}
|
||||
@@ -4428,7 +4429,6 @@ pub fn addModuleErrorMsg(
|
||||
already_added_error: bool,
|
||||
) Allocator.Error!void {
|
||||
const gpa = eb.gpa;
|
||||
const ip = &zcu.intern_pool;
|
||||
const err_src_loc = module_err_msg.src_loc.upgrade(zcu);
|
||||
const err_source = err_src_loc.file_scope.getSource(zcu) catch |err| {
|
||||
return unableToLoadZcuFile(zcu, eb, err_src_loc.file_scope, err);
|
||||
@@ -4441,66 +4441,12 @@ pub fn addModuleErrorMsg(
|
||||
var ref_traces: std.ArrayList(ErrorBundle.ReferenceTrace) = .empty;
|
||||
defer ref_traces.deinit(gpa);
|
||||
|
||||
rt: {
|
||||
const rt_root = module_err_msg.reference_trace_root.unwrap() orelse break :rt;
|
||||
const max_references = zcu.comp.reference_trace orelse refs: {
|
||||
if (already_added_error) break :rt;
|
||||
if (module_err_msg.reference_trace_root.unwrap()) |root| {
|
||||
const frame_limit: u32 = zcu.comp.reference_trace orelse refs: {
|
||||
if (already_added_error) break :refs 0;
|
||||
break :refs default_reference_trace_len;
|
||||
};
|
||||
|
||||
const all_references = try zcu.resolveReferences();
|
||||
|
||||
var seen: std.AutoHashMapUnmanaged(InternPool.AnalUnit, void) = .empty;
|
||||
defer seen.deinit(gpa);
|
||||
|
||||
var referenced_by = rt_root;
|
||||
while (all_references.get(referenced_by)) |maybe_ref| {
|
||||
const ref = maybe_ref orelse break;
|
||||
const gop = try seen.getOrPut(gpa, ref.referencer);
|
||||
if (gop.found_existing) break;
|
||||
if (ref_traces.items.len < max_references) {
|
||||
var last_call_src = ref.src;
|
||||
var opt_inline_frame = ref.inline_frame;
|
||||
while (opt_inline_frame.unwrap()) |inline_frame| {
|
||||
const f = inline_frame.ptr(zcu).*;
|
||||
const func_nav = ip.indexToKey(f.callee).func.owner_nav;
|
||||
const func_name = ip.getNav(func_nav).name.toSlice(ip);
|
||||
addReferenceTraceFrame(zcu, eb, &ref_traces, func_name, last_call_src, true) catch |err| switch (err) {
|
||||
error.OutOfMemory => |e| return e,
|
||||
error.AlreadyReported => {
|
||||
// An incomplete reference trace isn't the end of the world; just cut it off.
|
||||
break :rt;
|
||||
},
|
||||
};
|
||||
last_call_src = f.call_src;
|
||||
opt_inline_frame = f.parent;
|
||||
}
|
||||
const root_name: ?[]const u8 = switch (ref.referencer.unwrap()) {
|
||||
.@"comptime" => "comptime",
|
||||
.nav_val, .nav_ty => |nav| ip.getNav(nav).name.toSlice(ip),
|
||||
.type_layout => |ty| Type.fromInterned(ty).containerTypeName(ip).toSlice(ip),
|
||||
.func => |f| ip.getNav(zcu.funcInfo(f).owner_nav).name.toSlice(ip),
|
||||
.memoized_state => null,
|
||||
};
|
||||
if (root_name) |n| {
|
||||
addReferenceTraceFrame(zcu, eb, &ref_traces, n, last_call_src, false) catch |err| switch (err) {
|
||||
error.OutOfMemory => |e| return e,
|
||||
error.AlreadyReported => {
|
||||
// An incomplete reference trace isn't the end of the world; just cut it off.
|
||||
break :rt;
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
referenced_by = ref.referencer;
|
||||
}
|
||||
|
||||
if (seen.count() > ref_traces.items.len) {
|
||||
try ref_traces.append(gpa, .{
|
||||
.decl_name = @intCast(seen.count() - ref_traces.items.len),
|
||||
.src_loc = .none,
|
||||
});
|
||||
}
|
||||
try zcu.populateReferenceTrace(root, frame_limit, eb, &ref_traces);
|
||||
}
|
||||
|
||||
const src_loc = try eb.addSourceLocation(.{
|
||||
@@ -4565,43 +4511,10 @@ pub fn addModuleErrorMsg(
|
||||
const notes_start = try eb.reserveNotes(notes_len);
|
||||
|
||||
for (notes_start.., notes.keys()) |i, note| {
|
||||
eb.extra.items[i] = @intFromEnum(try eb.addErrorMessage(note));
|
||||
eb.extra.items[i] = @intFromEnum(eb.addErrorMessageAssumeCapacity(note));
|
||||
}
|
||||
}
|
||||
|
||||
fn addReferenceTraceFrame(
|
||||
zcu: *Zcu,
|
||||
eb: *ErrorBundle.Wip,
|
||||
ref_traces: *std.ArrayList(ErrorBundle.ReferenceTrace),
|
||||
name: []const u8,
|
||||
lazy_src: Zcu.LazySrcLoc,
|
||||
inlined: bool,
|
||||
) error{ OutOfMemory, AlreadyReported }!void {
|
||||
const gpa = zcu.gpa;
|
||||
const src = lazy_src.upgrade(zcu);
|
||||
const source = src.file_scope.getSource(zcu) catch |err| {
|
||||
try unableToLoadZcuFile(zcu, eb, src.file_scope, err);
|
||||
return error.AlreadyReported;
|
||||
};
|
||||
const span = src.span(zcu) catch |err| {
|
||||
try unableToLoadZcuFile(zcu, eb, src.file_scope, err);
|
||||
return error.AlreadyReported;
|
||||
};
|
||||
const loc = std.zig.findLineColumn(source, span.main);
|
||||
try ref_traces.append(gpa, .{
|
||||
.decl_name = try eb.printString("{s}{s}", .{ name, if (inlined) " [inlined]" else "" }),
|
||||
.src_loc = try eb.addSourceLocation(.{
|
||||
.src_path = try eb.printString("{f}", .{src.file_scope.path.fmt(zcu.comp)}),
|
||||
.span_start = span.start,
|
||||
.span_main = span.main,
|
||||
.span_end = span.end,
|
||||
.line = @intCast(loc.line),
|
||||
.column = @intCast(loc.column),
|
||||
.source_line = 0,
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
fn addWholeFileError(
|
||||
zcu: *Zcu,
|
||||
eb: *ErrorBundle.Wip,
|
||||
@@ -5243,11 +5156,11 @@ fn processOneJob(tid: Zcu.PerThread.Id, comp: *Compilation, job: Job) JobError!v
|
||||
|
||||
const maybe_err: Zcu.SemaError!void = switch (unit.unwrap()) {
|
||||
.@"comptime" => |cu| pt.ensureComptimeUnitUpToDate(cu),
|
||||
.nav_ty => |nav| pt.ensureNavTypeUpToDate(nav),
|
||||
.nav_val => |nav| pt.ensureNavValUpToDate(nav),
|
||||
.type_layout => |ty| pt.ensureTypeLayoutUpToDate(.fromInterned(ty)),
|
||||
.memoized_state => |stage| pt.ensureMemoizedStateUpToDate(stage),
|
||||
.func => |func| pt.ensureFuncBodyUpToDate(func),
|
||||
.nav_ty => |nav| pt.ensureNavTypeUpToDate(nav, null),
|
||||
.nav_val => |nav| pt.ensureNavValUpToDate(nav, null),
|
||||
.type_layout => |ty| pt.ensureTypeLayoutUpToDate(.fromInterned(ty), null),
|
||||
.memoized_state => |stage| pt.ensureMemoizedStateUpToDate(stage, null),
|
||||
.func => |func| pt.ensureFuncBodyUpToDate(func, null),
|
||||
};
|
||||
maybe_err catch |err| switch (err) {
|
||||
error.OutOfMemory => |e| return e,
|
||||
|
||||
+149
-113
@@ -3260,7 +3260,7 @@ fn zirAllocExtended(
|
||||
} else .none;
|
||||
|
||||
if (small.has_type) {
|
||||
try sema.ensureLayoutResolved(var_ty, ty_src);
|
||||
try sema.ensureLayoutResolved(var_ty, var_src, if (small.is_const) .constant else .variable);
|
||||
if (block.isComptime() or small.is_comptime or var_ty.comptimeOnly(zcu)) {
|
||||
return sema.analyzeComptimeAlloc(block, var_src, var_ty, alignment);
|
||||
}
|
||||
@@ -3324,7 +3324,7 @@ fn zirAllocComptime(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr
|
||||
const ty_src = block.src(.{ .node_offset_var_decl_ty = inst_data.src_node });
|
||||
const var_src = block.nodeOffset(inst_data.src_node);
|
||||
const var_ty = try sema.resolveType(block, ty_src, inst_data.operand);
|
||||
try sema.ensureLayoutResolved(var_ty, ty_src);
|
||||
try sema.ensureLayoutResolved(var_ty, var_src, .variable);
|
||||
return sema.analyzeComptimeAlloc(block, var_src, var_ty, .none);
|
||||
}
|
||||
|
||||
@@ -3758,7 +3758,7 @@ fn zirAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
|
||||
const var_src = block.nodeOffset(inst_data.src_node);
|
||||
|
||||
const var_ty = try sema.resolveType(block, ty_src, inst_data.operand);
|
||||
try sema.ensureLayoutResolved(var_ty, ty_src);
|
||||
try sema.ensureLayoutResolved(var_ty, var_src, .constant);
|
||||
if (block.isComptime() or var_ty.comptimeOnly(zcu)) {
|
||||
return sema.analyzeComptimeAlloc(block, var_src, var_ty, .none);
|
||||
}
|
||||
@@ -3790,7 +3790,7 @@ fn zirAllocMut(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
|
||||
const var_src = block.nodeOffset(inst_data.src_node);
|
||||
|
||||
const var_ty = try sema.resolveType(block, ty_src, inst_data.operand);
|
||||
try sema.ensureLayoutResolved(var_ty, ty_src);
|
||||
try sema.ensureLayoutResolved(var_ty, var_src, .variable);
|
||||
if (block.isComptime()) {
|
||||
return sema.analyzeComptimeAlloc(block, var_src, var_ty, .none);
|
||||
}
|
||||
@@ -4148,7 +4148,7 @@ fn zirOptEuBasePtrInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Compile
|
||||
const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
|
||||
const ptr = sema.resolveInst(un_node.operand);
|
||||
const src = block.nodeOffset(un_node.src_node);
|
||||
try sema.ensureLayoutResolved(sema.typeOf(ptr).childType(sema.pt.zcu), src);
|
||||
try sema.ensureLayoutResolved(sema.typeOf(ptr).childType(sema.pt.zcu), src, .init);
|
||||
return sema.optEuBasePtrInit(block, ptr, src);
|
||||
}
|
||||
|
||||
@@ -4651,7 +4651,7 @@ fn zirValidateDeref(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr
|
||||
}
|
||||
|
||||
const elem_ty = operand_ty.childType(zcu);
|
||||
try sema.ensureLayoutResolved(elem_ty, src);
|
||||
try sema.ensureLayoutResolved(elem_ty, src, .ptr_access);
|
||||
|
||||
const need_comptime = switch (elem_ty.classify(zcu)) {
|
||||
.no_possible_value => return sema.fail(block, src, "cannot load {s} type '{f}'", .{
|
||||
@@ -7037,7 +7037,7 @@ fn analyzeCall(
|
||||
|
||||
break :ret_ty full_ty;
|
||||
};
|
||||
try sema.ensureLayoutResolved(resolved_ret_ty, func_ret_ty_src);
|
||||
try sema.ensureLayoutResolved(resolved_ret_ty, func_ret_ty_src, .return_type);
|
||||
|
||||
// If we've discovered after evaluating arguments that a generic function instantiation is
|
||||
// comptime-only, then we can mark the block as comptime *now*.
|
||||
@@ -7134,7 +7134,6 @@ fn analyzeCall(
|
||||
.generic_owner = func_val.?.toIntern(),
|
||||
.comptime_args = comptime_args,
|
||||
});
|
||||
try sema.ensureLayoutResolved(.fromInterned(ip.typeOf(func_instance)), call_src);
|
||||
if (zcu.comp.debugIncremental()) {
|
||||
const nav = ip.indexToKey(func_instance).func.owner_nav;
|
||||
const gop = try zcu.incremental_debug_state.navs.getOrPut(gpa, nav);
|
||||
@@ -7169,7 +7168,7 @@ fn analyzeCall(
|
||||
};
|
||||
|
||||
try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Call).@"struct".fields.len + runtime_args.len);
|
||||
const maybe_opv = try block.addInst(.{
|
||||
const call_ref = try block.addInst(.{
|
||||
.tag = call_tag,
|
||||
.data = .{ .pl_op = .{
|
||||
.operand = runtime_func,
|
||||
@@ -7180,8 +7179,10 @@ fn analyzeCall(
|
||||
});
|
||||
sema.appendRefsAssumeCapacity(runtime_args);
|
||||
|
||||
const actual_ret_ty = sema.typeOf(call_ref);
|
||||
|
||||
if (ensure_result_used) {
|
||||
try sema.ensureResultUsed(block, sema.typeOf(maybe_opv), call_src);
|
||||
try sema.ensureResultUsed(block, actual_ret_ty, call_src);
|
||||
}
|
||||
|
||||
if (call_tag == .call_always_tail) {
|
||||
@@ -7191,28 +7192,31 @@ fn analyzeCall(
|
||||
.pointer => func_or_ptr_ty.childType(zcu),
|
||||
else => unreachable,
|
||||
};
|
||||
return sema.handleTailCall(block, call_src, runtime_func_ty, maybe_opv);
|
||||
return sema.handleTailCall(block, call_src, runtime_func_ty, call_ref);
|
||||
}
|
||||
|
||||
if (resolved_ret_ty.isNoReturn(zcu)) {
|
||||
const want_check = c: {
|
||||
if (!block.wantSafety()) break :c false;
|
||||
if (func_val != null) break :c false;
|
||||
break :c true;
|
||||
};
|
||||
if (want_check) {
|
||||
try sema.safetyPanic(block, call_src, .noreturn_returned);
|
||||
} else {
|
||||
_ = try block.addNoOp(.unreach);
|
||||
}
|
||||
return .unreachable_value;
|
||||
}
|
||||
|
||||
try sema.ensureLayoutResolved(sema.typeOf(maybe_opv), func_ret_ty_src);
|
||||
if (try sema.typeOf(maybe_opv).onePossibleValue(pt)) |opv| {
|
||||
return .fromValue(opv);
|
||||
} else {
|
||||
return maybe_opv;
|
||||
switch (actual_ret_ty.classify(zcu)) {
|
||||
.no_possible_value => {
|
||||
const want_check = c: {
|
||||
if (!block.wantSafety()) break :c false;
|
||||
if (func_val != null) break :c false;
|
||||
break :c true;
|
||||
};
|
||||
if (want_check) {
|
||||
try sema.safetyPanic(block, call_src, .noreturn_returned);
|
||||
} else {
|
||||
_ = try block.addNoOp(.unreach);
|
||||
}
|
||||
return .unreachable_value;
|
||||
},
|
||||
.one_possible_value => {
|
||||
return .fromValue((try actual_ret_ty.onePossibleValue(pt)).?);
|
||||
},
|
||||
.runtime => {
|
||||
return call_ref;
|
||||
},
|
||||
.partially_comptime => unreachable,
|
||||
.fully_comptime => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7279,11 +7283,6 @@ fn analyzeCall(
|
||||
}
|
||||
}
|
||||
|
||||
// We're about to do an inline call; if the return type expression was generic, the return type
|
||||
// may not be resolved yet. It's correct to resolve it because the function is going to return a
|
||||
// value of this type.
|
||||
try sema.ensureLayoutResolved(resolved_ret_ty, func_ret_ty_src);
|
||||
|
||||
// For an inline call, we depend on the source code of the whole function definition.
|
||||
try sema.declareDependency(.{ .src_hash = fn_nav.analysis.?.zir_index });
|
||||
|
||||
@@ -8051,7 +8050,7 @@ fn zirEnumFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
|
||||
if (dest_ty.zigTypeTag(zcu) != .@"enum") {
|
||||
return sema.fail(block, src, "expected enum, found '{f}'", .{dest_ty.fmt(pt)});
|
||||
}
|
||||
try sema.ensureLayoutResolved(dest_ty, src);
|
||||
try sema.ensureLayoutResolved(dest_ty, src, .init);
|
||||
_ = try sema.checkIntType(block, operand_src, operand_ty);
|
||||
|
||||
if (sema.resolveValue(operand)) |int_val| {
|
||||
@@ -8114,7 +8113,7 @@ fn zirOptionalPayloadPtr(
|
||||
|
||||
const ptr_ty = sema.typeOf(optional_ptr);
|
||||
assert(ptr_ty.zigTypeTag(sema.pt.zcu) == .pointer);
|
||||
try sema.ensureLayoutResolved(ptr_ty.childType(sema.pt.zcu), src);
|
||||
try sema.ensureLayoutResolved(ptr_ty.childType(sema.pt.zcu), src, .ptr_access);
|
||||
|
||||
return sema.analyzeOptionalPayloadPtr(block, src, optional_ptr, safety_check, false);
|
||||
}
|
||||
@@ -8311,7 +8310,7 @@ fn zirErrUnionPayloadPtr(
|
||||
|
||||
const ptr_ty = sema.typeOf(operand);
|
||||
assert(ptr_ty.zigTypeTag(sema.pt.zcu) == .pointer);
|
||||
try sema.ensureLayoutResolved(ptr_ty.childType(sema.pt.zcu), src);
|
||||
try sema.ensureLayoutResolved(ptr_ty.childType(sema.pt.zcu), src, .ptr_access);
|
||||
|
||||
return sema.analyzeErrUnionPayloadPtr(block, src, operand, false, false);
|
||||
}
|
||||
@@ -9020,7 +9019,6 @@ fn funcCommon(
|
||||
const io = comp.io;
|
||||
const ip = &zcu.intern_pool;
|
||||
|
||||
const src = block.nodeOffset(src_node_offset);
|
||||
const ret_ty_src = block.src(.{ .node_offset_fn_type_ret_ty = src_node_offset });
|
||||
const cc_src = block.src(.{ .node_offset_fn_type_cc = src_node_offset });
|
||||
|
||||
@@ -9090,7 +9088,7 @@ fn funcCommon(
|
||||
.lbrace_column = @as(u16, @truncate(src_locs.columns)),
|
||||
.rbrace_column = @as(u16, @truncate(src_locs.columns >> 16)),
|
||||
}));
|
||||
try sema.ensureLayoutResolved(func_val.typeOf(zcu), src);
|
||||
try sema.ensureLayoutResolved(func_val.typeOf(zcu), ret_ty_src, .return_type);
|
||||
return .fromValue(func_val);
|
||||
}
|
||||
|
||||
@@ -9105,7 +9103,7 @@ fn funcCommon(
|
||||
});
|
||||
|
||||
if (has_body) {
|
||||
try sema.ensureLayoutResolved(.fromInterned(func_ty), src);
|
||||
try sema.ensureLayoutResolved(.fromInterned(func_ty), ret_ty_src, .return_type);
|
||||
return .fromIntern(try ip.getFuncDecl(gpa, io, pt.tid, .{
|
||||
.owner_nav = sema.owner.unwrap().nav_val,
|
||||
.ty = func_ty,
|
||||
@@ -9761,7 +9759,7 @@ fn zirElemPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
|
||||
return sema.failWithOwnedErrorMsg(block, msg);
|
||||
}
|
||||
try sema.checkIndexable(block, src, indexable_ty);
|
||||
try sema.ensureLayoutResolved(indexable_ty.childType(zcu), src);
|
||||
try sema.ensureLayoutResolved(indexable_ty.childType(zcu), src, .ptr_access);
|
||||
return sema.elemPtrOneLayerOnly(block, src, array_ptr, elem_index, src, false, false);
|
||||
}
|
||||
|
||||
@@ -9975,14 +9973,15 @@ fn zirSwitchBlockErrUnion(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Comp
|
||||
const raw_operand_ty = sema.typeOf(eu_maybe_ptr);
|
||||
if (!non_err_case.operand_is_ref) break :err_union_ty raw_operand_ty;
|
||||
try sema.checkPtrOperand(block, operand_src, raw_operand_ty);
|
||||
break :err_union_ty raw_operand_ty.childType(zcu);
|
||||
const child_ty = raw_operand_ty.childType(zcu);
|
||||
try sema.ensureLayoutResolved(child_ty, operand_src, .ptr_access);
|
||||
break :err_union_ty child_ty;
|
||||
};
|
||||
if (err_union_ty.zigTypeTag(zcu) != .error_union) {
|
||||
return sema.fail(block, operand_src, "expected error union type, found '{f}'", .{
|
||||
err_union_ty.fmt(pt),
|
||||
});
|
||||
}
|
||||
try sema.ensureLayoutResolved(err_union_ty, operand_src);
|
||||
|
||||
const non_err_cond = if (non_err_case.operand_is_ref)
|
||||
try sema.analyzePtrIsNonErr(block, operand_src, eu_maybe_ptr)
|
||||
@@ -11281,11 +11280,12 @@ fn validateSwitchBlock(
|
||||
const raw_operand_ty = sema.typeOf(raw_operand);
|
||||
if (operand_is_ref) {
|
||||
try sema.checkPtrType(block, operand_src, raw_operand_ty, false);
|
||||
break :operand_ty raw_operand_ty.childType(zcu);
|
||||
const child_ty = raw_operand_ty.childType(zcu);
|
||||
try sema.ensureLayoutResolved(child_ty, operand_src, .ptr_access);
|
||||
break :operand_ty child_ty;
|
||||
}
|
||||
break :operand_ty raw_operand_ty;
|
||||
};
|
||||
try sema.ensureLayoutResolved(operand_ty, operand_src);
|
||||
|
||||
const item_ty: Type = item_ty: {
|
||||
switch (operand_ty.zigTypeTag(zcu)) {
|
||||
@@ -12844,7 +12844,7 @@ fn zirHasField(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
|
||||
const name_src = block.builtinCallArgSrc(inst_data.src_node, 1);
|
||||
const ty = try sema.resolveType(block, ty_src, extra.lhs);
|
||||
const field_name = try sema.resolveConstStringIntern(block, name_src, extra.rhs, .{ .simple = .field_name });
|
||||
try sema.ensureLayoutResolved(ty, ty_src);
|
||||
try sema.ensureLayoutResolved(ty, ty_src, .field_queried);
|
||||
const ip = &zcu.intern_pool;
|
||||
|
||||
const has_field = hf: {
|
||||
@@ -15194,6 +15194,7 @@ fn analyzeArithmetic(
|
||||
});
|
||||
}
|
||||
|
||||
try sema.ensureLayoutResolved(lhs_ty.childType(zcu), src, .ptr_offset);
|
||||
const elem_size = lhs_ty.childType(zcu).abiSize(zcu);
|
||||
if (elem_size == 0) {
|
||||
return sema.fail(block, src, "pointer subtraction requires element type '{f}' to have runtime bits", .{
|
||||
@@ -15246,7 +15247,7 @@ fn analyzeArithmetic(
|
||||
else => return sema.failWithInvalidPtrArithmetic(block, src, "pointer-integer", "addition and subtraction"),
|
||||
};
|
||||
|
||||
try sema.ensureLayoutResolved(lhs_ty.childType(zcu), src);
|
||||
try sema.ensureLayoutResolved(lhs_ty.childType(zcu), src, .ptr_offset);
|
||||
return sema.analyzePtrArithmetic(block, src, lhs, rhs, air_tag, rhs_src);
|
||||
},
|
||||
}
|
||||
@@ -15879,7 +15880,7 @@ fn zirSizeOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
|
||||
const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
|
||||
const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
|
||||
const ty = try sema.resolveType(block, operand_src, inst_data.operand);
|
||||
try sema.ensureLayoutResolved(ty, operand_src);
|
||||
try sema.ensureLayoutResolved(ty, operand_src, .size_of);
|
||||
switch (ty.classify(zcu)) {
|
||||
.no_possible_value,
|
||||
=> return sema.fail(block, operand_src, "no size available for uninstantiable type '{f}'", .{ty.fmt(pt)}),
|
||||
@@ -15934,7 +15935,7 @@ fn zirBitSizeOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
|
||||
.@"anyframe",
|
||||
=> {},
|
||||
}
|
||||
try sema.ensureLayoutResolved(operand_ty, operand_src);
|
||||
try sema.ensureLayoutResolved(operand_ty, operand_src, .size_of);
|
||||
return .fromValue(try pt.intValue(.comptime_int, operand_ty.bitSize(zcu)));
|
||||
}
|
||||
|
||||
@@ -16185,7 +16186,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
|
||||
const type_info_ty = try sema.getBuiltinType(src, .Type);
|
||||
const type_info_tag_ty = type_info_ty.unionTagType(zcu).?;
|
||||
|
||||
try sema.ensureLayoutResolved(ty, src);
|
||||
try sema.ensureLayoutResolved(ty, src, .type_info);
|
||||
|
||||
if (ty.typeDeclInst(zcu)) |type_decl_inst| {
|
||||
try sema.declareDependency(.{ .namespace = type_decl_inst });
|
||||
@@ -16345,7 +16346,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
|
||||
const alignment_val = try pt.intValue(.comptime_int, bytes: {
|
||||
if (info.flags.alignment.toByteUnits()) |b| break :bytes b;
|
||||
const elem_ty: Type = .fromInterned(info.child);
|
||||
try sema.ensureLayoutResolved(elem_ty, src);
|
||||
try sema.ensureLayoutResolved(elem_ty, src, .type_info);
|
||||
break :bytes elem_ty.abiAlignment(zcu).toByteUnits().?;
|
||||
});
|
||||
|
||||
@@ -18233,7 +18234,7 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
|
||||
elem_ty.fmt(pt), bit_offset, bit_offset - host_size * 8, host_size,
|
||||
});
|
||||
}
|
||||
try sema.ensureLayoutResolved(elem_ty, elem_ty_src);
|
||||
try sema.ensureLayoutResolved(elem_ty, elem_ty_src, .bit_ptr_child);
|
||||
const elem_bit_size = elem_ty.bitSize(zcu);
|
||||
if (elem_bit_size > host_size * 8 - bit_offset) {
|
||||
return sema.fail(block, bitoffset_src, "packed type '{f}' at bit offset {d} ends {d} bits after the end of a {d} byte host integer", .{
|
||||
@@ -18302,7 +18303,7 @@ fn zirStructInitEmpty(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
|
||||
const pt = sema.pt;
|
||||
const zcu = pt.zcu;
|
||||
|
||||
try sema.ensureLayoutResolved(obj_ty, ty_src);
|
||||
try sema.ensureLayoutResolved(obj_ty, ty_src, .init);
|
||||
|
||||
switch (obj_ty.zigTypeTag(zcu)) {
|
||||
.@"struct" => return sema.structInitEmpty(block, obj_ty, src, src),
|
||||
@@ -18367,7 +18368,7 @@ fn zirStructInitEmptyResult(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is
|
||||
});
|
||||
} else ty_operand;
|
||||
|
||||
try sema.ensureLayoutResolved(init_ty, src);
|
||||
try sema.ensureLayoutResolved(init_ty, src, .init);
|
||||
|
||||
const obj_ty = init_ty.optEuBaseType(zcu);
|
||||
|
||||
@@ -18486,7 +18487,7 @@ fn zirStructInit(
|
||||
// The type wasn't actually known, so treat this as an anon struct init.
|
||||
return sema.structInitAnon(block, src, inst, .typed_init, extra.data, extra.end, is_ref);
|
||||
};
|
||||
try sema.ensureLayoutResolved(result_ty, src);
|
||||
try sema.ensureLayoutResolved(result_ty, src, .init);
|
||||
const resolved_ty = result_ty.optEuBaseType(zcu);
|
||||
|
||||
if (resolved_ty.zigTypeTag(zcu) == .@"struct") {
|
||||
@@ -18951,7 +18952,7 @@ fn structInitAnon(
|
||||
};
|
||||
try sema.addTypeReferenceEntry(src, struct_ty);
|
||||
// No need for `ensureNamespaceUpToDate` because this type's namespace is always empty.
|
||||
try sema.ensureLayoutResolved(struct_ty, src);
|
||||
try sema.ensureLayoutResolved(struct_ty, src, .init);
|
||||
|
||||
_ = opt_runtime_index orelse {
|
||||
const struct_val = try pt.aggregateValue(struct_ty, values);
|
||||
@@ -19270,7 +19271,7 @@ fn zirFieldTypeRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro
|
||||
const field_src = block.builtinCallArgSrc(inst_data.src_node, 1);
|
||||
const aggregate_ty = try sema.resolveType(block, ty_src, extra.container_type);
|
||||
const field_name = try sema.resolveConstStringIntern(block, field_src, extra.field_name, .{ .simple = .field_name });
|
||||
try sema.ensureLayoutResolved(aggregate_ty, ty_src);
|
||||
try sema.ensureLayoutResolved(aggregate_ty, ty_src, .field_queried);
|
||||
return sema.fieldType(block, aggregate_ty, field_name, field_src, ty_src);
|
||||
}
|
||||
|
||||
@@ -19290,7 +19291,7 @@ fn zirStructInitFieldType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Comp
|
||||
const aggregate_ty = wrapped_aggregate_ty.optEuBaseType(zcu);
|
||||
const zir_field_name = sema.code.nullTerminatedString(extra.name_start);
|
||||
const field_name = try ip.getOrPutString(gpa, io, pt.tid, zir_field_name, .no_embedded_nulls);
|
||||
try sema.ensureLayoutResolved(aggregate_ty, ty_src);
|
||||
try sema.ensureLayoutResolved(aggregate_ty, ty_src, .init);
|
||||
return sema.fieldType(block, aggregate_ty, field_name, field_name_src, ty_src);
|
||||
}
|
||||
|
||||
@@ -19390,7 +19391,7 @@ fn zirAlignOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
|
||||
const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
|
||||
const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
|
||||
const ty = try sema.resolveType(block, operand_src, inst_data.operand);
|
||||
try sema.ensureLayoutResolved(ty, operand_src);
|
||||
try sema.ensureLayoutResolved(ty, operand_src, .align_of);
|
||||
if (ty.isNoReturn(zcu)) {
|
||||
return sema.fail(block, operand_src, "no align available for type '{f}'", .{ty.fmt(sema.pt)});
|
||||
}
|
||||
@@ -19874,7 +19875,6 @@ fn zirReifyFn(
|
||||
const param_attrs_arr = try sema.derefSliceAsArray(block, param_attrs_src, param_attrs_slice, .{ .simple = .fn_param_attrs });
|
||||
|
||||
const ret_ty = try sema.resolveType(block, ret_ty_src, extra.ret_ty);
|
||||
try sema.ensureLayoutResolved(ret_ty, ret_ty_src);
|
||||
|
||||
const fn_attrs_uncoerced = sema.resolveInst(extra.fn_attrs);
|
||||
const fn_attrs_coerced = try sema.coerce(block, fn_attrs_ty, fn_attrs_uncoerced, fn_attrs_src);
|
||||
@@ -19899,10 +19899,6 @@ fn zirReifyFn(
|
||||
param_types_src,
|
||||
fn_attrs.@"callconv",
|
||||
);
|
||||
try sema.ensureLayoutResolved(param_ty, param_types_src);
|
||||
if (param_ty.comptimeOnly(zcu)) {
|
||||
return sema.fail(block, param_attrs_src, "cannot reify function type with comptime-only parameter type '{f}'", .{param_ty.fmt(pt)});
|
||||
}
|
||||
if (param_attrs.@"noalias") {
|
||||
if (param_idx > 31) {
|
||||
return sema.fail(block, param_attrs_src, "this compiler implementation only supports 'noalias' on the first 32 parameters", .{});
|
||||
@@ -20811,8 +20807,7 @@ fn zirPtrFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
|
||||
|
||||
const elem_ty = ptr_ty.nullablePtrElem(zcu);
|
||||
|
||||
// We'll need to validate the pointer alignment.
|
||||
try sema.ensureLayoutResolved(elem_ty, src);
|
||||
try sema.ensureLayoutResolved(elem_ty, src, .align_check);
|
||||
const ptr_align = ptr_ty.ptrAlignment(zcu);
|
||||
|
||||
if (ptr_ty.isSlice(zcu)) {
|
||||
@@ -21155,8 +21150,8 @@ fn ptrCastFull(
|
||||
const src_info = operand_ty.ptrInfo(zcu);
|
||||
const dest_info = dest_ty.ptrInfo(zcu);
|
||||
|
||||
try sema.ensureLayoutResolved(.fromInterned(src_info.child), operand_src);
|
||||
try sema.ensureLayoutResolved(.fromInterned(dest_info.child), src);
|
||||
try sema.ensureLayoutResolved(.fromInterned(src_info.child), operand_src, .align_check);
|
||||
try sema.ensureLayoutResolved(.fromInterned(dest_info.child), src, .align_check);
|
||||
|
||||
const DestSliceLen = union(enum) {
|
||||
undef,
|
||||
@@ -21927,7 +21922,7 @@ fn bitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!u6
|
||||
const ty = try sema.resolveType(block, ty_src, extra.lhs);
|
||||
const field_name = try sema.resolveConstStringIntern(block, field_name_src, extra.rhs, .{ .simple = .field_name });
|
||||
|
||||
try sema.ensureLayoutResolved(ty, ty_src);
|
||||
try sema.ensureLayoutResolved(ty, ty_src, .field_queried);
|
||||
|
||||
const pt = sema.pt;
|
||||
const zcu = pt.zcu;
|
||||
@@ -23010,7 +23005,7 @@ fn zirAtomicLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
|
||||
const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, true);
|
||||
const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering, .{ .simple = .atomic_order });
|
||||
|
||||
try sema.ensureLayoutResolved(elem_ty, elem_ty_src);
|
||||
try sema.ensureLayoutResolved(elem_ty, elem_ty_src, .ptr_access);
|
||||
|
||||
switch (order) {
|
||||
.release, .acq_rel => {
|
||||
@@ -23330,7 +23325,7 @@ fn zirFieldParentPtr(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.Ins
|
||||
return sema.fail(block, inst_src, "expected single pointer type, found '{f}'", .{parent_ptr_ty.fmt(pt)});
|
||||
}
|
||||
const parent_ty: Type = .fromInterned(parent_ptr_info.child);
|
||||
try sema.ensureLayoutResolved(parent_ty, inst_src);
|
||||
try sema.ensureLayoutResolved(parent_ty, inst_src, .field_used);
|
||||
switch (parent_ty.zigTypeTag(zcu)) {
|
||||
.@"struct", .@"union" => {},
|
||||
else => return sema.fail(block, inst_src, "expected pointer to struct or union type, found '{f}'", .{parent_ptr_ty.fmt(pt)}),
|
||||
@@ -23938,8 +23933,8 @@ fn zirMemcpy(
|
||||
const dest_elem_ty = dest_ty.indexableElem(zcu);
|
||||
const src_elem_ty = src_ty.indexableElem(zcu);
|
||||
|
||||
try sema.ensureLayoutResolved(dest_elem_ty, dest_src);
|
||||
try sema.ensureLayoutResolved(src_elem_ty, src_src);
|
||||
try sema.ensureLayoutResolved(dest_elem_ty, dest_src, .ptr_access);
|
||||
try sema.ensureLayoutResolved(src_elem_ty, src_src, .ptr_access);
|
||||
|
||||
const imc = try sema.coerceInMemoryAllowed(
|
||||
block,
|
||||
@@ -25470,7 +25465,7 @@ fn fieldPtrLoad(
|
||||
const object_ptr_ty = sema.typeOf(object_ptr);
|
||||
assert(object_ptr_ty.zigTypeTag(zcu) == .pointer);
|
||||
const pointee_ty = object_ptr_ty.childType(zcu);
|
||||
try sema.ensureLayoutResolved(pointee_ty, src);
|
||||
try sema.ensureLayoutResolved(pointee_ty, src, .ptr_access);
|
||||
if (try pointee_ty.onePossibleValue(pt)) |opv| {
|
||||
const object: Air.Inst.Ref = .fromValue(opv);
|
||||
return fieldVal(sema, block, src, object, field_name, field_name_src);
|
||||
@@ -25606,7 +25601,7 @@ fn fieldVal(
|
||||
if (try sema.namespaceLookupVal(block, src, child_type.getNamespaceIndex(zcu), field_name)) |inst| {
|
||||
return inst;
|
||||
}
|
||||
try sema.ensureLayoutResolved(child_type, src);
|
||||
try sema.ensureLayoutResolved(child_type, src, .field_used);
|
||||
if (child_type.unionTagType(zcu)) |enum_ty| {
|
||||
if (enum_ty.enumFieldIndex(field_name, zcu)) |field_index_usize| {
|
||||
const field_index: u32 = @intCast(field_index_usize);
|
||||
@@ -25619,7 +25614,7 @@ fn fieldVal(
|
||||
if (try sema.namespaceLookupVal(block, src, child_type.getNamespaceIndex(zcu), field_name)) |inst| {
|
||||
return inst;
|
||||
}
|
||||
try sema.ensureLayoutResolved(child_type, src);
|
||||
try sema.ensureLayoutResolved(child_type, src, .field_used);
|
||||
const field_index_usize = child_type.enumFieldIndex(field_name, zcu) orelse
|
||||
return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name);
|
||||
const field_index: u32 = @intCast(field_index_usize);
|
||||
@@ -25645,7 +25640,7 @@ fn fieldVal(
|
||||
},
|
||||
.@"struct" => if (is_pointer_to) {
|
||||
// Avoid loading the entire struct by fetching a pointer and loading that
|
||||
try sema.ensureLayoutResolved(inner_ty, src);
|
||||
try sema.ensureLayoutResolved(inner_ty, src, .ptr_access);
|
||||
const field_ptr = try sema.structFieldPtr(block, src, object, field_name, field_name_src, inner_ty);
|
||||
return sema.analyzeLoad(block, src, field_ptr, object_src);
|
||||
} else {
|
||||
@@ -25653,7 +25648,7 @@ fn fieldVal(
|
||||
},
|
||||
.@"union" => if (is_pointer_to) {
|
||||
// Avoid loading the entire union by fetching a pointer and loading that
|
||||
try sema.ensureLayoutResolved(inner_ty, src);
|
||||
try sema.ensureLayoutResolved(inner_ty, src, .ptr_access);
|
||||
const field_ptr = try sema.unionFieldPtr(block, src, object, field_name, field_name_src, inner_ty, false);
|
||||
return sema.analyzeLoad(block, src, field_ptr, object_src);
|
||||
} else {
|
||||
@@ -25837,7 +25832,7 @@ fn fieldPtr(
|
||||
if (try sema.namespaceLookupRef(block, src, child_type.getNamespaceIndex(zcu), field_name)) |inst| {
|
||||
return inst;
|
||||
}
|
||||
try sema.ensureLayoutResolved(child_type, src);
|
||||
try sema.ensureLayoutResolved(child_type, src, .field_used);
|
||||
if (child_type.unionTagType(zcu)) |enum_ty| {
|
||||
if (enum_ty.enumFieldIndex(field_name, zcu)) |field_index| {
|
||||
const field_index_u32: u32 = @intCast(field_index);
|
||||
@@ -25851,7 +25846,7 @@ fn fieldPtr(
|
||||
if (try sema.namespaceLookupRef(block, src, child_type.getNamespaceIndex(zcu), field_name)) |inst| {
|
||||
return inst;
|
||||
}
|
||||
try sema.ensureLayoutResolved(child_type, src);
|
||||
try sema.ensureLayoutResolved(child_type, src, .field_used);
|
||||
const field_index = child_type.enumFieldIndex(field_name, zcu) orelse {
|
||||
return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name);
|
||||
};
|
||||
@@ -25873,7 +25868,7 @@ fn fieldPtr(
|
||||
try sema.analyzeLoad(block, src, object_ptr, object_ptr_src)
|
||||
else
|
||||
object_ptr;
|
||||
try sema.ensureLayoutResolved(inner_ty, src);
|
||||
try sema.ensureLayoutResolved(inner_ty, src, .ptr_access);
|
||||
const field_ptr = try sema.structFieldPtr(block, src, inner_ptr, field_name, field_name_src, inner_ty);
|
||||
try sema.checkKnownAllocPtr(block, inner_ptr, field_ptr);
|
||||
return field_ptr;
|
||||
@@ -25883,7 +25878,7 @@ fn fieldPtr(
|
||||
try sema.analyzeLoad(block, src, object_ptr, object_ptr_src)
|
||||
else
|
||||
object_ptr;
|
||||
try sema.ensureLayoutResolved(inner_ty, src);
|
||||
try sema.ensureLayoutResolved(inner_ty, src, .ptr_access);
|
||||
const field_ptr = try sema.unionFieldPtr(block, src, inner_ptr, field_name, field_name_src, inner_ty, initializing);
|
||||
try sema.checkKnownAllocPtr(block, inner_ptr, field_ptr);
|
||||
return field_ptr;
|
||||
@@ -25927,7 +25922,7 @@ fn fieldCallBind(
|
||||
// Optionally dereference a second pointer to get the concrete type.
|
||||
const is_double_ptr = inner_ty.zigTypeTag(zcu) == .pointer and inner_ty.ptrSize(zcu) == .one;
|
||||
const concrete_ty = if (is_double_ptr) inner_ty.childType(zcu) else inner_ty;
|
||||
try sema.ensureLayoutResolved(concrete_ty, src);
|
||||
try sema.ensureLayoutResolved(concrete_ty, src, .ptr_access);
|
||||
const ptr_ty = if (is_double_ptr) inner_ty else raw_ptr_ty;
|
||||
const object_ptr = if (is_double_ptr)
|
||||
try sema.analyzeLoad(block, src, raw_ptr, src)
|
||||
@@ -26514,7 +26509,7 @@ fn elemPtr(
|
||||
else => return sema.fail(block, indexable_ptr_src, "expected pointer, found '{f}'", .{indexable_ptr_ty.fmt(pt)}),
|
||||
};
|
||||
try sema.checkIndexable(block, src, indexable_ty);
|
||||
try sema.ensureLayoutResolved(indexable_ty, src);
|
||||
try sema.ensureLayoutResolved(indexable_ty, src, .ptr_access);
|
||||
|
||||
const elem_ptr = switch (indexable_ty.zigTypeTag(zcu)) {
|
||||
.vector => try sema.elemPtrVector(block, indexable_ptr_src, indexable_ptr, elem_index_src, elem_index, init),
|
||||
@@ -26522,7 +26517,7 @@ fn elemPtr(
|
||||
.@"struct" => try sema.tupleElemPtr(block, src, indexable_ptr, elem_index, elem_index_src),
|
||||
else => {
|
||||
const indexable = try sema.analyzeLoad(block, indexable_ptr_src, indexable_ptr, indexable_ptr_src);
|
||||
try sema.ensureLayoutResolved(sema.typeOf(indexable).childType(zcu), src);
|
||||
try sema.ensureLayoutResolved(sema.typeOf(indexable).childType(zcu), src, .ptr_access);
|
||||
return elemPtrOneLayerOnly(sema, block, src, indexable, elem_index, elem_index_src, init, oob_safety);
|
||||
},
|
||||
};
|
||||
@@ -26614,7 +26609,7 @@ fn elemVal(
|
||||
switch (indexable_ty.zigTypeTag(zcu)) {
|
||||
.pointer => {
|
||||
const child_ty = indexable_ty.childType(zcu);
|
||||
try sema.ensureLayoutResolved(child_ty, src);
|
||||
try sema.ensureLayoutResolved(child_ty, src, .ptr_access);
|
||||
switch (indexable_ty.ptrSize(zcu)) {
|
||||
.slice => return sema.elemValSlice(block, src, indexable_src, indexable, elem_index_src, elem_index, oob_safety),
|
||||
.many, .c => {
|
||||
@@ -27192,7 +27187,7 @@ fn coerceExtra(
|
||||
const target = zcu.getTarget();
|
||||
|
||||
inst_ty.assertHasLayout(zcu);
|
||||
try sema.ensureLayoutResolved(dest_ty, inst_src);
|
||||
try sema.ensureLayoutResolved(dest_ty, inst_src, .coerce);
|
||||
|
||||
// If the types are the same, we can return the operand.
|
||||
if (dest_ty.eql(inst_ty, zcu))
|
||||
@@ -28552,8 +28547,8 @@ fn coerceInMemoryAllowedFns(
|
||||
} };
|
||||
}
|
||||
|
||||
try sema.ensureLayoutResolved(src_ty, src_src);
|
||||
try sema.ensureLayoutResolved(dest_ty, dest_src);
|
||||
try sema.ensureLayoutResolved(src_ty, src_src, .coerce);
|
||||
try sema.ensureLayoutResolved(dest_ty, dest_src, .coerce);
|
||||
const src_is_runtime = src_ty.fnHasRuntimeBits(zcu);
|
||||
const dest_is_runtime = dest_ty.fnHasRuntimeBits(zcu);
|
||||
if (src_is_runtime != dest_is_runtime) return .{ .fn_generic = !dest_is_runtime };
|
||||
@@ -28607,7 +28602,6 @@ fn coerceInMemoryAllowedFns(
|
||||
const src_is_comptime = src_info.paramIsComptime(@intCast(param_i));
|
||||
const dest_is_comptime = dest_info.paramIsComptime(@intCast(param_i));
|
||||
if (src_is_comptime == dest_is_comptime) break :comptime_param;
|
||||
try sema.ensureLayoutResolved(dest_param_ty, dest_src);
|
||||
if (!dest_is_mut and src_is_comptime and !dest_is_comptime and dest_param_ty.comptimeOnly(zcu)) {
|
||||
// A parameter which is marked `comptime` can drop that annotation if the type is comptime-only.
|
||||
// The function remains generic, and the parameter is going to be comptime-resolved either way,
|
||||
@@ -28835,11 +28829,11 @@ fn coerceInMemoryAllowedPtrs(
|
||||
dest_info.child != src_info.child)
|
||||
{
|
||||
const src_align = if (src_info.flags.alignment == .none) a: {
|
||||
try sema.ensureLayoutResolved(src_child, src_src);
|
||||
try sema.ensureLayoutResolved(src_child, src_src, .align_check);
|
||||
break :a src_child.abiAlignment(zcu);
|
||||
} else src_info.flags.alignment;
|
||||
const dest_align = if (dest_info.flags.alignment == .none) a: {
|
||||
try sema.ensureLayoutResolved(dest_child, dest_src);
|
||||
try sema.ensureLayoutResolved(dest_child, dest_src, .align_check);
|
||||
break :a dest_child.abiAlignment(zcu);
|
||||
} else dest_info.flags.alignment;
|
||||
if (dest_align.compare(if (dest_is_mut) .neq else .gt, src_align)) {
|
||||
@@ -29189,7 +29183,7 @@ fn bitCast(
|
||||
const old_ty = sema.typeOf(inst);
|
||||
|
||||
old_ty.assertHasLayout(zcu);
|
||||
try sema.ensureLayoutResolved(dest_ty, inst_src);
|
||||
try sema.ensureLayoutResolved(dest_ty, inst_src, .init);
|
||||
|
||||
const dest_bits = dest_ty.bitSize(zcu);
|
||||
const old_bits = old_ty.bitSize(zcu);
|
||||
@@ -29837,10 +29831,11 @@ fn ensureMemoizedStateResolved(sema: *Sema, src: LazySrcLoc, stage: InternPool.M
|
||||
try sema.addReferenceEntry(null, src, unit);
|
||||
try sema.declareDependency(.{ .memoized_state = stage });
|
||||
|
||||
const reason: Zcu.DependencyReason = .{ .src = src, .type_layout_reason = undefined };
|
||||
if (pt.zcu.analysis_in_progress.contains(unit)) {
|
||||
return sema.failWithOwnedErrorMsg(null, try sema.errMsg(src, "dependency loop detected", .{}));
|
||||
return sema.failWithDependencyLoop(unit, &reason);
|
||||
}
|
||||
try pt.ensureMemoizedStateUpToDate(stage);
|
||||
try pt.ensureMemoizedStateUpToDate(stage, &reason);
|
||||
}
|
||||
|
||||
pub fn ensureNavResolved(sema: *Sema, block: *Block, src: LazySrcLoc, nav_index: InternPool.Nav.Index, kind: enum { type, fully }) CompileError!void {
|
||||
@@ -29854,11 +29849,6 @@ pub fn ensureNavResolved(sema: *Sema, block: *Block, src: LazySrcLoc, nav_index:
|
||||
return;
|
||||
}
|
||||
|
||||
try sema.declareDependency(switch (kind) {
|
||||
.type => .{ .nav_ty = nav_index },
|
||||
.fully => .{ .nav_val = nav_index },
|
||||
});
|
||||
|
||||
// Note that even if `nav.status == .resolved`, we must still trigger `ensureNavValUpToDate`
|
||||
// to make sure the value is up-to-date on incremental updates.
|
||||
|
||||
@@ -29867,20 +29857,23 @@ pub fn ensureNavResolved(sema: *Sema, block: *Block, src: LazySrcLoc, nav_index:
|
||||
.fully => .{ .nav_val = nav_index },
|
||||
});
|
||||
try sema.addReferenceEntry(block, src, anal_unit);
|
||||
try sema.declareDependency(switch (kind) {
|
||||
.type => .{ .nav_ty = nav_index },
|
||||
.fully => .{ .nav_val = nav_index },
|
||||
});
|
||||
|
||||
const reason: Zcu.DependencyReason = .{ .src = src, .type_layout_reason = undefined };
|
||||
|
||||
if (zcu.analysis_in_progress.contains(anal_unit)) {
|
||||
return sema.failWithOwnedErrorMsg(null, try sema.errMsg(.{
|
||||
.base_node_inst = nav.analysis.?.zir_index,
|
||||
.offset = LazySrcLoc.Offset.nodeOffset(.zero),
|
||||
}, "dependency loop detected", .{}));
|
||||
return sema.failWithDependencyLoop(anal_unit, &reason);
|
||||
}
|
||||
|
||||
switch (kind) {
|
||||
.type => {
|
||||
try zcu.ensureNavValAnalysisQueued(nav_index);
|
||||
return pt.ensureNavTypeUpToDate(nav_index);
|
||||
return pt.ensureNavTypeUpToDate(nav_index, &reason);
|
||||
},
|
||||
.fully => return pt.ensureNavValUpToDate(nav_index),
|
||||
.fully => return pt.ensureNavValUpToDate(nav_index, &reason),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30065,7 +30058,7 @@ fn analyzeLoad(
|
||||
return sema.fail(block, ptr_src, "cannot load opaque type '{f}'", .{elem_ty.fmt(pt)});
|
||||
}
|
||||
|
||||
try sema.ensureLayoutResolved(elem_ty, src);
|
||||
try sema.ensureLayoutResolved(elem_ty, src, .ptr_access);
|
||||
if (try elem_ty.onePossibleValue(pt)) |opv| return .fromValue(opv);
|
||||
|
||||
if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| {
|
||||
@@ -30249,7 +30242,7 @@ fn resolveIsNonErrFromType(
|
||||
// This is *our* error set; that is, we're currently analyzing the function
|
||||
// which owns it. Trying to resolve it now would cause a dependency loop.
|
||||
// Instead, accept that we don't know.
|
||||
if (true) return null;
|
||||
return null;
|
||||
},
|
||||
else => |set_ty| switch (ip.indexToKey(set_ty)) {
|
||||
.error_set_type => |error_set_type| switch (error_set_type.names.len) {
|
||||
@@ -30450,7 +30443,7 @@ fn analyzeSlice(
|
||||
else => return sema.fail(block, src, "slice of non-array type '{f}'", .{ptr_ptr_child_ty.fmt(pt)}),
|
||||
}
|
||||
|
||||
try sema.ensureLayoutResolved(elem_ty, src);
|
||||
try sema.ensureLayoutResolved(elem_ty, src, .ptr_access);
|
||||
|
||||
const ptr = if (slice_ty.isSlice(zcu))
|
||||
try sema.analyzeSlicePtr(block, ptr_src, ptr_or_slice, slice_ty)
|
||||
@@ -32831,11 +32824,13 @@ fn ensureFuncIesResolved(
|
||||
try sema.declareDependency(.{ .func_ies = func_index });
|
||||
try sema.addReferenceEntry(block, src, .wrap(.{ .func = func_index }));
|
||||
|
||||
const reason: Zcu.DependencyReason = .{ .src = src, .type_layout_reason = undefined };
|
||||
|
||||
if (zcu.analysis_in_progress.contains(.wrap(.{ .func = func_index }))) {
|
||||
return sema.fail(block, src, "unable to resolve inferred error set", .{});
|
||||
return sema.failWithDependencyLoop(.wrap(.{ .func = func_index }), &reason);
|
||||
}
|
||||
|
||||
try pt.ensureFuncBodyUpToDate(func_index);
|
||||
try pt.ensureFuncBodyUpToDate(func_index, &reason);
|
||||
}
|
||||
|
||||
pub fn resolveInferredErrorSetPtr(
|
||||
@@ -33749,6 +33744,8 @@ pub fn flushExports(sema: *Sema) !void {
|
||||
//
|
||||
// So, pick up and delete any existing exports. This strategy performs
|
||||
// redundant work, but that's okay, because this case is exceedingly rare.
|
||||
//
|
||||
// MLUGG TODO: is this still possible? if not, delete this logic and combine deleteUnitExports into resetUnit
|
||||
if (zcu.single_exports.get(sema.owner)) |export_idx| {
|
||||
try sema.exports.append(gpa, export_idx.ptr(zcu).*);
|
||||
} else if (zcu.multi_exports.get(sema.owner)) |info| {
|
||||
@@ -33940,7 +33937,7 @@ pub fn analyzeMemoizedState(sema: *Sema, stage: InternPool.MemoizedStateStage) C
|
||||
const val: Value = switch (builtin_decl.kind()) {
|
||||
.type => val: {
|
||||
const ty = try sema.analyzeAsType(&block, decl_src, .std_builtin_decl, uncoerced_val);
|
||||
try sema.ensureLayoutResolved(ty, decl_src);
|
||||
try sema.ensureLayoutResolved(ty, decl_src, .builtin_type);
|
||||
break :val ty.toValue();
|
||||
},
|
||||
.func => val: {
|
||||
@@ -34370,3 +34367,42 @@ fn zirOpaqueDecl(
|
||||
|
||||
return .fromType(ty);
|
||||
}
|
||||
|
||||
/// Registers an error indicating a dependency loop: we have introduced a dependency on `want` (with
|
||||
/// reason `want_reason`) but have learnt that `want` is already in `zcu.analysis_in_progress`.
|
||||
pub fn failWithDependencyLoop(
|
||||
sema: *Sema,
|
||||
want: AnalUnit,
|
||||
want_reason: *const Zcu.DependencyReason,
|
||||
) SemaError {
|
||||
const pt = sema.pt;
|
||||
const zcu = pt.zcu;
|
||||
const gpa = zcu.comp.gpa;
|
||||
|
||||
const in_progress_len = zcu.analysis_in_progress.count();
|
||||
var index = zcu.analysis_in_progress.getIndex(want).? + 1;
|
||||
|
||||
try zcu.dependency_loops.ensureUnusedCapacity(gpa, 1);
|
||||
try zcu.dependency_loop_nodes.ensureUnusedCapacity(gpa, in_progress_len - index + 1);
|
||||
|
||||
zcu.dependency_loops.putAssumeCapacityNoClobber(want, {});
|
||||
|
||||
while (index <= in_progress_len) : (index += 1) {
|
||||
const parent_unit = zcu.analysis_in_progress.keys()[index - 1];
|
||||
const unit, const reason = if (index == in_progress_len) .{
|
||||
want,
|
||||
want_reason,
|
||||
} else .{
|
||||
zcu.analysis_in_progress.keys()[index],
|
||||
zcu.analysis_in_progress.values()[index],
|
||||
};
|
||||
|
||||
zcu.dependency_loop_nodes.putAssumeCapacityNoClobber(parent_unit, .{
|
||||
.unit = unit,
|
||||
.reason = reason.?.*,
|
||||
});
|
||||
}
|
||||
|
||||
// A dependency loop error will be reported. Mark us all as transitive failures.
|
||||
return error.AnalysisFail;
|
||||
}
|
||||
|
||||
@@ -300,7 +300,7 @@ fn checkTypeInner(
|
||||
} else {
|
||||
const gop = try visited.getOrPut(sema.arena, ty.toIntern());
|
||||
if (gop.found_existing) return;
|
||||
try sema.ensureLayoutResolved(ty, self.import_loc);
|
||||
try sema.ensureLayoutResolved(ty, self.import_loc, .init);
|
||||
const struct_info = zcu.typeToStruct(ty).?;
|
||||
for (struct_info.field_types.get(ip)) |field_type| {
|
||||
try self.checkTypeInner(.fromInterned(field_type), null, visited);
|
||||
@@ -309,7 +309,7 @@ fn checkTypeInner(
|
||||
.@"union" => {
|
||||
const gop = try visited.getOrPut(sema.arena, ty.toIntern());
|
||||
if (gop.found_existing) return;
|
||||
try sema.ensureLayoutResolved(ty, self.import_loc);
|
||||
try sema.ensureLayoutResolved(ty, self.import_loc, .init);
|
||||
const union_info = zcu.typeToUnion(ty).?;
|
||||
for (union_info.field_types.get(ip)) |field_type| {
|
||||
if (field_type != .void_type) {
|
||||
@@ -646,7 +646,7 @@ fn lowerEnum(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.I
|
||||
const gpa = comp.gpa;
|
||||
const io = comp.io;
|
||||
const ip = &pt.zcu.intern_pool;
|
||||
try self.sema.ensureLayoutResolved(res_ty, self.import_loc);
|
||||
try self.sema.ensureLayoutResolved(res_ty, self.import_loc, .init);
|
||||
switch (node.get(self.file.zoir.?)) {
|
||||
.enum_literal => |field_name| {
|
||||
const field_name_interned = try ip.getOrPutString(
|
||||
@@ -769,7 +769,7 @@ fn lowerStruct(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool
|
||||
const io = comp.io;
|
||||
const ip = &pt.zcu.intern_pool;
|
||||
|
||||
try self.sema.ensureLayoutResolved(res_ty, self.import_loc);
|
||||
try self.sema.ensureLayoutResolved(res_ty, self.import_loc, .init);
|
||||
const struct_info = self.sema.pt.zcu.typeToStruct(res_ty).?;
|
||||
|
||||
const fields: @FieldType(Zoir.Node, "struct_literal") = switch (node.get(self.file.zoir.?)) {
|
||||
@@ -919,7 +919,7 @@ fn lowerUnion(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.
|
||||
const gpa = comp.gpa;
|
||||
const io = comp.io;
|
||||
const ip = &pt.zcu.intern_pool;
|
||||
try self.sema.ensureLayoutResolved(res_ty, self.import_loc);
|
||||
try self.sema.ensureLayoutResolved(res_ty, self.import_loc, .init);
|
||||
const union_info = pt.zcu.typeToUnion(res_ty).?;
|
||||
const enum_tag_info = ip.loadEnumType(union_info.enum_tag_type);
|
||||
|
||||
|
||||
@@ -14,12 +14,64 @@ const InternPool = @import("../InternPool.zig");
|
||||
const Alignment = InternPool.Alignment;
|
||||
const arith = @import("arith.zig");
|
||||
|
||||
pub const LayoutResolveReason = enum {
|
||||
variable,
|
||||
constant,
|
||||
parameter,
|
||||
return_type,
|
||||
field,
|
||||
backing_enum,
|
||||
init,
|
||||
coerce,
|
||||
ptr_access,
|
||||
ptr_offset,
|
||||
field_used,
|
||||
field_queried,
|
||||
size_of,
|
||||
align_of,
|
||||
type_info,
|
||||
align_check,
|
||||
bit_ptr_child,
|
||||
builtin_type,
|
||||
|
||||
/// Written after string: "while resolving type 'T' "
|
||||
/// e.g. "while resolving type 'MyStruct' for variable declared here"
|
||||
pub fn msg(r: LayoutResolveReason) []const u8 {
|
||||
return switch (r) {
|
||||
// zig fmt: off
|
||||
.variable => "for variable declared here",
|
||||
.constant => "for constant declared here",
|
||||
.parameter => "for function parameter declared here",
|
||||
.return_type => "for function return type declared here",
|
||||
.field => "for field declared here",
|
||||
.backing_enum => "for backing enum type declared here",
|
||||
.init => "for initialization performed here",
|
||||
.coerce => "for coercion performed here",
|
||||
.ptr_access => "for pointer access here",
|
||||
.ptr_offset => "for pointer offset here",
|
||||
.field_used => "for field usage here",
|
||||
.field_queried => "for field query here",
|
||||
.size_of => "for size query here",
|
||||
.align_of => "for alignment query here",
|
||||
.type_info => "for type information query here",
|
||||
.align_check => "for alignment check here",
|
||||
.bit_ptr_child => "for bit size check here",
|
||||
.builtin_type => "from 'std.builtin'",
|
||||
// zig fmt: on
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/// Ensures that `ty` has known layout, including alignment, size, and (where relevant) field offsets.
|
||||
/// `ty` may be any type; its layout is resolved *recursively* if necessary.
|
||||
/// Adds incremental dependencies tracking any required type resolution.
|
||||
/// MLUGG TODO: to make the langspec non-stupid, we need to call this from WAY fewer places (the conditions need to be less specific).
|
||||
/// MLUGG TODO: to be clear, i should audit EVERY use of this before PRing
|
||||
pub fn ensureLayoutResolved(sema: *Sema, ty: Type, src: LazySrcLoc) SemaError!void {
|
||||
pub fn ensureLayoutResolved(sema: *Sema, ty: Type, src: LazySrcLoc, reason: LayoutResolveReason) SemaError!void {
|
||||
return ensureLayoutResolvedInner(sema, ty, ty, &.{
|
||||
.src = src,
|
||||
.type_layout_reason = reason,
|
||||
});
|
||||
}
|
||||
fn ensureLayoutResolvedInner(sema: *Sema, ty: Type, orig_ty: Type, reason: *const Zcu.DependencyReason) SemaError!void {
|
||||
const pt = sema.pt;
|
||||
const zcu = pt.zcu;
|
||||
const ip = &zcu.intern_pool;
|
||||
@@ -35,30 +87,25 @@ pub fn ensureLayoutResolved(sema: *Sema, ty: Type, src: LazySrcLoc) SemaError!vo
|
||||
|
||||
.func_type => |func_type| {
|
||||
for (func_type.param_types.get(ip)) |param_ty| {
|
||||
try ensureLayoutResolved(sema, .fromInterned(param_ty), src);
|
||||
try ensureLayoutResolvedInner(sema, .fromInterned(param_ty), orig_ty, reason);
|
||||
}
|
||||
try ensureLayoutResolved(sema, .fromInterned(func_type.return_type), src);
|
||||
try ensureLayoutResolvedInner(sema, .fromInterned(func_type.return_type), orig_ty, reason);
|
||||
},
|
||||
|
||||
.array_type => |arr| return ensureLayoutResolved(sema, .fromInterned(arr.child), src),
|
||||
.vector_type => |vec| return ensureLayoutResolved(sema, .fromInterned(vec.child), src),
|
||||
.opt_type => |child| return ensureLayoutResolved(sema, .fromInterned(child), src),
|
||||
.error_union_type => |eu| return ensureLayoutResolved(sema, .fromInterned(eu.payload_type), src),
|
||||
.array_type => |arr| return ensureLayoutResolvedInner(sema, .fromInterned(arr.child), orig_ty, reason),
|
||||
.vector_type => |vec| return ensureLayoutResolvedInner(sema, .fromInterned(vec.child), orig_ty, reason),
|
||||
.opt_type => |child| return ensureLayoutResolvedInner(sema, .fromInterned(child), orig_ty, reason),
|
||||
.error_union_type => |eu| return ensureLayoutResolvedInner(sema, .fromInterned(eu.payload_type), orig_ty, reason),
|
||||
.tuple_type => |tuple| for (tuple.types.get(ip)) |field_ty| {
|
||||
try ensureLayoutResolved(sema, .fromInterned(field_ty), src);
|
||||
try ensureLayoutResolvedInner(sema, .fromInterned(field_ty), orig_ty, reason);
|
||||
},
|
||||
.struct_type, .union_type, .enum_type => {
|
||||
try sema.declareDependency(.{ .type_layout = ty.toIntern() });
|
||||
try sema.addReferenceEntry(null, src, .wrap(.{ .type_layout = ty.toIntern() }));
|
||||
try sema.addReferenceEntry(null, reason.src, .wrap(.{ .type_layout = ty.toIntern() }));
|
||||
if (zcu.analysis_in_progress.contains(.wrap(.{ .type_layout = ty.toIntern() }))) {
|
||||
// TODO: better error message
|
||||
return sema.failWithOwnedErrorMsg(null, try sema.errMsg(
|
||||
ty.srcLoc(zcu),
|
||||
"{s} '{f}' depends on itself",
|
||||
.{ @tagName(ty.zigTypeTag(zcu)), ty.fmt(pt) },
|
||||
));
|
||||
return sema.failWithDependencyLoop(.wrap(.{ .type_layout = ty.toIntern() }), reason);
|
||||
}
|
||||
try pt.ensureTypeLayoutUpToDate(ty);
|
||||
try pt.ensureTypeLayoutUpToDate(ty, reason);
|
||||
},
|
||||
|
||||
// values, not types
|
||||
@@ -228,7 +275,7 @@ pub fn resolveStructLayout(sema: *Sema, struct_ty: Type) CompileError!void {
|
||||
const field_ty: Type = .fromInterned(field_ty_ip);
|
||||
assert(!field_ty.isGenericPoison());
|
||||
const field_ty_src = block.src(.{ .container_field_type = @intCast(field_index) });
|
||||
try sema.ensureLayoutResolved(field_ty, field_ty_src);
|
||||
try sema.ensureLayoutResolved(field_ty, field_ty_src, .field);
|
||||
|
||||
if (field_ty.zigTypeTag(zcu) == .@"opaque") {
|
||||
return sema.failWithOwnedErrorMsg(&block, msg: {
|
||||
@@ -387,7 +434,7 @@ fn resolvePackedStructLayout(
|
||||
const field_ty: Type = .fromInterned(field_ty_ip);
|
||||
assert(!field_ty.isGenericPoison());
|
||||
const field_ty_src = block.src(.{ .container_field_type = @intCast(field_index) });
|
||||
try sema.ensureLayoutResolved(field_ty, field_ty_src);
|
||||
try sema.ensureLayoutResolved(field_ty, field_ty_src, .field);
|
||||
if (field_ty.zigTypeTag(zcu) == .@"opaque") {
|
||||
return sema.failWithOwnedErrorMsg(block, msg: {
|
||||
const msg = try sema.errMsg(field_ty_src, "cannot directly embed opaque type '{f}' in struct", .{field_ty.fmt(pt)});
|
||||
@@ -557,7 +604,7 @@ pub fn resolveUnionLayout(sema: *Sema, union_ty: Type) CompileError!void {
|
||||
},
|
||||
};
|
||||
|
||||
try sema.ensureLayoutResolved(enum_tag_ty, block.src(.container_arg));
|
||||
try sema.ensureLayoutResolved(enum_tag_ty, block.src(.container_arg), .backing_enum);
|
||||
const enum_obj = ip.loadEnumType(enum_tag_ty.toIntern());
|
||||
|
||||
if (union_obj.is_reified) {
|
||||
@@ -652,7 +699,7 @@ pub fn resolveUnionLayout(sema: *Sema, union_ty: Type) CompileError!void {
|
||||
const field_ty: Type = .fromInterned(field_ty_ip);
|
||||
assert(!field_ty.isGenericPoison());
|
||||
const field_ty_src = block.src(.{ .container_field_type = @intCast(field_index) });
|
||||
try sema.ensureLayoutResolved(field_ty, field_ty_src);
|
||||
try sema.ensureLayoutResolved(field_ty, field_ty_src, .field);
|
||||
if (field_ty.zigTypeTag(zcu) == .@"opaque") {
|
||||
return sema.failWithOwnedErrorMsg(&block, msg: {
|
||||
const msg = try sema.errMsg(field_ty_src, "cannot directly embed opaque type '{f}' in union", .{field_ty.fmt(pt)});
|
||||
@@ -832,7 +879,7 @@ fn resolvePackedUnionLayout(
|
||||
const field_ty: Type = .fromInterned(field_ty_ip);
|
||||
assert(!field_ty.isGenericPoison());
|
||||
const field_ty_src = block.src(.{ .container_field_type = @intCast(field_index) });
|
||||
try sema.ensureLayoutResolved(field_ty, field_ty_src);
|
||||
try sema.ensureLayoutResolved(field_ty, field_ty_src, .field);
|
||||
if (field_ty.zigTypeTag(zcu) == .@"opaque") {
|
||||
return sema.failWithOwnedErrorMsg(block, msg: {
|
||||
const msg = try sema.errMsg(field_ty_src, "cannot directly embed opaque type '{f}' in union", .{field_ty.fmt(pt)});
|
||||
|
||||
+332
-32
@@ -119,7 +119,7 @@ module_roots: std.AutoArrayHashMapUnmanaged(*Package.Module, File.Index.Optional
|
||||
///
|
||||
/// Always accessed through `ImportTableAdapter`, where keys are fully resolved
|
||||
/// file paths in order to ensure files are properly deduplicated. This table owns
|
||||
/// the keys and values.
|
||||
/// the keysand values.
|
||||
///
|
||||
/// Protected by Compilation's mutex.
|
||||
///
|
||||
@@ -177,7 +177,9 @@ embed_table: std.ArrayHashMapUnmanaged(
|
||||
/// is not yet implemented.
|
||||
intern_pool: InternPool = .empty,
|
||||
|
||||
analysis_in_progress: std.AutoArrayHashMapUnmanaged(AnalUnit, void) = .empty,
|
||||
/// Value explains why this `AnalUnit` is being analyzed. It is `null` for the topmost analysis
|
||||
/// (index 0), and non-`null` for all others.
|
||||
analysis_in_progress: std.AutoArrayHashMapUnmanaged(AnalUnit, ?*const DependencyReason) = .empty,
|
||||
/// The ErrorMsg memory is owned by the `AnalUnit`, using Module's general purpose allocator.
|
||||
failed_analysis: std.AutoArrayHashMapUnmanaged(AnalUnit, *ErrorMsg) = .empty,
|
||||
/// This `AnalUnit` failed semantic analysis because it required analysis of another `AnalUnit` which itself failed.
|
||||
@@ -189,6 +191,19 @@ transitive_failed_analysis: std.AutoArrayHashMapUnmanaged(AnalUnit, void) = .emp
|
||||
/// codegen and linking run on a separate thread.
|
||||
failed_codegen: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, *ErrorMsg) = .empty,
|
||||
failed_types: std.AutoArrayHashMapUnmanaged(InternPool.Index, *ErrorMsg) = .empty,
|
||||
|
||||
/// Key is an `AnalUnit` which is in `dependency_loop_nodes`. For each dependency loop, exactly one
|
||||
/// unit in the loop is in this map, though the choice is arbitrary and not necessarily reproducible
|
||||
/// between compilations. So, instead of (for instance) defining where the dependency loop "starts",
|
||||
/// this map simply exists to allow easily iterating all dependency loops exactly once.
|
||||
dependency_loops: std.AutoArrayHashMapUnmanaged(AnalUnit, void) = .empty,
|
||||
/// Key is an `AnalUnit`, value is the `AnalUnit` which the key references and why it does so.
|
||||
/// All units in here form loops. To iterate loops, see `dependency_loops`.
|
||||
dependency_loop_nodes: std.AutoArrayHashMapUnmanaged(AnalUnit, struct {
|
||||
unit: AnalUnit,
|
||||
reason: DependencyReason,
|
||||
}) = .empty,
|
||||
|
||||
/// Keep track of `@compileLog`s per `AnalUnit`.
|
||||
/// We track the source location of the first `@compileLog` call, and all logged lines as a linked list.
|
||||
/// The list is singly linked, but we do track its tail for fast appends (optimizing many logs in one unit).
|
||||
@@ -321,6 +336,12 @@ codegen_task_pool: CodegenTaskPool,
|
||||
|
||||
generation: u32 = 0,
|
||||
|
||||
pub const DependencyReason = struct {
|
||||
src: LazySrcLoc,
|
||||
/// Only populated if this is for a `.type_layout` unit.
|
||||
type_layout_reason: Sema.type_resolution.LayoutResolveReason,
|
||||
};
|
||||
|
||||
pub const IncrementalDebugState = struct {
|
||||
/// All container types in the ZCU, even dead ones.
|
||||
/// Value is the generation the type was created on.
|
||||
@@ -2778,6 +2799,8 @@ pub fn deinit(zcu: *Zcu) void {
|
||||
zcu.analysis_in_progress.deinit(gpa);
|
||||
zcu.failed_analysis.deinit(gpa);
|
||||
zcu.transitive_failed_analysis.deinit(gpa);
|
||||
zcu.dependency_loops.deinit(gpa);
|
||||
zcu.dependency_loop_nodes.deinit(gpa);
|
||||
zcu.failed_codegen.deinit(gpa);
|
||||
zcu.failed_types.deinit(gpa);
|
||||
|
||||
@@ -3536,15 +3559,47 @@ pub fn deleteUnitExports(zcu: *Zcu, anal_unit: AnalUnit) void {
|
||||
}
|
||||
}
|
||||
|
||||
/// Delete all references in `reference_table` which are caused by this `AnalUnit`.
|
||||
/// Prepares `unit` for re-analysis by clearing all of the following state:
|
||||
/// * Compile errors associated with `unit`
|
||||
/// * Compile logs associated with `unit`
|
||||
/// * Dependencies from `unit` on other things
|
||||
/// * References from `unit` to other units
|
||||
/// Delete all references in `reference_table` which are caused by `unit`, and all dependencies it
|
||||
/// has. Called in preparation for re-analysis, which will recreate references and dependencies.
|
||||
/// Re-analysis of the `AnalUnit` will cause appropriate references to be recreated.
|
||||
pub fn deleteUnitReferences(zcu: *Zcu, anal_unit: AnalUnit) void {
|
||||
const gpa = zcu.gpa;
|
||||
pub fn resetUnit(zcu: *Zcu, unit: AnalUnit) void {
|
||||
const gpa = zcu.comp.gpa;
|
||||
|
||||
// Compile errors
|
||||
if (zcu.failed_analysis.fetchSwapRemove(unit)) |kv| {
|
||||
kv.value.destroy(gpa);
|
||||
} else if (zcu.dependency_loop_nodes.swapRemove(unit)) {
|
||||
_ = zcu.dependency_loops.swapRemove(unit);
|
||||
_ = zcu.transitive_failed_analysis.swapRemove(unit);
|
||||
} else {
|
||||
_ = zcu.transitive_failed_analysis.swapRemove(unit);
|
||||
}
|
||||
|
||||
// Compile logs
|
||||
if (zcu.compile_logs.fetchSwapRemove(unit)) |kv| {
|
||||
var opt_line_idx = kv.value.first_line.toOptional();
|
||||
while (opt_line_idx.unwrap()) |line_idx| {
|
||||
zcu.free_compile_log_lines.append(gpa, line_idx) catch {
|
||||
// This space will be reused eventually, so we need not propagate this error.
|
||||
// Just leak it for now, and let GC reclaim it later on.
|
||||
break;
|
||||
};
|
||||
opt_line_idx = line_idx.get(zcu).next;
|
||||
}
|
||||
}
|
||||
|
||||
// Dependencies
|
||||
zcu.intern_pool.removeDependenciesForDepender(gpa, unit);
|
||||
|
||||
// References
|
||||
zcu.clearCachedResolvedReferences();
|
||||
|
||||
unit_refs: {
|
||||
const kv = zcu.reference_table.fetchSwapRemove(anal_unit) orelse break :unit_refs;
|
||||
const kv = zcu.reference_table.fetchSwapRemove(unit) orelse break :unit_refs;
|
||||
var idx = kv.value;
|
||||
|
||||
while (idx != std.math.maxInt(u32)) {
|
||||
@@ -3572,9 +3627,8 @@ pub fn deleteUnitReferences(zcu: *Zcu, anal_unit: AnalUnit) void {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type_refs: {
|
||||
const kv = zcu.type_reference_table.fetchSwapRemove(anal_unit) orelse break :type_refs;
|
||||
const kv = zcu.type_reference_table.fetchSwapRemove(unit) orelse break :type_refs;
|
||||
var idx = kv.value;
|
||||
|
||||
while (idx != std.math.maxInt(u32)) {
|
||||
@@ -3588,22 +3642,6 @@ pub fn deleteUnitReferences(zcu: *Zcu, anal_unit: AnalUnit) void {
|
||||
}
|
||||
}
|
||||
|
||||
/// Delete all compile logs performed by this `AnalUnit`.
|
||||
/// Re-analysis of the `AnalUnit` will cause logs to be rediscovered.
|
||||
pub fn deleteUnitCompileLogs(zcu: *Zcu, anal_unit: AnalUnit) void {
|
||||
const kv = zcu.compile_logs.fetchSwapRemove(anal_unit) orelse return;
|
||||
const gpa = zcu.gpa;
|
||||
var opt_line_idx = kv.value.first_line.toOptional();
|
||||
while (opt_line_idx.unwrap()) |line_idx| {
|
||||
zcu.free_compile_log_lines.append(gpa, line_idx) catch {
|
||||
// This space will be reused eventually, so we need not propagate this error.
|
||||
// Just leak it for now, and let GC reclaim it later on.
|
||||
return;
|
||||
};
|
||||
opt_line_idx = line_idx.get(zcu).next;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn addInlineReferenceFrame(zcu: *Zcu, frame: InlineReferenceFrame) Allocator.Error!Zcu.InlineReferenceFrame.Index {
|
||||
const frame_idx: InlineReferenceFrame.Index = zcu.free_inline_reference_frames.pop() orelse idx: {
|
||||
_ = try zcu.inline_reference_frames.addOne(zcu.gpa);
|
||||
@@ -4252,11 +4290,7 @@ pub fn fmtDependee(zcu: *Zcu, d: InternPool.Dependee) std.fmt.Alt(FormatDependee
|
||||
return .{ .data = .{ .dependee = d, .zcu = zcu } };
|
||||
}
|
||||
|
||||
const FormatAnalUnit = struct {
|
||||
unit: AnalUnit,
|
||||
zcu: *Zcu,
|
||||
};
|
||||
|
||||
const FormatAnalUnit = struct { unit: AnalUnit, zcu: *const Zcu };
|
||||
fn formatAnalUnit(data: FormatAnalUnit, writer: *Io.Writer) Io.Writer.Error!void {
|
||||
const zcu = data.zcu;
|
||||
const ip = &zcu.intern_pool;
|
||||
@@ -4280,8 +4314,7 @@ fn formatAnalUnit(data: FormatAnalUnit, writer: *Io.Writer) Io.Writer.Error!void
|
||||
}
|
||||
}
|
||||
|
||||
const FormatDependee = struct { dependee: InternPool.Dependee, zcu: *Zcu };
|
||||
|
||||
const FormatDependee = struct { dependee: InternPool.Dependee, zcu: *const Zcu };
|
||||
fn formatDependee(data: FormatDependee, writer: *Io.Writer) Io.Writer.Error!void {
|
||||
const zcu = data.zcu;
|
||||
const ip = &zcu.intern_pool;
|
||||
@@ -4666,6 +4699,273 @@ fn explainWhyFileIsInModule(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn addDependencyLoopErrors(zcu: *Zcu, eb: *std.zig.ErrorBundle.Wip) Allocator.Error!void {
|
||||
const gpa = zcu.comp.gpa;
|
||||
|
||||
const all_references = try zcu.resolveReferences();
|
||||
|
||||
var units: std.ArrayList(AnalUnit) = .empty;
|
||||
defer units.deinit(gpa);
|
||||
|
||||
// TODO: sort the dependency loops somehow to make the error bundle reproducible
|
||||
for (zcu.dependency_loops.keys()) |arbitrary_unit| {
|
||||
units.clearRetainingCapacity();
|
||||
|
||||
var cur = arbitrary_unit;
|
||||
while (true) {
|
||||
try units.append(gpa, cur);
|
||||
cur = zcu.dependency_loop_nodes.get(cur).?.unit;
|
||||
if (cur == arbitrary_unit) break;
|
||||
}
|
||||
|
||||
// `units` now contains all units in the loop. We need to pick a starting point somewhere
|
||||
// along that loop to begin. We will pick whichever node has the shortest reference trace,
|
||||
// because the other units may well just be referenced *by* that one! This is also likely
|
||||
// to match the user's intuition for where the loop "starts".
|
||||
var start_index: usize = 0;
|
||||
var start_depth: u32 = depth: {
|
||||
var depth: u32 = 0;
|
||||
var opt_ref = all_references.get(units.items[0]) orelse {
|
||||
// This dependency loop is actually unreferenced, so we don't need to emit a compile
|
||||
// error at all! Move onto the next dependency loop.
|
||||
continue;
|
||||
};
|
||||
while (opt_ref) |ref| : (opt_ref = all_references.get(ref.referencer).?) depth += 1;
|
||||
break :depth depth;
|
||||
};
|
||||
for (units.items[1..], 1..) |unit, index| {
|
||||
var depth: u32 = 0;
|
||||
var opt_ref = all_references.get(unit).?;
|
||||
while (opt_ref) |ref| : (opt_ref = all_references.get(ref.referencer).?) depth += 1;
|
||||
if (depth < start_depth) {
|
||||
start_index = index;
|
||||
start_depth = depth;
|
||||
}
|
||||
}
|
||||
|
||||
// Collect a reference trace for the start of the loop.
|
||||
var ref_trace: std.ArrayList(std.zig.ErrorBundle.ReferenceTrace) = .empty;
|
||||
defer ref_trace.deinit(gpa);
|
||||
const frame_limit = zcu.comp.reference_trace orelse 0;
|
||||
try zcu.populateReferenceTrace(units.items[start_index], frame_limit, eb, &ref_trace);
|
||||
|
||||
// Collect all notes first so we don't leave an incomplete root error message on `error.AlreadyReported`.
|
||||
const note_buf = try gpa.alloc(std.zig.ErrorBundle.MessageIndex, units.items.len + 1);
|
||||
defer gpa.free(note_buf);
|
||||
note_buf[0] = addDependencyLoopNote(zcu, eb, units.items[start_index], ref_trace.items) catch |err| switch (err) {
|
||||
error.AlreadyReported => return, // give up on the dep loop error
|
||||
error.OutOfMemory => |e| return e,
|
||||
};
|
||||
for (units.items[start_index + 1 ..], note_buf[1 .. units.items.len - start_index]) |unit, *note| {
|
||||
note.* = addDependencyLoopNote(zcu, eb, unit, &.{}) catch |err| switch (err) {
|
||||
error.AlreadyReported => return, // give up on the dep loop error
|
||||
error.OutOfMemory => |e| return e,
|
||||
};
|
||||
}
|
||||
for (units.items[0..start_index], note_buf[units.items.len - start_index .. units.items.len]) |unit, *note| {
|
||||
note.* = addDependencyLoopNote(zcu, eb, unit, &.{}) catch |err| switch (err) {
|
||||
error.AlreadyReported => return, // give up on the dep loop error
|
||||
error.OutOfMemory => |e| return e,
|
||||
};
|
||||
}
|
||||
note_buf[units.items.len] = try eb.addErrorMessage(.{
|
||||
.msg = try eb.addString("eliminate any one of these dependencies to break the loop"),
|
||||
.src_loc = .none,
|
||||
});
|
||||
|
||||
try eb.addRootErrorMessage(.{
|
||||
.msg = try eb.printString("dependency loop with length {d}", .{units.items.len}),
|
||||
.src_loc = .none,
|
||||
.notes_len = @intCast(units.items.len + 1),
|
||||
});
|
||||
const notes_start = try eb.reserveNotes(@intCast(units.items.len + 1));
|
||||
const notes: []std.zig.ErrorBundle.MessageIndex = @ptrCast(eb.extra.items[notes_start..]);
|
||||
@memcpy(notes, note_buf);
|
||||
}
|
||||
}
|
||||
fn addDependencyLoopNote(
|
||||
zcu: *Zcu,
|
||||
eb: *std.zig.ErrorBundle.Wip,
|
||||
source_unit: AnalUnit,
|
||||
ref_trace: []const std.zig.ErrorBundle.ReferenceTrace,
|
||||
) (Allocator.Error || error{AlreadyReported})!std.zig.ErrorBundle.MessageIndex {
|
||||
const ip = &zcu.intern_pool;
|
||||
const comp = zcu.comp;
|
||||
|
||||
const fmt_source: std.fmt.Alt(FormatAnalUnit, formatDependencyLoopSourceUnit) = .{ .data = .{
|
||||
.unit = source_unit,
|
||||
.zcu = zcu,
|
||||
} };
|
||||
|
||||
const dep_node = zcu.dependency_loop_nodes.get(source_unit).?;
|
||||
|
||||
const msg: std.zig.ErrorBundle.String = switch (dep_node.unit.unwrap()) {
|
||||
.@"comptime" => unreachable, // cannot be involved in a dependency loop
|
||||
.nav_val => |nav| try eb.printString("{f} uses value of declaration '{f}' here", .{
|
||||
fmt_source, ip.getNav(nav).fqn.fmt(ip),
|
||||
}),
|
||||
.nav_ty => |nav| try eb.printString("{f} uses type of declaration '{f}' here", .{
|
||||
fmt_source, ip.getNav(nav).fqn.fmt(ip),
|
||||
}),
|
||||
.memoized_state => |stage| switch (stage) {
|
||||
.panic => try eb.printString("{f} requires panic handler for call here", .{fmt_source}),
|
||||
else => try eb.printString("{f} requires 'std.builtin' declarations here", .{fmt_source}),
|
||||
},
|
||||
.func => |func| try eb.printString("{f} uses inferred error set of function '{f}' here", .{
|
||||
fmt_source, ip.getNav(zcu.funcInfo(func).owner_nav).fqn.fmt(ip),
|
||||
}),
|
||||
.type_layout => |ty| try eb.printString("{f} depends on type '{f}' {s}", .{
|
||||
fmt_source,
|
||||
Type.fromInterned(ty).containerTypeName(ip).fmt(ip),
|
||||
dep_node.reason.type_layout_reason.msg(),
|
||||
}),
|
||||
};
|
||||
|
||||
const src_loc = dep_node.reason.src.upgrade(zcu);
|
||||
const source = src_loc.file_scope.getSource(zcu) catch |err| {
|
||||
try Compilation.unableToLoadZcuFile(zcu, eb, src_loc.file_scope, err);
|
||||
return error.AlreadyReported;
|
||||
};
|
||||
const span = src_loc.span(zcu) catch |err| {
|
||||
try Compilation.unableToLoadZcuFile(zcu, eb, src_loc.file_scope, err);
|
||||
return error.AlreadyReported;
|
||||
};
|
||||
const loc = std.zig.findLineColumn(source, span.main);
|
||||
const eb_src = try eb.addSourceLocation(.{
|
||||
.src_path = try eb.printString("{f}", .{src_loc.file_scope.path.fmt(comp)}),
|
||||
.span_start = span.start,
|
||||
.span_main = span.main,
|
||||
.span_end = span.end,
|
||||
.line = @intCast(loc.line),
|
||||
.column = @intCast(loc.column),
|
||||
.source_line = try eb.addString(loc.source_line),
|
||||
.reference_trace_len = @intCast(ref_trace.len),
|
||||
});
|
||||
for (ref_trace) |rt| try eb.addReferenceTrace(rt);
|
||||
return eb.addErrorMessage(.{
|
||||
.msg = msg,
|
||||
.src_loc = eb_src,
|
||||
});
|
||||
}
|
||||
fn formatDependencyLoopSourceUnit(data: FormatAnalUnit, w: *Io.Writer) Io.Writer.Error!void {
|
||||
const zcu = data.zcu;
|
||||
const ip = &zcu.intern_pool;
|
||||
switch (data.unit.unwrap()) {
|
||||
.@"comptime" => unreachable, // cannot be involved in a dependency loop
|
||||
.nav_val => |nav| try w.print("value of declaration '{f}'", .{ip.getNav(nav).fqn.fmt(ip)}),
|
||||
.nav_ty => |nav| try w.print("type of declaration '{f}'", .{ip.getNav(nav).fqn.fmt(ip)}),
|
||||
.memoized_state => |stage| switch (stage) {
|
||||
.panic => try w.writeAll("panic handler"),
|
||||
else => try w.writeAll("'std.builtin' declarations"),
|
||||
},
|
||||
.type_layout => |ty| try w.print("type '{f}'", .{
|
||||
Type.fromInterned(ty).containerTypeName(ip).fmt(ip),
|
||||
}),
|
||||
.func => |func| try w.print("function '{f}'", .{
|
||||
ip.getNav(zcu.funcInfo(func).owner_nav).fqn.fmt(ip),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn populateReferenceTrace(
|
||||
zcu: *Zcu,
|
||||
root: AnalUnit,
|
||||
frame_limit: u32,
|
||||
eb: *std.zig.ErrorBundle.Wip,
|
||||
ref_trace: *std.ArrayList(std.zig.ErrorBundle.ReferenceTrace),
|
||||
) Allocator.Error!void {
|
||||
const ip = &zcu.intern_pool;
|
||||
const gpa = zcu.comp.gpa;
|
||||
|
||||
if (frame_limit == 0) return;
|
||||
|
||||
const all_references = try zcu.resolveReferences();
|
||||
|
||||
var seen: std.AutoHashMapUnmanaged(InternPool.AnalUnit, void) = .empty;
|
||||
defer seen.deinit(gpa);
|
||||
|
||||
var referenced_by = root;
|
||||
while (all_references.get(referenced_by)) |maybe_ref| {
|
||||
const ref = maybe_ref orelse break;
|
||||
const gop = try seen.getOrPut(gpa, ref.referencer);
|
||||
if (gop.found_existing) break;
|
||||
if (ref_trace.items.len < frame_limit) {
|
||||
var last_call_src = ref.src;
|
||||
var opt_inline_frame = ref.inline_frame;
|
||||
while (opt_inline_frame.unwrap()) |inline_frame| {
|
||||
const f = inline_frame.ptr(zcu).*;
|
||||
const func_nav = ip.indexToKey(f.callee).func.owner_nav;
|
||||
const func_name = ip.getNav(func_nav).name.toSlice(ip);
|
||||
addReferenceTraceFrame(zcu, eb, ref_trace, func_name, last_call_src, true) catch |err| switch (err) {
|
||||
error.OutOfMemory => |e| return e,
|
||||
error.AlreadyReported => {
|
||||
// An incomplete reference trace isn't the end of the world; just cut it off.
|
||||
return;
|
||||
},
|
||||
};
|
||||
last_call_src = f.call_src;
|
||||
opt_inline_frame = f.parent;
|
||||
}
|
||||
const root_name: ?[]const u8 = switch (ref.referencer.unwrap()) {
|
||||
.@"comptime" => "comptime",
|
||||
.nav_val, .nav_ty => |nav| ip.getNav(nav).name.toSlice(ip),
|
||||
.type_layout => |ty| Type.fromInterned(ty).containerTypeName(ip).toSlice(ip),
|
||||
.func => |f| ip.getNav(zcu.funcInfo(f).owner_nav).name.toSlice(ip),
|
||||
.memoized_state => null,
|
||||
};
|
||||
if (root_name) |n| {
|
||||
addReferenceTraceFrame(zcu, eb, ref_trace, n, last_call_src, false) catch |err| switch (err) {
|
||||
error.OutOfMemory => |e| return e,
|
||||
error.AlreadyReported => {
|
||||
// An incomplete reference trace isn't the end of the world; just cut it off.
|
||||
return;
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
referenced_by = ref.referencer;
|
||||
}
|
||||
|
||||
if (seen.count() > ref_trace.items.len) {
|
||||
try ref_trace.append(gpa, .{
|
||||
.decl_name = @intCast(seen.count() - ref_trace.items.len),
|
||||
.src_loc = .none,
|
||||
});
|
||||
}
|
||||
}
|
||||
fn addReferenceTraceFrame(
|
||||
zcu: *Zcu,
|
||||
eb: *std.zig.ErrorBundle.Wip,
|
||||
ref_trace: *std.ArrayList(std.zig.ErrorBundle.ReferenceTrace),
|
||||
name: []const u8,
|
||||
lazy_src: Zcu.LazySrcLoc,
|
||||
inlined: bool,
|
||||
) error{ OutOfMemory, AlreadyReported }!void {
|
||||
const gpa = zcu.gpa;
|
||||
const src = lazy_src.upgrade(zcu);
|
||||
const source = src.file_scope.getSource(zcu) catch |err| {
|
||||
try Compilation.unableToLoadZcuFile(zcu, eb, src.file_scope, err);
|
||||
return error.AlreadyReported;
|
||||
};
|
||||
const span = src.span(zcu) catch |err| {
|
||||
try Compilation.unableToLoadZcuFile(zcu, eb, src.file_scope, err);
|
||||
return error.AlreadyReported;
|
||||
};
|
||||
const loc = std.zig.findLineColumn(source, span.main);
|
||||
try ref_trace.append(gpa, .{
|
||||
.decl_name = try eb.printString("{s}{s}", .{ name, if (inlined) " [inlined]" else "" }),
|
||||
.src_loc = try eb.addSourceLocation(.{
|
||||
.src_path = try eb.printString("{f}", .{src.file_scope.path.fmt(zcu.comp)}),
|
||||
.span_start = span.start,
|
||||
.span_main = span.main,
|
||||
.span_end = span.end,
|
||||
.line = @intCast(loc.line),
|
||||
.column = @intCast(loc.column),
|
||||
.source_line = 0,
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
const TrackedUnitSema = struct {
|
||||
/// `null` means we created the node, so should end it.
|
||||
old_name: ?[std.Progress.Node.max_name_len]u8,
|
||||
|
||||
+93
-131
@@ -728,7 +728,12 @@ pub fn ensureFileAnalyzed(pt: Zcu.PerThread, file_index: Zcu.File.Index) (Alloca
|
||||
/// Ensures that all memoized state on `Zcu` is up-to-date, performing re-analysis if necessary.
|
||||
/// Returns `error.AnalysisFail` if an analysis error is encountered; the caller is free to ignore
|
||||
/// this, since the error is already registered, but it must not use the value of memoized fields.
|
||||
pub fn ensureMemoizedStateUpToDate(pt: Zcu.PerThread, stage: InternPool.MemoizedStateStage) Zcu.SemaError!void {
|
||||
pub fn ensureMemoizedStateUpToDate(
|
||||
pt: Zcu.PerThread,
|
||||
stage: InternPool.MemoizedStateStage,
|
||||
/// `null` is valid only for the "root" analysis, i.e. called from `Compilation.processOneJob`.
|
||||
reason: ?*const Zcu.DependencyReason,
|
||||
) Zcu.SemaError!void {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
@@ -748,12 +753,7 @@ pub fn ensureMemoizedStateUpToDate(pt: Zcu.PerThread, stage: InternPool.Memoized
|
||||
dev.check(.incremental);
|
||||
_ = zcu.outdated_ready.swapRemove(unit);
|
||||
// No need for `deleteUnitExports` because we never export anything.
|
||||
zcu.deleteUnitReferences(unit);
|
||||
zcu.deleteUnitCompileLogs(unit);
|
||||
if (zcu.failed_analysis.fetchSwapRemove(unit)) |kv| {
|
||||
kv.value.destroy(gpa);
|
||||
}
|
||||
_ = zcu.transitive_failed_analysis.swapRemove(unit);
|
||||
zcu.resetUnit(unit);
|
||||
} else {
|
||||
if (prev_failed) return error.AnalysisFail;
|
||||
// We use an arbitrary element to check if the state has been resolved yet.
|
||||
@@ -772,7 +772,7 @@ pub fn ensureMemoizedStateUpToDate(pt: Zcu.PerThread, stage: InternPool.Memoized
|
||||
info.deps.clearRetainingCapacity();
|
||||
}
|
||||
|
||||
const any_changed: bool, const new_failed: bool = if (pt.analyzeMemoizedState(stage)) |any_changed|
|
||||
const any_changed: bool, const new_failed: bool = if (pt.analyzeMemoizedState(stage, reason)) |any_changed|
|
||||
.{ any_changed or prev_failed, false }
|
||||
else |err| switch (err) {
|
||||
error.AnalysisFail => res: {
|
||||
@@ -805,14 +805,18 @@ pub fn ensureMemoizedStateUpToDate(pt: Zcu.PerThread, stage: InternPool.Memoized
|
||||
if (new_failed) return error.AnalysisFail;
|
||||
}
|
||||
|
||||
fn analyzeMemoizedState(pt: Zcu.PerThread, stage: InternPool.MemoizedStateStage) Zcu.CompileError!bool {
|
||||
fn analyzeMemoizedState(
|
||||
pt: Zcu.PerThread,
|
||||
stage: InternPool.MemoizedStateStage,
|
||||
reason: ?*const Zcu.DependencyReason,
|
||||
) Zcu.CompileError!bool {
|
||||
const zcu = pt.zcu;
|
||||
const comp = zcu.comp;
|
||||
const gpa = comp.gpa;
|
||||
|
||||
const unit: AnalUnit = .wrap(.{ .memoized_state = stage });
|
||||
|
||||
try zcu.analysis_in_progress.putNoClobber(gpa, unit, {});
|
||||
try zcu.analysis_in_progress.putNoClobber(gpa, unit, reason);
|
||||
defer assert(zcu.analysis_in_progress.swapRemove(unit));
|
||||
|
||||
var analysis_arena: std.heap.ArenaAllocator = .init(gpa);
|
||||
@@ -871,13 +875,7 @@ pub fn ensureComptimeUnitUpToDate(pt: Zcu.PerThread, cu_id: InternPool.ComptimeU
|
||||
// `was_outdated` can be true in the initial update for comptime units, so this isn't a `dev.check`.
|
||||
if (dev.env.supports(.incremental)) {
|
||||
zcu.deleteUnitExports(anal_unit);
|
||||
zcu.deleteUnitReferences(anal_unit);
|
||||
zcu.deleteUnitCompileLogs(anal_unit);
|
||||
if (zcu.failed_analysis.fetchSwapRemove(anal_unit)) |kv| {
|
||||
kv.value.destroy(gpa);
|
||||
}
|
||||
_ = zcu.transitive_failed_analysis.swapRemove(anal_unit);
|
||||
zcu.intern_pool.removeDependenciesForDepender(gpa, anal_unit);
|
||||
zcu.resetUnit(anal_unit);
|
||||
}
|
||||
} else {
|
||||
// We can trust the current information about this unit.
|
||||
@@ -943,7 +941,7 @@ fn analyzeComptimeUnit(pt: Zcu.PerThread, cu_id: InternPool.ComptimeUnit.Id) Zcu
|
||||
const file = zcu.fileByIndex(inst_resolved.file);
|
||||
const zir = file.zir.?;
|
||||
|
||||
try zcu.analysis_in_progress.putNoClobber(gpa, anal_unit, {});
|
||||
try zcu.analysis_in_progress.putNoClobber(gpa, anal_unit, null);
|
||||
defer assert(zcu.analysis_in_progress.swapRemove(anal_unit));
|
||||
|
||||
var analysis_arena: std.heap.ArenaAllocator = .init(gpa);
|
||||
@@ -1009,7 +1007,12 @@ fn analyzeComptimeUnit(pt: Zcu.PerThread, cu_id: InternPool.ComptimeUnit.Id) Zcu
|
||||
/// re-analysis if necessary. Asserts that `ty` is a struct (not a tuple!) or union. Returns
|
||||
/// `error.AnalysisFail` if an analysis error is encountered during type resolution; the caller is
|
||||
/// free to ignore this, since the error is already registered.
|
||||
pub fn ensureTypeLayoutUpToDate(pt: Zcu.PerThread, ty: Type) Zcu.SemaError!void {
|
||||
pub fn ensureTypeLayoutUpToDate(
|
||||
pt: Zcu.PerThread,
|
||||
ty: Type,
|
||||
/// `null` is valid only for the "root" analysis, i.e. called from `Compilation.processOneJob`.
|
||||
reason: ?*const Zcu.DependencyReason,
|
||||
) Zcu.SemaError!void {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
@@ -1031,13 +1034,7 @@ pub fn ensureTypeLayoutUpToDate(pt: Zcu.PerThread, ty: Type) Zcu.SemaError!void
|
||||
// `was_outdated` is true in the initial update, so this isn't a `dev.check`.
|
||||
if (dev.env.supports(.incremental)) {
|
||||
zcu.deleteUnitExports(anal_unit);
|
||||
zcu.deleteUnitReferences(anal_unit);
|
||||
zcu.deleteUnitCompileLogs(anal_unit);
|
||||
if (zcu.failed_analysis.fetchSwapRemove(anal_unit)) |kv| {
|
||||
kv.value.destroy(gpa);
|
||||
}
|
||||
_ = zcu.transitive_failed_analysis.swapRemove(anal_unit);
|
||||
zcu.intern_pool.removeDependenciesForDepender(gpa, anal_unit);
|
||||
zcu.resetUnit(anal_unit);
|
||||
}
|
||||
// For types, we already know that we have to invalidate all dependees.
|
||||
// TODO: we actually *could* detect whether everything was the same. should we bother?
|
||||
@@ -1058,7 +1055,7 @@ pub fn ensureTypeLayoutUpToDate(pt: Zcu.PerThread, ty: Type) Zcu.SemaError!void
|
||||
const unit_tracking = zcu.trackUnitSema(ty.containerTypeName(&zcu.intern_pool).toSlice(&zcu.intern_pool), null);
|
||||
defer unit_tracking.end(zcu);
|
||||
|
||||
try zcu.analysis_in_progress.put(gpa, anal_unit, {});
|
||||
try zcu.analysis_in_progress.put(gpa, anal_unit, reason);
|
||||
defer assert(zcu.analysis_in_progress.swapRemove(anal_unit));
|
||||
|
||||
var analysis_arena: std.heap.ArenaAllocator = .init(gpa);
|
||||
@@ -1114,7 +1111,12 @@ pub fn ensureTypeLayoutUpToDate(pt: Zcu.PerThread, ty: Type) Zcu.SemaError!void
|
||||
/// Ensures that the resolved value of the given `Nav` is fully up-to-date, performing re-analysis
|
||||
/// if necessary. Returns `error.AnalysisFail` if an analysis error is encountered; the caller is
|
||||
/// free to ignore this, since the error is already registered.
|
||||
pub fn ensureNavValUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.SemaError!void {
|
||||
pub fn ensureNavValUpToDate(
|
||||
pt: Zcu.PerThread,
|
||||
nav_id: InternPool.Nav.Index,
|
||||
/// `null` is valid only for the "root" analysis, i.e. called from `Compilation.processOneJob`.
|
||||
reason: ?*const Zcu.DependencyReason,
|
||||
) Zcu.SemaError!void {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
@@ -1150,13 +1152,7 @@ pub fn ensureNavValUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu
|
||||
dev.check(.incremental);
|
||||
_ = zcu.outdated_ready.swapRemove(anal_unit);
|
||||
zcu.deleteUnitExports(anal_unit);
|
||||
zcu.deleteUnitReferences(anal_unit);
|
||||
zcu.deleteUnitCompileLogs(anal_unit);
|
||||
if (zcu.failed_analysis.fetchSwapRemove(anal_unit)) |kv| {
|
||||
kv.value.destroy(gpa);
|
||||
}
|
||||
_ = zcu.transitive_failed_analysis.swapRemove(anal_unit);
|
||||
ip.removeDependenciesForDepender(gpa, anal_unit);
|
||||
zcu.resetUnit(anal_unit);
|
||||
} else {
|
||||
// We can trust the current information about this unit.
|
||||
if (prev_failed) return error.AnalysisFail;
|
||||
@@ -1173,7 +1169,7 @@ pub fn ensureNavValUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu
|
||||
const unit_tracking = zcu.trackUnitSema(nav.fqn.toSlice(ip), nav.srcInst(ip));
|
||||
defer unit_tracking.end(zcu);
|
||||
|
||||
const invalidate_value: bool, const new_failed: bool = if (pt.analyzeNavVal(nav_id)) |result| res: {
|
||||
const invalidate_value: bool, const new_failed: bool = if (pt.analyzeNavVal(nav_id, reason)) |result| res: {
|
||||
break :res .{
|
||||
// If the unit has gone from failed to success, we still need to invalidate the dependencies.
|
||||
result.val_changed or prev_failed,
|
||||
@@ -1217,39 +1213,14 @@ pub fn ensureNavValUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu
|
||||
}
|
||||
}
|
||||
|
||||
// If there isn't a type annotation, then we have also just resolved the type. That means the
|
||||
// the type is up-to-date, so it won't have the chance to mark its own dependency on the value;
|
||||
// we must do that ourselves.
|
||||
type_deps_on_val: {
|
||||
const inst_resolved = nav.analysis.?.zir_index.resolveFull(ip) orelse break :type_deps_on_val;
|
||||
const file = zcu.fileByIndex(inst_resolved.file);
|
||||
const zir_decl = file.zir.?.getDeclaration(inst_resolved.inst);
|
||||
if (zir_decl.type_body != null) break :type_deps_on_val;
|
||||
// The type does indeed depend on the value. We are responsible for populating all state of
|
||||
// the `nav_ty`, including exports, references, errors, and dependencies.
|
||||
const ty_unit: AnalUnit = .wrap(.{ .nav_ty = nav_id });
|
||||
const ty_was_outdated = zcu.outdated.swapRemove(ty_unit) or
|
||||
zcu.potentially_outdated.swapRemove(ty_unit);
|
||||
if (ty_was_outdated) {
|
||||
_ = zcu.outdated_ready.swapRemove(ty_unit);
|
||||
zcu.deleteUnitExports(ty_unit);
|
||||
zcu.deleteUnitReferences(ty_unit);
|
||||
zcu.deleteUnitCompileLogs(ty_unit);
|
||||
if (zcu.failed_analysis.fetchSwapRemove(ty_unit)) |kv| {
|
||||
kv.value.destroy(gpa);
|
||||
}
|
||||
_ = zcu.transitive_failed_analysis.swapRemove(ty_unit);
|
||||
ip.removeDependenciesForDepender(gpa, ty_unit);
|
||||
}
|
||||
try pt.addDependency(ty_unit, .{ .nav_val = nav_id });
|
||||
if (new_failed) try zcu.transitive_failed_analysis.put(gpa, ty_unit, {});
|
||||
if (ty_was_outdated) try zcu.markDependeeOutdated(.marked_po, .{ .nav_ty = nav_id });
|
||||
}
|
||||
|
||||
if (new_failed) return error.AnalysisFail;
|
||||
}
|
||||
|
||||
fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileError!struct { val_changed: bool } {
|
||||
fn analyzeNavVal(
|
||||
pt: Zcu.PerThread,
|
||||
nav_id: InternPool.Nav.Index,
|
||||
reason: ?*const Zcu.DependencyReason,
|
||||
) Zcu.CompileError!struct { val_changed: bool } {
|
||||
const zcu = pt.zcu;
|
||||
const ip = &zcu.intern_pool;
|
||||
const comp = zcu.comp;
|
||||
@@ -1266,17 +1237,9 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr
|
||||
const zir = file.zir.?;
|
||||
const zir_decl = zir.getDeclaration(inst_resolved.inst);
|
||||
|
||||
try zcu.analysis_in_progress.putNoClobber(gpa, anal_unit, {});
|
||||
try zcu.analysis_in_progress.putNoClobber(gpa, anal_unit, reason);
|
||||
errdefer _ = zcu.analysis_in_progress.swapRemove(anal_unit);
|
||||
|
||||
// If there's no type body, we are also resolving the type here.
|
||||
if (zir_decl.type_body == null) {
|
||||
try zcu.analysis_in_progress.putNoClobber(gpa, .wrap(.{ .nav_ty = nav_id }), {});
|
||||
}
|
||||
errdefer if (zir_decl.type_body == null) {
|
||||
_ = zcu.analysis_in_progress.swapRemove(.wrap(.{ .nav_ty = nav_id }));
|
||||
};
|
||||
|
||||
var analysis_arena: std.heap.ArenaAllocator = .init(gpa);
|
||||
defer analysis_arena.deinit();
|
||||
|
||||
@@ -1352,9 +1315,6 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr
|
||||
|
||||
const nav_ty: Type = maybe_ty orelse final_val.?.typeOf(zcu);
|
||||
|
||||
// First, we must resolve the declaration's type. To do this, we analyze the type body if available,
|
||||
// or otherwise, we analyze the value body, populating `early_val` in the process.
|
||||
|
||||
const is_const = is_const: switch (zir_decl.kind) {
|
||||
.@"comptime" => unreachable, // this is not a Nav
|
||||
.unnamed_test, .@"test", .decltest => {
|
||||
@@ -1441,7 +1401,7 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr
|
||||
|
||||
// This resolves the type of the resolved value, not that value itself. If `nav_val` is a struct type,
|
||||
// this resolves the type `type` (which needs no resolution), not the struct itself.
|
||||
try sema.ensureLayoutResolved(nav_ty, init_src);
|
||||
try sema.ensureLayoutResolved(nav_ty, block.nodeOffset(.zero), if (zir_decl.kind == .@"var") .variable else .constant);
|
||||
|
||||
const queue_linker_work, const is_owned_fn = switch (ip.indexToKey(nav_val.toIntern())) {
|
||||
.func => |f| .{ true, f.owner_nav == nav_id }, // note that this lets function aliases reach codegen
|
||||
@@ -1460,18 +1420,18 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr
|
||||
}
|
||||
} else if (nav_ty.comptimeOnly(zcu)) {
|
||||
// alignment, linksection, addrspace annotations are not allowed for comptime-only types.
|
||||
const reason: []const u8 = switch (ip.indexToKey(nav_val.toIntern())) {
|
||||
const cannot_align_reason: []const u8 = switch (ip.indexToKey(nav_val.toIntern())) {
|
||||
.func => "function alias", // slightly clearer message, since you *can* specify these on function *declarations*
|
||||
else => "comptime-only type",
|
||||
};
|
||||
if (zir_decl.align_body != null) {
|
||||
return sema.fail(&block, align_src, "cannot specify alignment of {s}", .{reason});
|
||||
return sema.fail(&block, align_src, "cannot specify alignment of {s}", .{cannot_align_reason});
|
||||
}
|
||||
if (zir_decl.linksection_body != null) {
|
||||
return sema.fail(&block, section_src, "cannot specify linksection of {s}", .{reason});
|
||||
return sema.fail(&block, section_src, "cannot specify linksection of {s}", .{cannot_align_reason});
|
||||
}
|
||||
if (zir_decl.addrspace_body != null) {
|
||||
return sema.fail(&block, addrspace_src, "cannot specify addrspace of {s}", .{reason});
|
||||
return sema.fail(&block, addrspace_src, "cannot specify addrspace of {s}", .{cannot_align_reason});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1484,10 +1444,8 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr
|
||||
});
|
||||
|
||||
// Mark the unit as completed before evaluating the export!
|
||||
// MLUGG TODO: do we really need to do this?
|
||||
assert(zcu.analysis_in_progress.swapRemove(anal_unit));
|
||||
if (zir_decl.type_body == null) {
|
||||
assert(zcu.analysis_in_progress.swapRemove(.wrap(.{ .nav_ty = nav_id })));
|
||||
}
|
||||
|
||||
if (zir_decl.linkage == .@"export") {
|
||||
const export_src = block.src(.{ .token_offset = @enumFromInt(@intFromBool(zir_decl.is_pub)) });
|
||||
@@ -1516,7 +1474,12 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ensureNavTypeUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.SemaError!void {
|
||||
pub fn ensureNavTypeUpToDate(
|
||||
pt: Zcu.PerThread,
|
||||
nav_id: InternPool.Nav.Index,
|
||||
/// `null` is valid only for the "root" analysis, i.e. called from `Compilation.processOneJob`.
|
||||
reason: ?*const Zcu.DependencyReason,
|
||||
) Zcu.SemaError!void {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
@@ -1533,18 +1496,6 @@ pub fn ensureNavTypeUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zc
|
||||
|
||||
try zcu.ensureNavValAnalysisQueued(nav_id);
|
||||
|
||||
const type_resolved_by_value: bool = from_val: {
|
||||
const analysis = nav.analysis orelse break :from_val false;
|
||||
const inst_resolved = analysis.zir_index.resolveFull(ip) orelse break :from_val false;
|
||||
const file = zcu.fileByIndex(inst_resolved.file);
|
||||
const zir_decl = file.zir.?.getDeclaration(inst_resolved.inst);
|
||||
break :from_val zir_decl.type_body == null;
|
||||
};
|
||||
if (type_resolved_by_value) {
|
||||
// Logic at the end of `ensureNavValUpToDate` is directly responsible for populating our state.
|
||||
return pt.ensureNavValUpToDate(nav_id);
|
||||
}
|
||||
|
||||
// Determine whether or not this `Nav`'s type is outdated. This also includes checking if the
|
||||
// status is `.unresolved`, which indicates that the value is outdated because it has *never*
|
||||
// been analyzed so far.
|
||||
@@ -1564,13 +1515,7 @@ pub fn ensureNavTypeUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zc
|
||||
dev.check(.incremental);
|
||||
_ = zcu.outdated_ready.swapRemove(anal_unit);
|
||||
zcu.deleteUnitExports(anal_unit);
|
||||
zcu.deleteUnitReferences(anal_unit);
|
||||
zcu.deleteUnitCompileLogs(anal_unit);
|
||||
if (zcu.failed_analysis.fetchSwapRemove(anal_unit)) |kv| {
|
||||
kv.value.destroy(gpa);
|
||||
}
|
||||
_ = zcu.transitive_failed_analysis.swapRemove(anal_unit);
|
||||
ip.removeDependenciesForDepender(gpa, anal_unit);
|
||||
zcu.resetUnit(anal_unit);
|
||||
} else {
|
||||
// We can trust the current information about this unit.
|
||||
if (prev_failed) return error.AnalysisFail;
|
||||
@@ -1587,7 +1532,7 @@ pub fn ensureNavTypeUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zc
|
||||
const unit_tracking = zcu.trackUnitSema(nav.fqn.toSlice(ip), nav.srcInst(ip));
|
||||
defer unit_tracking.end(zcu);
|
||||
|
||||
const invalidate_type: bool, const new_failed: bool = if (pt.analyzeNavType(nav_id)) |result| res: {
|
||||
const invalidate_type: bool, const new_failed: bool = if (pt.analyzeNavType(nav_id, reason)) |result| res: {
|
||||
break :res .{
|
||||
// If the unit has gone from failed to success, we still need to invalidate the dependencies.
|
||||
result.type_changed or prev_failed,
|
||||
@@ -1634,7 +1579,11 @@ pub fn ensureNavTypeUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zc
|
||||
if (new_failed) return error.AnalysisFail;
|
||||
}
|
||||
|
||||
fn analyzeNavType(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileError!struct { type_changed: bool } {
|
||||
fn analyzeNavType(
|
||||
pt: Zcu.PerThread,
|
||||
nav_id: InternPool.Nav.Index,
|
||||
reason: ?*const Zcu.DependencyReason,
|
||||
) Zcu.CompileError!struct { type_changed: bool } {
|
||||
const zcu = pt.zcu;
|
||||
const comp = zcu.comp;
|
||||
const gpa = comp.gpa;
|
||||
@@ -1650,11 +1599,10 @@ fn analyzeNavType(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileEr
|
||||
const file = zcu.fileByIndex(inst_resolved.file);
|
||||
const zir = file.zir.?;
|
||||
|
||||
try zcu.analysis_in_progress.putNoClobber(gpa, anal_unit, {});
|
||||
try zcu.analysis_in_progress.putNoClobber(gpa, anal_unit, reason);
|
||||
defer assert(zcu.analysis_in_progress.swapRemove(anal_unit));
|
||||
|
||||
const zir_decl = zir.getDeclaration(inst_resolved.inst);
|
||||
const type_body = zir_decl.type_body.?;
|
||||
|
||||
var analysis_arena: std.heap.ArenaAllocator = .init(gpa);
|
||||
defer analysis_arena.deinit();
|
||||
@@ -1696,6 +1644,17 @@ fn analyzeNavType(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileEr
|
||||
defer block.instructions.deinit(gpa);
|
||||
|
||||
const ty_src = block.src(.{ .node_offset_var_decl_ty = .zero });
|
||||
const init_src = block.src(.{ .node_offset_var_decl_init = .zero });
|
||||
|
||||
const type_body = zir_decl.type_body orelse {
|
||||
// There is no type annotation, so we just need to use the declaration's value.
|
||||
try sema.ensureNavResolved(&block, init_src, nav_id, .fully);
|
||||
// We don't actually know what the type of this Nav was before it was resolved, so we just
|
||||
// have to assume we were outdated. This isn't too bad, because assuming there was also no
|
||||
// type annotation last update, we should only be re-analyzed if the value changes (it's our
|
||||
// only dependency), or if there was a dependency loop.
|
||||
return .{ .type_changed = true };
|
||||
};
|
||||
|
||||
block.comptime_reason = .{ .reason = .{
|
||||
.src = ty_src,
|
||||
@@ -1708,7 +1667,7 @@ fn analyzeNavType(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileEr
|
||||
break :ty .fromInterned(type_ref.toInterned().?);
|
||||
};
|
||||
|
||||
try sema.ensureLayoutResolved(resolved_ty, ty_src);
|
||||
try sema.ensureLayoutResolved(resolved_ty, block.nodeOffset(.zero), if (zir_decl.kind == .@"var") .variable else .constant);
|
||||
|
||||
// In the case where the type is specified, this function is also responsible for resolving
|
||||
// the pointer modifiers, i.e. alignment, linksection, addrspace.
|
||||
@@ -1758,7 +1717,12 @@ fn analyzeNavType(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileEr
|
||||
return .{ .type_changed = true };
|
||||
}
|
||||
|
||||
pub fn ensureFuncBodyUpToDate(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.SemaError!void {
|
||||
pub fn ensureFuncBodyUpToDate(
|
||||
pt: Zcu.PerThread,
|
||||
func_index: InternPool.Index,
|
||||
/// `null` is valid only for the "root" analysis, i.e. called from `Compilation.processOneJob`.
|
||||
reason: ?*const Zcu.DependencyReason,
|
||||
) Zcu.SemaError!void {
|
||||
dev.check(.sema);
|
||||
|
||||
const tracy = trace(@src());
|
||||
@@ -1788,12 +1752,7 @@ pub fn ensureFuncBodyUpToDate(pt: Zcu.PerThread, func_index: InternPool.Index) Z
|
||||
dev.check(.incremental);
|
||||
_ = zcu.outdated_ready.swapRemove(anal_unit);
|
||||
zcu.deleteUnitExports(anal_unit);
|
||||
zcu.deleteUnitReferences(anal_unit);
|
||||
zcu.deleteUnitCompileLogs(anal_unit);
|
||||
if (zcu.failed_analysis.fetchSwapRemove(anal_unit)) |kv| {
|
||||
kv.value.destroy(gpa);
|
||||
}
|
||||
_ = zcu.transitive_failed_analysis.swapRemove(anal_unit);
|
||||
zcu.resetUnit(anal_unit);
|
||||
} else {
|
||||
// We can trust the current information about this function.
|
||||
if (prev_failed) return error.AnalysisFail;
|
||||
@@ -1813,7 +1772,7 @@ pub fn ensureFuncBodyUpToDate(pt: Zcu.PerThread, func_index: InternPool.Index) Z
|
||||
);
|
||||
defer unit_tracking.end(zcu);
|
||||
|
||||
const ies_outdated, const new_failed = if (pt.analyzeFuncBody(func_index)) |result|
|
||||
const ies_outdated, const new_failed = if (pt.analyzeFuncBody(func_index, reason)) |result|
|
||||
.{ prev_failed or result.ies_outdated, false }
|
||||
else |err| switch (err) {
|
||||
error.AnalysisFail => res: {
|
||||
@@ -1854,6 +1813,7 @@ pub fn ensureFuncBodyUpToDate(pt: Zcu.PerThread, func_index: InternPool.Index) Z
|
||||
fn analyzeFuncBody(
|
||||
pt: Zcu.PerThread,
|
||||
func_index: InternPool.Index,
|
||||
reason: ?*const Zcu.DependencyReason,
|
||||
) Zcu.SemaError!struct { ies_outdated: bool } {
|
||||
const zcu = pt.zcu;
|
||||
const gpa = zcu.gpa;
|
||||
@@ -1868,7 +1828,7 @@ fn analyzeFuncBody(
|
||||
|
||||
if (func.generic_owner == .none) {
|
||||
// Among another things, this ensures that the function's `zir_body_inst` is correct.
|
||||
try pt.ensureNavValUpToDate(func.owner_nav);
|
||||
try pt.ensureNavValUpToDate(func.owner_nav, reason);
|
||||
if (ip.getNav(func.owner_nav).status.fully_resolved.val != func_index) {
|
||||
// This function is no longer referenced! There's no point in re-analyzing it.
|
||||
// Just mark a transitive failure and move on.
|
||||
@@ -1877,7 +1837,7 @@ fn analyzeFuncBody(
|
||||
} else {
|
||||
const go_nav = zcu.funcInfo(func.generic_owner).owner_nav;
|
||||
// Among another things, this ensures that the function's `zir_body_inst` is correct.
|
||||
try pt.ensureNavValUpToDate(go_nav);
|
||||
try pt.ensureNavValUpToDate(go_nav, reason);
|
||||
if (ip.getNav(go_nav).status.fully_resolved.val != func.generic_owner) {
|
||||
// The generic owner is no longer referenced, so this function is also unreferenced.
|
||||
// There's no point in re-analyzing it. Just mark a transitive failure and move on.
|
||||
@@ -1894,7 +1854,7 @@ fn analyzeFuncBody(
|
||||
|
||||
log.debug("analyze and generate fn body {f}", .{zcu.fmtAnalUnit(anal_unit)});
|
||||
|
||||
var air = try pt.analyzeFuncBodyInner(func_index);
|
||||
var air = try pt.analyzeFuncBodyInner(func_index, reason);
|
||||
errdefer air.deinit(gpa);
|
||||
|
||||
const ies_outdated = !func.analysisUnordered(ip).inferred_error_set or
|
||||
@@ -2842,7 +2802,11 @@ const ScanDeclIter = struct {
|
||||
}
|
||||
};
|
||||
|
||||
fn analyzeFuncBodyInner(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.SemaError!Air {
|
||||
fn analyzeFuncBodyInner(
|
||||
pt: Zcu.PerThread,
|
||||
func_index: InternPool.Index,
|
||||
reason: ?*const Zcu.DependencyReason,
|
||||
) Zcu.SemaError!Air {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
@@ -2858,7 +2822,7 @@ fn analyzeFuncBodyInner(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.Sem
|
||||
const file = zcu.fileByIndex(inst_info.file);
|
||||
const zir = file.zir.?;
|
||||
|
||||
try zcu.analysis_in_progress.putNoClobber(gpa, anal_unit, {});
|
||||
try zcu.analysis_in_progress.putNoClobber(gpa, anal_unit, reason);
|
||||
defer assert(zcu.analysis_in_progress.swapRemove(anal_unit));
|
||||
|
||||
if (func.analysisUnordered(ip).inferred_error_set) {
|
||||
@@ -2879,8 +2843,6 @@ fn analyzeFuncBodyInner(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.Sem
|
||||
|
||||
const func_nav = ip.getNav(func.owner_nav);
|
||||
|
||||
zcu.intern_pool.removeDependenciesForDepender(gpa, anal_unit);
|
||||
|
||||
var analysis_arena = std.heap.ArenaAllocator.init(gpa);
|
||||
defer analysis_arena.deinit();
|
||||
|
||||
@@ -2978,7 +2940,7 @@ fn analyzeFuncBodyInner(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.Sem
|
||||
const param_ty: Type = .fromInterned(fn_ty_info.param_types.get(ip)[runtime_param_index]);
|
||||
runtime_param_index += 1;
|
||||
|
||||
try sema.ensureLayoutResolved(param_ty, inner_block.src(.{ .func_decl_param_ty = @intCast(zir_param_index) }));
|
||||
try sema.ensureLayoutResolved(param_ty, inner_block.src(.{ .func_decl_param_ty = @intCast(zir_param_index) }), .parameter);
|
||||
if (try param_ty.onePossibleValue(pt)) |opv| {
|
||||
gop.value_ptr.* = .fromValue(opv);
|
||||
continue;
|
||||
@@ -2995,7 +2957,7 @@ fn analyzeFuncBodyInner(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.Sem
|
||||
});
|
||||
}
|
||||
|
||||
try sema.ensureLayoutResolved(sema.fn_ret_ty, inner_block.src(.{ .node_offset_fn_type_ret_ty = .zero }));
|
||||
try sema.ensureLayoutResolved(sema.fn_ret_ty, inner_block.src(.{ .node_offset_fn_type_ret_ty = .zero }), .return_type);
|
||||
|
||||
const last_arg_index = inner_block.instructions.items.len;
|
||||
|
||||
@@ -4206,7 +4168,7 @@ pub fn resolveTypeForCodegen(pt: Zcu.PerThread, ty: Type) Zcu.SemaError!void {
|
||||
},
|
||||
|
||||
.@"struct" => switch (ip.indexToKey(ty.toIntern())) {
|
||||
.struct_type => try pt.ensureTypeLayoutUpToDate(ty),
|
||||
.struct_type => try pt.ensureTypeLayoutUpToDate(ty, null),
|
||||
.tuple_type => |tuple| for (0..tuple.types.len) |i| {
|
||||
const field_is_comptime = tuple.values.get(ip)[i] != .none;
|
||||
if (field_is_comptime) continue;
|
||||
@@ -4216,8 +4178,8 @@ pub fn resolveTypeForCodegen(pt: Zcu.PerThread, ty: Type) Zcu.SemaError!void {
|
||||
else => unreachable,
|
||||
},
|
||||
|
||||
.@"union" => try pt.ensureTypeLayoutUpToDate(ty),
|
||||
.@"enum" => try pt.ensureTypeLayoutUpToDate(ty),
|
||||
.@"union" => try pt.ensureTypeLayoutUpToDate(ty, null),
|
||||
.@"enum" => try pt.ensureTypeLayoutUpToDate(ty, null),
|
||||
}
|
||||
}
|
||||
pub fn resolveValueTypesForCodegen(pt: Zcu.PerThread, val: Value) Zcu.SemaError!void {
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
#target=x86_64-linux-selfhosted
|
||||
#target=x86_64-windows-selfhosted
|
||||
#target=x86_64-linux-cbe
|
||||
#target=x86_64-windows-cbe
|
||||
#target=wasm32-wasi-selfhosted
|
||||
#update=initial version
|
||||
#file=main.zig
|
||||
pub const A = struct { b: B };
|
||||
pub const B = struct { a: A };
|
||||
pub fn main() void {
|
||||
_ = @as(B, undefined);
|
||||
}
|
||||
#expect_error=:error: dependency loop with length 2
|
||||
#expect_error=main.zig:2:27: note: type 'main.B' depends on type 'main.A' for field declared here
|
||||
#expect_error=main.zig:1:27: note: type 'main.A' depends on type 'main.B' for field declared here
|
||||
#expect_error=:note: eliminate any one of these dependencies to break the loop
|
||||
|
||||
#update=remove reference to dependency loop
|
||||
#file=main.zig
|
||||
pub const A = struct { b: B };
|
||||
pub const B = struct { a: A };
|
||||
pub fn main() void {
|
||||
_ = B;
|
||||
}
|
||||
#expect_stdout=""
|
||||
|
||||
#update=change dependency loop without fixing it
|
||||
#file=main.zig
|
||||
pub const A = struct { b: B };
|
||||
pub const B = struct { a: *align(@alignOf(A)) A };
|
||||
pub fn main() void {
|
||||
_ = B;
|
||||
}
|
||||
#expect_stdout=""
|
||||
|
||||
#update=reference dependency loop again
|
||||
#file=main.zig
|
||||
pub const A = struct { b: B };
|
||||
pub const B = struct { a: *align(@alignOf(A)) A };
|
||||
pub fn main() void {
|
||||
_ = @as(B, undefined);
|
||||
}
|
||||
#expect_error=:error: dependency loop with length 2
|
||||
#expect_error=main.zig:2:43: note: type 'main.B' depends on type 'main.A' for alignment query here
|
||||
#expect_error=main.zig:1:27: note: type 'main.A' depends on type 'main.B' for field declared here
|
||||
#expect_error=:note: eliminate any one of these dependencies to break the loop
|
||||
|
||||
#update=fix dependency loop
|
||||
#file=main.zig
|
||||
pub const A = struct { b: B };
|
||||
pub const B = struct { a: *A };
|
||||
pub fn main() void {
|
||||
_ = @as(B, undefined);
|
||||
}
|
||||
#expect_stdout=""
|
||||
+41
-35
@@ -417,29 +417,32 @@ const Eval = struct {
|
||||
is_note: bool,
|
||||
err_idx: std.zig.ErrorBundle.MessageIndex,
|
||||
) Allocator.Error!void {
|
||||
const io = eval.io;
|
||||
const err = eb.getErrorMessage(err_idx);
|
||||
if (err.src_loc == .none) @panic("TODO error message with no source location");
|
||||
if (err.count != 1) @panic("TODO error message with count>1");
|
||||
const msg = eb.nullTerminatedString(err.msg);
|
||||
const src = eb.getSourceLocation(err.src_loc);
|
||||
const raw_filename = eb.nullTerminatedString(src.src_path);
|
||||
|
||||
const io = eval.io;
|
||||
|
||||
// We need to replace backslashes for consistency between platforms.
|
||||
const filename = name: {
|
||||
if (std.mem.indexOfScalar(u8, raw_filename, '\\') == null) break :name raw_filename;
|
||||
const copied = try eval.arena.dupe(u8, raw_filename);
|
||||
std.mem.replaceScalar(u8, copied, '\\', '/');
|
||||
break :name copied;
|
||||
const matches = matches: {
|
||||
if (expected.is_note != is_note) break :matches false;
|
||||
if (!std.mem.eql(u8, expected.msg, msg)) break :matches false;
|
||||
if (err.src_loc == .none) {
|
||||
break :matches expected.src == null;
|
||||
}
|
||||
const expected_src = expected.src orelse break :matches false;
|
||||
const src = eb.getSourceLocation(err.src_loc);
|
||||
const raw_filename = eb.nullTerminatedString(src.src_path);
|
||||
// We need to replace backslashes for consistency between platforms.
|
||||
const filename = name: {
|
||||
if (std.mem.indexOfScalar(u8, raw_filename, '\\') == null) break :name raw_filename;
|
||||
const copied = try eval.arena.dupe(u8, raw_filename);
|
||||
std.mem.replaceScalar(u8, copied, '\\', '/');
|
||||
break :name copied;
|
||||
};
|
||||
if (!std.mem.eql(u8, expected_src.filename, filename)) break :matches false;
|
||||
if (expected_src.line != src.line + 1) break :matches false;
|
||||
if (expected_src.column != src.column + 1) break :matches false;
|
||||
break :matches true;
|
||||
};
|
||||
|
||||
if (expected.is_note != is_note or
|
||||
!std.mem.eql(u8, expected.filename, filename) or
|
||||
expected.line != src.line + 1 or
|
||||
expected.column != src.column + 1 or
|
||||
!std.mem.eql(u8, expected.msg, msg))
|
||||
{
|
||||
if (!matches) {
|
||||
eb.renderToStderr(io, .{}, .auto) catch {};
|
||||
eval.fatal("compile error did not match expected error", .{});
|
||||
}
|
||||
@@ -714,10 +717,12 @@ const Case = struct {
|
||||
|
||||
const ExpectedError = struct {
|
||||
is_note: bool,
|
||||
filename: []const u8,
|
||||
line: u32,
|
||||
column: u32,
|
||||
msg: []const u8,
|
||||
src: ?struct {
|
||||
filename: []const u8,
|
||||
line: u32,
|
||||
column: u32,
|
||||
},
|
||||
};
|
||||
|
||||
fn parse(arena: Allocator, io: Io, bytes: []const u8) !Case {
|
||||
@@ -930,16 +935,16 @@ fn parseExpectedError(str: []const u8, l: usize) Case.ExpectedError {
|
||||
|
||||
var it = std.mem.splitScalar(u8, str, ':');
|
||||
const filename = it.first();
|
||||
const line_str = it.next() orelse fatal("line {d}: incomplete error specification", .{l});
|
||||
const column_str = it.next() orelse fatal("line {d}: incomplete error specification", .{l});
|
||||
const line_str, const column_str = if (filename.len > 0) .{
|
||||
it.next() orelse fatal("line {d}: incomplete error specification", .{l}),
|
||||
it.next() orelse fatal("line {d}: incomplete error specification", .{l}),
|
||||
} else .{ undefined, undefined };
|
||||
const error_or_note_str = std.mem.trim(
|
||||
u8,
|
||||
it.next() orelse fatal("line {d}: incomplete error specification", .{l}),
|
||||
" ",
|
||||
);
|
||||
const message = std.mem.trim(u8, it.rest(), " ");
|
||||
if (filename.len == 0) fatal("line {d}: empty filename", .{l});
|
||||
if (message.len == 0) fatal("line {d}: empty error message", .{l});
|
||||
|
||||
const is_note = if (std.mem.eql(u8, error_or_note_str, "error"))
|
||||
false
|
||||
else if (std.mem.eql(u8, error_or_note_str, "note"))
|
||||
@@ -947,18 +952,19 @@ fn parseExpectedError(str: []const u8, l: usize) Case.ExpectedError {
|
||||
else
|
||||
fatal("line {d}: expeted 'error' or 'note', found '{s}'", .{ l, error_or_note_str });
|
||||
|
||||
const line = std.fmt.parseInt(u32, line_str, 10) catch
|
||||
fatal("line {d}: invalid line number '{s}'", .{ l, line_str });
|
||||
|
||||
const column = std.fmt.parseInt(u32, column_str, 10) catch
|
||||
fatal("line {d}: invalid column number '{s}'", .{ l, column_str });
|
||||
const message = std.mem.trim(u8, it.rest(), " ");
|
||||
if (message.len == 0) fatal("line {d}: empty error message", .{l});
|
||||
|
||||
return .{
|
||||
.is_note = is_note,
|
||||
.filename = filename,
|
||||
.line = line,
|
||||
.column = column,
|
||||
.msg = message,
|
||||
.src = if (filename.len == 0) null else .{
|
||||
.filename = filename,
|
||||
.line = std.fmt.parseInt(u32, line_str, 10) catch
|
||||
fatal("line {d}: invalid line number '{s}'", .{ l, line_str }),
|
||||
.column = std.fmt.parseInt(u32, column_str, 10) catch
|
||||
fatal("line {d}: invalid column number '{s}'", .{ l, column_str }),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user