Commit 1d1364c3cd

Andrew Kelley <andrew@ziglang.org>
2022-03-18 03:59:20
Sema: change how undefined is handled in coerce
Instead of doing it before the switch tower, do it afterwards, so that special handling may be done before undefined gets casted to the destination type. In this case the special handling we want to do is *[N]T to []T setting the slice length based on the array length, even when the array value is undefined.
1 parent 46ba240
Changed files (2)
src
test
behavior
src/Sema.zig
@@ -16945,10 +16945,11 @@ fn coerce(
 
     const arena = sema.arena;
     const target = sema.mod.getTarget();
+    const maybe_inst_val = try sema.resolveMaybeUndefVal(block, inst_src, inst);
 
     const in_memory_result = try sema.coerceInMemoryAllowed(block, dest_ty, inst_ty, false, target, dest_ty_src, inst_src);
     if (in_memory_result == .ok) {
-        if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |val| {
+        if (maybe_inst_val) |val| {
             // Keep the comptime Value representation; take the new type.
             return sema.addConstant(dest_ty, val);
         }
@@ -16956,16 +16957,15 @@ fn coerce(
         return block.addBitCast(dest_ty, inst);
     }
 
-    // undefined to anything
-    if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |val| {
-        if (val.isUndef() or inst_ty.zigTypeTag() == .Undefined) {
-            return sema.addConstant(dest_ty, val);
-        }
-    }
-    assert(inst_ty.zigTypeTag() != .Undefined);
+    const is_undef = if (maybe_inst_val) |val| val.isUndef() else false;
 
     switch (dest_ty.zigTypeTag()) {
         .Optional => {
+            // undefined sets the optional bit also to undefined.
+            if (is_undef) {
+                return sema.addConstUndef(dest_ty);
+            }
+
             // null to ?T
             if (inst_ty.zigTypeTag() == .Null) {
                 return sema.addConstant(dest_ty, Value.@"null");
@@ -17167,8 +17167,8 @@ fn coerce(
                     }
                 },
                 .Many => p: {
+                    if (!inst_ty.isSlice()) break :p;
                     const inst_info = inst_ty.ptrInfo().data;
-                    if (inst_info.size != .Slice) break :p;
 
                     switch (try sema.coerceInMemoryAllowed(
                         block,
@@ -17191,9 +17191,6 @@ fn coerce(
                     return sema.coerceCompatiblePtrs(block, dest_ty, slice_ptr, inst_src);
                 },
             }
-
-            // This will give an extra hint on top of what the bottom of this func would provide.
-            try sema.checkPtrOperand(block, dest_ty_src, inst_ty);
         },
         .Int, .ComptimeInt => switch (inst_ty.zigTypeTag()) {
             .Float, .ComptimeFloat => float: {
@@ -17230,6 +17227,9 @@ fn coerce(
                     return block.addTyOp(.intcast, dest_ty, inst);
                 }
             },
+            .Undefined => {
+                return sema.addConstUndef(dest_ty);
+            },
             else => {},
         },
         .Float, .ComptimeFloat => switch (inst_ty.zigTypeTag()) {
@@ -17275,6 +17275,9 @@ fn coerce(
                 //}
                 return try sema.addConstant(dest_ty, result_val);
             },
+            .Undefined => {
+                return sema.addConstUndef(dest_ty);
+            },
             else => {},
         },
         .Enum => switch (inst_ty.zigTypeTag()) {
@@ -17313,11 +17316,14 @@ fn coerce(
                     return sema.unionToTag(block, dest_ty, inst, inst_src);
                 }
             },
+            .Undefined => {
+                return sema.addConstUndef(dest_ty);
+            },
             else => {},
         },
         .ErrorUnion => switch (inst_ty.zigTypeTag()) {
             .ErrorUnion => {
-                if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |inst_val| {
+                if (maybe_inst_val) |inst_val| {
                     switch (inst_val.tag()) {
                         .undef => return sema.addConstUndef(dest_ty),
                         .eu_payload => {
@@ -17341,7 +17347,15 @@ fn coerce(
                 // E to E!T
                 return sema.wrapErrorUnionSet(block, dest_ty, inst, inst_src);
             },
+            .Undefined => {
+                return sema.addConstUndef(dest_ty);
+            },
             else => {
+                // undefined sets the error code also to undefined.
+                if (is_undef) {
+                    return sema.addConstUndef(dest_ty);
+                }
+
                 // T to E!T
                 return sema.wrapErrorUnionPayload(block, dest_ty, inst, inst_src);
             },
@@ -17353,6 +17367,9 @@ fn coerce(
                     return sema.coerceAnonStructToUnion(block, dest_ty, dest_ty_src, inst, inst_src);
                 }
             },
+            .Undefined => {
+                return sema.addConstUndef(dest_ty);
+            },
             else => {},
         },
         .Array => switch (inst_ty.zigTypeTag()) {
@@ -17365,10 +17382,16 @@ fn coerce(
                     return sema.coerceTupleToArray(block, dest_ty, dest_ty_src, inst, inst_src);
                 }
             },
+            .Undefined => {
+                return sema.addConstUndef(dest_ty);
+            },
             else => {},
         },
         .Vector => switch (inst_ty.zigTypeTag()) {
             .Array, .Vector => return sema.coerceArrayLike(block, dest_ty, dest_ty_src, inst, inst_src),
+            .Undefined => {
+                return sema.addConstUndef(dest_ty);
+            },
             else => {},
         },
         .Struct => {
@@ -17382,6 +17405,13 @@ fn coerce(
         else => {},
     }
 
+    // undefined to anything. We do this after the big switch above so that
+    // special logic has a chance to run first, such as `*[N]T` to `[]T` which
+    // should initialize the length field of the slice.
+    if (is_undef) {
+        return sema.addConstUndef(dest_ty);
+    }
+
     return sema.fail(block, inst_src, "expected {}, found {}", .{ dest_ty, inst_ty });
 }
 
@@ -18452,7 +18482,7 @@ fn coerceArrayPtrToSlice(
     inst: Air.Inst.Ref,
     inst_src: LazySrcLoc,
 ) CompileError!Air.Inst.Ref {
-    if (try sema.resolveDefinedValue(block, inst_src, inst)) |val| {
+    if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |val| {
         const ptr_array_ty = sema.typeOf(inst);
         const array_ty = ptr_array_ty.childType();
         const slice_val = try Value.Tag.slice.create(sema.arena, .{
@@ -21336,7 +21366,7 @@ fn addIntUnsigned(sema: *Sema, ty: Type, int: u64) CompileError!Air.Inst.Ref {
 }
 
 fn addConstUndef(sema: *Sema, ty: Type) CompileError!Air.Inst.Ref {
-    return sema.addConstant(ty, Value.initTag(.undef));
+    return sema.addConstant(ty, Value.undef);
 }
 
 pub fn addConstant(sema: *Sema, ty: Type, val: Value) SemaError!Air.Inst.Ref {
test/behavior/struct.zig
@@ -1138,11 +1138,9 @@ test "packed struct with undefined initializers" {
 }
 
 test "for loop over pointers to struct, getting field from struct pointer" {
-    // When enabling this test, be careful. I have observed it to pass when compiling
-    // stage2 alone, but when using stage1 with -fno-stage1 -fLLVM it fails.
-    // Maybe eyeball the LLVM that it generates and run in valgrind, both the compiler
-    // and the generated test at runtime.
-    if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
 
     const S = struct {
         const Foo = struct {