authorgravatar for topolarity@tapscott.meCody Tapscott <topolarity@tapscott.me> 2022-03-21 12:18:58-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-03-23 16:29:38-04:00
loga9a91a5d49070acaa783d9d65f34a652659160c9
tree4cd4783cdaa0c5c1303dcb673ede437196fe503e
parent2f9264d8dcedba3a75dcf9d6a31b82447dfc57e8

stage2 CBE: Improve support for unions and error sets

This includes various fixes/improvements to the C backend to improve error/union support. It also fixes up our handling of decls, where some decls were not correctly marked alive.

4 files changed, 172 insertions(+), 55 deletions(-)

src/codegen/c.zig+105-35
...@@ -375,8 +375,6 @@ pub const DeclGen = struct {...@@ -375,8 +375,6 @@ pub const DeclGen = struct {
375 val: Value,375 val: Value,
376 decl: *Decl,376 decl: *Decl,
377 ) error{ OutOfMemory, AnalysisFail }!void {377 ) error{ OutOfMemory, AnalysisFail }!void {
378 decl.markAlive();
379
380 const target = dg.module.getTarget();378 const target = dg.module.getTarget();
381379
382 if (ty.isSlice()) {380 if (ty.isSlice()) {
...@@ -461,12 +459,14 @@ pub const DeclGen = struct {...@@ -461,12 +459,14 @@ pub const DeclGen = struct {
461 }459 }
462 }460 }
463461
464 // Renders a "child" pointer (e.g. ElemPtr, FieldPtr) by recursing462 // Renders a "parent" pointer by recursing to the root decl/variable
465 // to the root decl/variable that acts as its parent463 // that its contents are defined with respect to.
466 //464 //
467 // Used for .elem_ptr, .field_ptr, .opt_payload_ptr, .eu_payload_ptr, since465 // Used for .elem_ptr, .field_ptr, .opt_payload_ptr, .eu_payload_ptr
468 // the Type of their container cannot be retrieved from their own Type466 fn renderParentPtr(dg: *DeclGen, writer: anytype, ptr_val: Value, ptr_ty: Type) error{ OutOfMemory, AnalysisFail }!void {
469 fn renderChildPtr(dg: *DeclGen, writer: anytype, ptr_val: Value) error{ OutOfMemory, AnalysisFail }!Type {467 try writer.writeByte('(');
468 try dg.renderTypecast(writer, ptr_ty);
469 try writer.writeByte(')');
470 switch (ptr_val.tag()) {470 switch (ptr_val.tag()) {
471 .decl_ref_mut, .decl_ref, .variable => {471 .decl_ref_mut, .decl_ref, .variable => {
472 const decl = switch (ptr_val.tag()) {472 const decl = switch (ptr_val.tag()) {
...@@ -475,16 +475,12 @@ pub const DeclGen = struct {...@@ -475,16 +475,12 @@ pub const DeclGen = struct {
475 .variable => ptr_val.castTag(.variable).?.data.owner_decl,475 .variable => ptr_val.castTag(.variable).?.data.owner_decl,
476 else => unreachable,476 else => unreachable,
477 };477 };
478 try dg.renderDeclName(writer, decl);478 try dg.renderDeclValue(writer, ptr_ty, ptr_val, decl);
479 return decl.ty;
480 },479 },
481 .field_ptr => {480 .field_ptr => {
482 const field_ptr = ptr_val.castTag(.field_ptr).?.data;481 const field_ptr = ptr_val.castTag(.field_ptr).?.data;
482 const container_ty = field_ptr.container_ty;
483 const index = field_ptr.field_index;483 const index = field_ptr.field_index;
484
485 try writer.writeAll("&(");
486 const container_ty = try dg.renderChildPtr(writer, field_ptr.container_ptr);
487
488 const field_name = switch (container_ty.zigTypeTag()) {484 const field_name = switch (container_ty.zigTypeTag()) {
489 .Struct => container_ty.structFields().keys()[index],485 .Struct => container_ty.structFields().keys()[index],
490 .Union => container_ty.unionFields().keys()[index],486 .Union => container_ty.unionFields().keys()[index],
...@@ -495,19 +491,48 @@ pub const DeclGen = struct {...@@ -495,19 +491,48 @@ pub const DeclGen = struct {
495 .Union => container_ty.unionFields().values()[index].ty,491 .Union => container_ty.unionFields().values()[index].ty,
496 else => unreachable,492 else => unreachable,
497 };493 };
498 try writer.print(").{ }", .{fmtIdent(field_name)});494 var container_ptr_ty_pl: Type.Payload.ElemType = .{
495 .base = .{ .tag = .c_mut_pointer },
496 .data = field_ptr.container_ty,
497 };
498 const container_ptr_ty = Type.initPayload(&container_ptr_ty_pl.base);
499499
500 return field_ty;500 if (field_ty.hasRuntimeBitsIgnoreComptime()) {
501 try writer.writeAll("&(");
502 try dg.renderParentPtr(writer, field_ptr.container_ptr, container_ptr_ty);
503 if (field_ptr.container_ty.tag() == .union_tagged) {
504 try writer.print(")->payload.{ }", .{fmtIdent(field_name)});
505 } else {
506 try writer.print(")->{ }", .{fmtIdent(field_name)});
507 }
508 } else {
509 try dg.renderParentPtr(writer, field_ptr.container_ptr, field_ty);
510 }
501 },511 },
502 .elem_ptr => {512 .elem_ptr => {
503 const elem_ptr = ptr_val.castTag(.elem_ptr).?.data;513 const elem_ptr = ptr_val.castTag(.elem_ptr).?.data;
514 var elem_ptr_ty_pl: Type.Payload.ElemType = .{
515 .base = .{ .tag = .c_mut_pointer },
516 .data = elem_ptr.elem_ty,
517 };
518 const elem_ptr_ty = Type.initPayload(&elem_ptr_ty_pl.base);
519
504 try writer.writeAll("&(");520 try writer.writeAll("&(");
505 const container_ty = try dg.renderChildPtr(writer, elem_ptr.array_ptr);521 try dg.renderParentPtr(writer, elem_ptr.array_ptr, elem_ptr_ty);
506 try writer.print(")[{d}]", .{elem_ptr.index});522 try writer.print(")[{d}]", .{elem_ptr.index});
507 return container_ty.childType();
508 },523 },
509 .opt_payload_ptr => return dg.fail("implement renderChildPtr for optional payload", .{}),524 .opt_payload_ptr, .eu_payload_ptr => {
510 .eu_payload_ptr => return dg.fail("implement renderChildPtr for error union payload", .{}),525 const payload_ptr = ptr_val.cast(Value.Payload.PayloadPtr).?.data;
526 var container_ptr_ty_pl: Type.Payload.ElemType = .{
527 .base = .{ .tag = .c_mut_pointer },
528 .data = payload_ptr.container_ty,
529 };
530 const container_ptr_ty = Type.initPayload(&container_ptr_ty_pl.base);
531
532 try writer.writeAll("&(");
533 try dg.renderParentPtr(writer, payload_ptr.container_ptr, container_ptr_ty);
534 try writer.writeAll(")->payload");
535 },
511 else => unreachable,536 else => unreachable,
512 }537 }
513 }538 }
...@@ -559,6 +584,13 @@ pub const DeclGen = struct {...@@ -559,6 +584,13 @@ pub const DeclGen = struct {
559 .Int => switch (val.tag()) {584 .Int => switch (val.tag()) {
560 .int_big_positive => try dg.renderBigIntConst(writer, val.castTag(.int_big_positive).?.asBigInt(), ty.isSignedInt()),585 .int_big_positive => try dg.renderBigIntConst(writer, val.castTag(.int_big_positive).?.asBigInt(), ty.isSignedInt()),
561 .int_big_negative => try dg.renderBigIntConst(writer, val.castTag(.int_big_negative).?.asBigInt(), true),586 .int_big_negative => try dg.renderBigIntConst(writer, val.castTag(.int_big_negative).?.asBigInt(), true),
587 .field_ptr,
588 .elem_ptr,
589 .opt_payload_ptr,
590 .eu_payload_ptr,
591 .decl_ref_mut,
592 .decl_ref,
593 => try dg.renderParentPtr(writer, val, ty),
562 else => {594 else => {
563 if (ty.isSignedInt())595 if (ty.isSignedInt())
564 return writer.print("{d}", .{val.toSignedInt()});596 return writer.print("{d}", .{val.toSignedInt()});
...@@ -586,10 +618,6 @@ pub const DeclGen = struct {...@@ -586,10 +618,6 @@ pub const DeclGen = struct {
586 // to the assigned pointer type. Note this is just a hack to fix warnings from ordered comparisons (<, >, etc)618 // to the assigned pointer type. Note this is just a hack to fix warnings from ordered comparisons (<, >, etc)
587 // between pointers and 0, which is an extension to begin with.619 // between pointers and 0, which is an extension to begin with.
588 .zero => try writer.writeByte('0'),620 .zero => try writer.writeByte('0'),
589 .decl_ref => {
590 const decl = val.castTag(.decl_ref).?.data;
591 return dg.renderDeclValue(writer, ty, val, decl);
592 },
593 .variable => {621 .variable => {
594 const decl = val.castTag(.variable).?.data.owner_decl;622 const decl = val.castTag(.variable).?.data.owner_decl;
595 return dg.renderDeclValue(writer, ty, val, decl);623 return dg.renderDeclValue(writer, ty, val, decl);
...@@ -606,9 +634,6 @@ pub const DeclGen = struct {...@@ -606,9 +634,6 @@ pub const DeclGen = struct {
606 try dg.renderValue(writer, Type.usize, slice.len);634 try dg.renderValue(writer, Type.usize, slice.len);
607 try writer.writeAll("}");635 try writer.writeAll("}");
608 },636 },
609 .field_ptr, .elem_ptr, .opt_payload_ptr, .eu_payload_ptr => {
610 _ = try dg.renderChildPtr(writer, val);
611 },
612 .function => {637 .function => {
613 const func = val.castTag(.function).?.data;638 const func = val.castTag(.function).?.data;
614 try dg.renderDeclName(writer, func.owner_decl);639 try dg.renderDeclName(writer, func.owner_decl);
...@@ -622,6 +647,13 @@ pub const DeclGen = struct {...@@ -622,6 +647,13 @@ pub const DeclGen = struct {
622 try dg.renderTypecast(writer, ty);647 try dg.renderTypecast(writer, ty);
623 try writer.print(")0x{x}u)", .{val.toUnsignedInt(target)});648 try writer.print(")0x{x}u)", .{val.toUnsignedInt(target)});
624 },649 },
650 .field_ptr,
651 .elem_ptr,
652 .opt_payload_ptr,
653 .eu_payload_ptr,
654 .decl_ref_mut,
655 .decl_ref,
656 => try dg.renderParentPtr(writer, val, ty),
625 else => unreachable,657 else => unreachable,
626 },658 },
627 .Array => {659 .Array => {
...@@ -1326,7 +1358,7 @@ pub const DeclGen = struct {...@@ -1326,7 +1358,7 @@ pub const DeclGen = struct {
1326 return w.writeAll(name);1358 return w.writeAll(name);
1327 },1359 },
1328 .Struct => {1360 .Struct => {
1329 const name = dg.getTypedefName(t) orelse if (t.isTuple())1361 const name = dg.getTypedefName(t) orelse if (t.isTuple() or t.tag() == .anon_struct)
1330 try dg.renderTupleTypedef(t)1362 try dg.renderTupleTypedef(t)
1331 else1363 else
1332 try dg.renderStructTypedef(t);1364 try dg.renderStructTypedef(t);
...@@ -1346,11 +1378,11 @@ pub const DeclGen = struct {...@@ -1346,11 +1378,11 @@ pub const DeclGen = struct {
13461378
1347 try dg.renderType(w, int_tag_ty);1379 try dg.renderType(w, int_tag_ty);
1348 },1380 },
1381 .Opaque => return w.writeAll("void"),
13491382
1350 .Frame,1383 .Frame,
1351 .AnyFrame,1384 .AnyFrame,
1352 .Vector,1385 .Vector,
1353 .Opaque,
1354 => |tag| return dg.fail("TODO: C backend: implement value of type {s}", .{1386 => |tag| return dg.fail("TODO: C backend: implement value of type {s}", .{
1355 @tagName(tag),1387 @tagName(tag),
1356 }),1388 }),
...@@ -1497,6 +1529,8 @@ pub const DeclGen = struct {...@@ -1497,6 +1529,8 @@ pub const DeclGen = struct {
1497 }1529 }
14981530
1499 fn renderDeclName(dg: DeclGen, writer: anytype, decl: *Decl) !void {1531 fn renderDeclName(dg: DeclGen, writer: anytype, decl: *Decl) !void {
1532 decl.markAlive();
1533
1500 if (dg.module.decl_exports.get(decl)) |exports| {1534 if (dg.module.decl_exports.get(decl)) |exports| {
1501 return writer.writeAll(exports[0].options.name);1535 return writer.writeAll(exports[0].options.name);
1502 } else if (decl.val.tag() == .extern_fn) {1536 } else if (decl.val.tag() == .extern_fn) {
...@@ -3188,7 +3222,7 @@ fn structFieldPtr(f: *Function, inst: Air.Inst.Index, struct_ptr_ty: Type, struc...@@ -3188,7 +3222,7 @@ fn structFieldPtr(f: *Function, inst: Air.Inst.Index, struct_ptr_ty: Type, struc
3188 field_name = fields.keys()[index];3222 field_name = fields.keys()[index];
3189 field_val_ty = fields.values()[index].ty;3223 field_val_ty = fields.values()[index].ty;
3190 },3224 },
3191 .tuple => {3225 .tuple, .anon_struct => {
3192 const tuple = struct_ty.tupleFields();3226 const tuple = struct_ty.tupleFields();
3193 if (tuple.values[index].tag() != .unreachable_value) return CValue.none;3227 if (tuple.values[index].tag() != .unreachable_value) return CValue.none;
31943228
...@@ -3203,9 +3237,17 @@ fn structFieldPtr(f: *Function, inst: Air.Inst.Index, struct_ptr_ty: Type, struc...@@ -3203,9 +3237,17 @@ fn structFieldPtr(f: *Function, inst: Air.Inst.Index, struct_ptr_ty: Type, struc
3203 const inst_ty = f.air.typeOfIndex(inst);3237 const inst_ty = f.air.typeOfIndex(inst);
3204 const local = try f.allocLocal(inst_ty, .Const);3238 const local = try f.allocLocal(inst_ty, .Const);
32053239
3206 try writer.print(" = &", .{});3240 if (field_val_ty.hasRuntimeBitsIgnoreComptime()) {
3207 try f.writeCValueDeref(writer, struct_ptr);3241 try writer.writeAll(" = &");
3208 try writer.print(".{s}{ };\n", .{ payload, fmtIdent(field_name) });3242 try f.writeCValueDeref(writer, struct_ptr);
3243 try writer.print(".{s}{ };\n", .{ payload, fmtIdent(field_name) });
3244 } else {
3245 try writer.writeAll(" = (");
3246 try f.renderTypecast(writer, inst_ty);
3247 try writer.writeByte(')');
3248 try f.writeCValue(writer, struct_ptr);
3249 try writer.writeAll(";\n");
3250 }
3209 return local;3251 return local;
3210}3252}
32113253
...@@ -3223,7 +3265,7 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -3223,7 +3265,7 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue {
3223 const field_name = switch (struct_ty.tag()) {3265 const field_name = switch (struct_ty.tag()) {
3224 .@"struct" => struct_ty.structFields().keys()[extra.field_index],3266 .@"struct" => struct_ty.structFields().keys()[extra.field_index],
3225 .@"union", .union_tagged => struct_ty.unionFields().keys()[extra.field_index],3267 .@"union", .union_tagged => struct_ty.unionFields().keys()[extra.field_index],
3226 .tuple => blk: {3268 .tuple, .anon_struct => blk: {
3227 const tuple = struct_ty.tupleFields();3269 const tuple = struct_ty.tupleFields();
3228 if (tuple.values[extra.field_index].tag() != .unreachable_value) return CValue.none;3270 if (tuple.values[extra.field_index].tag() != .unreachable_value) return CValue.none;
32293271
...@@ -3348,8 +3390,36 @@ fn airWrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -3348,8 +3390,36 @@ fn airWrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue {
3348}3390}
33493391
3350fn airErrUnionPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue {3392fn airErrUnionPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue {
3351 _ = inst;3393 const writer = f.object.writer();
3352 return f.fail("TODO: C backend: implement airErrUnionPayloadPtrSet", .{});3394 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
3395 const operand = try f.resolveInst(ty_op.operand);
3396 const error_union_ty = f.air.typeOf(ty_op.operand).childType();
3397
3398 const error_ty = error_union_ty.errorUnionSet();
3399 const payload_ty = error_union_ty.errorUnionPayload();
3400
3401 // First, set the non-error value.
3402 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
3403 try f.writeCValueDeref(writer, operand);
3404 try writer.writeAll(" = ");
3405 try f.object.dg.renderValue(writer, error_ty, Value.zero);
3406 try writer.writeAll(";\n ");
3407
3408 return operand;
3409 }
3410 try f.writeCValueDeref(writer, operand);
3411 try writer.writeAll(".error = ");
3412 try f.object.dg.renderValue(writer, error_ty, Value.zero);
3413 try writer.writeAll(";\n");
3414
3415 // Then return the payload pointer (only if it is used)
3416 if (f.liveness.isUnused(inst)) return CValue.none;
3417
3418 const local = try f.allocLocal(f.air.typeOfIndex(inst), .Const);
3419 try writer.writeAll(" = &(");
3420 try f.writeCValueDeref(writer, operand);
3421 try writer.writeAll(").payload;\n");
3422 return local;
3353}3423}
33543424
3355fn airWrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue {3425fn airWrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue {
test/behavior/error.zig+12-7
...@@ -1,10 +1,20 @@...@@ -1,10 +1,20 @@
1const builtin = @import("builtin");1const builtin = @import("builtin");
2const std = @import("std");2const std = @import("std");
3const expect = std.testing.expect;3const expect = std.testing.expect;
4const expectError = std.testing.expectError;
5const expectEqual = std.testing.expectEqual;4const expectEqual = std.testing.expectEqual;
6const mem = std.mem;5const mem = std.mem;
76
7/// A more basic implementation of std.testing.expectError which
8/// does not require formatter/printing support
9fn expectError(expected_err: anyerror, observed_err_union: anytype) !void {
10 if (observed_err_union) {
11 return error.TestExpectedError;
12 } else |err| if (err == expected_err) {
13 return; // Success
14 }
15 return error.TestExpectedError;
16}
17
8test "error values" {18test "error values" {
9 const a = @errorToInt(error.err1);19 const a = @errorToInt(error.err1);
10 const b = @errorToInt(error.err2);20 const b = @errorToInt(error.err2);
...@@ -329,7 +339,6 @@ fn intLiteral(str: []const u8) !?i64 {...@@ -329,7 +339,6 @@ fn intLiteral(str: []const u8) !?i64 {
329339
330test "nested error union function call in optional unwrap" {340test "nested error union function call in optional unwrap" {
331 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO341 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
332 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
333 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO342 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
334 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO343 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
335 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO344 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
...@@ -377,7 +386,6 @@ test "nested error union function call in optional unwrap" {...@@ -377,7 +386,6 @@ test "nested error union function call in optional unwrap" {
377}386}
378387
379test "return function call to error set from error union function" {388test "return function call to error set from error union function" {
380 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
381 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO389 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
382 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO390 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
383 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO391 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
...@@ -410,7 +418,6 @@ test "optional error set is the same size as error set" {...@@ -410,7 +418,6 @@ test "optional error set is the same size as error set" {
410}418}
411419
412test "nested catch" {420test "nested catch" {
413 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
414 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO421 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
415 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO422 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
416 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO423 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
...@@ -471,7 +478,6 @@ test "function pointer with return type that is error union with payload which i...@@ -471,7 +478,6 @@ test "function pointer with return type that is error union with payload which i
471478
472test "return result loc as peer result loc in inferred error set function" {479test "return result loc as peer result loc in inferred error set function" {
473 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO480 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
474 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
475 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO481 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
476 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO482 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
477 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO483 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
...@@ -503,7 +509,6 @@ test "return result loc as peer result loc in inferred error set function" {...@@ -503,7 +509,6 @@ test "return result loc as peer result loc in inferred error set function" {
503}509}
504510
505test "error payload type is correctly resolved" {511test "error payload type is correctly resolved" {
506 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
507 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO512 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
508 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO513 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
509 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO514 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
...@@ -519,7 +524,7 @@ test "error payload type is correctly resolved" {...@@ -519,7 +524,7 @@ test "error payload type is correctly resolved" {
519 }524 }
520 };525 };
521526
522 try expectEqual(MyIntWrapper{ .x = 42 }, try MyIntWrapper.create());527 try expect(std.meta.eql(MyIntWrapper{ .x = 42 }, try MyIntWrapper.create()));
523}528}
524529
525test "error union comptime caching" {530test "error union comptime caching" {
test/behavior/ptrcast.zig+55-7
...@@ -39,7 +39,6 @@ fn testReinterpretWithOffsetAndNoWellDefinedLayout() !void {...@@ -39,7 +39,6 @@ fn testReinterpretWithOffsetAndNoWellDefinedLayout() !void {
39}39}
4040
41test "reinterpret bytes inside auto-layout struct as integer with nonzero offset" {41test "reinterpret bytes inside auto-layout struct as integer with nonzero offset" {
42 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
43 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO42 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
44 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO43 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
4544
...@@ -80,9 +79,9 @@ fn testReinterpretBytesAsExternStruct() !void {...@@ -80,9 +79,9 @@ fn testReinterpretBytesAsExternStruct() !void {
80 try expect(val == 5);79 try expect(val == 5);
81}80}
8281
83test "reinterpret bytes of an extern struct into another" {82test "reinterpret bytes of an extern struct (with under-aligned fields) into another" {
84 if (builtin.zig_backend == .stage1) return error.SkipZigTest;83 if (builtin.zig_backend == .stage1) return error.SkipZigTest;
85 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO84 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO: Under-aligned fields are not yet supported in the CBE
86 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO85 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
8786
88 try testReinterpretExternStructAsExternStruct();87 try testReinterpretExternStructAsExternStruct();
...@@ -106,12 +105,39 @@ fn testReinterpretExternStructAsExternStruct() !void {...@@ -106,12 +105,39 @@ fn testReinterpretExternStructAsExternStruct() !void {
106 try expect(val == 5);105 try expect(val == 5);
107}106}
108107
109test "lower reinterpreted comptime field ptr" {108test "reinterpret bytes of an extern struct into another" {
109 if (builtin.zig_backend == .stage1) return error.SkipZigTest;
110 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
111
112 try testReinterpretOverAlignedExternStructAsExternStruct();
113 comptime try testReinterpretOverAlignedExternStructAsExternStruct();
114}
115
116fn testReinterpretOverAlignedExternStructAsExternStruct() !void {
117 const S1 = extern struct {
118 a: u32,
119 b: u32,
120 c: u8,
121 };
122 comptime var bytes: S1 = .{ .a = 0, .b = 0, .c = 5 };
123
124 const S2 = extern struct {
125 a0: u32,
126 a1: u16,
127 a2: u16,
128 c: u8,
129 };
130 var ptr = @ptrCast(*const S2, &bytes);
131 var val = ptr.c;
132 try expect(val == 5);
133}
134
135test "lower reinterpreted comptime field ptr (with under-aligned fields)" {
110 if (builtin.zig_backend == .stage1) return error.SkipZigTest;136 if (builtin.zig_backend == .stage1) return error.SkipZigTest;
111 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO137 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
112 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO138 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
113 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO139 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
114 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO140 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO: CBE does not yet support under-aligned fields
115 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO141 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
116142
117 // Test lowering a field ptr143 // Test lowering a field ptr
...@@ -131,8 +157,31 @@ test "lower reinterpreted comptime field ptr" {...@@ -131,8 +157,31 @@ test "lower reinterpreted comptime field ptr" {
131 try expect(val2.* == 5);157 try expect(val2.* == 5);
132}158}
133159
160test "lower reinterpreted comptime field ptr" {
161 if (builtin.zig_backend == .stage1) return error.SkipZigTest;
162 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
163 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
164 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
165 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
166
167 // Test lowering a field ptr
168 comptime var bytes align(4) = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8 };
169 const S = extern struct {
170 a: u32,
171 c: u8,
172 };
173 comptime var ptr = @ptrCast(*const S, &bytes);
174 var val = &ptr.c;
175 try expect(val.* == 5);
176
177 // Test lowering an elem ptr
178 comptime var src_value = S{ .a = 15, .c = 5 };
179 comptime var ptr2 = @ptrCast(*[@sizeOf(S)]u8, &src_value);
180 var val2 = &ptr2[4];
181 try expect(val2.* == 5);
182}
183
134test "reinterpret struct field at comptime" {184test "reinterpret struct field at comptime" {
135 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
136 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO185 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
137 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO186 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
138187
...@@ -164,7 +213,6 @@ test "comptime ptrcast keeps larger alignment" {...@@ -164,7 +213,6 @@ test "comptime ptrcast keeps larger alignment" {
164}213}
165214
166test "implicit optional pointer to optional anyopaque pointer" {215test "implicit optional pointer to optional anyopaque pointer" {
167 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
168 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO216 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
169 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO217 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
170218
test/behavior/union.zig-6
...@@ -436,8 +436,6 @@ test "global union with single field is correctly initialized" {...@@ -436,8 +436,6 @@ test "global union with single field is correctly initialized" {
436 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;436 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
437 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;437 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
438438
439 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
440
441 glbl = Foo1{439 glbl = Foo1{
442 .f = @typeInfo(Foo1).Union.fields[0].field_type{ .x = 123 },440 .f = @typeInfo(Foo1).Union.fields[0].field_type{ .x = 123 },
443 };441 };
...@@ -456,8 +454,6 @@ test "initialize global array of union" {...@@ -456,8 +454,6 @@ test "initialize global array of union" {
456 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;454 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
457 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;455 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
458456
459 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
460
461 glbl_array[1] = FooUnion{ .U1 = 2 };457 glbl_array[1] = FooUnion{ .U1 = 2 };
462 glbl_array[0] = FooUnion{ .U0 = 1 };458 glbl_array[0] = FooUnion{ .U0 = 1 };
463 try expect(glbl_array[0].U0 == 1);459 try expect(glbl_array[0].U0 == 1);
...@@ -1033,7 +1029,6 @@ test "switching on non exhaustive union" {...@@ -1033,7 +1029,6 @@ test "switching on non exhaustive union" {
1033}1029}
10341030
1035test "containers with single-field enums" {1031test "containers with single-field enums" {
1036 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
1037 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO1032 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1038 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO1033 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
10391034
...@@ -1113,7 +1108,6 @@ test "union enum type gets a separate scope" {...@@ -1113,7 +1108,6 @@ test "union enum type gets a separate scope" {
1113}1108}
11141109
1115test "global variable struct contains union initialized to non-most-aligned field" {1110test "global variable struct contains union initialized to non-most-aligned field" {
1116 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
1117 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO1111 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1118 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO1112 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1119 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO1113 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO