Commit ea00ddfe37

Andrew Kelley <andrew@ziglang.org>
2021-04-22 05:42:49
AstGen: implement comptime locals
1 parent 8ee0cbe
Changed files (3)
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 } };
             };
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;
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);