| author | |
| committer | |
| log | a9a91a5d49070acaa783d9d65f34a652659160c9 |
| tree | 4cd4783cdaa0c5c1303dcb673ede437196fe503e |
| parent | 2f9264d8dcedba3a75dcf9d6a31b82447dfc57e8 |
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 | 375 | val: Value, |
| 376 | 376 | decl: *Decl, |
| 377 | 377 | ) error{ OutOfMemory, AnalysisFail }!void { |
| 378 | decl.markAlive(); | |
| 379 | ||
| 380 | 378 | const target = dg.module.getTarget(); |
| 381 | 379 | |
| 382 | 380 | if (ty.isSlice()) { |
| ... | ... | @@ -461,12 +459,14 @@ pub const DeclGen = struct { |
| 461 | 459 | } |
| 462 | 460 | } |
| 463 | 461 | |
| 464 | // Renders a "child" pointer (e.g. ElemPtr, FieldPtr) by recursing | |
| 465 | // to the root decl/variable that acts as its parent | |
| 462 | // Renders a "parent" pointer by recursing to the root decl/variable | |
| 463 | // that its contents are defined with respect to. | |
| 466 | 464 | // |
| 467 | // Used for .elem_ptr, .field_ptr, .opt_payload_ptr, .eu_payload_ptr, since | |
| 468 | // the Type of their container cannot be retrieved from their own Type | |
| 469 | fn renderChildPtr(dg: *DeclGen, writer: anytype, ptr_val: Value) error{ OutOfMemory, AnalysisFail }!Type { | |
| 465 | // Used for .elem_ptr, .field_ptr, .opt_payload_ptr, .eu_payload_ptr | |
| 466 | fn renderParentPtr(dg: *DeclGen, writer: anytype, ptr_val: Value, ptr_ty: Type) error{ OutOfMemory, AnalysisFail }!void { | |
| 467 | try writer.writeByte('('); | |
| 468 | try dg.renderTypecast(writer, ptr_ty); | |
| 469 | try writer.writeByte(')'); | |
| 470 | 470 | switch (ptr_val.tag()) { |
| 471 | 471 | .decl_ref_mut, .decl_ref, .variable => { |
| 472 | 472 | const decl = switch (ptr_val.tag()) { |
| ... | ... | @@ -475,16 +475,12 @@ pub const DeclGen = struct { |
| 475 | 475 | .variable => ptr_val.castTag(.variable).?.data.owner_decl, |
| 476 | 476 | else => unreachable, |
| 477 | 477 | }; |
| 478 | try dg.renderDeclName(writer, decl); | |
| 479 | return decl.ty; | |
| 478 | try dg.renderDeclValue(writer, ptr_ty, ptr_val, decl); | |
| 480 | 479 | }, |
| 481 | 480 | .field_ptr => { |
| 482 | 481 | const field_ptr = ptr_val.castTag(.field_ptr).?.data; |
| 482 | const container_ty = field_ptr.container_ty; | |
| 483 | 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 | 484 | const field_name = switch (container_ty.zigTypeTag()) { |
| 489 | 485 | .Struct => container_ty.structFields().keys()[index], |
| 490 | 486 | .Union => container_ty.unionFields().keys()[index], |
| ... | ... | @@ -495,19 +491,48 @@ pub const DeclGen = struct { |
| 495 | 491 | .Union => container_ty.unionFields().values()[index].ty, |
| 496 | 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); | |
| 499 | 499 | |
| 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 | 512 | .elem_ptr => { |
| 503 | 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 | 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 | 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", .{}), | |
| 510 | .eu_payload_ptr => return dg.fail("implement renderChildPtr for error union payload", .{}), | |
| 524 | .opt_payload_ptr, .eu_payload_ptr => { | |
| 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 | 536 | else => unreachable, |
| 512 | 537 | } |
| 513 | 538 | } |
| ... | ... | @@ -559,6 +584,13 @@ pub const DeclGen = struct { |
| 559 | 584 | .Int => switch (val.tag()) { |
| 560 | 585 | .int_big_positive => try dg.renderBigIntConst(writer, val.castTag(.int_big_positive).?.asBigInt(), ty.isSignedInt()), |
| 561 | 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 | 594 | else => { |
| 563 | 595 | if (ty.isSignedInt()) |
| 564 | 596 | return writer.print("{d}", .{val.toSignedInt()}); |
| ... | ... | @@ -586,10 +618,6 @@ pub const DeclGen = struct { |
| 586 | 618 | // to the assigned pointer type. Note this is just a hack to fix warnings from ordered comparisons (<, >, etc) |
| 587 | 619 | // between pointers and 0, which is an extension to begin with. |
| 588 | 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 | 621 | .variable => { |
| 594 | 622 | const decl = val.castTag(.variable).?.data.owner_decl; |
| 595 | 623 | return dg.renderDeclValue(writer, ty, val, decl); |
| ... | ... | @@ -606,9 +634,6 @@ pub const DeclGen = struct { |
| 606 | 634 | try dg.renderValue(writer, Type.usize, slice.len); |
| 607 | 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 | 637 | .function => { |
| 613 | 638 | const func = val.castTag(.function).?.data; |
| 614 | 639 | try dg.renderDeclName(writer, func.owner_decl); |
| ... | ... | @@ -622,6 +647,13 @@ pub const DeclGen = struct { |
| 622 | 647 | try dg.renderTypecast(writer, ty); |
| 623 | 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 | 657 | else => unreachable, |
| 626 | 658 | }, |
| 627 | 659 | .Array => { |
| ... | ... | @@ -1326,7 +1358,7 @@ pub const DeclGen = struct { |
| 1326 | 1358 | return w.writeAll(name); |
| 1327 | 1359 | }, |
| 1328 | 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 | 1362 | try dg.renderTupleTypedef(t) |
| 1331 | 1363 | else |
| 1332 | 1364 | try dg.renderStructTypedef(t); |
| ... | ... | @@ -1346,11 +1378,11 @@ pub const DeclGen = struct { |
| 1346 | 1378 | |
| 1347 | 1379 | try dg.renderType(w, int_tag_ty); |
| 1348 | 1380 | }, |
| 1381 | .Opaque => return w.writeAll("void"), | |
| 1349 | 1382 | |
| 1350 | 1383 | .Frame, |
| 1351 | 1384 | .AnyFrame, |
| 1352 | 1385 | .Vector, |
| 1353 | .Opaque, | |
| 1354 | 1386 | => |tag| return dg.fail("TODO: C backend: implement value of type {s}", .{ |
| 1355 | 1387 | @tagName(tag), |
| 1356 | 1388 | }), |
| ... | ... | @@ -1497,6 +1529,8 @@ pub const DeclGen = struct { |
| 1497 | 1529 | } |
| 1498 | 1530 | |
| 1499 | 1531 | fn renderDeclName(dg: DeclGen, writer: anytype, decl: *Decl) !void { |
| 1532 | decl.markAlive(); | |
| 1533 | ||
| 1500 | 1534 | if (dg.module.decl_exports.get(decl)) |exports| { |
| 1501 | 1535 | return writer.writeAll(exports[0].options.name); |
| 1502 | 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 | 3222 | field_name = fields.keys()[index]; |
| 3189 | 3223 | field_val_ty = fields.values()[index].ty; |
| 3190 | 3224 | }, |
| 3191 | .tuple => { | |
| 3225 | .tuple, .anon_struct => { | |
| 3192 | 3226 | const tuple = struct_ty.tupleFields(); |
| 3193 | 3227 | if (tuple.values[index].tag() != .unreachable_value) return CValue.none; |
| 3194 | 3228 | |
| ... | ... | @@ -3203,9 +3237,17 @@ fn structFieldPtr(f: *Function, inst: Air.Inst.Index, struct_ptr_ty: Type, struc |
| 3203 | 3237 | const inst_ty = f.air.typeOfIndex(inst); |
| 3204 | 3238 | const local = try f.allocLocal(inst_ty, .Const); |
| 3205 | 3239 | |
| 3206 | try writer.print(" = &", .{}); | |
| 3207 | try f.writeCValueDeref(writer, struct_ptr); | |
| 3208 | try writer.print(".{s}{ };\n", .{ payload, fmtIdent(field_name) }); | |
| 3240 | if (field_val_ty.hasRuntimeBitsIgnoreComptime()) { | |
| 3241 | try writer.writeAll(" = &"); | |
| 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 | 3251 | return local; |
| 3210 | 3252 | } |
| 3211 | 3253 | |
| ... | ... | @@ -3223,7 +3265,7 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3223 | 3265 | const field_name = switch (struct_ty.tag()) { |
| 3224 | 3266 | .@"struct" => struct_ty.structFields().keys()[extra.field_index], |
| 3225 | 3267 | .@"union", .union_tagged => struct_ty.unionFields().keys()[extra.field_index], |
| 3226 | .tuple => blk: { | |
| 3268 | .tuple, .anon_struct => blk: { | |
| 3227 | 3269 | const tuple = struct_ty.tupleFields(); |
| 3228 | 3270 | if (tuple.values[extra.field_index].tag() != .unreachable_value) return CValue.none; |
| 3229 | 3271 | |
| ... | ... | @@ -3348,8 +3390,36 @@ fn airWrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3348 | 3390 | } |
| 3349 | 3391 | |
| 3350 | 3392 | fn airErrUnionPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3351 | _ = inst; | |
| 3352 | return f.fail("TODO: C backend: implement airErrUnionPayloadPtrSet", .{}); | |
| 3393 | const writer = f.object.writer(); | |
| 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 | } |
| 3354 | 3424 | |
| 3355 | 3425 | fn airWrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue { |
test/behavior/error.zig+12-7| ... | ... | @@ -1,10 +1,20 @@ |
| 1 | 1 | const builtin = @import("builtin"); |
| 2 | 2 | const std = @import("std"); |
| 3 | 3 | const expect = std.testing.expect; |
| 4 | const expectError = std.testing.expectError; | |
| 5 | 4 | const expectEqual = std.testing.expectEqual; |
| 6 | 5 | const mem = std.mem; |
| 7 | 6 | |
| 7 | /// A more basic implementation of std.testing.expectError which | |
| 8 | /// does not require formatter/printing support | |
| 9 | fn 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 | ||
| 8 | 18 | test "error values" { |
| 9 | 19 | const a = @errorToInt(error.err1); |
| 10 | 20 | const b = @errorToInt(error.err2); |
| ... | ... | @@ -329,7 +339,6 @@ fn intLiteral(str: []const u8) !?i64 { |
| 329 | 339 | |
| 330 | 340 | test "nested error union function call in optional unwrap" { |
| 331 | 341 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 332 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | |
| 333 | 342 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 334 | 343 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 335 | 344 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| ... | ... | @@ -377,7 +386,6 @@ test "nested error union function call in optional unwrap" { |
| 377 | 386 | } |
| 378 | 387 | |
| 379 | 388 | test "return function call to error set from error union function" { |
| 380 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | |
| 381 | 389 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 382 | 390 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 383 | 391 | 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 | 418 | } |
| 411 | 419 | |
| 412 | 420 | test "nested catch" { |
| 413 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | |
| 414 | 421 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 415 | 422 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 416 | 423 | 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 | 478 | |
| 472 | 479 | test "return result loc as peer result loc in inferred error set function" { |
| 473 | 480 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 474 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | |
| 475 | 481 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 476 | 482 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 477 | 483 | 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 | 509 | } |
| 504 | 510 | |
| 505 | 511 | test "error payload type is correctly resolved" { |
| 506 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | |
| 507 | 512 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 508 | 513 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 509 | 514 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| ... | ... | @@ -519,7 +524,7 @@ test "error payload type is correctly resolved" { |
| 519 | 524 | } |
| 520 | 525 | }; |
| 521 | 526 | |
| 522 | try expectEqual(MyIntWrapper{ .x = 42 }, try MyIntWrapper.create()); | |
| 527 | try expect(std.meta.eql(MyIntWrapper{ .x = 42 }, try MyIntWrapper.create())); | |
| 523 | 528 | } |
| 524 | 529 | |
| 525 | 530 | test "error union comptime caching" { |
test/behavior/ptrcast.zig+55-7| ... | ... | @@ -39,7 +39,6 @@ fn testReinterpretWithOffsetAndNoWellDefinedLayout() !void { |
| 39 | 39 | } |
| 40 | 40 | |
| 41 | 41 | test "reinterpret bytes inside auto-layout struct as integer with nonzero offset" { |
| 42 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | |
| 43 | 42 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 44 | 43 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 45 | 44 | |
| ... | ... | @@ -80,9 +79,9 @@ fn testReinterpretBytesAsExternStruct() !void { |
| 80 | 79 | try expect(val == 5); |
| 81 | 80 | } |
| 82 | 81 | |
| 83 | test "reinterpret bytes of an extern struct into another" { | |
| 82 | test "reinterpret bytes of an extern struct (with under-aligned fields) into another" { | |
| 84 | 83 | if (builtin.zig_backend == .stage1) return error.SkipZigTest; |
| 85 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | |
| 84 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO: Under-aligned fields are not yet supported in the CBE | |
| 86 | 85 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 87 | 86 | |
| 88 | 87 | try testReinterpretExternStructAsExternStruct(); |
| ... | ... | @@ -106,12 +105,39 @@ fn testReinterpretExternStructAsExternStruct() !void { |
| 106 | 105 | try expect(val == 5); |
| 107 | 106 | } |
| 108 | 107 | |
| 109 | test "lower reinterpreted comptime field ptr" { | |
| 108 | test "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 | ||
| 116 | fn 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 | ||
| 135 | test "lower reinterpreted comptime field ptr (with under-aligned fields)" { | |
| 110 | 136 | if (builtin.zig_backend == .stage1) return error.SkipZigTest; |
| 111 | 137 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 112 | 138 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 113 | 139 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 114 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | |
| 140 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO: CBE does not yet support under-aligned fields | |
| 115 | 141 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO |
| 116 | 142 | |
| 117 | 143 | // Test lowering a field ptr |
| ... | ... | @@ -131,8 +157,31 @@ test "lower reinterpreted comptime field ptr" { |
| 131 | 157 | try expect(val2.* == 5); |
| 132 | 158 | } |
| 133 | 159 | |
| 160 | test "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 | ||
| 134 | 184 | test "reinterpret struct field at comptime" { |
| 135 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | |
| 136 | 185 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 137 | 186 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 138 | 187 | |
| ... | ... | @@ -164,7 +213,6 @@ test "comptime ptrcast keeps larger alignment" { |
| 164 | 213 | } |
| 165 | 214 | |
| 166 | 215 | test "implicit optional pointer to optional anyopaque pointer" { |
| 167 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | |
| 168 | 216 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 169 | 217 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 170 | 218 |
test/behavior/union.zig-6| ... | ... | @@ -436,8 +436,6 @@ test "global union with single field is correctly initialized" { |
| 436 | 436 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 437 | 437 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 438 | 438 | |
| 439 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | |
| 440 | ||
| 441 | 439 | glbl = Foo1{ |
| 442 | 440 | .f = @typeInfo(Foo1).Union.fields[0].field_type{ .x = 123 }, |
| 443 | 441 | }; |
| ... | ... | @@ -456,8 +454,6 @@ test "initialize global array of union" { |
| 456 | 454 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 457 | 455 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 458 | 456 | |
| 459 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | |
| 460 | ||
| 461 | 457 | glbl_array[1] = FooUnion{ .U1 = 2 }; |
| 462 | 458 | glbl_array[0] = FooUnion{ .U0 = 1 }; |
| 463 | 459 | try expect(glbl_array[0].U0 == 1); |
| ... | ... | @@ -1033,7 +1029,6 @@ test "switching on non exhaustive union" { |
| 1033 | 1029 | } |
| 1034 | 1030 | |
| 1035 | 1031 | test "containers with single-field enums" { |
| 1036 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | |
| 1037 | 1032 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 1038 | 1033 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 1039 | 1034 | |
| ... | ... | @@ -1113,7 +1108,6 @@ test "union enum type gets a separate scope" { |
| 1113 | 1108 | } |
| 1114 | 1109 | |
| 1115 | 1110 | test "global variable struct contains union initialized to non-most-aligned field" { |
| 1116 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | |
| 1117 | 1111 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 1118 | 1112 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 1119 | 1113 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |