authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-03-17 19:59:20-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-03-17 19:59:20-07:00
log1d1364c3cd296d628501bce131513ce18d2055a3
tree1cbd0803b10a57c980edca7223875d15e5a41af9
parent46ba24010af521936e416160decb660fe56cb484

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.

2 files changed, 48 insertions(+), 20 deletions(-)

src/Sema.zig+45-15
...@@ -16945,10 +16945,11 @@ fn coerce(...@@ -16945,10 +16945,11 @@ fn coerce(
1694516945
16946 const arena = sema.arena;16946 const arena = sema.arena;
16947 const target = sema.mod.getTarget();16947 const target = sema.mod.getTarget();
16948 const maybe_inst_val = try sema.resolveMaybeUndefVal(block, inst_src, inst);
1694816949
16949 const in_memory_result = try sema.coerceInMemoryAllowed(block, dest_ty, inst_ty, false, target, dest_ty_src, inst_src);16950 const in_memory_result = try sema.coerceInMemoryAllowed(block, dest_ty, inst_ty, false, target, dest_ty_src, inst_src);
16950 if (in_memory_result == .ok) {16951 if (in_memory_result == .ok) {
16951 if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |val| {16952 if (maybe_inst_val) |val| {
16952 // Keep the comptime Value representation; take the new type.16953 // Keep the comptime Value representation; take the new type.
16953 return sema.addConstant(dest_ty, val);16954 return sema.addConstant(dest_ty, val);
16954 }16955 }
...@@ -16956,16 +16957,15 @@ fn coerce(...@@ -16956,16 +16957,15 @@ fn coerce(
16956 return block.addBitCast(dest_ty, inst);16957 return block.addBitCast(dest_ty, inst);
16957 }16958 }
1695816959
16959 // undefined to anything16960 const is_undef = if (maybe_inst_val) |val| val.isUndef() else false;
16960 if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |val| {
16961 if (val.isUndef() or inst_ty.zigTypeTag() == .Undefined) {
16962 return sema.addConstant(dest_ty, val);
16963 }
16964 }
16965 assert(inst_ty.zigTypeTag() != .Undefined);
1696616961
16967 switch (dest_ty.zigTypeTag()) {16962 switch (dest_ty.zigTypeTag()) {
16968 .Optional => {16963 .Optional => {
16964 // undefined sets the optional bit also to undefined.
16965 if (is_undef) {
16966 return sema.addConstUndef(dest_ty);
16967 }
16968
16969 // null to ?T16969 // null to ?T
16970 if (inst_ty.zigTypeTag() == .Null) {16970 if (inst_ty.zigTypeTag() == .Null) {
16971 return sema.addConstant(dest_ty, Value.@"null");16971 return sema.addConstant(dest_ty, Value.@"null");
...@@ -17167,8 +17167,8 @@ fn coerce(...@@ -17167,8 +17167,8 @@ fn coerce(
17167 }17167 }
17168 },17168 },
17169 .Many => p: {17169 .Many => p: {
17170 if (!inst_ty.isSlice()) break :p;
17170 const inst_info = inst_ty.ptrInfo().data;17171 const inst_info = inst_ty.ptrInfo().data;
17171 if (inst_info.size != .Slice) break :p;
1717217172
17173 switch (try sema.coerceInMemoryAllowed(17173 switch (try sema.coerceInMemoryAllowed(
17174 block,17174 block,
...@@ -17191,9 +17191,6 @@ fn coerce(...@@ -17191,9 +17191,6 @@ fn coerce(
17191 return sema.coerceCompatiblePtrs(block, dest_ty, slice_ptr, inst_src);17191 return sema.coerceCompatiblePtrs(block, dest_ty, slice_ptr, inst_src);
17192 },17192 },
17193 }17193 }
17194
17195 // This will give an extra hint on top of what the bottom of this func would provide.
17196 try sema.checkPtrOperand(block, dest_ty_src, inst_ty);
17197 },17194 },
17198 .Int, .ComptimeInt => switch (inst_ty.zigTypeTag()) {17195 .Int, .ComptimeInt => switch (inst_ty.zigTypeTag()) {
17199 .Float, .ComptimeFloat => float: {17196 .Float, .ComptimeFloat => float: {
...@@ -17230,6 +17227,9 @@ fn coerce(...@@ -17230,6 +17227,9 @@ fn coerce(
17230 return block.addTyOp(.intcast, dest_ty, inst);17227 return block.addTyOp(.intcast, dest_ty, inst);
17231 }17228 }
17232 },17229 },
17230 .Undefined => {
17231 return sema.addConstUndef(dest_ty);
17232 },
17233 else => {},17233 else => {},
17234 },17234 },
17235 .Float, .ComptimeFloat => switch (inst_ty.zigTypeTag()) {17235 .Float, .ComptimeFloat => switch (inst_ty.zigTypeTag()) {
...@@ -17275,6 +17275,9 @@ fn coerce(...@@ -17275,6 +17275,9 @@ fn coerce(
17275 //}17275 //}
17276 return try sema.addConstant(dest_ty, result_val);17276 return try sema.addConstant(dest_ty, result_val);
17277 },17277 },
17278 .Undefined => {
17279 return sema.addConstUndef(dest_ty);
17280 },
17278 else => {},17281 else => {},
17279 },17282 },
17280 .Enum => switch (inst_ty.zigTypeTag()) {17283 .Enum => switch (inst_ty.zigTypeTag()) {
...@@ -17313,11 +17316,14 @@ fn coerce(...@@ -17313,11 +17316,14 @@ fn coerce(
17313 return sema.unionToTag(block, dest_ty, inst, inst_src);17316 return sema.unionToTag(block, dest_ty, inst, inst_src);
17314 }17317 }
17315 },17318 },
17319 .Undefined => {
17320 return sema.addConstUndef(dest_ty);
17321 },
17316 else => {},17322 else => {},
17317 },17323 },
17318 .ErrorUnion => switch (inst_ty.zigTypeTag()) {17324 .ErrorUnion => switch (inst_ty.zigTypeTag()) {
17319 .ErrorUnion => {17325 .ErrorUnion => {
17320 if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |inst_val| {17326 if (maybe_inst_val) |inst_val| {
17321 switch (inst_val.tag()) {17327 switch (inst_val.tag()) {
17322 .undef => return sema.addConstUndef(dest_ty),17328 .undef => return sema.addConstUndef(dest_ty),
17323 .eu_payload => {17329 .eu_payload => {
...@@ -17341,7 +17347,15 @@ fn coerce(...@@ -17341,7 +17347,15 @@ fn coerce(
17341 // E to E!T17347 // E to E!T
17342 return sema.wrapErrorUnionSet(block, dest_ty, inst, inst_src);17348 return sema.wrapErrorUnionSet(block, dest_ty, inst, inst_src);
17343 },17349 },
17350 .Undefined => {
17351 return sema.addConstUndef(dest_ty);
17352 },
17344 else => {17353 else => {
17354 // undefined sets the error code also to undefined.
17355 if (is_undef) {
17356 return sema.addConstUndef(dest_ty);
17357 }
17358
17345 // T to E!T17359 // T to E!T
17346 return sema.wrapErrorUnionPayload(block, dest_ty, inst, inst_src);17360 return sema.wrapErrorUnionPayload(block, dest_ty, inst, inst_src);
17347 },17361 },
...@@ -17353,6 +17367,9 @@ fn coerce(...@@ -17353,6 +17367,9 @@ fn coerce(
17353 return sema.coerceAnonStructToUnion(block, dest_ty, dest_ty_src, inst, inst_src);17367 return sema.coerceAnonStructToUnion(block, dest_ty, dest_ty_src, inst, inst_src);
17354 }17368 }
17355 },17369 },
17370 .Undefined => {
17371 return sema.addConstUndef(dest_ty);
17372 },
17356 else => {},17373 else => {},
17357 },17374 },
17358 .Array => switch (inst_ty.zigTypeTag()) {17375 .Array => switch (inst_ty.zigTypeTag()) {
...@@ -17365,10 +17382,16 @@ fn coerce(...@@ -17365,10 +17382,16 @@ fn coerce(
17365 return sema.coerceTupleToArray(block, dest_ty, dest_ty_src, inst, inst_src);17382 return sema.coerceTupleToArray(block, dest_ty, dest_ty_src, inst, inst_src);
17366 }17383 }
17367 },17384 },
17385 .Undefined => {
17386 return sema.addConstUndef(dest_ty);
17387 },
17368 else => {},17388 else => {},
17369 },17389 },
17370 .Vector => switch (inst_ty.zigTypeTag()) {17390 .Vector => switch (inst_ty.zigTypeTag()) {
17371 .Array, .Vector => return sema.coerceArrayLike(block, dest_ty, dest_ty_src, inst, inst_src),17391 .Array, .Vector => return sema.coerceArrayLike(block, dest_ty, dest_ty_src, inst, inst_src),
17392 .Undefined => {
17393 return sema.addConstUndef(dest_ty);
17394 },
17372 else => {},17395 else => {},
17373 },17396 },
17374 .Struct => {17397 .Struct => {
...@@ -17382,6 +17405,13 @@ fn coerce(...@@ -17382,6 +17405,13 @@ fn coerce(
17382 else => {},17405 else => {},
17383 }17406 }
1738417407
17408 // undefined to anything. We do this after the big switch above so that
17409 // special logic has a chance to run first, such as `*[N]T` to `[]T` which
17410 // should initialize the length field of the slice.
17411 if (is_undef) {
17412 return sema.addConstUndef(dest_ty);
17413 }
17414
17385 return sema.fail(block, inst_src, "expected {}, found {}", .{ dest_ty, inst_ty });17415 return sema.fail(block, inst_src, "expected {}, found {}", .{ dest_ty, inst_ty });
17386}17416}
1738717417
...@@ -18452,7 +18482,7 @@ fn coerceArrayPtrToSlice(...@@ -18452,7 +18482,7 @@ fn coerceArrayPtrToSlice(
18452 inst: Air.Inst.Ref,18482 inst: Air.Inst.Ref,
18453 inst_src: LazySrcLoc,18483 inst_src: LazySrcLoc,
18454) CompileError!Air.Inst.Ref {18484) CompileError!Air.Inst.Ref {
18455 if (try sema.resolveDefinedValue(block, inst_src, inst)) |val| {18485 if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |val| {
18456 const ptr_array_ty = sema.typeOf(inst);18486 const ptr_array_ty = sema.typeOf(inst);
18457 const array_ty = ptr_array_ty.childType();18487 const array_ty = ptr_array_ty.childType();
18458 const slice_val = try Value.Tag.slice.create(sema.arena, .{18488 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 {...@@ -21336,7 +21366,7 @@ fn addIntUnsigned(sema: *Sema, ty: Type, int: u64) CompileError!Air.Inst.Ref {
21336}21366}
2133721367
21338fn addConstUndef(sema: *Sema, ty: Type) CompileError!Air.Inst.Ref {21368fn addConstUndef(sema: *Sema, ty: Type) CompileError!Air.Inst.Ref {
21339 return sema.addConstant(ty, Value.initTag(.undef));21369 return sema.addConstant(ty, Value.undef);
21340}21370}
2134121371
21342pub fn addConstant(sema: *Sema, ty: Type, val: Value) SemaError!Air.Inst.Ref {21372pub fn addConstant(sema: *Sema, ty: Type, val: Value) SemaError!Air.Inst.Ref {
test/behavior/struct.zig+3-5
...@@ -1138,11 +1138,9 @@ test "packed struct with undefined initializers" {...@@ -1138,11 +1138,9 @@ test "packed struct with undefined initializers" {
1138}1138}
11391139
1140test "for loop over pointers to struct, getting field from struct pointer" {1140test "for loop over pointers to struct, getting field from struct pointer" {
1141 // When enabling this test, be careful. I have observed it to pass when compiling1141 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1142 // stage2 alone, but when using stage1 with -fno-stage1 -fLLVM it fails.1142 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1143 // Maybe eyeball the LLVM that it generates and run in valgrind, both the compiler1143 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1144 // and the generated test at runtime.
1145 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
11461144
1147 const S = struct {1145 const S = struct {
1148 const Foo = struct {1146 const Foo = struct {