Merge pull request #22303 from mlugg/131-new

compiler: analyze type and value of global declarations separately
This commit is contained in:
Matthew Lugg 2024-12-25 02:58:27 +00:00 committed by GitHub
commit 497592c9b4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
29 changed files with 3098 additions and 2549 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1868,10 +1868,6 @@ pub const Inst = struct {
/// Rarer instructions are here; ones that do not fit in the 8-bit `Tag` enum.
/// `noreturn` instructions may not go here; they must be part of the main `Tag` enum.
pub const Extended = enum(u16) {
/// Declares a global variable.
/// `operand` is payload index to `ExtendedVar`.
/// `small` is `ExtendedVar.Small`.
variable,
/// A struct type definition. Contains references to ZIR instructions for
/// the field types, defaults, and alignments.
/// `operand` is payload index to `StructDecl`.
@ -2493,26 +2489,25 @@ pub const Inst = struct {
};
/// Trailing:
/// 0. lib_name: NullTerminatedString, // null terminated string index, if has_lib_name is set
/// if (has_cc_ref and !has_cc_body) {
/// 1. cc: Ref,
/// 0. cc: Ref,
/// }
/// if (has_cc_body) {
/// 2. cc_body_len: u32
/// 3. cc_body: u32 // for each cc_body_len
/// 1. cc_body_len: u32
/// 2. cc_body: u32 // for each cc_body_len
/// }
/// if (has_ret_ty_ref and !has_ret_ty_body) {
/// 4. ret_ty: Ref,
/// 3. ret_ty: Ref,
/// }
/// if (has_ret_ty_body) {
/// 5. ret_ty_body_len: u32
/// 6. ret_ty_body: u32 // for each ret_ty_body_len
/// 4. ret_ty_body_len: u32
/// 5. ret_ty_body: u32 // for each ret_ty_body_len
/// }
/// 7. noalias_bits: u32 // if has_any_noalias
/// 6. noalias_bits: u32 // if has_any_noalias
/// - each bit starting with LSB corresponds to parameter indexes
/// 8. body: Index // for each body_len
/// 9. src_locs: Func.SrcLocs // if body_len != 0
/// 10. proto_hash: std.zig.SrcHash // if body_len != 0; hash of function prototype
/// 7. body: Index // for each body_len
/// 8. src_locs: Func.SrcLocs // if body_len != 0
/// 9. proto_hash: std.zig.SrcHash // if body_len != 0; hash of function prototype
pub const FuncFancy = struct {
/// Points to the block that contains the param instructions for this function.
/// If this is a `declaration`, it refers to the declaration's value body.
@ -2522,38 +2517,16 @@ pub const Inst = struct {
/// If both has_cc_ref and has_cc_body are false, it means auto calling convention.
/// If both has_ret_ty_ref and has_ret_ty_body are false, it means void return type.
pub const Bits = packed struct {
pub const Bits = packed struct(u32) {
is_var_args: bool,
is_inferred_error: bool,
is_test: bool,
is_extern: bool,
is_noinline: bool,
has_cc_ref: bool,
has_cc_body: bool,
has_ret_ty_ref: bool,
has_ret_ty_body: bool,
has_lib_name: bool,
has_any_noalias: bool,
_: u21 = undefined,
};
};
/// Trailing:
/// 0. lib_name: NullTerminatedString, // null terminated string index, if has_lib_name is set
/// 1. align: Ref, // if has_align is set
/// 2. init: Ref // if has_init is set
/// The source node is obtained from the containing `block_inline`.
pub const ExtendedVar = struct {
var_type: Ref,
pub const Small = packed struct {
has_lib_name: bool,
has_align: bool,
has_init: bool,
is_extern: bool,
is_const: bool,
is_threadlocal: bool,
_: u10 = undefined,
_: u24 = undefined,
};
};
@ -2582,39 +2555,301 @@ pub const Inst = struct {
};
/// Trailing:
/// 0. align_body_len: u32 // if `has_align_linksection_addrspace`; 0 means no `align`
/// 1. linksection_body_len: u32 // if `has_align_linksection_addrspace`; 0 means no `linksection`
/// 2. addrspace_body_len: u32 // if `has_align_linksection_addrspace`; 0 means no `addrspace`
/// 3. value_body_inst: Zir.Inst.Index
/// - for each `value_body_len`
/// 0. name: NullTerminatedString // if `flags.id.hasName()`
/// 1. lib_name: NullTerminatedString // if `flags.id.hasLibName()`
/// 2. type_body_len: u32 // if `flags.id.hasTypeBody()`
/// 3. align_body_len: u32 // if `flags.id.hasSpecialBodies()`
/// 4. linksection_body_len: u32 // if `flags.id.hasSpecialBodies()`
/// 5. addrspace_body_len: u32 // if `flags.id.hasSpecialBodies()`
/// 6. value_body_len: u32 // if `flags.id.hasValueBody()`
/// 7. type_body_inst: Zir.Inst.Index
/// - for each `type_body_len`
/// - body to be exited via `break_inline` to this `declaration` instruction
/// 4. align_body_inst: Zir.Inst.Index
/// 8. align_body_inst: Zir.Inst.Index
/// - for each `align_body_len`
/// - body to be exited via `break_inline` to this `declaration` instruction
/// 5. linksection_body_inst: Zir.Inst.Index
/// 9. linksection_body_inst: Zir.Inst.Index
/// - for each `linksection_body_len`
/// - body to be exited via `break_inline` to this `declaration` instruction
/// 6. addrspace_body_inst: Zir.Inst.Index
/// 10. addrspace_body_inst: Zir.Inst.Index
/// - for each `addrspace_body_len`
/// - body to be exited via `break_inline` to this `declaration` instruction
/// 11. value_body_inst: Zir.Inst.Index
/// - for each `value_body_len`
/// - body to be exited via `break_inline` to this `declaration` instruction
/// - within this body, the `declaration` instruction refers to the resolved type from the type body
pub const Declaration = struct {
// These fields should be concatenated and reinterpreted as a `std.zig.SrcHash`.
src_hash_0: u32,
src_hash_1: u32,
src_hash_2: u32,
src_hash_3: u32,
/// The name of this `Decl`. Also indicates whether it is a test, comptime block, etc.
name: Name,
src_line: u32,
src_column: u32,
flags: Flags,
// These fields should be concatenated and reinterpreted as a `Flags`.
flags_0: u32,
flags_1: u32,
pub const Flags = packed struct(u32) {
value_body_len: u28,
pub const Unwrapped = struct {
pub const Kind = enum {
unnamed_test,
@"test",
decltest,
@"comptime",
@"usingnamespace",
@"const",
@"var",
};
pub const Linkage = enum {
normal,
@"extern",
@"export",
};
src_node: Ast.Node.Index,
src_line: u32,
src_column: u32,
kind: Kind,
/// Always `.empty` for `kind` of `unnamed_test`, `.@"comptime"`, `.@"usingnamespace"`.
name: NullTerminatedString,
/// Always `false` for `kind` of `unnamed_test`, `.@"test"`, `.decltest`, `.@"comptime"`.
is_pub: bool,
is_export: bool,
test_is_decltest: bool,
has_align_linksection_addrspace: bool,
/// Always `false` for `kind != .@"var"`.
is_threadlocal: bool,
/// Always `.normal` for `kind != .@"const" and kind != .@"var"`.
linkage: Linkage,
/// Always `.empty` for `linkage != .@"extern"`.
lib_name: NullTerminatedString,
/// Always populated for `linkage == .@"extern".
type_body: ?[]const Inst.Index,
align_body: ?[]const Inst.Index,
linksection_body: ?[]const Inst.Index,
addrspace_body: ?[]const Inst.Index,
/// Always populated for `linkage != .@"extern".
value_body: ?[]const Inst.Index,
};
pub const Flags = packed struct(u64) {
src_line: u30,
src_column: u29,
id: Id,
pub const Id = enum(u5) {
unnamed_test,
@"test",
decltest,
@"comptime",
@"usingnamespace",
pub_usingnamespace,
const_simple,
const_typed,
@"const",
pub_const_simple,
pub_const_typed,
pub_const,
extern_const_simple,
extern_const,
pub_extern_const_simple,
pub_extern_const,
export_const,
pub_export_const,
var_simple,
@"var",
var_threadlocal,
pub_var_simple,
pub_var,
pub_var_threadlocal,
extern_var,
extern_var_threadlocal,
pub_extern_var,
pub_extern_var_threadlocal,
export_var,
export_var_threadlocal,
pub_export_var,
pub_export_var_threadlocal,
pub fn hasName(id: Id) bool {
return switch (id) {
.unnamed_test,
.@"comptime",
.@"usingnamespace",
.pub_usingnamespace,
=> false,
else => true,
};
}
pub fn hasLibName(id: Id) bool {
return switch (id) {
.extern_const,
.pub_extern_const,
.extern_var,
.extern_var_threadlocal,
.pub_extern_var,
.pub_extern_var_threadlocal,
=> true,
else => false,
};
}
pub fn hasTypeBody(id: Id) bool {
return switch (id) {
.unnamed_test,
.@"test",
.decltest,
.@"comptime",
.@"usingnamespace",
.pub_usingnamespace,
=> false, // these constructs are untyped
.const_simple,
.pub_const_simple,
.var_simple,
.pub_var_simple,
=> false, // these reprs omit type bodies
else => true,
};
}
pub fn hasValueBody(id: Id) bool {
return switch (id) {
.extern_const_simple,
.extern_const,
.pub_extern_const_simple,
.pub_extern_const,
.extern_var,
.extern_var_threadlocal,
.pub_extern_var,
.pub_extern_var_threadlocal,
=> false, // externs do not have values
else => true,
};
}
pub fn hasSpecialBodies(id: Id) bool {
return switch (id) {
.unnamed_test,
.@"test",
.decltest,
.@"comptime",
.@"usingnamespace",
.pub_usingnamespace,
=> false, // these constructs are untyped
.const_simple,
.const_typed,
.pub_const_simple,
.pub_const_typed,
.extern_const_simple,
.pub_extern_const_simple,
.var_simple,
.pub_var_simple,
=> false, // these reprs omit special bodies
else => true,
};
}
pub fn linkage(id: Id) Declaration.Unwrapped.Linkage {
return switch (id) {
.extern_const_simple,
.extern_const,
.pub_extern_const_simple,
.pub_extern_const,
.extern_var,
.extern_var_threadlocal,
.pub_extern_var,
.pub_extern_var_threadlocal,
=> .@"extern",
.export_const,
.pub_export_const,
.export_var,
.export_var_threadlocal,
.pub_export_var,
.pub_export_var_threadlocal,
=> .@"export",
else => .normal,
};
}
pub fn kind(id: Id) Declaration.Unwrapped.Kind {
return switch (id) {
.unnamed_test => .unnamed_test,
.@"test" => .@"test",
.decltest => .decltest,
.@"comptime" => .@"comptime",
.@"usingnamespace", .pub_usingnamespace => .@"usingnamespace",
.const_simple,
.const_typed,
.@"const",
.pub_const_simple,
.pub_const_typed,
.pub_const,
.extern_const_simple,
.extern_const,
.pub_extern_const_simple,
.pub_extern_const,
.export_const,
.pub_export_const,
=> .@"const",
.var_simple,
.@"var",
.var_threadlocal,
.pub_var_simple,
.pub_var,
.pub_var_threadlocal,
.extern_var,
.extern_var_threadlocal,
.pub_extern_var,
.pub_extern_var_threadlocal,
.export_var,
.export_var_threadlocal,
.pub_export_var,
.pub_export_var_threadlocal,
=> .@"var",
};
}
pub fn isPub(id: Id) bool {
return switch (id) {
.pub_usingnamespace,
.pub_const_simple,
.pub_const_typed,
.pub_const,
.pub_extern_const_simple,
.pub_extern_const,
.pub_export_const,
.pub_var_simple,
.pub_var,
.pub_var_threadlocal,
.pub_extern_var,
.pub_extern_var_threadlocal,
.pub_export_var,
.pub_export_var_threadlocal,
=> true,
else => false,
};
}
pub fn isThreadlocal(id: Id) bool {
return switch (id) {
.var_threadlocal,
.pub_var_threadlocal,
.extern_var_threadlocal,
.pub_extern_var_threadlocal,
.export_var_threadlocal,
.pub_export_var_threadlocal,
=> true,
else => false,
};
}
};
};
pub const Name = enum(u32) {
@ -2647,17 +2882,24 @@ pub const Inst = struct {
};
pub const Bodies = struct {
value_body: []const Index,
type_body: ?[]const Index,
align_body: ?[]const Index,
linksection_body: ?[]const Index,
addrspace_body: ?[]const Index,
value_body: ?[]const Index,
};
pub fn getBodies(declaration: Declaration, extra_end: u32, zir: Zir) Bodies {
var extra_index: u32 = extra_end;
const value_body_len = declaration.flags.value_body_len;
const value_body_len = declaration.value_body_len;
const type_body_len: u32 = len: {
if (!declaration.flags().kind.hasTypeBody()) break :len 0;
const len = zir.extra[extra_index];
extra_index += 1;
break :len len;
};
const align_body_len, const linksection_body_len, const addrspace_body_len = lens: {
if (!declaration.flags.has_align_linksection_addrspace) {
if (!declaration.flags.kind.hasSpecialBodies()) {
break :lens .{ 0, 0, 0 };
}
const lens = zir.extra[extra_index..][0..3].*;
@ -2665,21 +2907,30 @@ pub const Inst = struct {
break :lens lens;
};
return .{
.value_body = b: {
defer extra_index += value_body_len;
break :b zir.bodySlice(extra_index, value_body_len);
.type_body = if (type_body_len == 0) null else b: {
const b = zir.bodySlice(extra_index, type_body_len);
extra_index += type_body_len;
break :b b;
},
.align_body = if (align_body_len == 0) null else b: {
defer extra_index += align_body_len;
break :b zir.bodySlice(extra_index, align_body_len);
const b = zir.bodySlice(extra_index, align_body_len);
extra_index += align_body_len;
break :b b;
},
.linksection_body = if (linksection_body_len == 0) null else b: {
defer extra_index += linksection_body_len;
break :b zir.bodySlice(extra_index, linksection_body_len);
const b = zir.bodySlice(extra_index, linksection_body_len);
extra_index += linksection_body_len;
break :b b;
},
.addrspace_body = if (addrspace_body_len == 0) null else b: {
defer extra_index += addrspace_body_len;
break :b zir.bodySlice(extra_index, addrspace_body_len);
const b = zir.bodySlice(extra_index, addrspace_body_len);
extra_index += addrspace_body_len;
break :b b;
},
.value_body = if (value_body_len == 0) null else b: {
const b = zir.bodySlice(extra_index, value_body_len);
extra_index += value_body_len;
break :b b;
},
};
}
@ -3711,18 +3962,18 @@ pub const DeclContents = struct {
pub fn findTrackable(zir: Zir, gpa: Allocator, contents: *DeclContents, decl_inst: Zir.Inst.Index) !void {
contents.clear();
const declaration, const extra_end = zir.getDeclaration(decl_inst);
const bodies = declaration.getBodies(extra_end, zir);
const decl = zir.getDeclaration(decl_inst);
// `defer` instructions duplicate the same body arbitrarily many times, but we only want to traverse
// their contents once per defer. So, we store the extra index of the body here to deduplicate.
var found_defers: std.AutoHashMapUnmanaged(u32, void) = .empty;
defer found_defers.deinit(gpa);
try zir.findTrackableBody(gpa, contents, &found_defers, bodies.value_body);
if (bodies.align_body) |b| try zir.findTrackableBody(gpa, contents, &found_defers, b);
if (bodies.linksection_body) |b| try zir.findTrackableBody(gpa, contents, &found_defers, b);
if (bodies.addrspace_body) |b| try zir.findTrackableBody(gpa, contents, &found_defers, b);
if (decl.type_body) |b| try zir.findTrackableBody(gpa, contents, &found_defers, b);
if (decl.align_body) |b| try zir.findTrackableBody(gpa, contents, &found_defers, b);
if (decl.linksection_body) |b| try zir.findTrackableBody(gpa, contents, &found_defers, b);
if (decl.addrspace_body) |b| try zir.findTrackableBody(gpa, contents, &found_defers, b);
if (decl.value_body) |b| try zir.findTrackableBody(gpa, contents, &found_defers, b);
}
/// Like `findTrackable`, but only considers the `main_struct_inst` instruction. This may return more than
@ -3991,7 +4242,6 @@ fn findTrackableInner(
.value_placeholder => unreachable,
// Once again, we start with the boring tags.
.variable,
.this,
.ret_addr,
.builtin_src,
@ -4237,7 +4487,6 @@ fn findTrackableInner(
const inst_data = datas[@intFromEnum(inst)].pl_node;
const extra = zir.extraData(Inst.FuncFancy, inst_data.payload_index);
var extra_index: usize = extra.end;
extra_index += @intFromBool(extra.data.bits.has_lib_name);
if (extra.data.bits.has_cc_body) {
const body_len = zir.extra[extra_index];
@ -4470,8 +4719,7 @@ pub fn getParamBody(zir: Zir, fn_inst: Inst.Index) []const Zir.Inst.Index {
return zir.bodySlice(param_block.end, param_block.data.body_len);
},
.declaration => {
const decl, const extra_end = zir.getDeclaration(param_block_index);
return decl.getBodies(extra_end, zir).value_body;
return zir.getDeclaration(param_block_index).value_body.?;
},
else => unreachable,
}
@ -4526,7 +4774,6 @@ pub fn getFnInfo(zir: Zir, fn_inst: Inst.Index) FnInfo {
var ret_ty_ref: Inst.Ref = .void_type;
var ret_ty_body: []const Inst.Index = &.{};
extra_index += @intFromBool(extra.data.bits.has_lib_name);
if (extra.data.bits.has_cc_body) {
extra_index += zir.extra[extra_index] + 1;
} else if (extra.data.bits.has_cc_ref) {
@ -4555,17 +4802,7 @@ pub fn getFnInfo(zir: Zir, fn_inst: Inst.Index) FnInfo {
},
else => unreachable,
};
const param_body = switch (tags[@intFromEnum(info.param_block)]) {
.block, .block_comptime, .block_inline => param_body: {
const param_block = zir.extraData(Inst.Block, datas[@intFromEnum(info.param_block)].pl_node.payload_index);
break :param_body zir.bodySlice(param_block.end, param_block.data.body_len);
},
.declaration => param_body: {
const decl, const extra_end = zir.getDeclaration(info.param_block);
break :param_body decl.getBodies(extra_end, zir).value_body;
},
else => unreachable,
};
const param_body = zir.getParamBody(fn_inst);
var total_params_len: u32 = 0;
for (param_body) |inst| {
switch (tags[@intFromEnum(inst)]) {
@ -4585,13 +4822,74 @@ pub fn getFnInfo(zir: Zir, fn_inst: Inst.Index) FnInfo {
};
}
pub fn getDeclaration(zir: Zir, inst: Zir.Inst.Index) struct { Inst.Declaration, u32 } {
pub fn getDeclaration(zir: Zir, inst: Zir.Inst.Index) Inst.Declaration.Unwrapped {
assert(zir.instructions.items(.tag)[@intFromEnum(inst)] == .declaration);
const pl_node = zir.instructions.items(.data)[@intFromEnum(inst)].declaration;
const extra = zir.extraData(Inst.Declaration, pl_node.payload_index);
const flags_vals: [2]u32 = .{ extra.data.flags_0, extra.data.flags_1 };
const flags: Inst.Declaration.Flags = @bitCast(flags_vals);
var extra_index = extra.end;
const name: NullTerminatedString = if (flags.id.hasName()) name: {
const name = zir.extra[extra_index];
extra_index += 1;
break :name @enumFromInt(name);
} else .empty;
const lib_name: NullTerminatedString = if (flags.id.hasLibName()) lib_name: {
const lib_name = zir.extra[extra_index];
extra_index += 1;
break :lib_name @enumFromInt(lib_name);
} else .empty;
const type_body_len: u32 = if (flags.id.hasTypeBody()) len: {
const len = zir.extra[extra_index];
extra_index += 1;
break :len len;
} else 0;
const align_body_len: u32, const linksection_body_len: u32, const addrspace_body_len: u32 = lens: {
if (!flags.id.hasSpecialBodies()) break :lens .{ 0, 0, 0 };
const lens = zir.extra[extra_index..][0..3].*;
extra_index += 3;
break :lens lens;
};
const value_body_len: u32 = if (flags.id.hasValueBody()) len: {
const len = zir.extra[extra_index];
extra_index += 1;
break :len len;
} else 0;
const type_body = zir.bodySlice(extra_index, type_body_len);
extra_index += type_body_len;
const align_body = zir.bodySlice(extra_index, align_body_len);
extra_index += align_body_len;
const linksection_body = zir.bodySlice(extra_index, linksection_body_len);
extra_index += linksection_body_len;
const addrspace_body = zir.bodySlice(extra_index, addrspace_body_len);
extra_index += addrspace_body_len;
const value_body = zir.bodySlice(extra_index, value_body_len);
extra_index += value_body_len;
return .{
extra.data,
@intCast(extra.end),
.src_node = pl_node.src_node,
.src_line = flags.src_line,
.src_column = flags.src_column,
.kind = flags.id.kind(),
.name = name,
.is_pub = flags.id.isPub(),
.is_threadlocal = flags.id.isThreadlocal(),
.linkage = flags.id.linkage(),
.lib_name = lib_name,
.type_body = if (type_body_len == 0) null else type_body,
.align_body = if (align_body_len == 0) null else align_body,
.linksection_body = if (linksection_body_len == 0) null else linksection_body,
.addrspace_body = if (addrspace_body_len == 0) null else addrspace_body,
.value_body = if (value_body_len == 0) null else value_body,
};
}
@ -4636,7 +4934,6 @@ pub fn getAssociatedSrcHash(zir: Zir, inst: Zir.Inst.Index) ?std.zig.SrcHash {
}
const bits = extra.data.bits;
var extra_index = extra.end;
extra_index += @intFromBool(bits.has_lib_name);
if (bits.has_cc_body) {
const body_len = zir.extra[extra_index];
extra_index += 1 + body_len;

View File

@ -348,12 +348,15 @@ const Job = union(enum) {
/// Corresponds to the task in `link.Task`.
/// Only needed for backends that haven't yet been updated to not race against Sema.
codegen_type: InternPool.Index,
/// The `Cau` must be semantically analyzed (and possibly export itself).
/// The `AnalUnit`, which is *not* a `func`, must be semantically analyzed.
/// This may be its first time being analyzed, or it may be outdated.
/// If the unit is a function, a `codegen_func` job will then be queued.
analyze_comptime_unit: InternPool.AnalUnit,
/// This function must be semantically analyzed.
/// This may be its first time being analyzed, or it may be outdated.
analyze_cau: InternPool.Cau.Index,
/// Analyze the body of a runtime function.
/// After analysis, a `codegen_func` job will be queued.
/// These must be separate jobs to ensure any needed type resolution occurs *before* codegen.
/// This job is separate from `analyze_comptime_unit` because it has a different priority.
analyze_func: InternPool.Index,
/// The main source file for the module needs to be analyzed.
analyze_mod: *Package.Module,
@ -2903,6 +2906,7 @@ const Header = extern struct {
file_deps_len: u32,
src_hash_deps_len: u32,
nav_val_deps_len: u32,
nav_ty_deps_len: u32,
namespace_deps_len: u32,
namespace_name_deps_len: u32,
first_dependency_len: u32,
@ -2946,6 +2950,7 @@ pub fn saveState(comp: *Compilation) !void {
.file_deps_len = @intCast(ip.file_deps.count()),
.src_hash_deps_len = @intCast(ip.src_hash_deps.count()),
.nav_val_deps_len = @intCast(ip.nav_val_deps.count()),
.nav_ty_deps_len = @intCast(ip.nav_ty_deps.count()),
.namespace_deps_len = @intCast(ip.namespace_deps.count()),
.namespace_name_deps_len = @intCast(ip.namespace_name_deps.count()),
.first_dependency_len = @intCast(ip.first_dependency.count()),
@ -2976,6 +2981,8 @@ pub fn saveState(comp: *Compilation) !void {
addBuf(&bufs, mem.sliceAsBytes(ip.src_hash_deps.values()));
addBuf(&bufs, mem.sliceAsBytes(ip.nav_val_deps.keys()));
addBuf(&bufs, mem.sliceAsBytes(ip.nav_val_deps.values()));
addBuf(&bufs, mem.sliceAsBytes(ip.nav_ty_deps.keys()));
addBuf(&bufs, mem.sliceAsBytes(ip.nav_ty_deps.values()));
addBuf(&bufs, mem.sliceAsBytes(ip.namespace_deps.keys()));
addBuf(&bufs, mem.sliceAsBytes(ip.namespace_deps.values()));
addBuf(&bufs, mem.sliceAsBytes(ip.namespace_name_deps.keys()));
@ -3141,8 +3148,10 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
}
const file_index = switch (anal_unit.unwrap()) {
.cau => |cau| zcu.namespacePtr(ip.getCau(cau).namespace).file_scope,
.func => |ip_index| (zcu.funcInfo(ip_index).zir_body_inst.resolveFull(ip) orelse continue).file,
.@"comptime" => |cu| ip.getComptimeUnit(cu).zir_index.resolveFile(ip),
.nav_val, .nav_ty => |nav| ip.getNav(nav).analysis.?.zir_index.resolveFile(ip),
.type => |ty| Type.fromInterned(ty).typeDeclInst(zcu).?.resolveFile(ip),
.func => |ip_index| zcu.funcInfo(ip_index).zir_body_inst.resolveFile(ip),
};
// Skip errors for AnalUnits within files that had a parse failure.
@ -3374,11 +3383,9 @@ pub fn addModuleErrorMsg(
const rt_file_path = try src.file_scope.fullPath(gpa);
defer gpa.free(rt_file_path);
const name = switch (ref.referencer.unwrap()) {
.cau => |cau| switch (ip.getCau(cau).owner.unwrap()) {
.nav => |nav| ip.getNav(nav).name.toSlice(ip),
.type => |ty| Type.fromInterned(ty).containerTypeName(ip).toSlice(ip),
.none => "comptime",
},
.@"comptime" => "comptime",
.nav_val, .nav_ty => |nav| ip.getNav(nav).name.toSlice(ip),
.type => |ty| Type.fromInterned(ty).containerTypeName(ip).toSlice(ip),
.func => |f| ip.getNav(zcu.funcInfo(f).owner_nav).name.toSlice(ip),
};
try ref_traces.append(gpa, .{
@ -3641,10 +3648,14 @@ fn performAllTheWorkInner(
// If there's no work queued, check if there's anything outdated
// which we need to work on, and queue it if so.
if (try zcu.findOutdatedToAnalyze()) |outdated| {
switch (outdated.unwrap()) {
.cau => |cau| try comp.queueJob(.{ .analyze_cau = cau }),
.func => |func| try comp.queueJob(.{ .analyze_func = func }),
}
try comp.queueJob(switch (outdated.unwrap()) {
.func => |f| .{ .analyze_func = f },
.@"comptime",
.nav_ty,
.nav_val,
.type,
=> .{ .analyze_comptime_unit = outdated },
});
continue;
}
}
@ -3667,13 +3678,13 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job, prog_node: std.Progre
.codegen_nav => |nav_index| {
const zcu = comp.zcu.?;
const nav = zcu.intern_pool.getNav(nav_index);
if (nav.analysis_owner.unwrap()) |cau| {
const unit = InternPool.AnalUnit.wrap(.{ .cau = cau });
if (nav.analysis != null) {
const unit: InternPool.AnalUnit = .wrap(.{ .nav_val = nav_index });
if (zcu.failed_analysis.contains(unit) or zcu.transitive_failed_analysis.contains(unit)) {
return;
}
}
assert(nav.status == .resolved);
assert(nav.status == .fully_resolved);
comp.dispatchCodegenTask(tid, .{ .codegen_nav = nav_index });
},
.codegen_func => |func| {
@ -3688,36 +3699,48 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job, prog_node: std.Progre
const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid));
defer pt.deactivate();
pt.ensureFuncBodyAnalyzed(func) catch |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory,
pt.ensureFuncBodyUpToDate(func) catch |err| switch (err) {
error.OutOfMemory => |e| return e,
error.AnalysisFail => return,
};
},
.analyze_cau => |cau_index| {
.analyze_comptime_unit => |unit| {
const named_frame = tracy.namedFrame("analyze_comptime_unit");
defer named_frame.end();
const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid));
defer pt.deactivate();
pt.ensureCauAnalyzed(cau_index) catch |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory,
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 => |ty| if (pt.ensureTypeUpToDate(ty)) |_| {} else |err| err,
.func => unreachable,
};
maybe_err catch |err| switch (err) {
error.OutOfMemory => |e| return e,
error.AnalysisFail => return,
};
queue_test_analysis: {
if (!comp.config.is_test) break :queue_test_analysis;
const nav = switch (unit.unwrap()) {
.nav_val => |nav| nav,
else => break :queue_test_analysis,
};
// Check if this is a test function.
const ip = &pt.zcu.intern_pool;
const cau = ip.getCau(cau_index);
const nav_index = switch (cau.owner.unwrap()) {
.none, .type => break :queue_test_analysis,
.nav => |nav| nav,
};
if (!pt.zcu.test_functions.contains(nav_index)) {
if (!pt.zcu.test_functions.contains(nav)) {
break :queue_test_analysis;
}
// Tests are always emitted in test binaries. The decl_refs are created by
// Zcu.populateTestFunctions, but this will not queue body analysis, so do
// that now.
try pt.zcu.ensureFuncBodyAnalysisQueued(ip.getNav(nav_index).status.resolved.val);
try pt.zcu.ensureFuncBodyAnalysisQueued(ip.getNav(nav).status.fully_resolved.val);
}
},
.resolve_type_fully => |ty| {

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -219,9 +219,8 @@ fn loadComptimePtrInner(
const base_val: MutableValue = switch (ptr.base_addr) {
.nav => |nav| val: {
try sema.declareDependency(.{ .nav_val = nav });
try sema.ensureNavResolved(src, nav);
const val = ip.getNav(nav).status.resolved.val;
try sema.ensureNavResolved(src, nav, .fully);
const val = ip.getNav(nav).status.fully_resolved.val;
switch (ip.indexToKey(val)) {
.variable => return .runtime_load,
// We let `.@"extern"` through here if it's a function.

View File

@ -3851,7 +3851,7 @@ fn resolveStructInner(
const gpa = zcu.gpa;
const struct_obj = zcu.typeToStruct(ty).?;
const owner = InternPool.AnalUnit.wrap(.{ .cau = struct_obj.cau });
const owner: InternPool.AnalUnit = .wrap(.{ .type = ty.toIntern() });
if (zcu.failed_analysis.contains(owner) or zcu.transitive_failed_analysis.contains(owner)) {
return error.AnalysisFail;
@ -3905,7 +3905,7 @@ fn resolveUnionInner(
const gpa = zcu.gpa;
const union_obj = zcu.typeToUnion(ty).?;
const owner = InternPool.AnalUnit.wrap(.{ .cau = union_obj.cau });
const owner: InternPool.AnalUnit = .wrap(.{ .type = ty.toIntern() });
if (zcu.failed_analysis.contains(owner) or zcu.transitive_failed_analysis.contains(owner)) {
return error.AnalysisFail;

View File

@ -1343,7 +1343,12 @@ pub fn isLazySize(val: Value, zcu: *Zcu) bool {
pub fn isPtrRuntimeValue(val: Value, zcu: *Zcu) bool {
const ip = &zcu.intern_pool;
const nav = ip.getBackingNav(val.toIntern()).unwrap() orelse return false;
return switch (ip.indexToKey(ip.getNav(nav).status.resolved.val)) {
const nav_val = switch (ip.getNav(nav).status) {
.unresolved => unreachable,
.type_resolved => |r| return r.is_threadlocal,
.fully_resolved => |r| r.val,
};
return switch (ip.indexToKey(nav_val)) {
.@"extern" => |e| e.is_threadlocal or e.is_dll_import,
.variable => |v| v.is_threadlocal,
else => false,

View File

@ -170,6 +170,9 @@ outdated_ready: std.AutoArrayHashMapUnmanaged(AnalUnit, void) = .empty,
/// it as outdated.
retryable_failures: std.ArrayListUnmanaged(AnalUnit) = .empty,
func_body_analysis_queued: std.AutoArrayHashMapUnmanaged(InternPool.Index, void) = .empty,
nav_val_analysis_queued: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, void) = .empty,
/// These are the modules which we initially queue for analysis in `Compilation.update`.
/// `resolveReferences` will use these as the root of its reachability traversal.
analysis_roots: std.BoundedArray(*Package.Module, 3) = .{},
@ -192,7 +195,7 @@ compile_log_text: std.ArrayListUnmanaged(u8) = .empty,
test_functions: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, void) = .empty,
global_assembly: std.AutoArrayHashMapUnmanaged(InternPool.Cau.Index, []u8) = .empty,
global_assembly: std.AutoArrayHashMapUnmanaged(AnalUnit, []u8) = .empty,
/// Key is the `AnalUnit` *performing* the reference. This representation allows
/// incremental updates to quickly delete references caused by a specific `AnalUnit`.
@ -282,7 +285,11 @@ pub const Exported = union(enum) {
pub fn getAlign(exported: Exported, zcu: *Zcu) Alignment {
return switch (exported) {
.nav => |nav| zcu.intern_pool.getNav(nav).status.resolved.alignment,
.nav => |nav| switch (zcu.intern_pool.getNav(nav).status) {
.unresolved => unreachable,
.type_resolved => |r| r.alignment,
.fully_resolved => |r| r.alignment,
},
.uav => .none,
};
}
@ -344,9 +351,12 @@ pub const Namespace = struct {
pub_usingnamespace: std.ArrayListUnmanaged(InternPool.Nav.Index) = .empty,
/// All `usingnamespace` declarations in this namespace which are *not* marked `pub`.
priv_usingnamespace: std.ArrayListUnmanaged(InternPool.Nav.Index) = .empty,
/// All `comptime` and `test` declarations in this namespace. We store these purely so that
/// incremental compilation can re-use the existing `Cau`s when a namespace changes.
other_decls: std.ArrayListUnmanaged(InternPool.Cau.Index) = .empty,
/// All `comptime` declarations in this namespace. We store these purely so that incremental
/// compilation can re-use the existing `ComptimeUnit`s when a namespace changes.
comptime_decls: std.ArrayListUnmanaged(InternPool.ComptimeUnit.Id) = .empty,
/// All `test` declarations in this namespace. We store these purely so that incremental
/// compilation can re-use the existing `Nav`s when a namespace changes.
test_decls: std.ArrayListUnmanaged(InternPool.Nav.Index) = .empty,
pub const Index = InternPool.NamespaceIndex;
pub const OptionalIndex = InternPool.OptionalNamespaceIndex;
@ -2238,6 +2248,9 @@ pub fn deinit(zcu: *Zcu) void {
zcu.outdated_ready.deinit(gpa);
zcu.retryable_failures.deinit(gpa);
zcu.func_body_analysis_queued.deinit(gpa);
zcu.nav_val_analysis_queued.deinit(gpa);
zcu.test_functions.deinit(gpa);
for (zcu.global_assembly.values()) |s| {
@ -2436,11 +2449,10 @@ pub fn markPoDependeeUpToDate(zcu: *Zcu, dependee: InternPool.Dependee) !void {
// If this is a Decl, we must recursively mark dependencies on its tyval
// as no longer PO.
switch (depender.unwrap()) {
.cau => |cau| switch (zcu.intern_pool.getCau(cau).owner.unwrap()) {
.nav => |nav| try zcu.markPoDependeeUpToDate(.{ .nav_val = nav }),
.type => |ty| try zcu.markPoDependeeUpToDate(.{ .interned = ty }),
.none => {},
},
.@"comptime" => {},
.nav_val => |nav| try zcu.markPoDependeeUpToDate(.{ .nav_val = nav }),
.nav_ty => |nav| try zcu.markPoDependeeUpToDate(.{ .nav_ty = nav }),
.type => |ty| try zcu.markPoDependeeUpToDate(.{ .interned = ty }),
.func => |func| try zcu.markPoDependeeUpToDate(.{ .interned = func }),
}
}
@ -2451,11 +2463,10 @@ pub fn markPoDependeeUpToDate(zcu: *Zcu, dependee: InternPool.Dependee) !void {
fn markTransitiveDependersPotentiallyOutdated(zcu: *Zcu, maybe_outdated: AnalUnit) !void {
const ip = &zcu.intern_pool;
const dependee: InternPool.Dependee = switch (maybe_outdated.unwrap()) {
.cau => |cau| switch (ip.getCau(cau).owner.unwrap()) {
.nav => |nav| .{ .nav_val = nav }, // TODO: also `nav_ref` deps when introduced
.type => |ty| .{ .interned = ty },
.none => return, // analysis of this `Cau` can't outdate any dependencies
},
.@"comptime" => return, // analysis of a comptime decl can't outdate any dependencies
.nav_val => |nav| .{ .nav_val = nav },
.nav_ty => |nav| .{ .nav_ty = nav },
.type => |ty| .{ .interned = ty },
.func => |func_index| .{ .interned = func_index }, // IES
};
log.debug("potentially outdated dependee: {}", .{zcu.fmtDependee(dependee)});
@ -2512,14 +2523,14 @@ pub fn findOutdatedToAnalyze(zcu: *Zcu) Allocator.Error!?AnalUnit {
}
// There is no single AnalUnit which is ready for re-analysis. Instead, we must assume that some
// Cau with PO dependencies is outdated -- e.g. in the above example we arbitrarily pick one of
// A or B. We should select a Cau, since a Cau is definitely responsible for the loop in the
// dependency graph (since IES dependencies can't have loops). We should also, of course, not
// select a Cau owned by a `comptime` declaration, since you can't depend on those!
// AnalUnit with PO dependencies is outdated -- e.g. in the above example we arbitrarily pick one of
// A or B. We should definitely not select a function, since a function can't be responsible for the
// loop (IES dependencies can't have loops). We should also, of course, not select a `comptime`
// declaration, since you can't depend on those!
// The choice of this Cau could have a big impact on how much total analysis we perform, since
// The choice of this unit could have a big impact on how much total analysis we perform, since
// if analysis concludes any dependencies on its result are up-to-date, then other PO AnalUnit
// may be resolved as up-to-date. To hopefully avoid doing too much work, let's find a Decl
// may be resolved as up-to-date. To hopefully avoid doing too much work, let's find a unit
// which the most things depend on - the idea is that this will resolve a lot of loops (but this
// is only a heuristic).
@ -2530,33 +2541,29 @@ pub fn findOutdatedToAnalyze(zcu: *Zcu) Allocator.Error!?AnalUnit {
const ip = &zcu.intern_pool;
var chosen_cau: ?InternPool.Cau.Index = null;
var chosen_cau_dependers: u32 = undefined;
var chosen_unit: ?AnalUnit = null;
var chosen_unit_dependers: u32 = undefined;
inline for (.{ zcu.outdated.keys(), zcu.potentially_outdated.keys() }) |outdated_units| {
for (outdated_units) |unit| {
const cau = switch (unit.unwrap()) {
.cau => |cau| cau,
.func => continue, // a `func` definitely can't be causing the loop so it is a bad choice
};
const cau_owner = ip.getCau(cau).owner;
var n: u32 = 0;
var it = ip.dependencyIterator(switch (cau_owner.unwrap()) {
.none => continue, // there can be no dependencies on this `Cau` so it is a terrible choice
var it = ip.dependencyIterator(switch (unit.unwrap()) {
.func => continue, // a `func` definitely can't be causing the loop so it is a bad choice
.@"comptime" => continue, // a `comptime` block can't even be depended on so it is a terrible choice
.type => |ty| .{ .interned = ty },
.nav => |nav| .{ .nav_val = nav },
.nav_val => |nav| .{ .nav_val = nav },
.nav_ty => |nav| .{ .nav_ty = nav },
});
while (it.next()) |_| n += 1;
if (chosen_cau == null or n > chosen_cau_dependers) {
chosen_cau = cau;
chosen_cau_dependers = n;
if (chosen_unit == null or n > chosen_unit_dependers) {
chosen_unit = unit;
chosen_unit_dependers = n;
}
}
}
if (chosen_cau == null) {
if (chosen_unit == null) {
for (zcu.outdated.keys(), zcu.outdated.values()) |o, opod| {
const func = o.unwrap().func;
const nav = zcu.funcInfo(func).owner_nav;
@ -2570,11 +2577,11 @@ pub fn findOutdatedToAnalyze(zcu: *Zcu) Allocator.Error!?AnalUnit {
}
log.debug("findOutdatedToAnalyze: heuristic returned '{}' ({d} dependers)", .{
zcu.fmtAnalUnit(AnalUnit.wrap(.{ .cau = chosen_cau.? })),
chosen_cau_dependers,
zcu.fmtAnalUnit(chosen_unit.?),
chosen_unit_dependers,
});
return AnalUnit.wrap(.{ .cau = chosen_cau.? });
return chosen_unit.?;
}
/// During an incremental update, before semantic analysis, call this to flush all values from
@ -2679,24 +2686,14 @@ pub fn mapOldZirToNew(
{
var old_decl_it = old_zir.declIterator(match_item.old_inst);
while (old_decl_it.next()) |old_decl_inst| {
const old_decl, _ = old_zir.getDeclaration(old_decl_inst);
switch (old_decl.name) {
const old_decl = old_zir.getDeclaration(old_decl_inst);
switch (old_decl.kind) {
.@"comptime" => try comptime_decls.append(gpa, old_decl_inst),
.@"usingnamespace" => try usingnamespace_decls.append(gpa, old_decl_inst),
.unnamed_test => try unnamed_tests.append(gpa, old_decl_inst),
_ => {
const name_nts = old_decl.name.toString(old_zir).?;
const name = old_zir.nullTerminatedString(name_nts);
if (old_decl.name.isNamedTest(old_zir)) {
if (old_decl.flags.test_is_decltest) {
try named_decltests.put(gpa, name, old_decl_inst);
} else {
try named_tests.put(gpa, name, old_decl_inst);
}
} else {
try named_decls.put(gpa, name, old_decl_inst);
}
},
.@"test" => try named_tests.put(gpa, old_zir.nullTerminatedString(old_decl.name), old_decl_inst),
.decltest => try named_decltests.put(gpa, old_zir.nullTerminatedString(old_decl.name), old_decl_inst),
.@"const", .@"var" => try named_decls.put(gpa, old_zir.nullTerminatedString(old_decl.name), old_decl_inst),
}
}
}
@ -2707,7 +2704,7 @@ pub fn mapOldZirToNew(
var new_decl_it = new_zir.declIterator(match_item.new_inst);
while (new_decl_it.next()) |new_decl_inst| {
const new_decl, _ = new_zir.getDeclaration(new_decl_inst);
const new_decl = new_zir.getDeclaration(new_decl_inst);
// Attempt to match this to a declaration in the old ZIR:
// * For named declarations (`const`/`var`/`fn`), we match based on name.
// * For named tests (`test "foo"`) and decltests (`test foo`), we also match based on name.
@ -2715,7 +2712,7 @@ pub fn mapOldZirToNew(
// * For comptime blocks, we match based on order.
// * For usingnamespace decls, we match based on order.
// If we cannot match this declaration, we can't match anything nested inside of it either, so we just `continue`.
const old_decl_inst = switch (new_decl.name) {
const old_decl_inst = switch (new_decl.kind) {
.@"comptime" => inst: {
if (comptime_decl_idx == comptime_decls.items.len) continue;
defer comptime_decl_idx += 1;
@ -2731,18 +2728,17 @@ pub fn mapOldZirToNew(
defer unnamed_test_idx += 1;
break :inst unnamed_tests.items[unnamed_test_idx];
},
_ => inst: {
const name_nts = new_decl.name.toString(new_zir).?;
const name = new_zir.nullTerminatedString(name_nts);
if (new_decl.name.isNamedTest(new_zir)) {
if (new_decl.flags.test_is_decltest) {
break :inst named_decltests.get(name) orelse continue;
} else {
break :inst named_tests.get(name) orelse continue;
}
} else {
break :inst named_decls.get(name) orelse continue;
}
.@"test" => inst: {
const name = new_zir.nullTerminatedString(new_decl.name);
break :inst named_tests.get(name) orelse continue;
},
.decltest => inst: {
const name = new_zir.nullTerminatedString(new_decl.name);
break :inst named_decltests.get(name) orelse continue;
},
.@"const", .@"var" => inst: {
const name = new_zir.nullTerminatedString(new_decl.name);
break :inst named_decls.get(name) orelse continue;
},
};
@ -2797,14 +2793,39 @@ pub fn ensureFuncBodyAnalysisQueued(zcu: *Zcu, func_index: InternPool.Index) !vo
const ip = &zcu.intern_pool;
const func = zcu.funcInfo(func_index);
switch (func.analysisUnordered(ip).state) {
.unreferenced => {}, // We're the first reference!
.queued => return, // Analysis is already queued.
.analyzed => return, // Analysis is complete; if it's out-of-date, it'll be re-analyzed later this update.
if (zcu.func_body_analysis_queued.contains(func_index)) return;
if (func.analysisUnordered(ip).is_analyzed) {
if (!zcu.outdated.contains(.wrap(.{ .func = func_index })) and
!zcu.potentially_outdated.contains(.wrap(.{ .func = func_index })))
{
// This function has been analyzed before and is definitely up-to-date.
return;
}
}
try zcu.func_body_analysis_queued.ensureUnusedCapacity(zcu.gpa, 1);
try zcu.comp.queueJob(.{ .analyze_func = func_index });
func.setAnalysisState(ip, .queued);
zcu.func_body_analysis_queued.putAssumeCapacityNoClobber(func_index, {});
}
pub fn ensureNavValAnalysisQueued(zcu: *Zcu, nav_id: InternPool.Nav.Index) !void {
const ip = &zcu.intern_pool;
if (zcu.nav_val_analysis_queued.contains(nav_id)) return;
if (ip.getNav(nav_id).status == .fully_resolved) {
if (!zcu.outdated.contains(.wrap(.{ .nav_val = nav_id })) and
!zcu.potentially_outdated.contains(.wrap(.{ .nav_val = nav_id })))
{
// This `Nav` has been analyzed before and is definitely up-to-date.
return;
}
}
try zcu.nav_val_analysis_queued.ensureUnusedCapacity(zcu.gpa, 1);
try zcu.comp.queueJob(.{ .analyze_comptime_unit = .wrap(.{ .nav_val = nav_id }) });
zcu.nav_val_analysis_queued.putAssumeCapacityNoClobber(nav_id, {});
}
pub const ImportFileResult = struct {
@ -3030,9 +3051,9 @@ pub fn handleUpdateExports(
};
}
pub fn addGlobalAssembly(zcu: *Zcu, cau: InternPool.Cau.Index, source: []const u8) !void {
pub fn addGlobalAssembly(zcu: *Zcu, unit: AnalUnit, source: []const u8) !void {
const gpa = zcu.gpa;
const gop = try zcu.global_assembly.getOrPut(gpa, cau);
const gop = try zcu.global_assembly.getOrPut(gpa, unit);
if (gop.found_existing) {
const new_value = try std.fmt.allocPrint(gpa, "{s}\n{s}", .{ gop.value_ptr.*, source });
gpa.free(gop.value_ptr.*);
@ -3315,23 +3336,22 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolv
log.debug("handle type '{}'", .{Type.fromInterned(ty).containerTypeName(ip).fmt(ip)});
// If this type has a `Cau` for resolution, it's automatically referenced.
const resolution_cau: InternPool.Cau.Index.Optional = switch (ip.indexToKey(ty)) {
.struct_type => ip.loadStructType(ty).cau.toOptional(),
.union_type => ip.loadUnionType(ty).cau.toOptional(),
.enum_type => ip.loadEnumType(ty).cau,
.opaque_type => .none,
// If this type undergoes type resolution, the corresponding `AnalUnit` is automatically referenced.
const has_resolution: bool = switch (ip.indexToKey(ty)) {
.struct_type, .union_type => true,
.enum_type => |k| k != .generated_tag,
.opaque_type => false,
else => unreachable,
};
if (resolution_cau.unwrap()) |cau| {
if (has_resolution) {
// this should only be referenced by the type
const unit = AnalUnit.wrap(.{ .cau = cau });
const unit: AnalUnit = .wrap(.{ .type = ty });
assert(!result.contains(unit));
try unit_queue.putNoClobber(gpa, unit, referencer);
}
// If this is a union with a generated tag, its tag type is automatically referenced.
// We don't add this reference for non-generated tags, as those will already be referenced via the union's `Cau`, with a better source location.
// We don't add this reference for non-generated tags, as those will already be referenced via the union's type resolution, with a better source location.
if (zcu.typeToUnion(Type.fromInterned(ty))) |union_obj| {
const tag_ty = union_obj.enum_tag_ty;
if (tag_ty != .none) {
@ -3346,53 +3366,61 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolv
// Queue any decls within this type which would be automatically analyzed.
// Keep in sync with analysis queueing logic in `Zcu.PerThread.ScanDeclIter.scanDecl`.
const ns = Type.fromInterned(ty).getNamespace(zcu).unwrap().?;
for (zcu.namespacePtr(ns).other_decls.items) |cau| {
// These are `comptime` and `test` declarations.
// `comptime` decls are always analyzed; `test` declarations are analyzed depending on the test filter.
const inst_info = ip.getCau(cau).zir_index.resolveFull(ip) orelse continue;
for (zcu.namespacePtr(ns).comptime_decls.items) |cu| {
// `comptime` decls are always analyzed.
const unit: AnalUnit = .wrap(.{ .@"comptime" = cu });
if (!result.contains(unit)) {
log.debug("type '{}': ref comptime %{}", .{
Type.fromInterned(ty).containerTypeName(ip).fmt(ip),
@intFromEnum(ip.getComptimeUnit(cu).zir_index.resolve(ip) orelse continue),
});
try unit_queue.put(gpa, unit, referencer);
}
}
for (zcu.namespacePtr(ns).test_decls.items) |nav_id| {
const nav = ip.getNav(nav_id);
// `test` declarations are analyzed depending on the test filter.
const inst_info = nav.analysis.?.zir_index.resolveFull(ip) orelse continue;
const file = zcu.fileByIndex(inst_info.file);
// If the file failed AstGen, the TrackedInst refers to the old ZIR.
const zir = if (file.status == .success_zir) file.zir else file.prev_zir.?.*;
const declaration = zir.getDeclaration(inst_info.inst)[0];
const want_analysis = switch (declaration.name) {
const decl = zir.getDeclaration(inst_info.inst);
if (!comp.config.is_test or file.mod != zcu.main_mod) continue;
const want_analysis = switch (decl.kind) {
.@"usingnamespace" => unreachable,
.@"comptime" => true,
else => a: {
if (!comp.config.is_test) break :a false;
if (file.mod != zcu.main_mod) break :a false;
if (declaration.name.isNamedTest(zir)) {
const nav = ip.getCau(cau).owner.unwrap().nav;
const fqn_slice = ip.getNav(nav).fqn.toSlice(ip);
for (comp.test_filters) |test_filter| {
if (std.mem.indexOf(u8, fqn_slice, test_filter) != null) break;
} else break :a false;
}
.@"const", .@"var" => unreachable,
.@"comptime" => unreachable,
.unnamed_test => true,
.@"test", .decltest => a: {
const fqn_slice = nav.fqn.toSlice(ip);
for (comp.test_filters) |test_filter| {
if (std.mem.indexOf(u8, fqn_slice, test_filter) != null) break;
} else break :a false;
break :a true;
},
};
if (want_analysis) {
const unit = AnalUnit.wrap(.{ .cau = cau });
if (!result.contains(unit)) {
log.debug("type '{}': ref cau %{}", .{
Type.fromInterned(ty).containerTypeName(ip).fmt(ip),
@intFromEnum(inst_info.inst),
});
try unit_queue.put(gpa, unit, referencer);
}
log.debug("type '{}': ref test %{}", .{
Type.fromInterned(ty).containerTypeName(ip).fmt(ip),
@intFromEnum(inst_info.inst),
});
const unit: AnalUnit = .wrap(.{ .nav_val = nav_id });
try unit_queue.put(gpa, unit, referencer);
}
}
for (zcu.namespacePtr(ns).pub_decls.keys()) |nav| {
// These are named declarations. They are analyzed only if marked `export`.
const cau = ip.getNav(nav).analysis_owner.unwrap().?;
const inst_info = ip.getCau(cau).zir_index.resolveFull(ip) orelse continue;
const inst_info = ip.getNav(nav).analysis.?.zir_index.resolveFull(ip) orelse continue;
const file = zcu.fileByIndex(inst_info.file);
// If the file failed AstGen, the TrackedInst refers to the old ZIR.
const zir = if (file.status == .success_zir) file.zir else file.prev_zir.?.*;
const declaration = zir.getDeclaration(inst_info.inst)[0];
if (declaration.flags.is_export) {
const unit = AnalUnit.wrap(.{ .cau = cau });
const decl = zir.getDeclaration(inst_info.inst);
if (decl.linkage == .@"export") {
const unit: AnalUnit = .wrap(.{ .nav_val = nav });
if (!result.contains(unit)) {
log.debug("type '{}': ref cau %{}", .{
log.debug("type '{}': ref named %{}", .{
Type.fromInterned(ty).containerTypeName(ip).fmt(ip),
@intFromEnum(inst_info.inst),
});
@ -3402,16 +3430,15 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolv
}
for (zcu.namespacePtr(ns).priv_decls.keys()) |nav| {
// These are named declarations. They are analyzed only if marked `export`.
const cau = ip.getNav(nav).analysis_owner.unwrap().?;
const inst_info = ip.getCau(cau).zir_index.resolveFull(ip) orelse continue;
const inst_info = ip.getNav(nav).analysis.?.zir_index.resolveFull(ip) orelse continue;
const file = zcu.fileByIndex(inst_info.file);
// If the file failed AstGen, the TrackedInst refers to the old ZIR.
const zir = if (file.status == .success_zir) file.zir else file.prev_zir.?.*;
const declaration = zir.getDeclaration(inst_info.inst)[0];
if (declaration.flags.is_export) {
const unit = AnalUnit.wrap(.{ .cau = cau });
const decl = zir.getDeclaration(inst_info.inst);
if (decl.linkage == .@"export") {
const unit: AnalUnit = .wrap(.{ .nav_val = nav });
if (!result.contains(unit)) {
log.debug("type '{}': ref cau %{}", .{
log.debug("type '{}': ref named %{}", .{
Type.fromInterned(ty).containerTypeName(ip).fmt(ip),
@intFromEnum(inst_info.inst),
});
@ -3422,13 +3449,11 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolv
// Incremental compilation does not support `usingnamespace`.
// These are only included to keep good reference traces in non-incremental updates.
for (zcu.namespacePtr(ns).pub_usingnamespace.items) |nav| {
const cau = ip.getNav(nav).analysis_owner.unwrap().?;
const unit = AnalUnit.wrap(.{ .cau = cau });
const unit: AnalUnit = .wrap(.{ .nav_val = nav });
if (!result.contains(unit)) try unit_queue.put(gpa, unit, referencer);
}
for (zcu.namespacePtr(ns).priv_usingnamespace.items) |nav| {
const cau = ip.getNav(nav).analysis_owner.unwrap().?;
const unit = AnalUnit.wrap(.{ .cau = cau });
const unit: AnalUnit = .wrap(.{ .nav_val = nav });
if (!result.contains(unit)) try unit_queue.put(gpa, unit, referencer);
}
continue;
@ -3437,6 +3462,17 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolv
const unit = kv.key;
try result.putNoClobber(gpa, unit, kv.value);
// `nav_val` and `nav_ty` reference each other *implicitly* to save memory.
queue_paired: {
const other: AnalUnit = .wrap(switch (unit.unwrap()) {
.nav_val => |n| .{ .nav_ty = n },
.nav_ty => |n| .{ .nav_val = n },
.@"comptime", .type, .func => break :queue_paired,
});
if (result.contains(other)) break :queue_paired;
try unit_queue.put(gpa, other, kv.value); // same reference location
}
log.debug("handle unit '{}'", .{zcu.fmtAnalUnit(unit)});
if (zcu.reference_table.get(unit)) |first_ref_idx| {
@ -3522,13 +3558,11 @@ pub fn navSrcLine(zcu: *Zcu, nav_index: InternPool.Nav.Index) u32 {
const ip = &zcu.intern_pool;
const inst_info = ip.getNav(nav_index).srcInst(ip).resolveFull(ip).?;
const zir = zcu.fileByIndex(inst_info.file).zir;
const inst = zir.instructions.get(@intFromEnum(inst_info.inst));
assert(inst.tag == .declaration);
return zir.extraData(Zir.Inst.Declaration, inst.data.declaration.payload_index).data.src_line;
return zir.getDeclaration(inst_info.inst).src_line;
}
pub fn navValue(zcu: *const Zcu, nav_index: InternPool.Nav.Index) Value {
return Value.fromInterned(zcu.intern_pool.getNav(nav_index).status.resolved.val);
return Value.fromInterned(zcu.intern_pool.getNav(nav_index).status.fully_resolved.val);
}
pub fn navFileScopeIndex(zcu: *Zcu, nav: InternPool.Nav.Index) File.Index {
@ -3540,12 +3574,6 @@ pub fn navFileScope(zcu: *Zcu, nav: InternPool.Nav.Index) *File {
return zcu.fileByIndex(zcu.navFileScopeIndex(nav));
}
pub fn cauFileScope(zcu: *Zcu, cau: InternPool.Cau.Index) *File {
const ip = &zcu.intern_pool;
const file_index = ip.getCau(cau).zir_index.resolveFile(ip);
return zcu.fileByIndex(file_index);
}
pub fn fmtAnalUnit(zcu: *Zcu, unit: AnalUnit) std.fmt.Formatter(formatAnalUnit) {
return .{ .data = .{ .unit = unit, .zcu = zcu } };
}
@ -3558,19 +3586,18 @@ fn formatAnalUnit(data: struct { unit: AnalUnit, zcu: *Zcu }, comptime fmt: []co
const zcu = data.zcu;
const ip = &zcu.intern_pool;
switch (data.unit.unwrap()) {
.cau => |cau_index| {
const cau = ip.getCau(cau_index);
switch (cau.owner.unwrap()) {
.nav => |nav| return writer.print("cau(decl='{}')", .{ip.getNav(nav).fqn.fmt(ip)}),
.type => |ty| return writer.print("cau(ty='{}')", .{Type.fromInterned(ty).containerTypeName(ip).fmt(ip)}),
.none => if (cau.zir_index.resolveFull(ip)) |resolved| {
const file_path = zcu.fileByIndex(resolved.file).sub_file_path;
return writer.print("cau(inst=('{s}', %{}))", .{ file_path, @intFromEnum(resolved.inst) });
} else {
return writer.writeAll("cau(inst=<lost>)");
},
.@"comptime" => |cu_id| {
const cu = ip.getComptimeUnit(cu_id);
if (cu.zir_index.resolveFull(ip)) |resolved| {
const file_path = zcu.fileByIndex(resolved.file).sub_file_path;
return writer.print("comptime(inst=('{s}', %{}))", .{ file_path, @intFromEnum(resolved.inst) });
} else {
return writer.writeAll("comptime(inst=<list>)");
}
},
.nav_val => |nav| return writer.print("nav_val('{}')", .{ip.getNav(nav).fqn.fmt(ip)}),
.nav_ty => |nav| return writer.print("nav_ty('{}')", .{ip.getNav(nav).fqn.fmt(ip)}),
.type => |ty| return writer.print("ty('{}')", .{Type.fromInterned(ty).containerTypeName(ip).fmt(ip)}),
.func => |func| {
const nav = zcu.funcInfo(func).owner_nav;
return writer.print("func('{}')", .{ip.getNav(nav).fqn.fmt(ip)});
@ -3595,7 +3622,11 @@ fn formatDependee(data: struct { dependee: InternPool.Dependee, zcu: *Zcu }, com
},
.nav_val => |nav| {
const fqn = ip.getNav(nav).fqn;
return writer.print("nav('{}')", .{fqn.fmt(ip)});
return writer.print("nav_val('{}')", .{fqn.fmt(ip)});
},
.nav_ty => |nav| {
const fqn = ip.getNav(nav).fqn;
return writer.print("nav_ty('{}')", .{fqn.fmt(ip)});
},
.interned => |ip_index| switch (ip.indexToKey(ip_index)) {
.struct_type, .union_type, .enum_type => return writer.print("type('{}')", .{Type.fromInterned(ip_index).containerTypeName(ip).fmt(ip)}),
@ -3772,3 +3803,12 @@ pub fn callconvSupported(zcu: *Zcu, cc: std.builtin.CallingConvention) union(enu
if (!backend_ok) return .{ .bad_backend = backend };
return .ok;
}
/// Given that a `Nav` has value `val`, determine if a ref of that `Nav` gives a `const` pointer.
pub fn navValIsConst(zcu: *const Zcu, val: InternPool.Index) bool {
return switch (zcu.intern_pool.indexToKey(val)) {
.variable => false,
.@"extern" => |e| e.is_const,
else => true,
};
}

File diff suppressed because it is too large Load Diff

View File

@ -3218,15 +3218,7 @@ fn lowerNavRef(func: *CodeGen, nav_index: InternPool.Nav.Index, offset: u32) Inn
const zcu = pt.zcu;
const ip = &zcu.intern_pool;
// check if decl is an alias to a function, in which case we
// want to lower the actual decl, rather than the alias itself.
const owner_nav = switch (ip.indexToKey(zcu.navValue(nav_index).toIntern())) {
.func => |function| function.owner_nav,
.variable => |variable| variable.owner_nav,
.@"extern" => |@"extern"| @"extern".owner_nav,
else => nav_index,
};
const nav_ty = ip.getNav(owner_nav).typeOf(ip);
const nav_ty = ip.getNav(nav_index).typeOf(ip);
if (!ip.isFunctionType(nav_ty) and !Type.fromInterned(nav_ty).hasRuntimeBitsIgnoreComptime(zcu)) {
return .{ .imm32 = 0xaaaaaaaa };
}

View File

@ -817,7 +817,7 @@ fn genNavRef(
pt: Zcu.PerThread,
src_loc: Zcu.LazySrcLoc,
val: Value,
ref_nav_index: InternPool.Nav.Index,
nav_index: InternPool.Nav.Index,
target: std.Target,
) CodeGenError!GenResult {
const zcu = pt.zcu;
@ -851,14 +851,15 @@ fn genNavRef(
}
}
const nav_index, const is_extern, const lib_name, const is_threadlocal = switch (ip.indexToKey(zcu.navValue(ref_nav_index).toIntern())) {
.func => |func| .{ func.owner_nav, false, .none, false },
.variable => |variable| .{ variable.owner_nav, false, variable.lib_name, variable.is_threadlocal },
.@"extern" => |@"extern"| .{ @"extern".owner_nav, true, @"extern".lib_name, @"extern".is_threadlocal },
else => .{ ref_nav_index, false, .none, false },
};
const nav = ip.getNav(nav_index);
const is_extern, const lib_name, const is_threadlocal = if (nav.getExtern(ip)) |e|
.{ true, e.lib_name, e.is_threadlocal }
else
.{ false, .none, nav.isThreadlocal(ip) };
const single_threaded = zcu.navFileScope(nav_index).mod.single_threaded;
const name = ip.getNav(nav_index).name;
const name = nav.name;
if (lf.cast(.elf)) |elf_file| {
const zo = elf_file.zigObjectPtr().?;
if (is_extern) {

View File

@ -770,11 +770,14 @@ pub const DeclGen = struct {
const ctype_pool = &dg.ctype_pool;
// Chase function values in order to be able to reference the original function.
const owner_nav = switch (ip.indexToKey(zcu.navValue(nav_index).toIntern())) {
.variable => |variable| variable.owner_nav,
.func => |func| func.owner_nav,
.@"extern" => |@"extern"| @"extern".owner_nav,
else => nav_index,
const owner_nav = switch (ip.getNav(nav_index).status) {
.unresolved => unreachable,
.type_resolved => nav_index, // this can't be an extern or a function
.fully_resolved => |r| switch (ip.indexToKey(r.val)) {
.func => |f| f.owner_nav,
.@"extern" => |e| e.owner_nav,
else => nav_index,
},
};
// Render an undefined pointer if we have a pointer to a zero-bit or comptime type.
@ -2237,7 +2240,7 @@ pub const DeclGen = struct {
Type.fromInterned(nav.typeOf(ip)),
.{ .nav = nav_index },
CQualifiers.init(.{ .@"const" = flags.is_const }),
nav.status.resolved.alignment,
nav.getAlignment(),
.complete,
);
try fwd.writeAll(";\n");
@ -2246,19 +2249,19 @@ pub const DeclGen = struct {
fn renderNavName(dg: *DeclGen, writer: anytype, nav_index: InternPool.Nav.Index) !void {
const zcu = dg.pt.zcu;
const ip = &zcu.intern_pool;
switch (ip.indexToKey(zcu.navValue(nav_index).toIntern())) {
.@"extern" => |@"extern"| try writer.print("{ }", .{
const nav = ip.getNav(nav_index);
if (nav.getExtern(ip)) |@"extern"| {
try writer.print("{ }", .{
fmtIdent(ip.getNav(@"extern".owner_nav).name.toSlice(ip)),
}),
else => {
// MSVC has a limit of 4095 character token length limit, and fmtIdent can (worst case),
// expand to 3x the length of its input, but let's cut it off at a much shorter limit.
const fqn_slice = ip.getNav(nav_index).fqn.toSlice(ip);
try writer.print("{}__{d}", .{
fmtIdent(fqn_slice[0..@min(fqn_slice.len, 100)]),
@intFromEnum(nav_index),
});
},
});
} else {
// MSVC has a limit of 4095 character token length limit, and fmtIdent can (worst case),
// expand to 3x the length of its input, but let's cut it off at a much shorter limit.
const fqn_slice = ip.getNav(nav_index).fqn.toSlice(ip);
try writer.print("{}__{d}", .{
fmtIdent(fqn_slice[0..@min(fqn_slice.len, 100)]),
@intFromEnum(nav_index),
});
}
}
@ -2826,7 +2829,7 @@ pub fn genLazyFn(o: *Object, lazy_ctype_pool: *const CType.Pool, lazy_fn: LazyFn
const fwd = o.dg.fwdDeclWriter();
try fwd.print("static zig_{s} ", .{@tagName(key)});
try o.dg.renderFunctionSignature(fwd, fn_val, ip.getNav(fn_nav_index).status.resolved.alignment, .forward, .{
try o.dg.renderFunctionSignature(fwd, fn_val, ip.getNav(fn_nav_index).getAlignment(), .forward, .{
.fmt_ctype_pool_string = fn_name,
});
try fwd.writeAll(";\n");
@ -2867,13 +2870,13 @@ pub fn genFunc(f: *Function) !void {
try o.dg.renderFunctionSignature(
fwd,
nav_val,
nav.status.resolved.alignment,
nav.status.fully_resolved.alignment,
.forward,
.{ .nav = nav_index },
);
try fwd.writeAll(";\n");
if (nav.status.resolved.@"linksection".toSlice(ip)) |s|
if (nav.status.fully_resolved.@"linksection".toSlice(ip)) |s|
try o.writer().print("zig_linksection_fn({s}) ", .{fmtStringLiteral(s, null)});
try o.dg.renderFunctionSignature(
o.writer(),
@ -2952,7 +2955,7 @@ pub fn genDecl(o: *Object) !void {
const nav_ty = Type.fromInterned(nav.typeOf(ip));
if (!nav_ty.isFnOrHasRuntimeBitsIgnoreComptime(zcu)) return;
switch (ip.indexToKey(nav.status.resolved.val)) {
switch (ip.indexToKey(nav.status.fully_resolved.val)) {
.@"extern" => |@"extern"| {
if (!ip.isFunctionType(nav_ty.toIntern())) return o.dg.renderFwdDecl(o.dg.pass.nav, .{
.is_extern = true,
@ -2965,8 +2968,8 @@ pub fn genDecl(o: *Object) !void {
try fwd.writeAll("zig_extern ");
try o.dg.renderFunctionSignature(
fwd,
Value.fromInterned(nav.status.resolved.val),
nav.status.resolved.alignment,
Value.fromInterned(nav.status.fully_resolved.val),
nav.status.fully_resolved.alignment,
.forward,
.{ .@"export" = .{
.main_name = nav.name,
@ -2985,14 +2988,14 @@ pub fn genDecl(o: *Object) !void {
const w = o.writer();
if (variable.is_weak_linkage) try w.writeAll("zig_weak_linkage ");
if (variable.is_threadlocal and !o.dg.mod.single_threaded) try w.writeAll("zig_threadlocal ");
if (nav.status.resolved.@"linksection".toSlice(&zcu.intern_pool)) |s|
if (nav.status.fully_resolved.@"linksection".toSlice(&zcu.intern_pool)) |s|
try w.print("zig_linksection({s}) ", .{fmtStringLiteral(s, null)});
try o.dg.renderTypeAndName(
w,
nav_ty,
.{ .nav = o.dg.pass.nav },
.{},
nav.status.resolved.alignment,
nav.status.fully_resolved.alignment,
.complete,
);
try w.writeAll(" = ");
@ -3002,10 +3005,10 @@ pub fn genDecl(o: *Object) !void {
},
else => try genDeclValue(
o,
Value.fromInterned(nav.status.resolved.val),
Value.fromInterned(nav.status.fully_resolved.val),
.{ .nav = o.dg.pass.nav },
nav.status.resolved.alignment,
nav.status.resolved.@"linksection",
nav.status.fully_resolved.alignment,
nav.status.fully_resolved.@"linksection",
),
}
}

View File

@ -1476,7 +1476,7 @@ pub const Object = struct {
} }, &o.builder);
}
if (nav.status.resolved.@"linksection".toSlice(ip)) |section|
if (nav.status.fully_resolved.@"linksection".toSlice(ip)) |section|
function_index.setSection(try o.builder.string(section), &o.builder);
var deinit_wip = true;
@ -1684,7 +1684,7 @@ pub const Object = struct {
const file = try o.getDebugFile(file_scope);
const line_number = zcu.navSrcLine(func.owner_nav) + 1;
const is_internal_linkage = ip.indexToKey(nav.status.resolved.val) != .@"extern";
const is_internal_linkage = ip.indexToKey(nav.status.fully_resolved.val) != .@"extern";
const debug_decl_type = try o.lowerDebugType(fn_ty);
const subprogram = try o.builder.debugSubprogram(
@ -2928,9 +2928,7 @@ pub const Object = struct {
const gpa = o.gpa;
const nav = ip.getNav(nav_index);
const owner_mod = zcu.navFileScope(nav_index).mod;
const resolved = nav.status.resolved;
const val = Value.fromInterned(resolved.val);
const ty = val.typeOf(zcu);
const ty: Type = .fromInterned(nav.typeOf(ip));
const gop = try o.nav_map.getOrPut(gpa, nav_index);
if (gop.found_existing) return gop.value_ptr.ptr(&o.builder).kind.function;
@ -2938,15 +2936,14 @@ pub const Object = struct {
const target = owner_mod.resolved_target.result;
const sret = firstParamSRet(fn_info, zcu, target);
const is_extern, const lib_name = switch (ip.indexToKey(val.toIntern())) {
.variable => |variable| .{ false, variable.lib_name },
.@"extern" => |@"extern"| .{ true, @"extern".lib_name },
else => .{ false, .none },
};
const is_extern, const lib_name = if (nav.getExtern(ip)) |@"extern"|
.{ true, @"extern".lib_name }
else
.{ false, .none };
const function_index = try o.builder.addFunction(
try o.lowerType(ty),
try o.builder.strtabString((if (is_extern) nav.name else nav.fqn).toSlice(ip)),
toLlvmAddressSpace(resolved.@"addrspace", target),
toLlvmAddressSpace(nav.getAddrspace(), target),
);
gop.value_ptr.* = function_index.ptrConst(&o.builder).global;
@ -3064,8 +3061,8 @@ pub const Object = struct {
}
}
if (resolved.alignment != .none)
function_index.setAlignment(resolved.alignment.toLlvm(), &o.builder);
if (nav.getAlignment() != .none)
function_index.setAlignment(nav.getAlignment().toLlvm(), &o.builder);
// Function attributes that are independent of analysis results of the function body.
try o.addCommonFnAttributes(
@ -3250,17 +3247,21 @@ pub const Object = struct {
const zcu = pt.zcu;
const ip = &zcu.intern_pool;
const nav = ip.getNav(nav_index);
const resolved = nav.status.resolved;
const is_extern, const is_threadlocal, const is_weak_linkage, const is_dll_import = switch (ip.indexToKey(resolved.val)) {
.variable => |variable| .{ false, variable.is_threadlocal, variable.is_weak_linkage, false },
.@"extern" => |@"extern"| .{ true, @"extern".is_threadlocal, @"extern".is_weak_linkage, @"extern".is_dll_import },
else => .{ false, false, false, false },
const is_extern, const is_threadlocal, const is_weak_linkage, const is_dll_import = switch (nav.status) {
.unresolved => unreachable,
.fully_resolved => |r| switch (ip.indexToKey(r.val)) {
.variable => |variable| .{ false, variable.is_threadlocal, variable.is_weak_linkage, false },
.@"extern" => |@"extern"| .{ true, @"extern".is_threadlocal, @"extern".is_weak_linkage, @"extern".is_dll_import },
else => .{ false, false, false, false },
},
// This means it's a source declaration which is not `extern`!
.type_resolved => |r| .{ false, r.is_threadlocal, false, false },
};
const variable_index = try o.builder.addVariable(
try o.builder.strtabString((if (is_extern) nav.name else nav.fqn).toSlice(ip)),
try o.lowerType(Type.fromInterned(nav.typeOf(ip))),
toLlvmGlobalAddressSpace(resolved.@"addrspace", zcu.getTarget()),
toLlvmGlobalAddressSpace(nav.getAddrspace(), zcu.getTarget()),
);
gop.value_ptr.* = variable_index.ptrConst(&o.builder).global;
@ -4529,20 +4530,10 @@ pub const Object = struct {
const zcu = pt.zcu;
const ip = &zcu.intern_pool;
// In the case of something like:
// fn foo() void {}
// const bar = foo;
// ... &bar;
// `bar` is just an alias and we actually want to lower a reference to `foo`.
const owner_nav_index = switch (ip.indexToKey(zcu.navValue(nav_index).toIntern())) {
.func => |func| func.owner_nav,
.@"extern" => |@"extern"| @"extern".owner_nav,
else => nav_index,
};
const owner_nav = ip.getNav(owner_nav_index);
const nav = ip.getNav(nav_index);
const nav_ty = Type.fromInterned(owner_nav.typeOf(ip));
const ptr_ty = try pt.navPtrType(owner_nav_index);
const nav_ty = Type.fromInterned(nav.typeOf(ip));
const ptr_ty = try pt.navPtrType(nav_index);
const is_fn_body = nav_ty.zigTypeTag(zcu) == .@"fn";
if ((!is_fn_body and !nav_ty.hasRuntimeBits(zcu)) or
@ -4552,13 +4543,13 @@ pub const Object = struct {
}
const llvm_global = if (is_fn_body)
(try o.resolveLlvmFunction(owner_nav_index)).ptrConst(&o.builder).global
(try o.resolveLlvmFunction(nav_index)).ptrConst(&o.builder).global
else
(try o.resolveGlobalNav(owner_nav_index)).ptrConst(&o.builder).global;
(try o.resolveGlobalNav(nav_index)).ptrConst(&o.builder).global;
const llvm_val = try o.builder.convConst(
llvm_global.toConst(),
try o.builder.ptrType(toLlvmAddressSpace(owner_nav.status.resolved.@"addrspace", zcu.getTarget())),
try o.builder.ptrType(toLlvmAddressSpace(nav.getAddrspace(), zcu.getTarget())),
);
return o.builder.convConst(llvm_val, try o.lowerType(ptr_ty));
@ -4800,10 +4791,10 @@ pub const NavGen = struct {
const ip = &zcu.intern_pool;
const nav_index = ng.nav_index;
const nav = ip.getNav(nav_index);
const resolved = nav.status.resolved;
const resolved = nav.status.fully_resolved;
const is_extern, const lib_name, const is_threadlocal, const is_weak_linkage, const is_dll_import, const is_const, const init_val, const owner_nav = switch (ip.indexToKey(resolved.val)) {
.variable => |variable| .{ false, variable.lib_name, variable.is_threadlocal, variable.is_weak_linkage, false, false, variable.init, variable.owner_nav },
.variable => |variable| .{ false, .none, variable.is_threadlocal, variable.is_weak_linkage, false, false, variable.init, variable.owner_nav },
.@"extern" => |@"extern"| .{ true, @"extern".lib_name, @"extern".is_threadlocal, @"extern".is_weak_linkage, @"extern".is_dll_import, @"extern".is_const, .none, @"extern".owner_nav },
else => .{ false, .none, false, false, false, true, resolved.val, nav_index },
};
@ -5766,7 +5757,7 @@ pub const FuncGen = struct {
const msg_nav_index = zcu.panic_messages[@intFromEnum(panic_id)].unwrap().?;
const msg_nav = ip.getNav(msg_nav_index);
const msg_len = Type.fromInterned(msg_nav.typeOf(ip)).childType(zcu).arrayLen(zcu);
const msg_ptr = try o.lowerValue(msg_nav.status.resolved.val);
const msg_ptr = try o.lowerValue(msg_nav.status.fully_resolved.val);
const null_opt_addr_global = try fg.resolveNullOptUsize();
const target = zcu.getTarget();
const llvm_usize = try o.lowerType(Type.usize);

View File

@ -268,7 +268,7 @@ pub const Object = struct {
// TODO: Extern fn?
const kind: SpvModule.Decl.Kind = if (ip.isFunctionType(nav.typeOf(ip)))
.func
else switch (nav.status.resolved.@"addrspace") {
else switch (nav.getAddrspace()) {
.generic => .invocation_global,
else => .global,
};
@ -1279,17 +1279,20 @@ const NavGen = struct {
const ip = &zcu.intern_pool;
const ty_id = try self.resolveType(ty, .direct);
const nav = ip.getNav(nav_index);
const nav_val = zcu.navValue(nav_index);
const nav_ty = nav_val.typeOf(zcu);
const nav_ty: Type = .fromInterned(nav.typeOf(ip));
switch (ip.indexToKey(nav_val.toIntern())) {
.func => {
// TODO: Properly lower function pointers. For now we are going to hack around it and
// just generate an empty pointer. Function pointers are represented by a pointer to usize.
return try self.spv.constUndef(ty_id);
switch (nav.status) {
.unresolved => unreachable,
.type_resolved => {}, // this is not a function or extern
.fully_resolved => |r| switch (ip.indexToKey(r.val)) {
.func => {
// TODO: Properly lower function pointers. For now we are going to hack around it and
// just generate an empty pointer. Function pointers are represented by a pointer to usize.
return try self.spv.constUndef(ty_id);
},
.@"extern" => if (ip.isFunctionType(nav_ty.toIntern())) @panic("TODO"),
else => {},
},
.@"extern" => assert(!ip.isFunctionType(nav_ty.toIntern())), // TODO
else => {},
}
if (!nav_ty.isFnOrHasRuntimeBitsIgnoreComptime(zcu)) {
@ -1305,7 +1308,7 @@ const NavGen = struct {
.global, .invocation_global => spv_decl.result_id,
};
const storage_class = self.spvStorageClass(nav.status.resolved.@"addrspace");
const storage_class = self.spvStorageClass(nav.getAddrspace());
try self.addFunctionDep(spv_decl_index, storage_class);
const decl_ptr_ty_id = try self.ptrType(nav_ty, storage_class);
@ -3182,7 +3185,7 @@ const NavGen = struct {
};
assert(maybe_init_val == null); // TODO
const storage_class = self.spvStorageClass(nav.status.resolved.@"addrspace");
const storage_class = self.spvStorageClass(nav.getAddrspace());
assert(storage_class != .Generic); // These should be instance globals
const ptr_ty_id = try self.ptrType(ty, storage_class);

View File

@ -692,7 +692,7 @@ pub const File = struct {
/// May be called before or after updateExports for any given Nav.
pub fn updateNav(base: *File, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) UpdateNavError!void {
const nav = pt.zcu.intern_pool.getNav(nav_index);
assert(nav.status == .resolved);
assert(nav.status == .fully_resolved);
switch (base.tag) {
inline else => |tag| {
dev.check(tag.devFeature());

View File

@ -217,7 +217,7 @@ pub fn updateFunc(
.mod = zcu.navFileScope(func.owner_nav).mod,
.error_msg = null,
.pass = .{ .nav = func.owner_nav },
.is_naked_fn = zcu.navValue(func.owner_nav).typeOf(zcu).fnCallingConvention(zcu) == .naked,
.is_naked_fn = Type.fromInterned(func.ty).fnCallingConvention(zcu) == .naked,
.fwd_decl = fwd_decl.toManaged(gpa),
.ctype_pool = ctype_pool.*,
.scratch = .{},
@ -320,11 +320,11 @@ pub fn updateNav(self: *C, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !
const ip = &zcu.intern_pool;
const nav = ip.getNav(nav_index);
const nav_init = switch (ip.indexToKey(nav.status.resolved.val)) {
const nav_init = switch (ip.indexToKey(nav.status.fully_resolved.val)) {
.func => return,
.@"extern" => .none,
.variable => |variable| variable.init,
else => nav.status.resolved.val,
else => nav.status.fully_resolved.val,
};
if (nav_init != .none and !Value.fromInterned(nav_init).typeOf(zcu).hasRuntimeBits(zcu)) return;
@ -499,7 +499,7 @@ pub fn flushModule(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:
av_block,
self.exported_navs.getPtr(nav),
export_names,
if (ip.indexToKey(zcu.navValue(nav).toIntern()) == .@"extern")
if (ip.getNav(nav).getExtern(ip) != null)
ip.getNav(nav).name.toOptional()
else
.none,
@ -544,13 +544,11 @@ pub fn flushModule(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node:
},
self.getString(av_block.code),
);
for (self.navs.keys(), self.navs.values()) |nav, av_block| f.appendCodeAssumeCapacity(
if (self.exported_navs.contains(nav)) .default else switch (ip.indexToKey(zcu.navValue(nav).toIntern())) {
.@"extern" => .zig_extern,
else => .static,
},
self.getString(av_block.code),
);
for (self.navs.keys(), self.navs.values()) |nav, av_block| f.appendCodeAssumeCapacity(storage: {
if (self.exported_navs.contains(nav)) break :storage .default;
if (ip.getNav(nav).getExtern(ip) != null) break :storage .zig_extern;
break :storage .static;
}, self.getString(av_block.code));
const file = self.base.file.?;
try file.setEndPos(f.file_size);

View File

@ -1110,6 +1110,8 @@ pub fn updateFunc(coff: *Coff, pt: Zcu.PerThread, func_index: InternPool.Index,
const atom_index = try coff.getOrCreateAtomForNav(func.owner_nav);
coff.freeRelocations(atom_index);
coff.navs.getPtr(func.owner_nav).?.section = coff.text_section_index.?;
var code_buffer = std.ArrayList(u8).init(gpa);
defer code_buffer.deinit();
@ -1223,6 +1225,8 @@ pub fn updateNav(
coff.freeRelocations(atom_index);
const atom = coff.getAtom(atom_index);
coff.navs.getPtr(nav_index).?.section = coff.getNavOutputSection(nav_index);
var code_buffer = std.ArrayList(u8).init(gpa);
defer code_buffer.deinit();
@ -1342,7 +1346,8 @@ pub fn getOrCreateAtomForNav(coff: *Coff, nav_index: InternPool.Nav.Index) !Atom
if (!gop.found_existing) {
gop.value_ptr.* = .{
.atom = try coff.createAtom(),
.section = coff.getNavOutputSection(nav_index),
// If necessary, this will be modified by `updateNav` or `updateFunc`.
.section = coff.rdata_section_index.?,
.exports = .{},
};
}
@ -1355,7 +1360,7 @@ fn getNavOutputSection(coff: *Coff, nav_index: InternPool.Nav.Index) u16 {
const nav = ip.getNav(nav_index);
const ty = Type.fromInterned(nav.typeOf(ip));
const zig_ty = ty.zigTypeTag(zcu);
const val = Value.fromInterned(nav.status.resolved.val);
const val = Value.fromInterned(nav.status.fully_resolved.val);
const index: u16 = blk: {
if (val.isUndefDeep(zcu)) {
// TODO in release-fast and release-small, we should put undef in .bss
@ -2348,10 +2353,10 @@ pub fn getNavVAddr(
const ip = &zcu.intern_pool;
const nav = ip.getNav(nav_index);
log.debug("getNavVAddr {}({d})", .{ nav.fqn.fmt(ip), nav_index });
const sym_index = switch (ip.indexToKey(nav.status.resolved.val)) {
.@"extern" => |@"extern"| try coff.getGlobalSymbol(nav.name.toSlice(ip), @"extern".lib_name.toSlice(ip)),
else => coff.getAtom(try coff.getOrCreateAtomForNav(nav_index)).getSymbolIndex().?,
};
const sym_index = if (nav.getExtern(ip)) |e|
try coff.getGlobalSymbol(nav.name.toSlice(ip), e.lib_name.toSlice(ip))
else
coff.getAtom(try coff.getOrCreateAtomForNav(nav_index)).getSymbolIndex().?;
const atom_index = coff.getAtomIndexForSymbol(.{
.sym_index = reloc_info.parent.atom_index,
.file = null,

View File

@ -2259,24 +2259,13 @@ pub fn initWipNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool.Nav.In
switch (ip.indexToKey(nav_val.toIntern())) {
else => {
assert(file.zir_loaded);
const decl = file.zir.getDeclaration(inst_info.inst)[0];
const decl = file.zir.getDeclaration(inst_info.inst);
const parent_type, const accessibility: u8 = if (nav.analysis_owner.unwrap()) |cau| parent: {
const parent_namespace_ptr = ip.namespacePtr(ip.getCau(cau).namespace);
const parent_type, const accessibility: u8 = if (nav.analysis) |a| parent: {
const parent_namespace_ptr = ip.namespacePtr(a.namespace);
break :parent .{
parent_namespace_ptr.owner_type,
switch (decl.name) {
.@"comptime",
.@"usingnamespace",
.unnamed_test,
=> DW.ACCESS.private,
_ => if (decl.name.isNamedTest(file.zir))
DW.ACCESS.private
else if (decl.flags.is_pub)
DW.ACCESS.public
else
DW.ACCESS.private,
},
if (decl.is_pub) DW.ACCESS.public else DW.ACCESS.private,
};
} else .{ zcu.fileRootType(inst_info.file), DW.ACCESS.private };
@ -2292,7 +2281,7 @@ pub fn initWipNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool.Nav.In
const nav_ty = nav_val.typeOf(zcu);
const nav_ty_reloc_index = try wip_nav.refForward();
try wip_nav.infoExprloc(.{ .addr = .{ .sym = sym_index } });
try uleb128(diw, nav.status.resolved.alignment.toByteUnits() orelse
try uleb128(diw, nav.status.fully_resolved.alignment.toByteUnits() orelse
nav_ty.abiAlignment(zcu).toByteUnits().?);
try diw.writeByte(@intFromBool(false));
wip_nav.finishForward(nav_ty_reloc_index);
@ -2301,24 +2290,13 @@ pub fn initWipNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool.Nav.In
},
.variable => |variable| {
assert(file.zir_loaded);
const decl = file.zir.getDeclaration(inst_info.inst)[0];
const decl = file.zir.getDeclaration(inst_info.inst);
const parent_type, const accessibility: u8 = if (nav.analysis_owner.unwrap()) |cau| parent: {
const parent_namespace_ptr = ip.namespacePtr(ip.getCau(cau).namespace);
const parent_type, const accessibility: u8 = if (nav.analysis) |a| parent: {
const parent_namespace_ptr = ip.namespacePtr(a.namespace);
break :parent .{
parent_namespace_ptr.owner_type,
switch (decl.name) {
.@"comptime",
.@"usingnamespace",
.unnamed_test,
=> DW.ACCESS.private,
_ => if (decl.name.isNamedTest(file.zir))
DW.ACCESS.private
else if (decl.flags.is_pub)
DW.ACCESS.public
else
DW.ACCESS.private,
},
if (decl.is_pub) DW.ACCESS.public else DW.ACCESS.private,
};
} else .{ zcu.fileRootType(inst_info.file), DW.ACCESS.private };
@ -2335,30 +2313,19 @@ pub fn initWipNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool.Nav.In
try wip_nav.refType(ty);
const addr: Loc = .{ .addr = .{ .sym = sym_index } };
try wip_nav.infoExprloc(if (variable.is_threadlocal) .{ .form_tls_address = &addr } else addr);
try uleb128(diw, nav.status.resolved.alignment.toByteUnits() orelse
try uleb128(diw, nav.status.fully_resolved.alignment.toByteUnits() orelse
ty.abiAlignment(zcu).toByteUnits().?);
try diw.writeByte(@intFromBool(false));
},
.func => |func| {
assert(file.zir_loaded);
const decl = file.zir.getDeclaration(inst_info.inst)[0];
const decl = file.zir.getDeclaration(inst_info.inst);
const parent_type, const accessibility: u8 = if (nav.analysis_owner.unwrap()) |cau| parent: {
const parent_namespace_ptr = ip.namespacePtr(ip.getCau(cau).namespace);
const parent_type, const accessibility: u8 = if (nav.analysis) |a| parent: {
const parent_namespace_ptr = ip.namespacePtr(a.namespace);
break :parent .{
parent_namespace_ptr.owner_type,
switch (decl.name) {
.@"comptime",
.@"usingnamespace",
.unnamed_test,
=> DW.ACCESS.private,
_ => if (decl.name.isNamedTest(file.zir))
DW.ACCESS.private
else if (decl.flags.is_pub)
DW.ACCESS.public
else
DW.ACCESS.private,
},
if (decl.is_pub) DW.ACCESS.public else DW.ACCESS.private,
};
} else .{ zcu.fileRootType(inst_info.file), DW.ACCESS.private };
@ -2421,7 +2388,7 @@ pub fn initWipNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool.Nav.In
wip_nav.func_high_pc = @intCast(wip_nav.debug_info.items.len);
try diw.writeInt(u32, 0, dwarf.endian);
const target = file.mod.resolved_target.result;
try uleb128(diw, switch (nav.status.resolved.alignment) {
try uleb128(diw, switch (nav.status.fully_resolved.alignment) {
.none => target_info.defaultFunctionAlignment(target),
else => |a| a.maxStrict(target_info.minFunctionAlignment(target)),
}.toByteUnits().?);
@ -2585,23 +2552,22 @@ pub fn updateComptimeNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool
const inst_info = nav.srcInst(ip).resolveFull(ip).?;
const file = zcu.fileByIndex(inst_info.file);
assert(file.zir_loaded);
const decl = file.zir.getDeclaration(inst_info.inst)[0];
const decl = file.zir.getDeclaration(inst_info.inst);
const is_test = switch (decl.name) {
.unnamed_test => true,
.@"comptime", .@"usingnamespace" => false,
_ => decl.name.isNamedTest(file.zir),
const is_test = switch (decl.kind) {
.unnamed_test, .@"test", .decltest => true,
.@"comptime", .@"usingnamespace", .@"const", .@"var" => false,
};
if (is_test) {
// This isn't actually a comptime Nav! It's a test, so it'll definitely never be referenced at comptime.
return;
}
const parent_type, const accessibility: u8 = if (nav.analysis_owner.unwrap()) |cau| parent: {
const parent_namespace_ptr = ip.namespacePtr(ip.getCau(cau).namespace);
const parent_type, const accessibility: u8 = if (nav.analysis) |a| parent: {
const parent_namespace_ptr = ip.namespacePtr(a.namespace);
break :parent .{
parent_namespace_ptr.owner_type,
if (decl.flags.is_pub) DW.ACCESS.public else DW.ACCESS.private,
if (decl.is_pub) DW.ACCESS.public else DW.ACCESS.private,
};
} else .{ zcu.fileRootType(inst_info.file), DW.ACCESS.private };
@ -2986,7 +2952,7 @@ pub fn updateComptimeNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool
const nav_ty = nav_val.typeOf(zcu);
try wip_nav.refType(nav_ty);
try wip_nav.blockValue(nav_src_loc, nav_val);
try uleb128(diw, nav.status.resolved.alignment.toByteUnits() orelse
try uleb128(diw, nav.status.fully_resolved.alignment.toByteUnits() orelse
nav_ty.abiAlignment(zcu).toByteUnits().?);
try diw.writeByte(@intFromBool(false));
},
@ -3011,7 +2977,7 @@ pub fn updateComptimeNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool
try wip_nav.strp(nav.name.toSlice(ip));
try wip_nav.strp(nav.fqn.toSlice(ip));
const nav_ty_reloc_index = try wip_nav.refForward();
try uleb128(diw, nav.status.resolved.alignment.toByteUnits() orelse
try uleb128(diw, nav.status.fully_resolved.alignment.toByteUnits() orelse
nav_ty.abiAlignment(zcu).toByteUnits().?);
try diw.writeByte(@intFromBool(false));
if (has_runtime_bits) try wip_nav.blockValue(nav_src_loc, nav_val);
@ -4198,9 +4164,7 @@ pub fn updateNavLineNumber(dwarf: *Dwarf, zcu: *Zcu, nav_index: InternPool.Nav.I
assert(inst_info.inst != .main_struct_inst);
const file = zcu.fileByIndex(inst_info.file);
const inst = file.zir.instructions.get(@intFromEnum(inst_info.inst));
assert(inst.tag == .declaration);
const line = file.zir.extraData(Zir.Inst.Declaration, inst.data.declaration.payload_index).data.src_line;
const line = file.zir.getDeclaration(inst_info.inst).src_line;
var line_buf: [4]u8 = undefined;
std.mem.writeInt(u32, &line_buf, line, dwarf.endian);

View File

@ -925,14 +925,11 @@ pub fn getNavVAddr(
const ip = &zcu.intern_pool;
const nav = ip.getNav(nav_index);
log.debug("getNavVAddr {}({d})", .{ nav.fqn.fmt(ip), nav_index });
const this_sym_index = switch (ip.indexToKey(nav.status.resolved.val)) {
.@"extern" => |@"extern"| try self.getGlobalSymbol(
elf_file,
nav.name.toSlice(ip),
@"extern".lib_name.toSlice(ip),
),
else => try self.getOrCreateMetadataForNav(zcu, nav_index),
};
const this_sym_index = if (nav.getExtern(ip)) |@"extern"| try self.getGlobalSymbol(
elf_file,
nav.name.toSlice(ip),
@"extern".lib_name.toSlice(ip),
) else try self.getOrCreateMetadataForNav(zcu, nav_index);
const this_sym = self.symbol(this_sym_index);
const vaddr = this_sym.address(.{}, elf_file);
switch (reloc_info.parent) {
@ -1107,15 +1104,13 @@ pub fn freeNav(self: *ZigObject, elf_file: *Elf, nav_index: InternPool.Nav.Index
pub fn getOrCreateMetadataForNav(self: *ZigObject, zcu: *Zcu, nav_index: InternPool.Nav.Index) !Symbol.Index {
const gpa = zcu.gpa;
const ip = &zcu.intern_pool;
const gop = try self.navs.getOrPut(gpa, nav_index);
if (!gop.found_existing) {
const symbol_index = try self.newSymbolWithAtom(gpa, 0);
const nav_val = Value.fromInterned(zcu.intern_pool.getNav(nav_index).status.resolved.val);
const sym = self.symbol(symbol_index);
if (nav_val.getVariable(zcu)) |variable| {
if (variable.is_threadlocal and zcu.comp.config.any_non_single_threaded) {
sym.flags.is_tls = true;
}
if (ip.getNav(nav_index).isThreadlocal(ip) and zcu.comp.config.any_non_single_threaded) {
sym.flags.is_tls = true;
}
gop.value_ptr.* = .{ .symbol_index = symbol_index };
}
@ -1547,7 +1542,7 @@ pub fn updateNav(
log.debug("updateNav {}({d})", .{ nav.fqn.fmt(ip), nav_index });
const nav_init = switch (ip.indexToKey(nav.status.resolved.val)) {
const nav_init = switch (ip.indexToKey(nav.status.fully_resolved.val)) {
.func => .none,
.variable => |variable| variable.init,
.@"extern" => |@"extern"| {
@ -1560,7 +1555,7 @@ pub fn updateNav(
self.symbol(sym_index).flags.is_extern_ptr = true;
return;
},
else => nav.status.resolved.val,
else => nav.status.fully_resolved.val,
};
if (nav_init != .none and Value.fromInterned(nav_init).typeOf(zcu).hasRuntimeBits(zcu)) {

View File

@ -608,14 +608,11 @@ pub fn getNavVAddr(
const ip = &zcu.intern_pool;
const nav = ip.getNav(nav_index);
log.debug("getNavVAddr {}({d})", .{ nav.fqn.fmt(ip), nav_index });
const sym_index = switch (ip.indexToKey(nav.status.resolved.val)) {
.@"extern" => |@"extern"| try self.getGlobalSymbol(
macho_file,
nav.name.toSlice(ip),
@"extern".lib_name.toSlice(ip),
),
else => try self.getOrCreateMetadataForNav(macho_file, nav_index),
};
const sym_index = if (nav.getExtern(ip)) |@"extern"| try self.getGlobalSymbol(
macho_file,
nav.name.toSlice(ip),
@"extern".lib_name.toSlice(ip),
) else try self.getOrCreateMetadataForNav(macho_file, nav_index);
const sym = self.symbols.items[sym_index];
const vaddr = sym.getAddress(.{}, macho_file);
switch (reloc_info.parent) {
@ -882,7 +879,7 @@ pub fn updateNav(
const ip = &zcu.intern_pool;
const nav = ip.getNav(nav_index);
const nav_init = switch (ip.indexToKey(nav.status.resolved.val)) {
const nav_init = switch (ip.indexToKey(nav.status.fully_resolved.val)) {
.func => .none,
.variable => |variable| variable.init,
.@"extern" => |@"extern"| {
@ -895,7 +892,7 @@ pub fn updateNav(
sym.flags.is_extern_ptr = true;
return;
},
else => nav.status.resolved.val,
else => nav.status.fully_resolved.val,
};
if (nav_init != .none and Value.fromInterned(nav_init).typeOf(zcu).hasRuntimeBits(zcu)) {
@ -1561,11 +1558,7 @@ fn isThreadlocal(macho_file: *MachO, nav_index: InternPool.Nav.Index) bool {
if (!macho_file.base.comp.config.any_non_single_threaded)
return false;
const ip = &macho_file.base.comp.zcu.?.intern_pool;
return switch (ip.indexToKey(ip.getNav(nav_index).status.resolved.val)) {
.variable => |variable| variable.is_threadlocal,
.@"extern" => |@"extern"| @"extern".is_threadlocal,
else => false,
};
return ip.getNav(nav_index).isThreadlocal(ip);
}
fn addAtom(self: *ZigObject, allocator: Allocator) !Atom.Index {

View File

@ -1021,7 +1021,7 @@ pub fn seeNav(self: *Plan9, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index)
const atom_idx = gop.value_ptr.index;
// handle externs here because they might not get updateDecl called on them
const nav = ip.getNav(nav_index);
if (ip.indexToKey(nav.status.resolved.val) == .@"extern") {
if (nav.getExtern(ip) != null) {
// this is a "phantom atom" - it is never actually written to disk, just convenient for us to store stuff about externs
if (nav.name.eqlSlice("etext", ip)) {
self.etext_edata_end_atom_indices[0] = atom_idx;
@ -1370,7 +1370,7 @@ pub fn getNavVAddr(
const ip = &pt.zcu.intern_pool;
const nav = ip.getNav(nav_index);
log.debug("getDeclVAddr for {}", .{nav.name.fmt(ip)});
if (ip.indexToKey(nav.status.resolved.val) == .@"extern") {
if (nav.getExtern(ip) != null) {
if (nav.name.eqlSlice("etext", ip)) {
try self.addReloc(reloc_info.parent.atom_index, .{
.target = undefined,

View File

@ -241,7 +241,7 @@ pub fn updateNav(
const nav_val = zcu.navValue(nav_index);
const is_extern, const lib_name, const nav_init = switch (ip.indexToKey(nav_val.toIntern())) {
.variable => |variable| .{ false, variable.lib_name, Value.fromInterned(variable.init) },
.variable => |variable| .{ false, .none, Value.fromInterned(variable.init) },
.func => return,
.@"extern" => |@"extern"| if (ip.isFunctionType(nav.typeOf(ip)))
return
@ -734,15 +734,14 @@ pub fn getNavVAddr(
const target_atom_index = try zig_object.getOrCreateAtomForNav(wasm, pt, nav_index);
const target_atom = wasm.getAtom(target_atom_index);
const target_symbol_index = @intFromEnum(target_atom.sym_index);
switch (ip.indexToKey(nav.status.resolved.val)) {
.@"extern" => |@"extern"| try zig_object.addOrUpdateImport(
if (nav.getExtern(ip)) |@"extern"| {
try zig_object.addOrUpdateImport(
wasm,
nav.name.toSlice(ip),
target_atom.sym_index,
@"extern".lib_name.toSlice(ip),
null,
),
else => {},
);
}
std.debug.assert(reloc_info.parent.atom_index != 0);
@ -945,8 +944,8 @@ pub fn freeNav(zig_object: *ZigObject, wasm: *Wasm, nav_index: InternPool.Nav.In
segment.name = &.{}; // Ensure no accidental double free
}
const nav_val = zcu.navValue(nav_index).toIntern();
if (ip.indexToKey(nav_val) == .@"extern") {
const nav = ip.getNav(nav_index);
if (nav.getExtern(ip) != null) {
std.debug.assert(zig_object.imports.remove(atom.sym_index));
}
std.debug.assert(wasm.symbol_atom.remove(atom.symbolLoc()));
@ -960,7 +959,7 @@ pub fn freeNav(zig_object: *ZigObject, wasm: *Wasm, nav_index: InternPool.Nav.In
if (sym.isGlobal()) {
std.debug.assert(zig_object.global_syms.remove(atom.sym_index));
}
if (ip.isFunctionType(ip.typeOf(nav_val))) {
if (ip.isFunctionType(nav.typeOf(ip))) {
zig_object.functions_free_list.append(gpa, sym.index) catch {};
std.debug.assert(zig_object.atom_types.remove(atom_index));
} else {

View File

@ -542,7 +542,6 @@ const Writer = struct {
.@"asm" => try self.writeAsm(stream, extended, false),
.asm_expr => try self.writeAsm(stream, extended, true),
.variable => try self.writeVarExtended(stream, extended),
.alloc => try self.writeAllocExtended(stream, extended),
.compile_log => try self.writeNodeMultiOp(stream, extended),
@ -2347,7 +2346,6 @@ const Writer = struct {
inferred_error_set,
false,
false,
false,
.none,
&.{},
@ -2371,13 +2369,6 @@ const Writer = struct {
var ret_ty_ref: Zir.Inst.Ref = .none;
var ret_ty_body: []const Zir.Inst.Index = &.{};
if (extra.data.bits.has_lib_name) {
const lib_name = self.code.nullTerminatedString(@enumFromInt(self.code.extra[extra_index]));
extra_index += 1;
try stream.print("lib_name=\"{}\", ", .{std.zig.fmtEscapes(lib_name)});
}
try self.writeFlag(stream, "test, ", extra.data.bits.is_test);
if (extra.data.bits.has_cc_body) {
const body_len = self.code.extra[extra_index];
extra_index += 1;
@ -2414,7 +2405,6 @@ const Writer = struct {
stream,
extra.data.bits.is_inferred_error,
extra.data.bits.is_var_args,
extra.data.bits.is_extern,
extra.data.bits.is_noinline,
cc_ref,
cc_body,
@ -2427,36 +2417,6 @@ const Writer = struct {
);
}
fn writeVarExtended(self: *Writer, stream: anytype, extended: Zir.Inst.Extended.InstData) !void {
const extra = self.code.extraData(Zir.Inst.ExtendedVar, extended.operand);
const small = @as(Zir.Inst.ExtendedVar.Small, @bitCast(extended.small));
try self.writeInstRef(stream, extra.data.var_type);
var extra_index: usize = extra.end;
if (small.has_lib_name) {
const lib_name_index: Zir.NullTerminatedString = @enumFromInt(self.code.extra[extra_index]);
const lib_name = self.code.nullTerminatedString(lib_name_index);
extra_index += 1;
try stream.print(", lib_name=\"{}\"", .{std.zig.fmtEscapes(lib_name)});
}
const align_inst: Zir.Inst.Ref = if (!small.has_align) .none else blk: {
const align_inst = @as(Zir.Inst.Ref, @enumFromInt(self.code.extra[extra_index]));
extra_index += 1;
break :blk align_inst;
};
const init_inst: Zir.Inst.Ref = if (!small.has_init) .none else blk: {
const init_inst = @as(Zir.Inst.Ref, @enumFromInt(self.code.extra[extra_index]));
extra_index += 1;
break :blk init_inst;
};
try self.writeFlag(stream, ", is_extern", small.is_extern);
try self.writeFlag(stream, ", is_threadlocal", small.is_threadlocal);
try self.writeOptionalInstRef(stream, ", align=", align_inst);
try self.writeOptionalInstRef(stream, ", init=", init_inst);
try stream.writeAll("))");
}
fn writeAllocExtended(self: *Writer, stream: anytype, extended: Zir.Inst.Extended.InstData) !void {
const extra = self.code.extraData(Zir.Inst.AllocExtended, extended.operand);
const small = @as(Zir.Inst.AllocExtended.Small, @bitCast(extended.small));
@ -2604,7 +2564,6 @@ const Writer = struct {
stream: anytype,
inferred_error_set: bool,
var_args: bool,
is_extern: bool,
is_noinline: bool,
cc_ref: Zir.Inst.Ref,
cc_body: []const Zir.Inst.Index,
@ -2618,7 +2577,6 @@ const Writer = struct {
try self.writeOptionalInstRefOrBody(stream, "cc=", cc_ref, cc_body);
try self.writeOptionalInstRefOrBody(stream, "ret_ty=", ret_ty_ref, ret_ty_body);
try self.writeFlag(stream, "vargs, ", var_args);
try self.writeFlag(stream, "extern, ", is_extern);
try self.writeFlag(stream, "inferror, ", inferred_error_set);
try self.writeFlag(stream, "noinline, ", is_noinline);
@ -2664,56 +2622,58 @@ const Writer = struct {
}
fn writeDeclaration(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].declaration;
const extra = self.code.extraData(Zir.Inst.Declaration, inst_data.payload_index);
const decl = self.code.getDeclaration(inst);
const prev_parent_decl_node = self.parent_decl_node;
defer self.parent_decl_node = prev_parent_decl_node;
self.parent_decl_node = inst_data.src_node;
self.parent_decl_node = decl.src_node;
if (extra.data.flags.is_pub) try stream.writeAll("pub ");
if (extra.data.flags.is_export) try stream.writeAll("export ");
switch (extra.data.name) {
if (decl.is_pub) try stream.writeAll("pub ");
switch (decl.linkage) {
.normal => {},
.@"export" => try stream.writeAll("export "),
.@"extern" => try stream.writeAll("extern "),
}
switch (decl.kind) {
.@"comptime" => try stream.writeAll("comptime"),
.@"usingnamespace" => try stream.writeAll("usingnamespace"),
.unnamed_test => try stream.writeAll("test"),
_ => {
const name = extra.data.name.toString(self.code).?;
const prefix = if (extra.data.name.isNamedTest(self.code)) p: {
break :p if (extra.data.flags.test_is_decltest) "decltest " else "test ";
} else "";
try stream.print("{s}'{s}'", .{ prefix, self.code.nullTerminatedString(name) });
.@"test", .decltest, .@"const", .@"var" => {
try stream.print("{s} '{s}'", .{ @tagName(decl.kind), self.code.nullTerminatedString(decl.name) });
},
}
const src_hash_arr: [4]u32 = .{
extra.data.src_hash_0,
extra.data.src_hash_1,
extra.data.src_hash_2,
extra.data.src_hash_3,
};
const src_hash_bytes: [16]u8 = @bitCast(src_hash_arr);
try stream.print(" line({d}) hash({})", .{ extra.data.src_line, std.fmt.fmtSliceHexLower(&src_hash_bytes) });
const src_hash = self.code.getAssociatedSrcHash(inst).?;
try stream.print(" line({d}) column({d}) hash({})", .{
decl.src_line,
decl.src_column,
std.fmt.fmtSliceHexLower(&src_hash),
});
{
const bodies = extra.data.getBodies(@intCast(extra.end), self.code);
if (decl.type_body) |b| {
try stream.writeAll(" type=");
try self.writeBracedDecl(stream, b);
}
try stream.writeAll(" value=");
try self.writeBracedDecl(stream, bodies.value_body);
if (bodies.align_body) |b| {
if (decl.align_body) |b| {
try stream.writeAll(" align=");
try self.writeBracedDecl(stream, b);
}
if (bodies.linksection_body) |b| {
if (decl.linksection_body) |b| {
try stream.writeAll(" linksection=");
try self.writeBracedDecl(stream, b);
}
if (bodies.addrspace_body) |b| {
if (decl.addrspace_body) |b| {
try stream.writeAll(" addrspace=");
try self.writeBracedDecl(stream, b);
}
if (decl.value_body) |b| {
try stream.writeAll(" value=");
try self.writeBracedDecl(stream, b);
}
}
try stream.writeAll(") ");

View File

@ -66,3 +66,99 @@ test "global loads can affect liveness" {
S.f();
try std.testing.expect(y.a == 1);
}
test "global const can be self-referential" {
const S = struct {
self: *const @This(),
x: u32,
const foo: @This() = .{ .self = &foo, .x = 123 };
};
try std.testing.expect(S.foo.x == 123);
try std.testing.expect(S.foo.self.x == 123);
try std.testing.expect(S.foo.self.self.x == 123);
try std.testing.expect(S.foo.self == &S.foo);
try std.testing.expect(S.foo.self.self == &S.foo);
}
test "global var can be self-referential" {
const S = struct {
self: *@This(),
x: u32,
var foo: @This() = .{ .self = &foo, .x = undefined };
};
S.foo.x = 123;
try std.testing.expect(S.foo.x == 123);
try std.testing.expect(S.foo.self.x == 123);
try std.testing.expect(S.foo.self == &S.foo);
S.foo.self.x = 456;
try std.testing.expect(S.foo.x == 456);
try std.testing.expect(S.foo.self.x == 456);
try std.testing.expect(S.foo.self == &S.foo);
S.foo.self.self.x = 789;
try std.testing.expect(S.foo.x == 789);
try std.testing.expect(S.foo.self.x == 789);
try std.testing.expect(S.foo.self == &S.foo);
}
test "global const can be indirectly self-referential" {
const S = struct {
other: *const @This(),
x: u32,
const foo: @This() = .{ .other = &bar, .x = 123 };
const bar: @This() = .{ .other = &foo, .x = 456 };
};
try std.testing.expect(S.foo.x == 123);
try std.testing.expect(S.foo.other.x == 456);
try std.testing.expect(S.foo.other.other.x == 123);
try std.testing.expect(S.foo.other.other.other.x == 456);
try std.testing.expect(S.foo.other == &S.bar);
try std.testing.expect(S.foo.other.other == &S.foo);
try std.testing.expect(S.bar.x == 456);
try std.testing.expect(S.bar.other.x == 123);
try std.testing.expect(S.bar.other.other.x == 456);
try std.testing.expect(S.bar.other.other.other.x == 123);
try std.testing.expect(S.bar.other == &S.foo);
try std.testing.expect(S.bar.other.other == &S.bar);
}
test "global var can be indirectly self-referential" {
const S = struct {
other: *@This(),
x: u32,
var foo: @This() = .{ .other = &bar, .x = undefined };
var bar: @This() = .{ .other = &foo, .x = undefined };
};
S.foo.other.x = 123; // bar.x
S.foo.other.other.x = 456; // foo.x
try std.testing.expect(S.foo.x == 456);
try std.testing.expect(S.foo.other.x == 123);
try std.testing.expect(S.foo.other.other.x == 456);
try std.testing.expect(S.foo.other.other.other.x == 123);
try std.testing.expect(S.foo.other == &S.bar);
try std.testing.expect(S.foo.other.other == &S.foo);
S.bar.other.x = 111; // foo.x
S.bar.other.other.x = 222; // bar.x
try std.testing.expect(S.bar.x == 222);
try std.testing.expect(S.bar.other.x == 111);
try std.testing.expect(S.bar.other.other.x == 222);
try std.testing.expect(S.bar.other.other.other.x == 111);
try std.testing.expect(S.bar.other == &S.foo);
try std.testing.expect(S.bar.other.other == &S.bar);
}

View File

@ -10,4 +10,5 @@ pub export fn entry() void {
// target=native
//
// :2:36: error: unable to resolve comptime value
// :2:36: note: container level variable initializers must be comptime-known
// :2:36: note: global variable initializer must be comptime-known
// :2:36: note: thread local and dll imported variables have runtime-known addresses

View File

@ -0,0 +1,11 @@
const S = struct { self: *S, x: u32 };
const s: S = .{ .self = &s, .x = 123 };
comptime {
_ = s;
}
// error
//
// :2:18: error: expected type '*tmp.S', found '*const tmp.S'
// :2:18: note: cast discards const qualifier

View File

@ -7,5 +7,5 @@ export fn entry() foo {
// backend=stage2
// target=native
//
// :1:5: error: variable of type 'type' must be const or comptime
// :1:5: note: types are not available at runtime
// :1:11: error: variable of type 'type' must be const or comptime
// :1:11: note: types are not available at runtime

View File

@ -8,5 +8,5 @@ export fn entry() void {
// backend=stage2
// target=native
//
// :1:5: error: variable of type 'comptime_int' must be const or comptime
// :1:5: note: to modify this variable at runtime, it must be given an explicit fixed-size number type
// :1:9: error: variable of type 'comptime_int' must be const or comptime
// :1:9: note: to modify this variable at runtime, it must be given an explicit fixed-size number type