mirror of
https://github.com/ziglang/zig.git
synced 2024-11-27 07:32:44 +00:00
stage2: first pass at implementing usingnamespace
Ran into a design flaw here which will need to get solved by having AstGen annotate ZIR with which instructions are closed over.
This commit is contained in:
parent
c05a20fc8c
commit
332eafeb7f
@ -6416,6 +6416,9 @@ fn identifier(
|
||||
},
|
||||
.top => break,
|
||||
};
|
||||
if (found_already == null) {
|
||||
return astgen.failNode(ident, "use of undeclared identifier '{s}'", .{ident_name});
|
||||
}
|
||||
|
||||
// Decl references happen by name rather than ZIR index so that when unrelated
|
||||
// decls are modified, ZIR code containing references to them can be unmodified.
|
||||
@ -10052,7 +10055,7 @@ fn isPrimitive(name: []const u8) bool {
|
||||
}
|
||||
}
|
||||
|
||||
/// Local variables shadowing detection, including function parameters and primitives.
|
||||
/// Local variables shadowing detection, including function parameters.
|
||||
fn detectLocalShadowing(
|
||||
astgen: *AstGen,
|
||||
scope: *Scope,
|
||||
|
@ -365,6 +365,8 @@ pub const Decl = struct {
|
||||
/// Decl is marked alive, then it sends the Decl to the linker. Otherwise it
|
||||
/// deletes the Decl on the spot.
|
||||
alive: bool,
|
||||
/// Whether the Decl is a `usingnamespace` declaration.
|
||||
is_usingnamespace: bool,
|
||||
|
||||
/// Represents the position of the code in the output file.
|
||||
/// This is populated regardless of semantic analysis and code generation.
|
||||
@ -1008,6 +1010,11 @@ pub const Scope = struct {
|
||||
|
||||
anon_decls: std.AutoArrayHashMapUnmanaged(*Decl, void) = .{},
|
||||
|
||||
/// Key is usingnamespace Decl itself. To find the namespace being included,
|
||||
/// the Decl Value has to be resolved as a Type which has a Namespace.
|
||||
/// Value is whether the usingnamespace decl is marked `pub`.
|
||||
usingnamespace_set: std.AutoHashMapUnmanaged(*Decl, bool) = .{},
|
||||
|
||||
pub fn deinit(ns: *Namespace, mod: *Module) void {
|
||||
ns.destroyDecls(mod);
|
||||
ns.* = undefined;
|
||||
@ -3174,6 +3181,31 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
|
||||
errdefer decl_arena.deinit();
|
||||
const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State);
|
||||
|
||||
if (decl.is_usingnamespace) {
|
||||
const ty_ty = Type.initTag(.type);
|
||||
if (!decl_tv.ty.eql(ty_ty)) {
|
||||
return mod.fail(&block_scope.base, src, "expected type, found {}", .{decl_tv.ty});
|
||||
}
|
||||
var buffer: Value.ToTypeBuffer = undefined;
|
||||
const ty = decl_tv.val.toType(&buffer);
|
||||
if (ty.getNamespace() == null) {
|
||||
return mod.fail(&block_scope.base, src, "type {} has no namespace", .{ty});
|
||||
}
|
||||
|
||||
decl.ty = ty_ty;
|
||||
decl.val = try Value.Tag.ty.create(&decl_arena.allocator, ty);
|
||||
decl.align_val = Value.initTag(.null_value);
|
||||
decl.linksection_val = Value.initTag(.null_value);
|
||||
decl.has_tv = true;
|
||||
decl.owns_tv = false;
|
||||
decl_arena_state.* = decl_arena.state;
|
||||
decl.value_arena = decl_arena_state;
|
||||
decl.analysis = .complete;
|
||||
decl.generation = mod.generation;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (decl_tv.val.castTag(.function)) |fn_payload| {
|
||||
const func = fn_payload.data;
|
||||
const owns_tv = func.owner_decl == decl;
|
||||
@ -3269,16 +3301,13 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
|
||||
if (type_changed and mod.emit_h != null) {
|
||||
try mod.comp.work_queue.writeItem(.{ .emit_h_decl = decl });
|
||||
}
|
||||
} else if (decl_tv.ty.zigTypeTag() == .Type) {
|
||||
// In case this Decl is a struct or union, we need to resolve the fields
|
||||
// while we still have the `Sema` in scope, so that the field type expressions
|
||||
// can use the resolved AIR instructions that they possibly reference.
|
||||
// We do this after the decl is populated and set to `complete` so that a `Decl`
|
||||
// may reference itself.
|
||||
var buffer: Value.ToTypeBuffer = undefined;
|
||||
const ty = decl.val.toType(&buffer);
|
||||
try sema.resolveDeclFields(&block_scope, src, ty);
|
||||
}
|
||||
// In case this Decl is a struct or union, we need to resolve the fields
|
||||
// while we still have the `Sema` in scope, so that the field type expressions
|
||||
// can use the resolved AIR instructions that they possibly reference.
|
||||
// We do this after the decl is populated and set to `complete` so that a `Decl`
|
||||
// may reference itself.
|
||||
try sema.resolvePendingTypes(&block_scope);
|
||||
|
||||
if (decl.is_exported) {
|
||||
const export_src = src; // TODO point to the export token
|
||||
@ -3494,7 +3523,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi
|
||||
|
||||
// zig fmt: off
|
||||
const is_pub = (flags & 0b0001) != 0;
|
||||
const is_exported = (flags & 0b0010) != 0;
|
||||
const export_bit = (flags & 0b0010) != 0;
|
||||
const has_align = (flags & 0b0100) != 0;
|
||||
const has_linksection = (flags & 0b1000) != 0;
|
||||
// zig fmt: on
|
||||
@ -3509,7 +3538,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi
|
||||
var is_named_test = false;
|
||||
const decl_name: [:0]const u8 = switch (decl_name_index) {
|
||||
0 => name: {
|
||||
if (is_exported) {
|
||||
if (export_bit) {
|
||||
const i = iter.usingnamespace_index;
|
||||
iter.usingnamespace_index += 1;
|
||||
break :name try std.fmt.allocPrintZ(gpa, "usingnamespace_{d}", .{i});
|
||||
@ -3535,11 +3564,17 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi
|
||||
}
|
||||
},
|
||||
};
|
||||
const is_exported = export_bit and decl_name_index != 0;
|
||||
const is_usingnamespace = export_bit and decl_name_index == 0;
|
||||
if (is_usingnamespace) try namespace.usingnamespace_set.ensureUnusedCapacity(gpa, 1);
|
||||
|
||||
// We create a Decl for it regardless of analysis status.
|
||||
const gop = try namespace.decls.getOrPut(gpa, decl_name);
|
||||
if (!gop.found_existing) {
|
||||
const new_decl = try mod.allocateNewDecl(namespace, decl_node);
|
||||
if (is_usingnamespace) {
|
||||
namespace.usingnamespace_set.putAssumeCapacity(new_decl, is_pub);
|
||||
}
|
||||
log.debug("scan new {*} ({s}) into {*}", .{ new_decl, decl_name, namespace });
|
||||
new_decl.src_line = line;
|
||||
new_decl.name = decl_name;
|
||||
@ -3548,7 +3583,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi
|
||||
// test decls if in test mode, get analyzed.
|
||||
const decl_pkg = namespace.file_scope.pkg;
|
||||
const want_analysis = is_exported or switch (decl_name_index) {
|
||||
0 => true, // comptime decl
|
||||
0 => true, // comptime or usingnamespace decl
|
||||
1 => blk: {
|
||||
// test decl with no name. Skip the part where we check against
|
||||
// the test name filter.
|
||||
@ -3571,6 +3606,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi
|
||||
}
|
||||
new_decl.is_pub = is_pub;
|
||||
new_decl.is_exported = is_exported;
|
||||
new_decl.is_usingnamespace = is_usingnamespace;
|
||||
new_decl.has_align = has_align;
|
||||
new_decl.has_linksection = has_linksection;
|
||||
new_decl.zir_decl_index = @intCast(u32, decl_sub_index);
|
||||
@ -3587,6 +3623,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) SemaError!voi
|
||||
|
||||
decl.is_pub = is_pub;
|
||||
decl.is_exported = is_exported;
|
||||
decl.is_usingnamespace = is_usingnamespace;
|
||||
decl.has_align = has_align;
|
||||
decl.has_linksection = has_linksection;
|
||||
decl.zir_decl_index = @intCast(u32, decl_sub_index);
|
||||
@ -3979,6 +4016,7 @@ pub fn allocateNewDecl(mod: *Module, namespace: *Scope.Namespace, src_node: ast.
|
||||
.has_linksection = false,
|
||||
.has_align = false,
|
||||
.alive = false,
|
||||
.is_usingnamespace = false,
|
||||
};
|
||||
return new_decl;
|
||||
}
|
||||
|
115
src/Sema.zig
115
src/Sema.zig
@ -58,6 +58,9 @@ comptime_args_fn_inst: Zir.Inst.Index = 0,
|
||||
/// extra hash table lookup in the `monomorphed_funcs` set.
|
||||
/// Sema will set this to null when it takes ownership.
|
||||
preallocated_new_func: ?*Module.Fn = null,
|
||||
/// Collects struct, union, enum, and opaque decls which need to have their
|
||||
/// fields resolved before this Sema is deinitialized.
|
||||
types_pending_resolution: std.ArrayListUnmanaged(Type) = .{},
|
||||
|
||||
const std = @import("std");
|
||||
const mem = std.mem;
|
||||
@ -90,6 +93,7 @@ pub fn deinit(sema: *Sema) void {
|
||||
sema.air_values.deinit(gpa);
|
||||
sema.inst_map.deinit(gpa);
|
||||
sema.decl_val_table.deinit(gpa);
|
||||
sema.types_pending_resolution.deinit(gpa);
|
||||
sema.* = undefined;
|
||||
}
|
||||
|
||||
@ -908,7 +912,9 @@ fn zirStructDecl(
|
||||
&struct_obj.namespace, new_decl, new_decl.name,
|
||||
});
|
||||
try sema.analyzeStructDecl(new_decl, inst, struct_obj);
|
||||
try sema.types_pending_resolution.ensureUnusedCapacity(sema.gpa, 1);
|
||||
try new_decl.finalizeNewArena(&new_decl_arena);
|
||||
sema.types_pending_resolution.appendAssumeCapacity(struct_ty);
|
||||
return sema.analyzeDeclVal(block, src, new_decl);
|
||||
}
|
||||
|
||||
@ -1198,7 +1204,9 @@ fn zirUnionDecl(
|
||||
|
||||
_ = try sema.mod.scanNamespace(&union_obj.namespace, extra_index, decls_len, new_decl);
|
||||
|
||||
try sema.types_pending_resolution.ensureUnusedCapacity(sema.gpa, 1);
|
||||
try new_decl.finalizeNewArena(&new_decl_arena);
|
||||
sema.types_pending_resolution.appendAssumeCapacity(union_ty);
|
||||
return sema.analyzeDeclVal(block, src, new_decl);
|
||||
}
|
||||
|
||||
@ -2324,42 +2332,105 @@ fn zirDeclVal(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErr
|
||||
}
|
||||
|
||||
fn lookupIdentifier(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, name: []const u8) !*Decl {
|
||||
// TODO emit a compile error if more than one decl would be matched.
|
||||
var namespace = sema.namespace;
|
||||
while (true) {
|
||||
if (try sema.lookupInNamespace(namespace, name)) |decl| {
|
||||
if (try sema.lookupInNamespace(block, src, namespace, name, false)) |decl| {
|
||||
return decl;
|
||||
}
|
||||
namespace = namespace.parent orelse break;
|
||||
}
|
||||
return sema.mod.fail(&block.base, src, "use of undeclared identifier '{s}'", .{name});
|
||||
unreachable; // AstGen detects use of undeclared identifier errors.
|
||||
}
|
||||
|
||||
/// This looks up a member of a specific namespace. It is affected by `usingnamespace` but
|
||||
/// only for ones in the specified namespace.
|
||||
fn lookupInNamespace(
|
||||
sema: *Sema,
|
||||
block: *Scope.Block,
|
||||
src: LazySrcLoc,
|
||||
namespace: *Scope.Namespace,
|
||||
ident_name: []const u8,
|
||||
observe_usingnamespace: bool,
|
||||
) CompileError!?*Decl {
|
||||
const mod = sema.mod;
|
||||
|
||||
const namespace_decl = namespace.getDecl();
|
||||
if (namespace_decl.analysis == .file_failure) {
|
||||
try sema.mod.declareDeclDependency(sema.owner_decl, namespace_decl);
|
||||
try mod.declareDeclDependency(sema.owner_decl, namespace_decl);
|
||||
return error.AnalysisFail;
|
||||
}
|
||||
|
||||
// TODO implement usingnamespace
|
||||
if (namespace.decls.get(ident_name)) |decl| {
|
||||
try sema.mod.declareDeclDependency(sema.owner_decl, decl);
|
||||
if (observe_usingnamespace and namespace.usingnamespace_set.count() != 0) {
|
||||
const src_file = block.src_decl.namespace.file_scope;
|
||||
|
||||
const gpa = sema.gpa;
|
||||
var checked_namespaces: std.AutoArrayHashMapUnmanaged(*Scope.Namespace, void) = .{};
|
||||
defer checked_namespaces.deinit(gpa);
|
||||
|
||||
// Keep track of name conflicts for error notes.
|
||||
var candidates: std.ArrayListUnmanaged(*Decl) = .{};
|
||||
defer candidates.deinit(gpa);
|
||||
|
||||
try checked_namespaces.put(gpa, namespace, {});
|
||||
var check_i: usize = 0;
|
||||
|
||||
while (check_i < checked_namespaces.count()) : (check_i += 1) {
|
||||
const check_ns = checked_namespaces.keys()[check_i];
|
||||
if (check_ns.decls.get(ident_name)) |decl| {
|
||||
// Skip decls which are not marked pub, which are in a different
|
||||
// file than the `a.b`/`@hasDecl` syntax.
|
||||
if (decl.is_pub or src_file == decl.namespace.file_scope) {
|
||||
try candidates.append(gpa, decl);
|
||||
}
|
||||
}
|
||||
var it = check_ns.usingnamespace_set.iterator();
|
||||
while (it.next()) |entry| {
|
||||
const sub_usingnamespace_decl = entry.key_ptr.*;
|
||||
const sub_is_pub = entry.value_ptr.*;
|
||||
if (!sub_is_pub and src_file != sub_usingnamespace_decl.namespace.file_scope) {
|
||||
// Skip usingnamespace decls which are not marked pub, which are in
|
||||
// a different file than the `a.b`/`@hasDecl` syntax.
|
||||
continue;
|
||||
}
|
||||
try sema.ensureDeclAnalyzed(sub_usingnamespace_decl);
|
||||
const ns_ty = sub_usingnamespace_decl.val.castTag(.ty).?.data;
|
||||
const sub_ns = ns_ty.getNamespace().?;
|
||||
try checked_namespaces.put(gpa, sub_ns, {});
|
||||
}
|
||||
}
|
||||
|
||||
switch (candidates.items.len) {
|
||||
0 => {},
|
||||
1 => {
|
||||
const decl = candidates.items[0];
|
||||
try mod.declareDeclDependency(sema.owner_decl, decl);
|
||||
return decl;
|
||||
},
|
||||
else => {
|
||||
const msg = msg: {
|
||||
const msg = try mod.errMsg(&block.base, src, "ambiguous reference", .{});
|
||||
errdefer msg.destroy(gpa);
|
||||
for (candidates.items) |candidate| {
|
||||
const src_loc = candidate.srcLoc();
|
||||
try mod.errNoteNonLazy(src_loc, msg, "declared here", .{});
|
||||
}
|
||||
break :msg msg;
|
||||
};
|
||||
return mod.failWithOwnedErrorMsg(&block.base, msg);
|
||||
},
|
||||
}
|
||||
} else if (namespace.decls.get(ident_name)) |decl| {
|
||||
try mod.declareDeclDependency(sema.owner_decl, decl);
|
||||
return decl;
|
||||
}
|
||||
|
||||
log.debug("{*} ({s}) depends on non-existence of '{s}' in {*} ({s})", .{
|
||||
sema.owner_decl, sema.owner_decl.name, ident_name, namespace_decl, namespace_decl.name,
|
||||
});
|
||||
// TODO This dependency is too strong. Really, it should only be a dependency
|
||||
// on the non-existence of `ident_name` in the namespace. We can lessen the number of
|
||||
// outdated declarations by making this dependency more sophisticated.
|
||||
try sema.mod.declareDeclDependency(sema.owner_decl, namespace_decl);
|
||||
try mod.declareDeclDependency(sema.owner_decl, namespace_decl);
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -2727,10 +2798,7 @@ fn analyzeCall(
|
||||
// we need to resolve the field type expressions right here, right now, while
|
||||
// the child `Sema` is still available, with the AIR instruction map intact,
|
||||
// because the field type expressions may reference into it.
|
||||
if (sema.typeOf(result).zigTypeTag() == .Type) {
|
||||
const ty = try sema.analyzeAsType(&child_block, call_src, result);
|
||||
try sema.resolveDeclFields(&child_block, call_src, ty);
|
||||
}
|
||||
try sema.resolvePendingTypes(&child_block);
|
||||
}
|
||||
|
||||
break :res2 result;
|
||||
@ -5332,6 +5400,7 @@ fn zirHasField(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr
|
||||
fn zirHasDecl(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
|
||||
const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
|
||||
const src = inst_data.src();
|
||||
const lhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
|
||||
const rhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
|
||||
const container_type = try sema.resolveType(block, lhs_src, extra.lhs);
|
||||
@ -5344,7 +5413,7 @@ fn zirHasDecl(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErr
|
||||
"expected struct, enum, union, or opaque, found '{}'",
|
||||
.{container_type},
|
||||
);
|
||||
if (try sema.lookupInNamespace(namespace, decl_name)) |decl| {
|
||||
if (try sema.lookupInNamespace(block, src, namespace, decl_name, true)) |decl| {
|
||||
if (decl.is_pub or decl.namespace.file_scope == block.base.namespace().file_scope) {
|
||||
return Air.Inst.Ref.bool_true;
|
||||
}
|
||||
@ -8203,7 +8272,7 @@ fn namespaceLookup(
|
||||
) CompileError!?*Decl {
|
||||
const mod = sema.mod;
|
||||
const gpa = sema.gpa;
|
||||
if (try sema.lookupInNamespace(namespace, decl_name)) |decl| {
|
||||
if (try sema.lookupInNamespace(block, src, namespace, decl_name, true)) |decl| {
|
||||
if (!decl.is_pub and decl.namespace.file_scope != block.getFileScope()) {
|
||||
const msg = msg: {
|
||||
const msg = try mod.errMsg(&block.base, src, "'{s}' is not marked 'pub'", .{
|
||||
@ -8919,8 +8988,7 @@ fn analyzeDeclVal(
|
||||
return result;
|
||||
}
|
||||
|
||||
fn analyzeDeclRef(sema: *Sema, decl: *Decl) CompileError!Air.Inst.Ref {
|
||||
try sema.mod.declareDeclDependency(sema.owner_decl, decl);
|
||||
fn ensureDeclAnalyzed(sema: *Sema, decl: *Decl) CompileError!void {
|
||||
sema.mod.ensureDeclAnalyzed(decl) catch |err| {
|
||||
if (sema.owner_func) |owner_func| {
|
||||
owner_func.state = .dependency_failure;
|
||||
@ -8929,6 +8997,11 @@ fn analyzeDeclRef(sema: *Sema, decl: *Decl) CompileError!Air.Inst.Ref {
|
||||
}
|
||||
return err;
|
||||
};
|
||||
}
|
||||
|
||||
fn analyzeDeclRef(sema: *Sema, decl: *Decl) CompileError!Air.Inst.Ref {
|
||||
try sema.mod.declareDeclDependency(sema.owner_decl, decl);
|
||||
try sema.ensureDeclAnalyzed(decl);
|
||||
|
||||
const decl_tv = try decl.typedValue();
|
||||
if (decl_tv.val.castTag(.variable)) |payload| {
|
||||
@ -9560,6 +9633,16 @@ pub fn resolveTypeLayout(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolvePendingTypes(sema: *Sema, block: *Scope.Block) !void {
|
||||
for (sema.types_pending_resolution.items) |ty| {
|
||||
// If an error happens resolving the fields of a struct, it will be marked
|
||||
// invalid and a proper compile error set up. But we should still look at the
|
||||
// other types pending resolution.
|
||||
const src: LazySrcLoc = .{ .node_offset = 0 };
|
||||
sema.resolveDeclFields(block, src, ty) catch continue;
|
||||
}
|
||||
}
|
||||
|
||||
/// `sema` and `block` are expected to be the same ones used for the `Decl`.
|
||||
pub fn resolveDeclFields(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty: Type) !void {
|
||||
switch (ty.tag()) {
|
||||
|
@ -899,7 +899,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
fn dbgSetPrologueEnd(self: *Self) InnerError!void {
|
||||
switch (self.debug_output) {
|
||||
.dwarf => |dbg_out| {
|
||||
try dbg_out.dbg_line.append(DW.LNS_set_prologue_end);
|
||||
try dbg_out.dbg_line.append(DW.LNS.set_prologue_end);
|
||||
try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column);
|
||||
},
|
||||
.none => {},
|
||||
@ -909,7 +909,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
fn dbgSetEpilogueBegin(self: *Self) InnerError!void {
|
||||
switch (self.debug_output) {
|
||||
.dwarf => |dbg_out| {
|
||||
try dbg_out.dbg_line.append(DW.LNS_set_epilogue_begin);
|
||||
try dbg_out.dbg_line.append(DW.LNS.set_epilogue_begin);
|
||||
try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column);
|
||||
},
|
||||
.none => {},
|
||||
@ -925,13 +925,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
// It lets you emit single-byte opcodes that add different numbers to
|
||||
// both the PC and the line number at the same time.
|
||||
try dbg_out.dbg_line.ensureUnusedCapacity(11);
|
||||
dbg_out.dbg_line.appendAssumeCapacity(DW.LNS_advance_pc);
|
||||
dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.advance_pc);
|
||||
leb128.writeULEB128(dbg_out.dbg_line.writer(), delta_pc) catch unreachable;
|
||||
if (delta_line != 0) {
|
||||
dbg_out.dbg_line.appendAssumeCapacity(DW.LNS_advance_line);
|
||||
dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.advance_line);
|
||||
leb128.writeILEB128(dbg_out.dbg_line.writer(), delta_line) catch unreachable;
|
||||
}
|
||||
dbg_out.dbg_line.appendAssumeCapacity(DW.LNS_copy);
|
||||
dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.copy);
|
||||
},
|
||||
.none => {},
|
||||
}
|
||||
@ -1010,7 +1010,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
.dwarf => |dbg_out| {
|
||||
assert(ty.hasCodeGenBits());
|
||||
const index = dbg_out.dbg_info.items.len;
|
||||
try dbg_out.dbg_info.resize(index + 4); // DW.AT_type, DW.FORM_ref4
|
||||
try dbg_out.dbg_info.resize(index + 4); // DW.AT.type, DW.FORM.ref4
|
||||
|
||||
const gop = try dbg_out.dbg_info_type_relocs.getOrPut(self.gpa, ty);
|
||||
if (!gop.found_existing) {
|
||||
@ -2438,13 +2438,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
.dwarf => |dbg_out| {
|
||||
try dbg_out.dbg_info.ensureCapacity(dbg_out.dbg_info.items.len + 3);
|
||||
dbg_out.dbg_info.appendAssumeCapacity(link.File.Elf.abbrev_parameter);
|
||||
dbg_out.dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT_location, DW.FORM_exprloc
|
||||
dbg_out.dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc
|
||||
1, // ULEB128 dwarf expression length
|
||||
reg.dwarfLocOp(),
|
||||
});
|
||||
try dbg_out.dbg_info.ensureCapacity(dbg_out.dbg_info.items.len + 5 + name_with_null.len);
|
||||
try self.addDbgInfoTypeReloc(ty); // DW.AT_type, DW.FORM_ref4
|
||||
dbg_out.dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT_name, DW.FORM_string
|
||||
try self.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4
|
||||
dbg_out.dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string
|
||||
},
|
||||
.none => {},
|
||||
}
|
||||
@ -2467,15 +2467,15 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
var counting_writer = std.io.countingWriter(std.io.null_writer);
|
||||
leb128.writeILEB128(counting_writer.writer(), adjusted_stack_offset) catch unreachable;
|
||||
|
||||
// DW.AT_location, DW.FORM_exprloc
|
||||
// DW.AT.location, DW.FORM.exprloc
|
||||
// ULEB128 dwarf expression length
|
||||
try leb128.writeULEB128(dbg_out.dbg_info.writer(), counting_writer.bytes_written + 1);
|
||||
try dbg_out.dbg_info.append(DW.OP_breg11);
|
||||
try dbg_out.dbg_info.append(DW.OP.breg11);
|
||||
try leb128.writeILEB128(dbg_out.dbg_info.writer(), adjusted_stack_offset);
|
||||
|
||||
try dbg_out.dbg_info.ensureCapacity(dbg_out.dbg_info.items.len + 5 + name_with_null.len);
|
||||
try self.addDbgInfoTypeReloc(ty); // DW.AT_type, DW.FORM_ref4
|
||||
dbg_out.dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT_name, DW.FORM_string
|
||||
try self.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4
|
||||
dbg_out.dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ pub const Register = enum(u6) {
|
||||
}
|
||||
|
||||
pub fn dwarfLocOp(self: Register) u8 {
|
||||
return @as(u8, self.id()) + DW.OP_reg0;
|
||||
return @as(u8, self.id()) + DW.OP.reg0;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -170,7 +170,7 @@ pub const Register = enum(u5) {
|
||||
}
|
||||
|
||||
pub fn dwarfLocOp(self: Register) u8 {
|
||||
return @as(u8, self.id()) + DW.OP_reg0;
|
||||
return @as(u8, self.id()) + DW.OP.reg0;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -390,7 +390,7 @@ pub const RawRegister = enum(u5) {
|
||||
x24, x25, x26, x27, x28, x29, x30, x31,
|
||||
|
||||
pub fn dwarfLocOp(reg: RawRegister) u8 {
|
||||
return @enumToInt(reg) + DW.OP_reg0;
|
||||
return @enumToInt(reg) + DW.OP.reg0;
|
||||
}
|
||||
};
|
||||
|
||||
@ -424,7 +424,7 @@ pub const Register = enum(u5) {
|
||||
}
|
||||
|
||||
pub fn dwarfLocOp(reg: Register) u8 {
|
||||
return @as(u8, @enumToInt(reg)) + DW.OP_reg0;
|
||||
return @as(u8, @enumToInt(reg)) + DW.OP.reg0;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -59,14 +59,14 @@ pub const Register = enum(u8) {
|
||||
|
||||
pub fn dwarfLocOp(reg: Register) u8 {
|
||||
return switch (reg.to32()) {
|
||||
.eax => DW.OP_reg0,
|
||||
.ecx => DW.OP_reg1,
|
||||
.edx => DW.OP_reg2,
|
||||
.ebx => DW.OP_reg3,
|
||||
.esp => DW.OP_reg4,
|
||||
.ebp => DW.OP_reg5,
|
||||
.esi => DW.OP_reg6,
|
||||
.edi => DW.OP_reg7,
|
||||
.eax => DW.OP.reg0,
|
||||
.ecx => DW.OP.reg1,
|
||||
.edx => DW.OP.reg2,
|
||||
.ebx => DW.OP.reg3,
|
||||
.esp => DW.OP.reg4,
|
||||
.ebp => DW.OP.reg5,
|
||||
.esi => DW.OP.reg6,
|
||||
.edi => DW.OP.reg7,
|
||||
else => unreachable,
|
||||
};
|
||||
}
|
||||
|
@ -115,23 +115,23 @@ pub const Register = enum(u8) {
|
||||
|
||||
pub fn dwarfLocOp(self: Register) u8 {
|
||||
return switch (self.to64()) {
|
||||
.rax => DW.OP_reg0,
|
||||
.rdx => DW.OP_reg1,
|
||||
.rcx => DW.OP_reg2,
|
||||
.rbx => DW.OP_reg3,
|
||||
.rsi => DW.OP_reg4,
|
||||
.rdi => DW.OP_reg5,
|
||||
.rbp => DW.OP_reg6,
|
||||
.rsp => DW.OP_reg7,
|
||||
.rax => DW.OP.reg0,
|
||||
.rdx => DW.OP.reg1,
|
||||
.rcx => DW.OP.reg2,
|
||||
.rbx => DW.OP.reg3,
|
||||
.rsi => DW.OP.reg4,
|
||||
.rdi => DW.OP.reg5,
|
||||
.rbp => DW.OP.reg6,
|
||||
.rsp => DW.OP.reg7,
|
||||
|
||||
.r8 => DW.OP_reg8,
|
||||
.r9 => DW.OP_reg9,
|
||||
.r10 => DW.OP_reg10,
|
||||
.r11 => DW.OP_reg11,
|
||||
.r12 => DW.OP_reg12,
|
||||
.r13 => DW.OP_reg13,
|
||||
.r14 => DW.OP_reg14,
|
||||
.r15 => DW.OP_reg15,
|
||||
.r8 => DW.OP.reg8,
|
||||
.r9 => DW.OP.reg9,
|
||||
.r10 => DW.OP.reg10,
|
||||
.r11 => DW.OP.reg11,
|
||||
.r12 => DW.OP.reg12,
|
||||
.r13 => DW.OP.reg13,
|
||||
.r14 => DW.OP.reg14,
|
||||
.r15 => DW.OP.reg15,
|
||||
|
||||
else => unreachable,
|
||||
};
|
||||
|
@ -179,7 +179,7 @@ pub const File = struct {
|
||||
/// This is where the .debug_info tag for the type is.
|
||||
off: u32,
|
||||
/// Offset from `TextBlock.dbg_info_off` (the buffer that is local to a Decl).
|
||||
/// List of DW.AT_type / DW.FORM_ref4 that points to the type.
|
||||
/// List of DW.AT.type / DW.FORM.ref4 that points to the type.
|
||||
relocs: std.ArrayListUnmanaged(u32),
|
||||
};
|
||||
|
||||
|
152
src/link/Elf.zig
152
src/link/Elf.zig
@ -772,48 +772,48 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void {
|
||||
// These are LEB encoded but since the values are all less than 127
|
||||
// we can simply append these bytes.
|
||||
const abbrev_buf = [_]u8{
|
||||
abbrev_compile_unit, DW.TAG_compile_unit, DW.CHILDREN_yes, // header
|
||||
DW.AT_stmt_list, DW.FORM_sec_offset, DW.AT_low_pc,
|
||||
DW.FORM_addr, DW.AT_high_pc, DW.FORM_addr,
|
||||
DW.AT_name, DW.FORM_strp, DW.AT_comp_dir,
|
||||
DW.FORM_strp, DW.AT_producer, DW.FORM_strp,
|
||||
DW.AT_language, DW.FORM_data2, 0,
|
||||
abbrev_compile_unit, DW.TAG.compile_unit, DW.CHILDREN.yes, // header
|
||||
DW.AT.stmt_list, DW.FORM.sec_offset, DW.AT.low_pc,
|
||||
DW.FORM.addr, DW.AT.high_pc, DW.FORM.addr,
|
||||
DW.AT.name, DW.FORM.strp, DW.AT.comp_dir,
|
||||
DW.FORM.strp, DW.AT.producer, DW.FORM.strp,
|
||||
DW.AT.language, DW.FORM.data2, 0,
|
||||
0, // table sentinel
|
||||
abbrev_subprogram,
|
||||
DW.TAG_subprogram,
|
||||
DW.CHILDREN_yes, // header
|
||||
DW.AT_low_pc,
|
||||
DW.FORM_addr,
|
||||
DW.AT_high_pc,
|
||||
DW.FORM_data4,
|
||||
DW.AT_type,
|
||||
DW.FORM_ref4,
|
||||
DW.AT_name,
|
||||
DW.FORM_string,
|
||||
DW.TAG.subprogram,
|
||||
DW.CHILDREN.yes, // header
|
||||
DW.AT.low_pc,
|
||||
DW.FORM.addr,
|
||||
DW.AT.high_pc,
|
||||
DW.FORM.data4,
|
||||
DW.AT.type,
|
||||
DW.FORM.ref4,
|
||||
DW.AT.name,
|
||||
DW.FORM.string,
|
||||
0, 0, // table sentinel
|
||||
abbrev_subprogram_retvoid,
|
||||
DW.TAG_subprogram, DW.CHILDREN_yes, // header
|
||||
DW.AT_low_pc, DW.FORM_addr,
|
||||
DW.AT_high_pc, DW.FORM_data4,
|
||||
DW.AT_name, DW.FORM_string,
|
||||
DW.TAG.subprogram, DW.CHILDREN.yes, // header
|
||||
DW.AT.low_pc, DW.FORM.addr,
|
||||
DW.AT.high_pc, DW.FORM.data4,
|
||||
DW.AT.name, DW.FORM.string,
|
||||
0,
|
||||
0, // table sentinel
|
||||
abbrev_base_type,
|
||||
DW.TAG_base_type,
|
||||
DW.CHILDREN_no, // header
|
||||
DW.AT_encoding,
|
||||
DW.FORM_data1,
|
||||
DW.AT_byte_size,
|
||||
DW.FORM_data1,
|
||||
DW.AT_name,
|
||||
DW.FORM_string, 0, 0, // table sentinel
|
||||
abbrev_pad1, DW.TAG_unspecified_type, DW.CHILDREN_no, // header
|
||||
DW.TAG.base_type,
|
||||
DW.CHILDREN.no, // header
|
||||
DW.AT.encoding,
|
||||
DW.FORM.data1,
|
||||
DW.AT.byte_size,
|
||||
DW.FORM.data1,
|
||||
DW.AT.name,
|
||||
DW.FORM.string, 0, 0, // table sentinel
|
||||
abbrev_pad1, DW.TAG.unspecified_type, DW.CHILDREN.no, // header
|
||||
0, 0, // table sentinel
|
||||
abbrev_parameter,
|
||||
DW.TAG_formal_parameter, DW.CHILDREN_no, // header
|
||||
DW.AT_location, DW.FORM_exprloc,
|
||||
DW.AT_type, DW.FORM_ref4,
|
||||
DW.AT_name, DW.FORM_string,
|
||||
DW.TAG.formal_parameter, DW.CHILDREN.no, // header
|
||||
DW.AT.location, DW.FORM.exprloc,
|
||||
DW.AT.type, DW.FORM.ref4,
|
||||
DW.AT.name, DW.FORM.string,
|
||||
0,
|
||||
0, // table sentinel
|
||||
0,
|
||||
@ -897,7 +897,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void {
|
||||
const high_pc = text_phdr.p_vaddr + text_phdr.p_memsz;
|
||||
|
||||
di_buf.appendAssumeCapacity(abbrev_compile_unit);
|
||||
self.writeDwarfAddrAssumeCapacity(&di_buf, 0); // DW.AT_stmt_list, DW.FORM_sec_offset
|
||||
self.writeDwarfAddrAssumeCapacity(&di_buf, 0); // DW.AT.stmt_list, DW.FORM.sec_offset
|
||||
self.writeDwarfAddrAssumeCapacity(&di_buf, low_pc);
|
||||
self.writeDwarfAddrAssumeCapacity(&di_buf, high_pc);
|
||||
self.writeDwarfAddrAssumeCapacity(&di_buf, name_strp);
|
||||
@ -906,7 +906,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void {
|
||||
// We are still waiting on dwarf-std.org to assign DW_LANG_Zig a number:
|
||||
// http://dwarfstd.org/ShowIssue.php?issue=171115.1
|
||||
// Until then we say it is C99.
|
||||
mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), DW.LANG_C99, target_endian);
|
||||
mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), DW.LANG.C99, target_endian);
|
||||
|
||||
if (di_buf.items.len > first_dbg_info_decl.dbg_info_off) {
|
||||
// Move the first N decls to the end to make more padding for the header.
|
||||
@ -1030,7 +1030,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void {
|
||||
di_buf.items.len += ptr_width_bytes; // We will come back and write this.
|
||||
const after_header_len = di_buf.items.len;
|
||||
|
||||
const opcode_base = DW.LNS_set_isa + 1;
|
||||
const opcode_base = DW.LNS.set_isa + 1;
|
||||
di_buf.appendSliceAssumeCapacity(&[_]u8{
|
||||
1, // minimum_instruction_length
|
||||
1, // maximum_operations_per_instruction
|
||||
@ -1041,18 +1041,18 @@ pub fn flushModule(self: *Elf, comp: *Compilation) !void {
|
||||
|
||||
// Standard opcode lengths. The number of items here is based on `opcode_base`.
|
||||
// The value is the number of LEB128 operands the instruction takes.
|
||||
0, // `DW.LNS_copy`
|
||||
1, // `DW.LNS_advance_pc`
|
||||
1, // `DW.LNS_advance_line`
|
||||
1, // `DW.LNS_set_file`
|
||||
1, // `DW.LNS_set_column`
|
||||
0, // `DW.LNS_negate_stmt`
|
||||
0, // `DW.LNS_set_basic_block`
|
||||
0, // `DW.LNS_const_add_pc`
|
||||
1, // `DW.LNS_fixed_advance_pc`
|
||||
0, // `DW.LNS_set_prologue_end`
|
||||
0, // `DW.LNS_set_epilogue_begin`
|
||||
1, // `DW.LNS_set_isa`
|
||||
0, // `DW.LNS.copy`
|
||||
1, // `DW.LNS.advance_pc`
|
||||
1, // `DW.LNS.advance_line`
|
||||
1, // `DW.LNS.set_file`
|
||||
1, // `DW.LNS.set_column`
|
||||
0, // `DW.LNS.negate_stmt`
|
||||
0, // `DW.LNS.set_basic_block`
|
||||
0, // `DW.LNS.const_add_pc`
|
||||
1, // `DW.LNS.fixed_advance_pc`
|
||||
0, // `DW.LNS.set_prologue_end`
|
||||
0, // `DW.LNS.set_epilogue_begin`
|
||||
1, // `DW.LNS.set_isa`
|
||||
0, // include_directories (none except the compilation unit cwd)
|
||||
});
|
||||
// file_names[0]
|
||||
@ -2053,7 +2053,7 @@ fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, al
|
||||
|
||||
// The .debug_info section has `low_pc` and `high_pc` values which is the virtual address
|
||||
// range of the compilation unit. When we expand the text section, this range changes,
|
||||
// so the DW_TAG_compile_unit tag of the .debug_info section becomes dirty.
|
||||
// so the DW_TAG.compile_unit tag of the .debug_info section becomes dirty.
|
||||
self.debug_info_header_dirty = true;
|
||||
// This becomes dirty for the same reason. We could potentially make this more
|
||||
// fine-grained with the addition of support for more compilation units. It is planned to
|
||||
@ -2303,22 +2303,22 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven
|
||||
|
||||
const ptr_width_bytes = self.ptrWidthBytes();
|
||||
dbg_line_buffer.appendSliceAssumeCapacity(&[_]u8{
|
||||
DW.LNS_extended_op,
|
||||
DW.LNS.extended_op,
|
||||
ptr_width_bytes + 1,
|
||||
DW.LNE_set_address,
|
||||
DW.LNE.set_address,
|
||||
});
|
||||
// This is the "relocatable" vaddr, corresponding to `code_buffer` index `0`.
|
||||
assert(dbg_line_vaddr_reloc_index == dbg_line_buffer.items.len);
|
||||
dbg_line_buffer.items.len += ptr_width_bytes;
|
||||
|
||||
dbg_line_buffer.appendAssumeCapacity(DW.LNS_advance_line);
|
||||
dbg_line_buffer.appendAssumeCapacity(DW.LNS.advance_line);
|
||||
// This is the "relocatable" relative line offset from the previous function's end curly
|
||||
// to this function's begin curly.
|
||||
assert(self.getRelocDbgLineOff() == dbg_line_buffer.items.len);
|
||||
// Here we use a ULEB128-fixed-4 to make sure this field can be overwritten later.
|
||||
leb128.writeUnsignedFixed(4, dbg_line_buffer.addManyAsArrayAssumeCapacity(4), line_off);
|
||||
|
||||
dbg_line_buffer.appendAssumeCapacity(DW.LNS_set_file);
|
||||
dbg_line_buffer.appendAssumeCapacity(DW.LNS.set_file);
|
||||
assert(self.getRelocDbgFileIndex() == dbg_line_buffer.items.len);
|
||||
// Once we support more than one source file, this will have the ability to be more
|
||||
// than one possible value.
|
||||
@ -2327,7 +2327,7 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven
|
||||
|
||||
// Emit a line for the begin curly with prologue_end=false. The codegen will
|
||||
// do the work of setting prologue_end=true and epilogue_begin=true.
|
||||
dbg_line_buffer.appendAssumeCapacity(DW.LNS_copy);
|
||||
dbg_line_buffer.appendAssumeCapacity(DW.LNS.copy);
|
||||
|
||||
// .debug_info subprogram
|
||||
const decl_name_with_null = decl.name[0 .. mem.lenZ(decl.name) + 1];
|
||||
@ -2344,9 +2344,9 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven
|
||||
// "relocations" and have to be in this fixed place so that functions can be
|
||||
// moved in virtual address space.
|
||||
assert(dbg_info_low_pc_reloc_index == dbg_info_buffer.items.len);
|
||||
dbg_info_buffer.items.len += ptr_width_bytes; // DW.AT_low_pc, DW.FORM_addr
|
||||
dbg_info_buffer.items.len += ptr_width_bytes; // DW.AT.low_pc, DW.FORM.addr
|
||||
assert(self.getRelocDbgInfoSubprogramHighPC() == dbg_info_buffer.items.len);
|
||||
dbg_info_buffer.items.len += 4; // DW.AT_high_pc, DW.FORM_data4
|
||||
dbg_info_buffer.items.len += 4; // DW.AT.high_pc, DW.FORM.data4
|
||||
if (fn_ret_has_bits) {
|
||||
const gop = try dbg_info_type_relocs.getOrPut(self.base.allocator, fn_ret_type);
|
||||
if (!gop.found_existing) {
|
||||
@ -2356,9 +2356,9 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven
|
||||
};
|
||||
}
|
||||
try gop.value_ptr.relocs.append(self.base.allocator, @intCast(u32, dbg_info_buffer.items.len));
|
||||
dbg_info_buffer.items.len += 4; // DW.AT_type, DW.FORM_ref4
|
||||
dbg_info_buffer.items.len += 4; // DW.AT.type, DW.FORM.ref4
|
||||
}
|
||||
dbg_info_buffer.appendSliceAssumeCapacity(decl_name_with_null); // DW.AT_name, DW.FORM_string
|
||||
dbg_info_buffer.appendSliceAssumeCapacity(decl_name_with_null); // DW.AT.name, DW.FORM.string
|
||||
|
||||
const res = try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .{
|
||||
.dwarf = .{
|
||||
@ -2409,7 +2409,7 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven
|
||||
mem.writeInt(u32, ptr, @intCast(u32, local_sym.st_size), target_endian);
|
||||
}
|
||||
|
||||
try dbg_line_buffer.appendSlice(&[_]u8{ DW.LNS_extended_op, 1, DW.LNE_end_sequence });
|
||||
try dbg_line_buffer.appendSlice(&[_]u8{ DW.LNS.extended_op, 1, DW.LNE.end_sequence });
|
||||
|
||||
// Now we have the full contents and may allocate a region to store it.
|
||||
|
||||
@ -2493,7 +2493,7 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven
|
||||
const file_pos = debug_line_sect.sh_offset + src_fn.off;
|
||||
try self.pwriteDbgLineNops(prev_padding_size, dbg_line_buffer.items, next_padding_size, file_pos);
|
||||
|
||||
// .debug_info - End the TAG_subprogram children.
|
||||
// .debug_info - End the TAG.subprogram children.
|
||||
try dbg_info_buffer.append(0);
|
||||
|
||||
return self.finishUpdateDecl(module, decl, &dbg_info_type_relocs, &dbg_info_buffer);
|
||||
@ -2566,34 +2566,34 @@ fn addDbgInfoType(self: *Elf, ty: Type, dbg_info_buffer: *std.ArrayList(u8)) !vo
|
||||
.Bool => {
|
||||
try dbg_info_buffer.appendSlice(&[_]u8{
|
||||
abbrev_base_type,
|
||||
DW.ATE_boolean, // DW.AT_encoding , DW.FORM_data1
|
||||
1, // DW.AT_byte_size, DW.FORM_data1
|
||||
'b', 'o', 'o', 'l', 0, // DW.AT_name, DW.FORM_string
|
||||
DW.ATE.boolean, // DW.AT.encoding , DW.FORM.data1
|
||||
1, // DW.AT.byte_size, DW.FORM.data1
|
||||
'b', 'o', 'o', 'l', 0, // DW.AT.name, DW.FORM.string
|
||||
});
|
||||
},
|
||||
.Int => {
|
||||
const info = ty.intInfo(self.base.options.target);
|
||||
try dbg_info_buffer.ensureCapacity(dbg_info_buffer.items.len + 12);
|
||||
dbg_info_buffer.appendAssumeCapacity(abbrev_base_type);
|
||||
// DW.AT_encoding, DW.FORM_data1
|
||||
// DW.AT.encoding, DW.FORM.data1
|
||||
dbg_info_buffer.appendAssumeCapacity(switch (info.signedness) {
|
||||
.signed => DW.ATE_signed,
|
||||
.unsigned => DW.ATE_unsigned,
|
||||
.signed => DW.ATE.signed,
|
||||
.unsigned => DW.ATE.unsigned,
|
||||
});
|
||||
// DW.AT_byte_size, DW.FORM_data1
|
||||
// DW.AT.byte_size, DW.FORM.data1
|
||||
dbg_info_buffer.appendAssumeCapacity(@intCast(u8, ty.abiSize(self.base.options.target)));
|
||||
// DW.AT_name, DW.FORM_string
|
||||
// DW.AT.name, DW.FORM.string
|
||||
try dbg_info_buffer.writer().print("{}\x00", .{ty});
|
||||
},
|
||||
.Optional => {
|
||||
if (ty.isPtrLikeOptional()) {
|
||||
try dbg_info_buffer.ensureCapacity(dbg_info_buffer.items.len + 12);
|
||||
dbg_info_buffer.appendAssumeCapacity(abbrev_base_type);
|
||||
// DW.AT_encoding, DW.FORM_data1
|
||||
dbg_info_buffer.appendAssumeCapacity(DW.ATE_address);
|
||||
// DW.AT_byte_size, DW.FORM_data1
|
||||
// DW.AT.encoding, DW.FORM.data1
|
||||
dbg_info_buffer.appendAssumeCapacity(DW.ATE.address);
|
||||
// DW.AT.byte_size, DW.FORM.data1
|
||||
dbg_info_buffer.appendAssumeCapacity(@intCast(u8, ty.abiSize(self.base.options.target)));
|
||||
// DW.AT_name, DW.FORM_string
|
||||
// DW.AT.name, DW.FORM.string
|
||||
try dbg_info_buffer.writer().print("{}\x00", .{ty});
|
||||
} else {
|
||||
log.err("TODO implement .debug_info for type '{}'", .{ty});
|
||||
@ -3034,7 +3034,7 @@ fn archPtrWidthBytes(self: Elf) u8 {
|
||||
/// The reloc offset for the virtual address of a function in its Line Number Program.
|
||||
/// Size is a virtual address integer.
|
||||
const dbg_line_vaddr_reloc_index = 3;
|
||||
/// The reloc offset for the virtual address of a function in its .debug_info TAG_subprogram.
|
||||
/// The reloc offset for the virtual address of a function in its .debug_info TAG.subprogram.
|
||||
/// Size is a virtual address integer.
|
||||
const dbg_info_low_pc_reloc_index = 1;
|
||||
|
||||
@ -3060,7 +3060,7 @@ fn dbgLineNeededHeaderBytes(self: Elf) u32 {
|
||||
const root_src_dir_path_len = if (self.base.options.module.?.root_pkg.root_src_directory.path) |p| p.len else 1; // "."
|
||||
return @intCast(u32, 53 + directory_entry_format_count * 2 + file_name_entry_format_count * 2 +
|
||||
directory_count * 8 + file_name_count * 8 +
|
||||
// These are encoded as DW.FORM_string rather than DW.FORM_strp as we would like
|
||||
// These are encoded as DW.FORM.string rather than DW.FORM.strp as we would like
|
||||
// because of a workaround for readelf and gdb failing to understand DWARFv5 correctly.
|
||||
root_src_dir_path_len +
|
||||
self.base.options.module.?.root_pkg.root_src_path.len);
|
||||
@ -3088,8 +3088,8 @@ fn pwriteDbgLineNops(
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
const page_of_nops = [1]u8{DW.LNS_negate_stmt} ** 4096;
|
||||
const three_byte_nop = [3]u8{ DW.LNS_advance_pc, 0b1000_0000, 0 };
|
||||
const page_of_nops = [1]u8{DW.LNS.negate_stmt} ** 4096;
|
||||
const three_byte_nop = [3]u8{ DW.LNS.advance_pc, 0b1000_0000, 0 };
|
||||
var vecs: [512]std.os.iovec_const = undefined;
|
||||
var vec_index: usize = 0;
|
||||
{
|
||||
|
@ -93,7 +93,7 @@ const abbrev_parameter = 6;
|
||||
/// The reloc offset for the virtual address of a function in its Line Number Program.
|
||||
/// Size is a virtual address integer.
|
||||
const dbg_line_vaddr_reloc_index = 3;
|
||||
/// The reloc offset for the virtual address of a function in its .debug_info TAG_subprogram.
|
||||
/// The reloc offset for the virtual address of a function in its .debug_info TAG.subprogram.
|
||||
/// Size is a virtual address integer.
|
||||
const dbg_info_low_pc_reloc_index = 1;
|
||||
|
||||
@ -299,40 +299,40 @@ pub fn flushModule(self: *DebugSymbols, allocator: *Allocator, options: link.Opt
|
||||
// These are LEB encoded but since the values are all less than 127
|
||||
// we can simply append these bytes.
|
||||
const abbrev_buf = [_]u8{
|
||||
abbrev_compile_unit, DW.TAG_compile_unit, DW.CHILDREN_yes, // header
|
||||
DW.AT_stmt_list, DW.FORM_sec_offset, // offset
|
||||
DW.AT_low_pc, DW.FORM_addr,
|
||||
DW.AT_high_pc, DW.FORM_addr,
|
||||
DW.AT_name, DW.FORM_strp,
|
||||
DW.AT_comp_dir, DW.FORM_strp,
|
||||
DW.AT_producer, DW.FORM_strp,
|
||||
DW.AT_language, DW.FORM_data2,
|
||||
abbrev_compile_unit, DW.TAG.compile_unit, DW.CHILDREN.yes, // header
|
||||
DW.AT.stmt_list, DW.FORM.sec_offset, // offset
|
||||
DW.AT.low_pc, DW.FORM.addr,
|
||||
DW.AT.high_pc, DW.FORM.addr,
|
||||
DW.AT.name, DW.FORM.strp,
|
||||
DW.AT.comp_dir, DW.FORM.strp,
|
||||
DW.AT.producer, DW.FORM.strp,
|
||||
DW.AT.language, DW.FORM.data2,
|
||||
0, 0, // table sentinel
|
||||
abbrev_subprogram, DW.TAG_subprogram, DW.CHILDREN_yes, // header
|
||||
DW.AT_low_pc, DW.FORM_addr, // start VM address
|
||||
DW.AT_high_pc, DW.FORM_data4,
|
||||
DW.AT_type, DW.FORM_ref4,
|
||||
DW.AT_name, DW.FORM_string,
|
||||
DW.AT_decl_line, DW.FORM_data4,
|
||||
DW.AT_decl_file, DW.FORM_data1,
|
||||
abbrev_subprogram, DW.TAG.subprogram, DW.CHILDREN.yes, // header
|
||||
DW.AT.low_pc, DW.FORM.addr, // start VM address
|
||||
DW.AT.high_pc, DW.FORM.data4,
|
||||
DW.AT.type, DW.FORM.ref4,
|
||||
DW.AT.name, DW.FORM.string,
|
||||
DW.AT.decl_line, DW.FORM.data4,
|
||||
DW.AT.decl_file, DW.FORM.data1,
|
||||
0, 0, // table sentinel
|
||||
abbrev_subprogram_retvoid,
|
||||
DW.TAG_subprogram, DW.CHILDREN_yes, // header
|
||||
DW.AT_low_pc, DW.FORM_addr,
|
||||
DW.AT_high_pc, DW.FORM_data4,
|
||||
DW.AT_name, DW.FORM_string,
|
||||
DW.AT_decl_line, DW.FORM_data4,
|
||||
DW.AT_decl_file, DW.FORM_data1,
|
||||
DW.TAG.subprogram, DW.CHILDREN.yes, // header
|
||||
DW.AT.low_pc, DW.FORM.addr,
|
||||
DW.AT.high_pc, DW.FORM.data4,
|
||||
DW.AT.name, DW.FORM.string,
|
||||
DW.AT.decl_line, DW.FORM.data4,
|
||||
DW.AT.decl_file, DW.FORM.data1,
|
||||
0, 0, // table sentinel
|
||||
abbrev_base_type, DW.TAG_base_type, DW.CHILDREN_no, // header
|
||||
DW.AT_encoding, DW.FORM_data1, DW.AT_byte_size,
|
||||
DW.FORM_data1, DW.AT_name, DW.FORM_string,
|
||||
abbrev_base_type, DW.TAG.base_type, DW.CHILDREN.no, // header
|
||||
DW.AT.encoding, DW.FORM.data1, DW.AT.byte_size,
|
||||
DW.FORM.data1, DW.AT.name, DW.FORM.string,
|
||||
0, 0, // table sentinel
|
||||
abbrev_pad1, DW.TAG_unspecified_type, DW.CHILDREN_no, // header
|
||||
abbrev_pad1, DW.TAG.unspecified_type, DW.CHILDREN.no, // header
|
||||
0, 0, // table sentinel
|
||||
abbrev_parameter, DW.TAG_formal_parameter, DW.CHILDREN_no, // header
|
||||
DW.AT_location, DW.FORM_exprloc, DW.AT_type,
|
||||
DW.FORM_ref4, DW.AT_name, DW.FORM_string,
|
||||
abbrev_parameter, DW.TAG.formal_parameter, DW.CHILDREN.no, // header
|
||||
DW.AT.location, DW.FORM.exprloc, DW.AT.type,
|
||||
DW.FORM.ref4, DW.AT.name, DW.FORM.string,
|
||||
0, 0, // table sentinel
|
||||
0, 0, 0, // section sentinel
|
||||
};
|
||||
@ -397,7 +397,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: *Allocator, options: link.Opt
|
||||
const high_pc = text_section.addr + text_section.size;
|
||||
|
||||
di_buf.appendAssumeCapacity(abbrev_compile_unit);
|
||||
mem.writeIntLittle(u32, di_buf.addManyAsArrayAssumeCapacity(4), 0); // DW.AT_stmt_list, DW.FORM_sec_offset
|
||||
mem.writeIntLittle(u32, di_buf.addManyAsArrayAssumeCapacity(4), 0); // DW.AT.stmt_list, DW.FORM.sec_offset
|
||||
mem.writeIntLittle(u64, di_buf.addManyAsArrayAssumeCapacity(8), low_pc);
|
||||
mem.writeIntLittle(u64, di_buf.addManyAsArrayAssumeCapacity(8), high_pc);
|
||||
mem.writeIntLittle(u32, di_buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, name_strp));
|
||||
@ -406,7 +406,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: *Allocator, options: link.Opt
|
||||
// We are still waiting on dwarf-std.org to assign DW_LANG_Zig a number:
|
||||
// http://dwarfstd.org/ShowIssue.php?issue=171115.1
|
||||
// Until then we say it is C99.
|
||||
mem.writeIntLittle(u16, di_buf.addManyAsArrayAssumeCapacity(2), DW.LANG_C99);
|
||||
mem.writeIntLittle(u16, di_buf.addManyAsArrayAssumeCapacity(2), DW.LANG.C99);
|
||||
|
||||
if (di_buf.items.len > first_dbg_info_decl.dbg_info_off) {
|
||||
// Move the first N decls to the end to make more padding for the header.
|
||||
@ -514,7 +514,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: *Allocator, options: link.Opt
|
||||
di_buf.items.len += @sizeOf(u32); // We will come back and write this.
|
||||
const after_header_len = di_buf.items.len;
|
||||
|
||||
const opcode_base = DW.LNS_set_isa + 1;
|
||||
const opcode_base = DW.LNS.set_isa + 1;
|
||||
di_buf.appendSliceAssumeCapacity(&[_]u8{
|
||||
1, // minimum_instruction_length
|
||||
1, // maximum_operations_per_instruction
|
||||
@ -525,18 +525,18 @@ pub fn flushModule(self: *DebugSymbols, allocator: *Allocator, options: link.Opt
|
||||
|
||||
// Standard opcode lengths. The number of items here is based on `opcode_base`.
|
||||
// The value is the number of LEB128 operands the instruction takes.
|
||||
0, // `DW.LNS_copy`
|
||||
1, // `DW.LNS_advance_pc`
|
||||
1, // `DW.LNS_advance_line`
|
||||
1, // `DW.LNS_set_file`
|
||||
1, // `DW.LNS_set_column`
|
||||
0, // `DW.LNS_negate_stmt`
|
||||
0, // `DW.LNS_set_basic_block`
|
||||
0, // `DW.LNS_const_add_pc`
|
||||
1, // `DW.LNS_fixed_advance_pc`
|
||||
0, // `DW.LNS_set_prologue_end`
|
||||
0, // `DW.LNS_set_epilogue_begin`
|
||||
1, // `DW.LNS_set_isa`
|
||||
0, // `DW.LNS.copy`
|
||||
1, // `DW.LNS.advance_pc`
|
||||
1, // `DW.LNS.advance_line`
|
||||
1, // `DW.LNS.set_file`
|
||||
1, // `DW.LNS.set_column`
|
||||
0, // `DW.LNS.negate_stmt`
|
||||
0, // `DW.LNS.set_basic_block`
|
||||
0, // `DW.LNS.const_add_pc`
|
||||
1, // `DW.LNS.fixed_advance_pc`
|
||||
0, // `DW.LNS.set_prologue_end`
|
||||
0, // `DW.LNS.set_epilogue_begin`
|
||||
1, // `DW.LNS.set_isa`
|
||||
0, // include_directories (none except the compilation unit cwd)
|
||||
});
|
||||
// file_names[0]
|
||||
@ -876,22 +876,22 @@ pub fn initDeclDebugBuffers(
|
||||
const line_off = @intCast(u28, decl.src_line + func.lbrace_line);
|
||||
|
||||
dbg_line_buffer.appendSliceAssumeCapacity(&[_]u8{
|
||||
DW.LNS_extended_op,
|
||||
DW.LNS.extended_op,
|
||||
@sizeOf(u64) + 1,
|
||||
DW.LNE_set_address,
|
||||
DW.LNE.set_address,
|
||||
});
|
||||
// This is the "relocatable" vaddr, corresponding to `code_buffer` index `0`.
|
||||
assert(dbg_line_vaddr_reloc_index == dbg_line_buffer.items.len);
|
||||
dbg_line_buffer.items.len += @sizeOf(u64);
|
||||
|
||||
dbg_line_buffer.appendAssumeCapacity(DW.LNS_advance_line);
|
||||
dbg_line_buffer.appendAssumeCapacity(DW.LNS.advance_line);
|
||||
// This is the "relocatable" relative line offset from the previous function's end curly
|
||||
// to this function's begin curly.
|
||||
assert(getRelocDbgLineOff() == dbg_line_buffer.items.len);
|
||||
// Here we use a ULEB128-fixed-4 to make sure this field can be overwritten later.
|
||||
leb.writeUnsignedFixed(4, dbg_line_buffer.addManyAsArrayAssumeCapacity(4), line_off);
|
||||
|
||||
dbg_line_buffer.appendAssumeCapacity(DW.LNS_set_file);
|
||||
dbg_line_buffer.appendAssumeCapacity(DW.LNS.set_file);
|
||||
assert(getRelocDbgFileIndex() == dbg_line_buffer.items.len);
|
||||
// Once we support more than one source file, this will have the ability to be more
|
||||
// than one possible value.
|
||||
@ -900,7 +900,7 @@ pub fn initDeclDebugBuffers(
|
||||
|
||||
// Emit a line for the begin curly with prologue_end=false. The codegen will
|
||||
// do the work of setting prologue_end=true and epilogue_begin=true.
|
||||
dbg_line_buffer.appendAssumeCapacity(DW.LNS_copy);
|
||||
dbg_line_buffer.appendAssumeCapacity(DW.LNS.copy);
|
||||
|
||||
// .debug_info subprogram
|
||||
const decl_name_with_null = decl.name[0 .. mem.lenZ(decl.name) + 1];
|
||||
@ -917,9 +917,9 @@ pub fn initDeclDebugBuffers(
|
||||
// "relocations" and have to be in this fixed place so that functions can be
|
||||
// moved in virtual address space.
|
||||
assert(dbg_info_low_pc_reloc_index == dbg_info_buffer.items.len);
|
||||
dbg_info_buffer.items.len += @sizeOf(u64); // DW.AT_low_pc, DW.FORM_addr
|
||||
dbg_info_buffer.items.len += @sizeOf(u64); // DW.AT.low_pc, DW.FORM.addr
|
||||
assert(getRelocDbgInfoSubprogramHighPC() == dbg_info_buffer.items.len);
|
||||
dbg_info_buffer.items.len += 4; // DW.AT_high_pc, DW.FORM_data4
|
||||
dbg_info_buffer.items.len += 4; // DW.AT.high_pc, DW.FORM.data4
|
||||
if (fn_ret_has_bits) {
|
||||
const gop = try dbg_info_type_relocs.getOrPut(allocator, fn_ret_type);
|
||||
if (!gop.found_existing) {
|
||||
@ -929,11 +929,11 @@ pub fn initDeclDebugBuffers(
|
||||
};
|
||||
}
|
||||
try gop.value_ptr.relocs.append(allocator, @intCast(u32, dbg_info_buffer.items.len));
|
||||
dbg_info_buffer.items.len += 4; // DW.AT_type, DW.FORM_ref4
|
||||
dbg_info_buffer.items.len += 4; // DW.AT.type, DW.FORM.ref4
|
||||
}
|
||||
dbg_info_buffer.appendSliceAssumeCapacity(decl_name_with_null); // DW.AT_name, DW.FORM_string
|
||||
mem.writeIntLittle(u32, dbg_info_buffer.addManyAsArrayAssumeCapacity(4), line_off + 1); // DW.AT_decl_line, DW.FORM_data4
|
||||
dbg_info_buffer.appendAssumeCapacity(file_index); // DW.AT_decl_file, DW.FORM_data1
|
||||
dbg_info_buffer.appendSliceAssumeCapacity(decl_name_with_null); // DW.AT.name, DW.FORM.string
|
||||
mem.writeIntLittle(u32, dbg_info_buffer.addManyAsArrayAssumeCapacity(4), line_off + 1); // DW.AT.decl_line, DW.FORM.data4
|
||||
dbg_info_buffer.appendAssumeCapacity(file_index); // DW.AT.decl_file, DW.FORM.data1
|
||||
},
|
||||
else => {
|
||||
// TODO implement .debug_info for global variables
|
||||
@ -985,16 +985,16 @@ pub fn commitDeclDebugInfo(
|
||||
{
|
||||
// Advance line and PC.
|
||||
// TODO encapsulate logic in a helper function.
|
||||
try dbg_line_buffer.append(DW.LNS_advance_pc);
|
||||
try dbg_line_buffer.append(DW.LNS.advance_pc);
|
||||
try leb.writeULEB128(dbg_line_buffer.writer(), text_block.size);
|
||||
|
||||
try dbg_line_buffer.append(DW.LNS_advance_line);
|
||||
try dbg_line_buffer.append(DW.LNS.advance_line);
|
||||
const func = decl.val.castTag(.function).?.data;
|
||||
const line_off = @intCast(u28, func.rbrace_line - func.lbrace_line);
|
||||
try leb.writeULEB128(dbg_line_buffer.writer(), line_off);
|
||||
}
|
||||
|
||||
try dbg_line_buffer.appendSlice(&[_]u8{ DW.LNS_extended_op, 1, DW.LNE_end_sequence });
|
||||
try dbg_line_buffer.appendSlice(&[_]u8{ DW.LNS.extended_op, 1, DW.LNE.end_sequence });
|
||||
|
||||
// Now we have the full contents and may allocate a region to store it.
|
||||
|
||||
@ -1075,7 +1075,7 @@ pub fn commitDeclDebugInfo(
|
||||
const file_pos = debug_line_sect.offset + src_fn.off;
|
||||
try self.pwriteDbgLineNops(prev_padding_size, dbg_line_buffer.items, next_padding_size, file_pos);
|
||||
|
||||
// .debug_info - End the TAG_subprogram children.
|
||||
// .debug_info - End the TAG.subprogram children.
|
||||
try dbg_info_buffer.append(0);
|
||||
},
|
||||
else => {},
|
||||
@ -1128,27 +1128,27 @@ fn addDbgInfoType(
|
||||
.Bool => {
|
||||
try dbg_info_buffer.appendSlice(&[_]u8{
|
||||
abbrev_base_type,
|
||||
DW.ATE_boolean, // DW.AT_encoding , DW.FORM_data1
|
||||
1, // DW.AT_byte_size, DW.FORM_data1
|
||||
DW.ATE.boolean, // DW.AT.encoding , DW.FORM.data1
|
||||
1, // DW.AT.byte_size, DW.FORM.data1
|
||||
'b',
|
||||
'o',
|
||||
'o',
|
||||
'l',
|
||||
0, // DW.AT_name, DW.FORM_string
|
||||
0, // DW.AT.name, DW.FORM.string
|
||||
});
|
||||
},
|
||||
.Int => {
|
||||
const info = ty.intInfo(target);
|
||||
try dbg_info_buffer.ensureCapacity(dbg_info_buffer.items.len + 12);
|
||||
dbg_info_buffer.appendAssumeCapacity(abbrev_base_type);
|
||||
// DW.AT_encoding, DW.FORM_data1
|
||||
// DW.AT.encoding, DW.FORM.data1
|
||||
dbg_info_buffer.appendAssumeCapacity(switch (info.signedness) {
|
||||
.signed => DW.ATE_signed,
|
||||
.unsigned => DW.ATE_unsigned,
|
||||
.signed => DW.ATE.signed,
|
||||
.unsigned => DW.ATE.unsigned,
|
||||
});
|
||||
// DW.AT_byte_size, DW.FORM_data1
|
||||
// DW.AT.byte_size, DW.FORM.data1
|
||||
dbg_info_buffer.appendAssumeCapacity(@intCast(u8, ty.abiSize(target)));
|
||||
// DW.AT_name, DW.FORM_string
|
||||
// DW.AT.name, DW.FORM.string
|
||||
try dbg_info_buffer.writer().print("{}\x00", .{ty});
|
||||
},
|
||||
else => {
|
||||
@ -1306,7 +1306,7 @@ fn dbgLineNeededHeaderBytes(self: DebugSymbols, module: *Module) u32 {
|
||||
const root_src_dir_path_len = if (module.root_pkg.root_src_directory.path) |p| p.len else 1; // "."
|
||||
return @intCast(u32, 53 + directory_entry_format_count * 2 + file_name_entry_format_count * 2 +
|
||||
directory_count * 8 + file_name_count * 8 +
|
||||
// These are encoded as DW.FORM_string rather than DW.FORM_strp as we would like
|
||||
// These are encoded as DW.FORM.string rather than DW.FORM.strp as we would like
|
||||
// because of a workaround for readelf and gdb failing to understand DWARFv5 correctly.
|
||||
root_src_dir_path_len +
|
||||
module.root_pkg.root_src_path.len);
|
||||
@ -1332,8 +1332,8 @@ fn pwriteDbgLineNops(
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
const page_of_nops = [1]u8{DW.LNS_negate_stmt} ** 4096;
|
||||
const three_byte_nop = [3]u8{ DW.LNS_advance_pc, 0b1000_0000, 0 };
|
||||
const page_of_nops = [1]u8{DW.LNS.negate_stmt} ** 4096;
|
||||
const three_byte_nop = [3]u8{ DW.LNS.advance_pc, 0b1000_0000, 0 };
|
||||
var vecs: [32]std.os.iovec_const = undefined;
|
||||
var vec_index: usize = 0;
|
||||
{
|
||||
|
@ -836,8 +836,8 @@ pub fn parseDebugInfo(self: *Object, allocator: *Allocator) !void {
|
||||
},
|
||||
else => |e| return e,
|
||||
};
|
||||
const name = try compile_unit.die.getAttrString(&debug_info.inner, dwarf.AT_name);
|
||||
const comp_dir = try compile_unit.die.getAttrString(&debug_info.inner, dwarf.AT_comp_dir);
|
||||
const name = try compile_unit.die.getAttrString(&debug_info.inner, dwarf.AT.name);
|
||||
const comp_dir = try compile_unit.die.getAttrString(&debug_info.inner, dwarf.AT.comp_dir);
|
||||
|
||||
self.debug_info = debug_info;
|
||||
self.tu_name = try allocator.dupe(u8, name);
|
||||
|
@ -10,6 +10,7 @@ test {
|
||||
_ = @import("behavior/if.zig");
|
||||
_ = @import("behavior/cast.zig");
|
||||
_ = @import("behavior/array.zig");
|
||||
_ = @import("behavior/usingnamespace.zig");
|
||||
|
||||
if (!builtin.zig_is_stage2) {
|
||||
// Tests that only pass for stage1.
|
||||
@ -148,7 +149,7 @@ test {
|
||||
_ = @import("behavior/undefined.zig");
|
||||
_ = @import("behavior/underscore.zig");
|
||||
_ = @import("behavior/union.zig");
|
||||
_ = @import("behavior/usingnamespace.zig");
|
||||
_ = @import("behavior/usingnamespace_stage1.zig");
|
||||
_ = @import("behavior/var_args.zig");
|
||||
_ = @import("behavior/vector.zig");
|
||||
_ = @import("behavior/void.zig");
|
||||
|
@ -125,12 +125,6 @@ test "pointer to type" {
|
||||
}
|
||||
}
|
||||
|
||||
test "no undeclared identifier error in unanalyzed branches" {
|
||||
if (false) {
|
||||
lol_this_doesnt_exist = nonsense;
|
||||
}
|
||||
}
|
||||
|
||||
test "a type constructed in a global expression" {
|
||||
var l: List = undefined;
|
||||
l.array[0] = 10;
|
||||
|
@ -1,22 +1,13 @@
|
||||
const std = @import("std");
|
||||
|
||||
fn Foo(comptime T: type) type {
|
||||
return struct {
|
||||
usingnamespace T;
|
||||
};
|
||||
}
|
||||
|
||||
test "usingnamespace inside a generic struct" {
|
||||
const std2 = Foo(std);
|
||||
const testing2 = Foo(std.testing);
|
||||
try std2.testing.expect(true);
|
||||
try testing2.expect(true);
|
||||
}
|
||||
|
||||
usingnamespace struct {
|
||||
pub const foo = 42;
|
||||
const A = struct {
|
||||
pub const B = bool;
|
||||
};
|
||||
|
||||
test "usingnamespace does not redeclare an imported variable" {
|
||||
comptime try std.testing.expect(foo == 42);
|
||||
const C = struct {
|
||||
usingnamespace A;
|
||||
};
|
||||
|
||||
test "basic usingnamespace" {
|
||||
try std.testing.expect(C.B == bool);
|
||||
}
|
||||
|
22
test/behavior/usingnamespace_stage1.zig
Normal file
22
test/behavior/usingnamespace_stage1.zig
Normal file
@ -0,0 +1,22 @@
|
||||
const std = @import("std");
|
||||
|
||||
fn Foo(comptime T: type) type {
|
||||
return struct {
|
||||
usingnamespace T;
|
||||
};
|
||||
}
|
||||
|
||||
test "usingnamespace inside a generic struct" {
|
||||
const std2 = Foo(std);
|
||||
const testing2 = Foo(std.testing);
|
||||
try std2.testing.expect(true);
|
||||
try testing2.expect(true);
|
||||
}
|
||||
|
||||
usingnamespace struct {
|
||||
pub const foo = 42;
|
||||
};
|
||||
|
||||
test "usingnamespace does not redeclare an imported variable" {
|
||||
comptime try std.testing.expect(foo == 42);
|
||||
}
|
@ -8846,4 +8846,14 @@ pub fn addCases(ctx: *TestContext) !void {
|
||||
, &[_][]const u8{
|
||||
"error: invalid operands to binary expression: 'f32' and 'f32'",
|
||||
});
|
||||
|
||||
ctx.objErrStage1("undeclared identifier in unanalyzed branch",
|
||||
\\export fn a() void {
|
||||
\\ if (false) {
|
||||
\\ lol_this_doesnt_exist = nonsense;
|
||||
\\ }
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
"tmp.zig:3:9: error: use of undeclared identifier 'lol_this_doesnt_exist'",
|
||||
});
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user