diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2021-03-25 23:00:38 -0700 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2021-03-25 23:45:17 -0700 |
| commit | b2deaf80279aab1322036e55a9646ecbaaa47f44 (patch) | |
| tree | f950b7d882299bab45a6d5ea8cd213d1850c2bb0 /src | |
| parent | 4bfcd105eff797aceb621d2c8b971c15fef6e450 (diff) | |
| download | zig-b2deaf80279aab1322036e55a9646ecbaaa47f44.tar.gz zig-b2deaf80279aab1322036e55a9646ecbaaa47f44.zip | |
stage2: improve source locations of Decl access
* zir.Code: introduce a decls array. This is so that `decl_val` and
`decl_ref` instructions can refer to a Decl with a u32 and therefore
they can also store a source location. This is needed for proper
compile error reporting.
* astgen uses a hash map to avoid redundantly adding a Decl to the
decls array.
* fixed reporting "instruction illegal outside function body" instead
of the desired message "unable to resolve comptime value".
* astgen skips emitting dbg_stmt instructions in comptime scopes.
* astgen has some logic to avoid adding unnecessary type coercion
instructions for common values.
Diffstat (limited to 'src')
| -rw-r--r-- | src/Module.zig | 33 | ||||
| -rw-r--r-- | src/Sema.zig | 19 | ||||
| -rw-r--r-- | src/astgen.zig | 89 | ||||
| -rw-r--r-- | src/zir.zig | 40 |
4 files changed, 130 insertions, 51 deletions
diff --git a/src/Module.zig b/src/Module.zig index 9f57bdb93d..25d83e2d56 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -103,7 +103,7 @@ stage1_flags: packed struct { emit_h: ?Compilation.EmitLoc, -compile_log_text: std.ArrayListUnmanaged(u8) = .{}, +compile_log_text: ArrayListUnmanaged(u8) = .{}, pub const Export = struct { options: std.builtin.ExportOptions, @@ -335,7 +335,7 @@ pub const Decl = struct { /// This state is attached to every Decl when Module emit_h is non-null. pub const EmitH = struct { - fwd_decl: std.ArrayListUnmanaged(u8) = .{}, + fwd_decl: ArrayListUnmanaged(u8) = .{}, }; /// Some Fn struct memory is owned by the Decl's TypedValue.Managed arena allocator. @@ -916,7 +916,7 @@ pub const Scope = struct { zir_code: *WipZirCode, /// Keeps track of the list of instructions in this scope only. Indexes /// to instructions in `zir_code`. - instructions: std.ArrayListUnmanaged(zir.Inst.Index) = .{}, + instructions: ArrayListUnmanaged(zir.Inst.Index) = .{}, label: ?Label = null, break_block: zir.Inst.Index = 0, continue_block: zir.Inst.Index = 0, @@ -935,11 +935,11 @@ pub const Scope = struct { break_count: usize = 0, /// Tracks `break :foo bar` instructions so they can possibly be elided later if /// the labeled block ends up not needing a result location pointer. - labeled_breaks: std.ArrayListUnmanaged(zir.Inst.Index) = .{}, + labeled_breaks: ArrayListUnmanaged(zir.Inst.Index) = .{}, /// Tracks `store_to_block_ptr` instructions that correspond to break instructions /// so they can possibly be elided later if the labeled block ends up not needing /// a result location pointer. - labeled_store_to_block_ptr_list: std.ArrayListUnmanaged(zir.Inst.Index) = .{}, + labeled_store_to_block_ptr_list: ArrayListUnmanaged(zir.Inst.Index) = .{}, pub const Label = struct { token: ast.TokenIndex, @@ -957,6 +957,7 @@ pub const Scope = struct { .instructions = gz.zir_code.instructions.toOwnedSlice(), .string_bytes = gz.zir_code.string_bytes.toOwnedSlice(gpa), .extra = gz.zir_code.extra.toOwnedSlice(gpa), + .decls = gz.zir_code.decls.toOwnedSlice(gpa), }; } @@ -1253,11 +1254,15 @@ pub const Scope = struct { pub fn addDecl( gz: *GenZir, tag: zir.Inst.Tag, - decl: *Decl, + decl_index: u32, + src_node: ast.Node.Index, ) !zir.Inst.Ref { return gz.add(.{ .tag = tag, - .data = .{ .decl = decl }, + .data = .{ .pl_node = .{ + .src_node = gz.zir_code.decl.nodeIndexToRelative(src_node), + .payload_index = decl_index, + } }, }); } @@ -1379,8 +1384,10 @@ pub const Scope = struct { /// The `GenZir.finish` function converts this to a `zir.Code`. pub const WipZirCode = struct { instructions: std.MultiArrayList(zir.Inst) = .{}, - string_bytes: std.ArrayListUnmanaged(u8) = .{}, - extra: std.ArrayListUnmanaged(u32) = .{}, + string_bytes: ArrayListUnmanaged(u8) = .{}, + extra: ArrayListUnmanaged(u32) = .{}, + decl_map: std.StringArrayHashMapUnmanaged(void) = .{}, + decls: ArrayListUnmanaged(*Decl) = .{}, /// The end of special indexes. `zir.Inst.Ref` subtracts against this number to convert /// to `zir.Inst.Index`. The default here is correct if there are 0 parameters. ref_start_index: u32 = zir.Inst.Ref.typed_value_map.len, @@ -1442,6 +1449,8 @@ pub const WipZirCode = struct { wzc.instructions.deinit(wzc.gpa); wzc.extra.deinit(wzc.gpa); wzc.string_bytes.deinit(wzc.gpa); + wzc.decl_map.deinit(wzc.gpa); + wzc.decls.deinit(wzc.gpa); } }; @@ -4062,7 +4071,7 @@ pub fn identifierTokenString(mod: *Module, scope: *Scope, token: ast.TokenIndex) if (!mem.startsWith(u8, ident_name, "@")) { return ident_name; } - var buf: std.ArrayListUnmanaged(u8) = .{}; + var buf: ArrayListUnmanaged(u8) = .{}; defer buf.deinit(mod.gpa); try parseStrLit(mod, scope, token, &buf, ident_name, 1); return buf.toOwnedSlice(mod.gpa); @@ -4075,7 +4084,7 @@ pub fn appendIdentStr( mod: *Module, scope: *Scope, token: ast.TokenIndex, - buf: *std.ArrayListUnmanaged(u8), + buf: *ArrayListUnmanaged(u8), ) InnerError!void { const tree = scope.tree(); const token_tags = tree.tokens.items(.tag); @@ -4093,7 +4102,7 @@ pub fn parseStrLit( mod: *Module, scope: *Scope, token: ast.TokenIndex, - buf: *std.ArrayListUnmanaged(u8), + buf: *ArrayListUnmanaged(u8), bytes: []const u8, offset: u32, ) InnerError!void { diff --git a/src/Sema.zig b/src/Sema.zig index 6cd05135d4..f6ffd640ab 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1102,10 +1102,15 @@ fn zirDbgStmtNode(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerE const tracy = trace(@src()); defer tracy.end(); + // We do not set sema.src here because dbg_stmt instructions are only emitted for + // ZIR code that possibly will need to generate runtime code. So error messages + // and other source locations must not rely on sema.src being set from dbg_stmt + // instructions. if (block.is_comptime) return; const src_node = sema.code.instructions.items(.data)[inst].node; const src: LazySrcLoc = .{ .node_offset = src_node }; + const src_loc = src.toSrcLoc(&block.base); const abs_byte_off = try src_loc.byteOffset(); _ = try block.addDbgStmt(src, abs_byte_off); @@ -1115,16 +1120,20 @@ fn zirDeclRef(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError const tracy = trace(@src()); defer tracy.end(); - const decl = sema.code.instructions.items(.data)[inst].decl; - return sema.analyzeDeclRef(block, .unneeded, decl); + const inst_data = sema.code.instructions.items(.data)[inst].pl_node; + const src = inst_data.src(); + const decl = sema.code.decls[inst_data.payload_index]; + return sema.analyzeDeclRef(block, src, decl); } fn zirDeclVal(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); - const decl = sema.code.instructions.items(.data)[inst].decl; - return sema.analyzeDeclVal(block, .unneeded, decl); + const inst_data = sema.code.instructions.items(.data)[inst].pl_node; + const src = inst_data.src(); + const decl = sema.code.decls[inst_data.payload_index]; + return sema.analyzeDeclVal(block, src, decl); } fn zirCallNone( @@ -3211,10 +3220,10 @@ fn requireFunctionBlock(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) !void } fn requireRuntimeBlock(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) !void { - try sema.requireFunctionBlock(block, src); if (block.is_comptime) { return sema.mod.fail(&block.base, src, "unable to resolve comptime value", .{}); } + try sema.requireFunctionBlock(block, src); } fn validateVarType(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty: Type) !void { diff --git a/src/astgen.zig b/src/astgen.zig index 675bf77065..58d97bf9c2 100644 --- a/src/astgen.zig +++ b/src/astgen.zig @@ -952,7 +952,9 @@ fn blockExprStmts( var scope = parent_scope; for (statements) |statement| { - _ = try gz.addNode(.dbg_stmt_node, statement); + if (!gz.force_comptime) { + _ = try gz.addNode(.dbg_stmt_node, statement); + } switch (node_tags[statement]) { .global_var_decl => scope = try varDecl(mod, scope, statement, &block_arena.allocator, tree.globalVarDecl(statement)), .local_var_decl => scope = try varDecl(mod, scope, statement, &block_arena.allocator, tree.localVarDecl(statement)), @@ -2846,14 +2848,17 @@ fn identifier( }; } - if (mod.lookupDeclName(scope, ident_name)) |decl| { - return if (rl == .ref) - gz.addDecl(.decl_ref, decl) - else - rvalue(mod, scope, rl, try gz.addDecl(.decl_val, decl), ident); + const gop = try gz.zir_code.decl_map.getOrPut(mod.gpa, ident_name); + if (!gop.found_existing) { + const decl = mod.lookupDeclName(scope, ident_name) orelse + return mod.failNode(scope, ident, "use of undeclared identifier '{s}'", .{ident_name}); + try gz.zir_code.decls.append(mod.gpa, decl); + } + const decl_index = @intCast(u32, gop.index); + switch (rl) { + .ref => return gz.addDecl(.decl_ref, decl_index, ident), + else => return rvalue(mod, scope, rl, try gz.addDecl(.decl_val, decl_index, ident), ident), } - - return mod.failNode(scope, ident, "use of undeclared identifier '{s}'", .{ident_name}); } fn stringLiteral( @@ -3743,10 +3748,70 @@ fn rvalue( const src_token = tree.firstToken(src_node); return gz.addUnTok(.ref, result, src_token); }, - .ty => |ty_inst| return gz.addPlNode(.as_node, src_node, zir.Inst.As{ - .dest_type = ty_inst, - .operand = result, - }), + .ty => |ty_inst| { + // Quickly eliminate some common, unnecessary type coercion. + const as_ty = @as(u64, @enumToInt(zir.Inst.Ref.type_type)) << 32; + const as_comptime_int = @as(u64, @enumToInt(zir.Inst.Ref.comptime_int_type)) << 32; + const as_bool = @as(u64, @enumToInt(zir.Inst.Ref.bool_type)) << 32; + const as_usize = @as(u64, @enumToInt(zir.Inst.Ref.usize_type)) << 32; + const as_void = @as(u64, @enumToInt(zir.Inst.Ref.void_type)) << 32; + switch ((@as(u64, @enumToInt(ty_inst)) << 32) | @as(u64, @enumToInt(result))) { + as_ty | @enumToInt(zir.Inst.Ref.u8_type), + as_ty | @enumToInt(zir.Inst.Ref.i8_type), + as_ty | @enumToInt(zir.Inst.Ref.u16_type), + as_ty | @enumToInt(zir.Inst.Ref.i16_type), + as_ty | @enumToInt(zir.Inst.Ref.u32_type), + as_ty | @enumToInt(zir.Inst.Ref.i32_type), + as_ty | @enumToInt(zir.Inst.Ref.u64_type), + as_ty | @enumToInt(zir.Inst.Ref.i64_type), + as_ty | @enumToInt(zir.Inst.Ref.usize_type), + as_ty | @enumToInt(zir.Inst.Ref.isize_type), + as_ty | @enumToInt(zir.Inst.Ref.c_short_type), + as_ty | @enumToInt(zir.Inst.Ref.c_ushort_type), + as_ty | @enumToInt(zir.Inst.Ref.c_int_type), + as_ty | @enumToInt(zir.Inst.Ref.c_uint_type), + as_ty | @enumToInt(zir.Inst.Ref.c_long_type), + as_ty | @enumToInt(zir.Inst.Ref.c_ulong_type), + as_ty | @enumToInt(zir.Inst.Ref.c_longlong_type), + as_ty | @enumToInt(zir.Inst.Ref.c_ulonglong_type), + as_ty | @enumToInt(zir.Inst.Ref.c_longdouble_type), + as_ty | @enumToInt(zir.Inst.Ref.f16_type), + as_ty | @enumToInt(zir.Inst.Ref.f32_type), + as_ty | @enumToInt(zir.Inst.Ref.f64_type), + as_ty | @enumToInt(zir.Inst.Ref.f128_type), + as_ty | @enumToInt(zir.Inst.Ref.c_void_type), + as_ty | @enumToInt(zir.Inst.Ref.bool_type), + as_ty | @enumToInt(zir.Inst.Ref.void_type), + as_ty | @enumToInt(zir.Inst.Ref.type_type), + as_ty | @enumToInt(zir.Inst.Ref.anyerror_type), + as_ty | @enumToInt(zir.Inst.Ref.comptime_int_type), + as_ty | @enumToInt(zir.Inst.Ref.comptime_float_type), + as_ty | @enumToInt(zir.Inst.Ref.noreturn_type), + as_ty | @enumToInt(zir.Inst.Ref.null_type), + as_ty | @enumToInt(zir.Inst.Ref.undefined_type), + as_ty | @enumToInt(zir.Inst.Ref.fn_noreturn_no_args_type), + as_ty | @enumToInt(zir.Inst.Ref.fn_void_no_args_type), + as_ty | @enumToInt(zir.Inst.Ref.fn_naked_noreturn_no_args_type), + as_ty | @enumToInt(zir.Inst.Ref.fn_ccc_void_no_args_type), + as_ty | @enumToInt(zir.Inst.Ref.single_const_pointer_to_comptime_int_type), + as_ty | @enumToInt(zir.Inst.Ref.const_slice_u8_type), + as_ty | @enumToInt(zir.Inst.Ref.enum_literal_type), + as_comptime_int | @enumToInt(zir.Inst.Ref.zero), + as_comptime_int | @enumToInt(zir.Inst.Ref.one), + as_bool | @enumToInt(zir.Inst.Ref.bool_true), + as_bool | @enumToInt(zir.Inst.Ref.bool_false), + as_usize | @enumToInt(zir.Inst.Ref.zero_usize), + as_usize | @enumToInt(zir.Inst.Ref.one_usize), + as_void | @enumToInt(zir.Inst.Ref.void_value), + => return result, // type of result is already correct + + // Need an explicit type coercion instruction. + else => return gz.addPlNode(.as_node, src_node, zir.Inst.As{ + .dest_type = ty_inst, + .operand = result, + }), + } + }, .ptr => |ptr_inst| { _ = try gz.addPlNode(.store_node, src_node, zir.Inst.Bin{ .lhs = ptr_inst, diff --git a/src/zir.zig b/src/zir.zig index 8d20601b3e..c3f5a52c51 100644 --- a/src/zir.zig +++ b/src/zir.zig @@ -37,6 +37,8 @@ pub const Code = struct { string_bytes: []u8, /// The meaning of this data is determined by `Inst.Tag` value. extra: []u32, + /// Used for decl_val and decl_ref instructions. + decls: []*Module.Decl, /// Returns the requested data, as well as the new index which is at the start of the /// trailers for the object. @@ -76,6 +78,7 @@ pub const Code = struct { code.instructions.deinit(gpa); gpa.free(code.string_bytes); gpa.free(code.extra); + gpa.free(code.decls); code.* = undefined; } @@ -103,7 +106,7 @@ pub const Code = struct { const stderr = std.io.getStdErr().writer(); try stderr.print("ZIR {s} {s} %0 ", .{ kind, decl_name }); try writer.writeInstToStream(stderr, 0); - try stderr.print("}} // ZIR {s} {s}\n\n", .{ kind, decl_name }); + try stderr.print(" // end ZIR {s} {s}\n\n", .{ kind, decl_name }); } }; @@ -115,7 +118,7 @@ pub const Inst = struct { data: Data, /// These names are used directly as the instruction names in the text format. - pub const Tag = enum { + pub const Tag = enum(u8) { /// Arithmetic addition, asserts no integer overflow. /// Uses the `pl_node` union field. Payload is `Bin`. add, @@ -274,10 +277,10 @@ pub const Inst = struct { /// Uses the `node` union field. dbg_stmt_node, /// Represents a pointer to a global decl. - /// Uses the `decl` union field. + /// Uses the `pl_node` union field. `payload_index` is into `decls`. decl_ref, /// Equivalent to a decl_ref followed by load. - /// Uses the `decl` union field. + /// Uses the `pl_node` union field. `payload_index` is into `decls`. decl_val, /// Load the value from a pointer. Assumes `x.*` syntax. /// Uses `un_node` field. AST node is the `x.*` syntax. @@ -612,10 +615,6 @@ pub const Inst = struct { // /// validated by the switch_br instruction. // switch_range, - comptime { - assert(@sizeOf(Tag) == 1); - } - /// Returns whether the instruction is one of the control flow "noreturn" types. /// Function calls do not count. pub fn isNoReturn(tag: Tag) bool { @@ -1099,7 +1098,6 @@ pub const Inst = struct { } }, bin: Bin, - decl: *Module.Decl, @"const": *TypedValue, /// For strings which may contain null bytes. str: struct { @@ -1503,6 +1501,10 @@ const Writer = struct { .typeof_peer, => try self.writePlNodeMultiOp(stream, inst), + .decl_ref, + .decl_val, + => try self.writePlNodeDecl(stream, inst), + .as_node => try self.writeAs(stream, inst), .breakpoint, @@ -1513,10 +1515,6 @@ const Writer = struct { .repeat_inline, => try self.writeNode(stream, inst), - .decl_ref, - .decl_val, - => try self.writeDecl(stream, inst), - .error_value, .enum_literal, => try self.writeStrTok(stream, inst), @@ -1715,6 +1713,13 @@ const Writer = struct { try self.writeSrc(stream, inst_data.src()); } + fn writePlNodeDecl(self: *Writer, stream: anytype, inst: Inst.Index) !void { + const inst_data = self.code.instructions.items(.data)[inst].pl_node; + const decl = self.code.decls[inst_data.payload_index]; + try stream.print("{s}) ", .{decl.name}); + try self.writeSrc(stream, inst_data.src()); + } + fn writeAs(self: *Writer, stream: anytype, inst: Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].pl_node; const extra = self.code.extraData(Inst.As, inst_data.payload_index).data; @@ -1736,15 +1741,6 @@ const Writer = struct { try self.writeSrc(stream, src); } - fn writeDecl( - self: *Writer, - stream: anytype, - inst: Inst.Index, - ) (@TypeOf(stream).Error || error{OutOfMemory})!void { - const decl = self.code.instructions.items(.data)[inst].decl; - try stream.print("{s})", .{decl.name}); - } - fn writeStrTok( self: *Writer, stream: anytype, |
