From ea00ddfe3710e386b62849fa4d275b5cbd82e28c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 21 Apr 2021 20:42:49 -0700 Subject: [PATCH] AstGen: implement comptime locals --- src/AstGen.zig | 17 ++++++++----- src/Sema.zig | 28 ++++++++++---------- src/Zir.zig | 69 ++++++++++++++++++++++++++------------------------ 3 files changed, 60 insertions(+), 54 deletions(-) diff --git a/src/AstGen.zig b/src/AstGen.zig index e7eedca70c..2c01779705 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -1576,8 +1576,10 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: ast.Node.Index) Inner .addwrap, .alloc, .alloc_mut, + .alloc_comptime, .alloc_inferred, .alloc_inferred_mut, + .alloc_inferred_comptime, .array_cat, .array_mul, .array_type, @@ -1665,7 +1667,6 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: ast.Node.Index) Inner .ptr_type, .ptr_type_simple, .enum_literal, - .enum_literal_small, .merge_error_sets, .error_union_type, .bit_not, @@ -1901,9 +1902,6 @@ fn varDecl( ) InnerError!*Scope { try emitDbgNode(gz, node); const astgen = gz.astgen; - if (var_decl.comptime_token) |comptime_token| { - return astgen.failTok(comptime_token, "TODO implement comptime locals", .{}); - } if (var_decl.ast.align_node != 0) { return astgen.failNode(var_decl.ast.align_node, "TODO implement alignment on locals", .{}); } @@ -1961,6 +1959,9 @@ fn varDecl( switch (token_tags[var_decl.ast.mut_token]) { .keyword_const => { + if (var_decl.comptime_token) |comptime_token| { + return astgen.failTok(comptime_token, "'comptime const' is redundant; instead wrap the initialization expression with 'comptime'", .{}); + } // Depending on the type of AST the initialization expression is, we may need an lvalue // or an rvalue as a result location. If it is an rvalue, we can use the instruction as // the variable, no memory location needed. @@ -2062,17 +2063,19 @@ fn varDecl( return &sub_scope.base; }, .keyword_var => { + const is_comptime = var_decl.comptime_token != null; var resolve_inferred_alloc: Zir.Inst.Ref = .none; const var_data: struct { result_loc: ResultLoc, alloc: Zir.Inst.Ref, } = if (var_decl.ast.type_node != 0) a: { const type_inst = try typeExpr(gz, scope, var_decl.ast.type_node); - - const alloc = try gz.addUnNode(.alloc_mut, type_inst, node); + const tag: Zir.Inst.Tag = if (is_comptime) .alloc_comptime else .alloc_mut; + const alloc = try gz.addUnNode(tag, type_inst, node); break :a .{ .alloc = alloc, .result_loc = .{ .ptr = alloc } }; } else a: { - const alloc = try gz.addNode(.alloc_inferred_mut, node); + const tag: Zir.Inst.Tag = if (is_comptime) .alloc_inferred_comptime else .alloc_inferred_mut; + const alloc = try gz.addNode(tag, node); resolve_inferred_alloc = alloc; break :a .{ .alloc = alloc, .result_loc = .{ .inferred_ptr = alloc } }; }; diff --git a/src/Sema.zig b/src/Sema.zig index ab912f5fbe..5a40409c86 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -135,7 +135,9 @@ pub fn analyzeBody( .alloc => try sema.zirAlloc(block, inst), .alloc_inferred => try sema.zirAllocInferred(block, inst, Type.initTag(.inferred_alloc_const)), .alloc_inferred_mut => try sema.zirAllocInferred(block, inst, Type.initTag(.inferred_alloc_mut)), + .alloc_inferred_comptime => try sema.zirAllocInferredComptime(block, inst), .alloc_mut => try sema.zirAllocMut(block, inst), + .alloc_comptime => try sema.zirAllocComptime(block, inst), .array_cat => try sema.zirArrayCat(block, inst), .array_mul => try sema.zirArrayMul(block, inst), .array_type => try sema.zirArrayType(block, inst), @@ -177,7 +179,6 @@ pub fn analyzeBody( .elem_val_node => try sema.zirElemValNode(block, inst), .elem_type => try sema.zirElemType(block, inst), .enum_literal => try sema.zirEnumLiteral(block, inst), - .enum_literal_small => try sema.zirEnumLiteralSmall(block, inst), .enum_to_int => try sema.zirEnumToInt(block, inst), .int_to_enum => try sema.zirIntToEnum(block, inst), .err_union_code => try sema.zirErrUnionCode(block, inst), @@ -1130,6 +1131,18 @@ fn zirIndexablePtrLen(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) In return sema.analyzeLoad(block, src, result_ptr, result_ptr.src); } +fn zirAllocComptime(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst { + const inst_data = sema.code.instructions.items(.data)[inst].un_node; + const src = inst_data.src(); + return sema.mod.fail(&block.base, src, "TODO implement Sema.zirAllocComptime", .{}); +} + +fn zirAllocInferredComptime(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst { + const src_node = sema.code.instructions.items(.data)[inst].node; + const src: LazySrcLoc = .{ .node_offset = src_node }; + return sema.mod.fail(&block.base, src, "TODO implement Sema.zirAllocInferredComptime", .{}); +} + fn zirAlloc(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); @@ -2334,19 +2347,6 @@ fn zirEnumLiteral(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerE }); } -fn zirEnumLiteralSmall(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst { - const tracy = trace(@src()); - defer tracy.end(); - - const name = sema.code.instructions.items(.data)[inst].small_str.get(); - const src: LazySrcLoc = .unneeded; - const duped_name = try sema.arena.dupe(u8, name); - return sema.mod.constInst(sema.arena, src, .{ - .ty = Type.initTag(.enum_literal), - .val = try Value.Tag.enum_literal.create(sema.arena, duped_name), - }); -} - fn zirEnumToInt(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst { const mod = sema.mod; const arena = sema.arena; diff --git a/src/Zir.zig b/src/Zir.zig index 803ef898b8..4cf7f75373 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -145,18 +145,6 @@ pub const Inst = struct { /// Twos complement wrapping integer addition. /// Uses the `pl_node` union field. Payload is `Bin`. addwrap, - /// Allocates stack local memory. - /// Uses the `un_node` union field. The operand is the type of the allocated object. - /// The node source location points to a var decl node. - /// Indicates the beginning of a new statement in debug info. - alloc, - /// Same as `alloc` except mutable. - alloc_mut, - /// Same as `alloc` except the type is inferred. - /// Uses the `node` union field. - alloc_inferred, - /// Same as `alloc_inferred` except mutable. - alloc_inferred_mut, /// Array concatenation. `a ++ b` /// Uses the `pl_node` union field. Payload is `Bin`. array_cat, @@ -483,12 +471,6 @@ pub const Inst = struct { /// Create a pointer type which can have a sentinel, alignment, and/or bit range. /// Uses the `ptr_type` union field. ptr_type, - /// Each `store_to_inferred_ptr` puts the type of the stored value into a set, - /// and then `resolve_inferred_alloc` triggers peer type resolution on the set. - /// The operand is a `alloc_inferred` or `alloc_inferred_mut` instruction, which - /// is the allocation that needs to have its type inferred. - /// Uses the `un_node` field. The AST node is the var decl. - resolve_inferred_alloc, /// Slice operation `lhs[rhs..]`. No sentinel and no end offset. /// Uses the `pl_node` field. AST node is the slice syntax. Payload is `SliceStart`. slice_start, @@ -613,37 +595,40 @@ pub const Inst = struct { ensure_err_payload_void, /// An enum literal. Uses the `str_tok` union field. enum_literal, - /// An enum literal 8 or fewer bytes. No source location. - /// Uses the `small_str` field. - enum_literal_small, /// A switch expression. Uses the `pl_node` union field. /// AST node is the switch, payload is `SwitchBlock`. /// All prongs of target handled. switch_block, /// Same as switch_block, except one or more prongs have multiple items. + /// Payload is `SwitchBlockMulti` switch_block_multi, /// Same as switch_block, except has an else prong. switch_block_else, /// Same as switch_block_else, except one or more prongs have multiple items. + /// Payload is `SwitchBlockMulti` switch_block_else_multi, /// Same as switch_block, except has an underscore prong. switch_block_under, /// Same as switch_block, except one or more prongs have multiple items. + /// Payload is `SwitchBlockMulti` switch_block_under_multi, /// Same as `switch_block` but the target is a pointer to the value being switched on. switch_block_ref, /// Same as `switch_block_multi` but the target is a pointer to the value being switched on. + /// Payload is `SwitchBlockMulti` switch_block_ref_multi, /// Same as `switch_block_else` but the target is a pointer to the value being switched on. switch_block_ref_else, /// Same as `switch_block_else_multi` but the target is a pointer to the /// value being switched on. + /// Payload is `SwitchBlockMulti` switch_block_ref_else_multi, /// Same as `switch_block_under` but the target is a pointer to the value /// being switched on. switch_block_ref_under, /// Same as `switch_block_under_multi` but the target is a pointer to /// the value being switched on. + /// Payload is `SwitchBlockMulti` switch_block_ref_under_multi, /// Produces the capture value for a switch prong. /// Uses the `switch_capture` field. @@ -937,6 +922,32 @@ pub const Inst = struct { /// Implements the `@cImport` builtin. /// Uses the `pl_node` union field with payload `Block`. c_import, + + /// Allocates stack local memory. + /// Uses the `un_node` union field. The operand is the type of the allocated object. + /// The node source location points to a var decl node. + /// Indicates the beginning of a new statement in debug info. + alloc, + /// Same as `alloc` except mutable. + alloc_mut, + /// Allocates comptime-mutable memory. + /// Uses the `un_node` union field. The operand is the type of the allocated object. + /// The node source location points to a var decl node. + alloc_comptime, + /// Same as `alloc` except the type is inferred. + /// Uses the `node` union field. + alloc_inferred, + /// Same as `alloc_inferred` except mutable. + alloc_inferred_mut, + /// Same as `alloc_comptime` except the type is inferred. + alloc_inferred_comptime, + /// Each `store_to_inferred_ptr` puts the type of the stored value into a set, + /// and then `resolve_inferred_alloc` triggers peer type resolution on the set. + /// The operand is a `alloc_inferred` or `alloc_inferred_mut` instruction, which + /// is the allocation that needs to have its type inferred. + /// Uses the `un_node` field. The AST node is the var decl. + resolve_inferred_alloc, + /// The ZIR instruction tag is one of the `Extended` ones. /// Uses the `extended` union field. extended, @@ -949,8 +960,10 @@ pub const Inst = struct { .addwrap, .alloc, .alloc_mut, + .alloc_comptime, .alloc_inferred, .alloc_inferred_mut, + .alloc_inferred_comptime, .array_cat, .array_mul, .array_type, @@ -1066,7 +1079,6 @@ pub const Inst = struct { .ptr_type_simple, .ensure_err_payload_void, .enum_literal, - .enum_literal_small, .merge_error_sets, .error_union_type, .bit_not, @@ -2251,6 +2263,7 @@ const Writer = struct { .alloc, .alloc_mut, + .alloc_comptime, .indexable_ptr_len, .bit_not, .bool_not, @@ -2516,6 +2529,7 @@ const Writer = struct { .repeat_inline, .alloc_inferred, .alloc_inferred_mut, + .alloc_inferred_comptime, => try self.writeNode(stream, inst), .error_value, @@ -2530,8 +2544,6 @@ const Writer = struct { .@"unreachable" => try self.writeUnreachable(stream, inst), - .enum_literal_small => try self.writeSmallStr(stream, inst), - .switch_capture, .switch_capture_ref, .switch_capture_multi, @@ -3442,15 +3454,6 @@ const Writer = struct { try self.writeSrc(stream, src); } - fn writeSmallStr( - self: *Writer, - stream: anytype, - inst: Inst.Index, - ) (@TypeOf(stream).Error || error{OutOfMemory})!void { - const str = self.code.instructions.items(.data)[inst].small_str.get(); - try stream.print("\"{}\")", .{std.zig.fmtEscapes(str)}); - } - fn writeSwitchCapture(self: *Writer, stream: anytype, inst: Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].switch_capture; try self.writeInstIndex(stream, inst_data.switch_inst);