Commit 982acc22fd

Andrew Kelley <andrew@ziglang.org>
2021-01-01 01:24:36
stage2: compile error for invalid `var` type
1 parent 79a9391
src/astgen.zig
@@ -625,7 +625,7 @@ fn varDecl(
                 const alloc = try addZIRUnOp(mod, scope, name_src, .alloc_mut, type_inst);
                 break :a .{ .alloc = alloc, .result_loc = .{ .ptr = alloc } };
             } else a: {
-                const alloc = try addZIRNoOpT(mod, scope, name_src, .alloc_inferred);
+                const alloc = try addZIRNoOpT(mod, scope, name_src, .alloc_inferred_mut);
                 resolve_inferred_alloc = &alloc.base;
                 break :a .{ .alloc = &alloc.base, .result_loc = .{ .inferred_ptr = alloc } };
             };
src/Module.zig
@@ -3421,3 +3421,9 @@ pub fn getTarget(self: Module) Target {
 pub fn optimizeMode(self: Module) std.builtin.Mode {
     return self.comp.bin_file.options.optimize_mode;
 }
+
+pub fn validateVarType(mod: *Module, scope: *Scope, src: usize, ty: Type) !void {
+    if (!ty.isValidVarType(false)) {
+        return mod.fail(scope, src, "variable of type '{}' must be const or comptime", .{ty});
+    }
+}
src/type.zig
@@ -78,7 +78,8 @@ pub const Type = extern union {
             .const_slice,
             .mut_slice,
             .pointer,
-            .inferred_alloc,
+            .inferred_alloc_const,
+            .inferred_alloc_mut,
             => return .Pointer,
 
             .optional,
@@ -159,7 +160,8 @@ pub const Type = extern union {
             .optional_single_mut_pointer,
             => self.cast(Payload.ElemType),
 
-            .inferred_alloc => unreachable,
+            .inferred_alloc_const => unreachable,
+            .inferred_alloc_mut => unreachable,
 
             else => null,
         };
@@ -387,7 +389,8 @@ pub const Type = extern union {
             .enum_literal,
             .anyerror_void_error_union,
             .@"anyframe",
-            .inferred_alloc,
+            .inferred_alloc_const,
+            .inferred_alloc_mut,
             => unreachable,
 
             .array_u8,
@@ -690,7 +693,8 @@ pub const Type = extern union {
                     const name = ty.castTag(.error_set_single).?.data;
                     return out_stream.print("error{{{s}}}", .{name});
                 },
-                .inferred_alloc => return out_stream.writeAll("(inferred allocation type)"),
+                .inferred_alloc_const => return out_stream.writeAll("(inferred_alloc_const)"),
+                .inferred_alloc_mut => return out_stream.writeAll("(inferred_alloc_mut)"),
             }
             unreachable;
         }
@@ -738,7 +742,8 @@ pub const Type = extern union {
             .single_const_pointer_to_comptime_int => return Value.initTag(.single_const_pointer_to_comptime_int_type),
             .const_slice_u8 => return Value.initTag(.const_slice_u8_type),
             .enum_literal => return Value.initTag(.enum_literal_type),
-            .inferred_alloc => unreachable,
+            .inferred_alloc_const => unreachable,
+            .inferred_alloc_mut => unreachable,
             else => return Value.Tag.ty.create(allocator, self),
         }
     }
@@ -810,7 +815,8 @@ pub const Type = extern union {
             .empty_struct,
             => false,
 
-            .inferred_alloc => unreachable,
+            .inferred_alloc_const => unreachable,
+            .inferred_alloc_mut => unreachable,
         };
     }
 
@@ -928,7 +934,8 @@ pub const Type = extern union {
             .@"undefined",
             .enum_literal,
             .empty_struct,
-            .inferred_alloc,
+            .inferred_alloc_const,
+            .inferred_alloc_mut,
             => unreachable,
         };
     }
@@ -952,7 +959,8 @@ pub const Type = extern union {
             .enum_literal => unreachable,
             .single_const_pointer_to_comptime_int => unreachable,
             .empty_struct => unreachable,
-            .inferred_alloc => unreachable,
+            .inferred_alloc_const => unreachable,
+            .inferred_alloc_mut => unreachable,
 
             .u8,
             .i8,
@@ -1131,7 +1139,8 @@ pub const Type = extern union {
             .single_const_pointer,
             .single_mut_pointer,
             .single_const_pointer_to_comptime_int,
-            .inferred_alloc,
+            .inferred_alloc_const,
+            .inferred_alloc_mut,
             => true,
 
             .pointer => self.castTag(.pointer).?.data.size == .One,
@@ -1214,7 +1223,8 @@ pub const Type = extern union {
             .single_const_pointer,
             .single_mut_pointer,
             .single_const_pointer_to_comptime_int,
-            .inferred_alloc,
+            .inferred_alloc_const,
+            .inferred_alloc_mut,
             => .One,
 
             .pointer => self.castTag(.pointer).?.data.size,
@@ -1285,7 +1295,8 @@ pub const Type = extern union {
             .error_set,
             .error_set_single,
             .empty_struct,
-            .inferred_alloc,
+            .inferred_alloc_const,
+            .inferred_alloc_mut,
             => false,
 
             .const_slice,
@@ -1358,7 +1369,8 @@ pub const Type = extern union {
             .error_set,
             .error_set_single,
             .empty_struct,
-            .inferred_alloc,
+            .inferred_alloc_const,
+            .inferred_alloc_mut,
             => false,
 
             .single_const_pointer,
@@ -1440,7 +1452,8 @@ pub const Type = extern union {
             .error_set,
             .error_set_single,
             .empty_struct,
-            .inferred_alloc,
+            .inferred_alloc_const,
+            .inferred_alloc_mut,
             => false,
 
             .pointer => {
@@ -1517,7 +1530,8 @@ pub const Type = extern union {
             .error_set,
             .error_set_single,
             .empty_struct,
-            .inferred_alloc,
+            .inferred_alloc_const,
+            .inferred_alloc_mut,
             => false,
 
             .pointer => {
@@ -1636,7 +1650,8 @@ pub const Type = extern union {
             .error_set => unreachable,
             .error_set_single => unreachable,
             .empty_struct => unreachable,
-            .inferred_alloc => unreachable,
+            .inferred_alloc_const => unreachable,
+            .inferred_alloc_mut => unreachable,
 
             .array => self.castTag(.array).?.data.elem_type,
             .array_sentinel => self.castTag(.array_sentinel).?.data.elem_type,
@@ -1758,7 +1773,8 @@ pub const Type = extern union {
             .error_set,
             .error_set_single,
             .empty_struct,
-            .inferred_alloc,
+            .inferred_alloc_const,
+            .inferred_alloc_mut,
             => unreachable,
 
             .array => self.castTag(.array).?.data.len,
@@ -1825,7 +1841,8 @@ pub const Type = extern union {
             .error_set,
             .error_set_single,
             .empty_struct,
-            .inferred_alloc,
+            .inferred_alloc_const,
+            .inferred_alloc_mut,
             => unreachable,
 
             .single_const_pointer,
@@ -1909,7 +1926,8 @@ pub const Type = extern union {
             .error_set,
             .error_set_single,
             .empty_struct,
-            .inferred_alloc,
+            .inferred_alloc_const,
+            .inferred_alloc_mut,
             => false,
 
             .int_signed,
@@ -1985,7 +2003,8 @@ pub const Type = extern union {
             .error_set,
             .error_set_single,
             .empty_struct,
-            .inferred_alloc,
+            .inferred_alloc_const,
+            .inferred_alloc_mut,
             => false,
 
             .int_unsigned,
@@ -2051,7 +2070,8 @@ pub const Type = extern union {
             .error_set,
             .error_set_single,
             .empty_struct,
-            .inferred_alloc,
+            .inferred_alloc_const,
+            .inferred_alloc_mut,
             => unreachable,
 
             .int_unsigned => .{
@@ -2141,7 +2161,8 @@ pub const Type = extern union {
             .error_set,
             .error_set_single,
             .empty_struct,
-            .inferred_alloc,
+            .inferred_alloc_const,
+            .inferred_alloc_mut,
             => false,
 
             .usize,
@@ -2254,7 +2275,8 @@ pub const Type = extern union {
             .error_set,
             .error_set_single,
             .empty_struct,
-            .inferred_alloc,
+            .inferred_alloc_const,
+            .inferred_alloc_mut,
             => unreachable,
         };
     }
@@ -2333,7 +2355,8 @@ pub const Type = extern union {
             .error_set,
             .error_set_single,
             .empty_struct,
-            .inferred_alloc,
+            .inferred_alloc_const,
+            .inferred_alloc_mut,
             => unreachable,
         }
     }
@@ -2411,7 +2434,8 @@ pub const Type = extern union {
             .error_set,
             .error_set_single,
             .empty_struct,
-            .inferred_alloc,
+            .inferred_alloc_const,
+            .inferred_alloc_mut,
             => unreachable,
         }
     }
@@ -2489,7 +2513,8 @@ pub const Type = extern union {
             .error_set,
             .error_set_single,
             .empty_struct,
-            .inferred_alloc,
+            .inferred_alloc_const,
+            .inferred_alloc_mut,
             => unreachable,
         };
     }
@@ -2564,7 +2589,8 @@ pub const Type = extern union {
             .error_set,
             .error_set_single,
             .empty_struct,
-            .inferred_alloc,
+            .inferred_alloc_const,
+            .inferred_alloc_mut,
             => unreachable,
         };
     }
@@ -2639,7 +2665,8 @@ pub const Type = extern union {
             .error_set,
             .error_set_single,
             .empty_struct,
-            .inferred_alloc,
+            .inferred_alloc_const,
+            .inferred_alloc_mut,
             => unreachable,
         };
     }
@@ -2714,7 +2741,8 @@ pub const Type = extern union {
             .error_set,
             .error_set_single,
             .empty_struct,
-            .inferred_alloc,
+            .inferred_alloc_const,
+            .inferred_alloc_mut,
             => false,
         };
     }
@@ -2807,7 +2835,8 @@ pub const Type = extern union {
                 ty = ty.castTag(.pointer).?.data.pointee_type;
                 continue;
             },
-            .inferred_alloc => unreachable,
+            .inferred_alloc_const => unreachable,
+            .inferred_alloc_mut => unreachable,
         };
     }
 
@@ -2876,7 +2905,8 @@ pub const Type = extern union {
             .error_set,
             .error_set_single,
             .empty_struct,
-            .inferred_alloc,
+            .inferred_alloc_const,
+            .inferred_alloc_mut,
             => return false,
 
             .c_const_pointer,
@@ -2962,7 +2992,8 @@ pub const Type = extern union {
             .c_const_pointer,
             .c_mut_pointer,
             .pointer,
-            .inferred_alloc,
+            .inferred_alloc_const,
+            .inferred_alloc_mut,
             => unreachable,
 
             .empty_struct => self.castTag(.empty_struct).?.data,
@@ -3077,7 +3108,9 @@ pub const Type = extern union {
         /// This is a special value that tracks a set of types that have been stored
         /// to an inferred allocation. It does not support most of the normal type queries.
         /// However it does respond to `isConstPtr`, `ptrSize`, `zigTypeTag`, etc.
-        inferred_alloc, // See last_no_payload_tag below.
+        inferred_alloc_mut,
+        /// Same as `inferred_alloc_mut` but the local is `var` not `const`.
+        inferred_alloc_const, // See last_no_payload_tag below.
         // After this, the tag requires a payload.
 
         array_u8,
@@ -3105,7 +3138,7 @@ pub const Type = extern union {
         error_set_single,
         empty_struct,
 
-        pub const last_no_payload_tag = Tag.inferred_alloc;
+        pub const last_no_payload_tag = Tag.inferred_alloc_const;
         pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
 
         pub fn Type(comptime t: Tag) type {
@@ -3152,7 +3185,8 @@ pub const Type = extern union {
                 .anyerror_void_error_union,
                 .@"anyframe",
                 .const_slice_u8,
-                .inferred_alloc,
+                .inferred_alloc_const,
+                .inferred_alloc_mut,
                 => @compileError("Type Tag " ++ @tagName(t) ++ " has no payload"),
 
                 .array_u8,
src/zir_sema.zig
@@ -30,8 +30,18 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!
     switch (old_inst.tag) {
         .alloc => return analyzeInstAlloc(mod, scope, old_inst.castTag(.alloc).?),
         .alloc_mut => return analyzeInstAllocMut(mod, scope, old_inst.castTag(.alloc_mut).?),
-        .alloc_inferred => return analyzeInstAllocInferred(mod, scope, old_inst.castTag(.alloc_inferred).?),
-        .alloc_inferred_mut => return analyzeInstAllocInferredMut(mod, scope, old_inst.castTag(.alloc_inferred_mut).?),
+        .alloc_inferred => return analyzeInstAllocInferred(
+            mod,
+            scope,
+            old_inst.castTag(.alloc_inferred).?,
+            .inferred_alloc_const,
+        ),
+        .alloc_inferred_mut => return analyzeInstAllocInferred(
+            mod,
+            scope,
+            old_inst.castTag(.alloc_inferred_mut).?,
+            .inferred_alloc_mut,
+        ),
         .arg => return analyzeInstArg(mod, scope, old_inst.castTag(.arg).?),
         .bitcast_ref => return analyzeInstBitCastRef(mod, scope, old_inst.castTag(.bitcast_ref).?),
         .bitcast_result_ptr => return analyzeInstBitCastResultPtr(mod, scope, old_inst.castTag(.bitcast_result_ptr).?),
@@ -423,15 +433,18 @@ fn analyzeInstAlloc(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerErro
 
 fn analyzeInstAllocMut(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
     const var_type = try resolveType(mod, scope, inst.positionals.operand);
-    if (!var_type.isValidVarType(false)) {
-        return mod.fail(scope, inst.base.src, "variable of type '{}' must be const or comptime", .{var_type});
-    }
+    try mod.validateVarType(scope, inst.base.src, var_type);
     const ptr_type = try mod.simplePtrType(scope, inst.base.src, var_type, true, .One);
     const b = try mod.requireRuntimeBlock(scope, inst.base.src);
     return mod.addNoOp(b, inst.base.src, ptr_type, .alloc);
 }
 
-fn analyzeInstAllocInferred(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {
+fn analyzeInstAllocInferred(
+    mod: *Module,
+    scope: *Scope,
+    inst: *zir.Inst.NoOp,
+    mut_tag: Type.Tag,
+) InnerError!*Inst {
     const val_payload = try scope.arena().create(Value.Payload.InferredAlloc);
     val_payload.* = .{
         .data = .{},
@@ -441,7 +454,11 @@ fn analyzeInstAllocInferred(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) I
     // to a normal instruction when we hit `resolve_inferred_alloc`. So we append
     // to the block even though it is currently a `.constant`.
     const result = try mod.constInst(scope, inst.base.src, .{
-        .ty = Type.initTag(.inferred_alloc),
+        .ty = switch (mut_tag) {
+            .inferred_alloc_const => Type.initTag(.inferred_alloc_const),
+            .inferred_alloc_mut => Type.initTag(.inferred_alloc_mut),
+            else => unreachable,
+        },
         .val = Value.initPayload(&val_payload.base),
     });
     const block = try mod.requireFunctionBlock(scope, inst.base.src);
@@ -449,10 +466,6 @@ fn analyzeInstAllocInferred(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) I
     return result;
 }
 
-fn analyzeInstAllocInferredMut(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {
-    return mod.fail(scope, inst.base.src, "TODO implement analyzeInstAllocInferredMut", .{});
-}
-
 fn analyzeInstResolveInferredAlloc(
     mod: *Module,
     scope: *Scope,
@@ -463,8 +476,15 @@ fn analyzeInstResolveInferredAlloc(
     const inferred_alloc = ptr_val.castTag(.inferred_alloc).?;
     const peer_inst_list = inferred_alloc.data.stored_inst_list.items;
     const final_elem_ty = try mod.resolvePeerTypes(scope, peer_inst_list);
-    const is_mut = true;
-    const final_ptr_ty = try mod.simplePtrType(scope, inst.base.src, final_elem_ty, is_mut, .One);
+    const var_is_mut = switch (ptr.ty.tag()) {
+        .inferred_alloc_const => false,
+        .inferred_alloc_mut => true,
+        else => unreachable,
+    };
+    if (var_is_mut) {
+        try mod.validateVarType(scope, inst.base.src, final_elem_ty);
+    }
+    const final_ptr_ty = try mod.simplePtrType(scope, inst.base.src, final_elem_ty, true, .One);
 
     // Change it to a normal alloc.
     ptr.ty = final_ptr_ty;
test/stage2/test.zig
@@ -1322,4 +1322,13 @@ pub fn addCases(ctx: *TestContext) !void {
             \\}
         , &[_][]const u8{":2:5: error: unused for label"});
     }
+
+    {
+        var case = ctx.exe("bad inferred variable type", linux_x64);
+        case.addError(
+            \\export fn foo() void {
+            \\    var x = null;
+            \\}
+        , &[_][]const u8{":2:9: error: variable of type '@Type(.Null)' must be const or comptime"});
+    }
 }
BRANCH_TODO
@@ -1,2 +0,0 @@
- * compile error for "variable of type '{}' must be const or comptime" after resolving types
- * test with branches