| author | |
| committer | |
| log | bcf15e39e2d4e2243f475852aca7749e40a70fbd |
| tree | 367c697ea25a0649a263d13131ffaf0ec3f82ad4 |
| parent | dae22a0a1f13cc963e96cd704941eed29b8dde27 |
Decl objects need to know whether they are the owner of the Type/Value
associated with them, in order to decide whether to destroy the
associated Namespace, Fn, or Var when cleaning up.6 files changed, 128 insertions(+), 59 deletions(-)
BRANCH_TODO-10| ... | ... | @@ -1,13 +1,3 @@ |
| 1 | * The next problem is that when trying to deinitialize everything, when we | |
| 2 | deinit a Decl that is the owner of a Namespace, there may still be other Decl | |
| 3 | objects that reference that Namespace. They want to check if they are the owner | |
| 4 | in order to find out if they should destroy it. But they can't check if they are | |
| 5 | the owner because the owner_decl field is destroyed. | |
| 6 | So there's a memory management problem to solve. We could easily solve this with | |
| 7 | ref counting or whatever but the goal is to not introduce extra overhead / unnecessary | |
| 8 | fields just to help figure out how to free stuff. So come up with some way to make | |
| 9 | this sound, and easily debuggable when something goes wrong. | |
| 10 | ||
| 11 | 1 | * get stage2 tests passing |
| 12 | 2 | * modify stage2 tests so that only 1 uses _start and the rest use |
| 13 | 3 | pub fn main |
src/Module.zig+57-29| ... | ... | @@ -227,6 +227,10 @@ pub const Decl = struct { |
| 227 | 227 | }, |
| 228 | 228 | /// Whether `typed_value`, `align_val`, and `linksection_val` are populated. |
| 229 | 229 | has_tv: bool, |
| 230 | /// If `true` it means the `Decl` is the resource owner of the type/value associated | |
| 231 | /// with it. That means when `Decl` is destroyed, the cleanup code should additionally | |
| 232 | /// check if the value owns a `Namespace`, and destroy that too. | |
| 233 | owns_tv: bool, | |
| 230 | 234 | /// This flag is set when this Decl is added to `Module.deletion_set`, and cleared |
| 231 | 235 | /// when removed. |
| 232 | 236 | deletion_flag: bool, |
| ... | ... | @@ -278,9 +282,7 @@ pub const Decl = struct { |
| 278 | 282 | decl.clearName(gpa); |
| 279 | 283 | if (decl.has_tv) { |
| 280 | 284 | if (decl.getInnerNamespace()) |namespace| { |
| 281 | if (namespace.getDecl() == decl) { | |
| 282 | namespace.clearDecls(module); | |
| 283 | } | |
| 285 | namespace.clearDecls(module); | |
| 284 | 286 | } |
| 285 | 287 | decl.clearValues(gpa); |
| 286 | 288 | } |
| ... | ... | @@ -307,6 +309,7 @@ pub const Decl = struct { |
| 307 | 309 | arena_state.promote(gpa).deinit(); |
| 308 | 310 | decl.value_arena = null; |
| 309 | 311 | decl.has_tv = false; |
| 312 | decl.owns_tv = false; | |
| 310 | 313 | } |
| 311 | 314 | } |
| 312 | 315 | |
| ... | ... | @@ -441,36 +444,36 @@ pub const Decl = struct { |
| 441 | 444 | /// If the Decl has a value and it is a struct, return it, |
| 442 | 445 | /// otherwise null. |
| 443 | 446 | pub fn getStruct(decl: *Decl) ?*Struct { |
| 444 | if (!decl.has_tv) return null; | |
| 447 | if (!decl.owns_tv) return null; | |
| 445 | 448 | const ty = (decl.val.castTag(.ty) orelse return null).data; |
| 446 | 449 | const struct_obj = (ty.castTag(.@"struct") orelse return null).data; |
| 447 | if (struct_obj.owner_decl != decl) return null; | |
| 450 | assert(struct_obj.owner_decl == decl); | |
| 448 | 451 | return struct_obj; |
| 449 | 452 | } |
| 450 | 453 | |
| 451 | 454 | /// If the Decl has a value and it is a union, return it, |
| 452 | 455 | /// otherwise null. |
| 453 | 456 | pub fn getUnion(decl: *Decl) ?*Union { |
| 454 | if (!decl.has_tv) return null; | |
| 457 | if (!decl.owns_tv) return null; | |
| 455 | 458 | const ty = (decl.val.castTag(.ty) orelse return null).data; |
| 456 | 459 | const union_obj = (ty.cast(Type.Payload.Union) orelse return null).data; |
| 457 | if (union_obj.owner_decl != decl) return null; | |
| 460 | assert(union_obj.owner_decl == decl); | |
| 458 | 461 | return union_obj; |
| 459 | 462 | } |
| 460 | 463 | |
| 461 | 464 | /// If the Decl has a value and it is a function, return it, |
| 462 | 465 | /// otherwise null. |
| 463 | 466 | pub fn getFunction(decl: *Decl) ?*Fn { |
| 464 | if (!decl.has_tv) return null; | |
| 467 | if (!decl.owns_tv) return null; | |
| 465 | 468 | const func = (decl.val.castTag(.function) orelse return null).data; |
| 466 | if (func.owner_decl != decl) return null; | |
| 469 | assert(func.owner_decl == decl); | |
| 467 | 470 | return func; |
| 468 | 471 | } |
| 469 | 472 | |
| 470 | 473 | pub fn getVariable(decl: *Decl) ?*Var { |
| 471 | if (!decl.has_tv) return null; | |
| 474 | if (!decl.owns_tv) return null; | |
| 472 | 475 | const variable = (decl.val.castTag(.variable) orelse return null).data; |
| 473 | if (variable.owner_decl != decl) return null; | |
| 476 | assert(variable.owner_decl == decl); | |
| 474 | 477 | return variable; |
| 475 | 478 | } |
| 476 | 479 | |
| ... | ... | @@ -478,29 +481,28 @@ pub const Decl = struct { |
| 478 | 481 | /// enum, or opaque. |
| 479 | 482 | /// Only returns it if the Decl is the owner. |
| 480 | 483 | pub fn getInnerNamespace(decl: *Decl) ?*Scope.Namespace { |
| 481 | if (!decl.has_tv) return null; | |
| 484 | if (!decl.owns_tv) return null; | |
| 482 | 485 | const ty = (decl.val.castTag(.ty) orelse return null).data; |
| 483 | 486 | switch (ty.tag()) { |
| 484 | 487 | .@"struct" => { |
| 485 | 488 | const struct_obj = ty.castTag(.@"struct").?.data; |
| 486 | if (struct_obj.owner_decl != decl) return null; | |
| 489 | assert(struct_obj.owner_decl == decl); | |
| 487 | 490 | return &struct_obj.namespace; |
| 488 | 491 | }, |
| 489 | 492 | .enum_full => { |
| 490 | 493 | const enum_obj = ty.castTag(.enum_full).?.data; |
| 491 | if (enum_obj.owner_decl != decl) return null; | |
| 494 | assert(enum_obj.owner_decl == decl); | |
| 492 | 495 | return &enum_obj.namespace; |
| 493 | 496 | }, |
| 494 | 497 | .empty_struct => { |
| 495 | // design flaw, can't verify the owner is this decl | |
| 496 | @panic("TODO can't implement getInnerNamespace for this type"); | |
| 498 | return ty.castTag(.empty_struct).?.data; | |
| 497 | 499 | }, |
| 498 | 500 | .@"opaque" => { |
| 499 | 501 | @panic("TODO opaque types"); |
| 500 | 502 | }, |
| 501 | 503 | .@"union", .union_tagged => { |
| 502 | 504 | const union_obj = ty.cast(Type.Payload.Union).?.data; |
| 503 | if (union_obj.owner_decl != decl) return null; | |
| 505 | assert(union_obj.owner_decl == decl); | |
| 504 | 506 | return &union_obj.namespace; |
| 505 | 507 | }, |
| 506 | 508 | |
| ... | ... | @@ -554,7 +556,11 @@ pub const Decl = struct { |
| 554 | 556 | |
| 555 | 557 | .complete, |
| 556 | 558 | .outdated, |
| 557 | => true, | |
| 559 | => { | |
| 560 | if (!decl.owns_tv) | |
| 561 | return false; | |
| 562 | return decl.ty.hasCodeGenBits(); | |
| 563 | }, | |
| 558 | 564 | }; |
| 559 | 565 | } |
| 560 | 566 | }; |
| ... | ... | @@ -2838,6 +2844,7 @@ pub fn semaFile(mod: *Module, file: *Scope.File) InnerError!void { |
| 2838 | 2844 | new_decl.ty = struct_ty; |
| 2839 | 2845 | new_decl.val = struct_val; |
| 2840 | 2846 | new_decl.has_tv = true; |
| 2847 | new_decl.owns_tv = true; | |
| 2841 | 2848 | new_decl.analysis = .complete; |
| 2842 | 2849 | new_decl.generation = mod.generation; |
| 2843 | 2850 | |
| ... | ... | @@ -2948,7 +2955,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { |
| 2948 | 2955 | errdefer decl_arena.deinit(); |
| 2949 | 2956 | const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State); |
| 2950 | 2957 | |
| 2951 | if (decl_tv.val.tag() == .function) { | |
| 2958 | if (decl_tv.val.castTag(.function)) |fn_payload| { | |
| 2952 | 2959 | var prev_type_has_bits = false; |
| 2953 | 2960 | var prev_is_inline = false; |
| 2954 | 2961 | var type_changed = true; |
| ... | ... | @@ -2956,10 +2963,8 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { |
| 2956 | 2963 | if (decl.has_tv) { |
| 2957 | 2964 | prev_type_has_bits = decl.ty.hasCodeGenBits(); |
| 2958 | 2965 | type_changed = !decl.ty.eql(decl_tv.ty); |
| 2959 | if (decl.val.castTag(.function)) |payload| { | |
| 2960 | const prev_func = payload.data; | |
| 2966 | if (decl.getFunction()) |prev_func| { | |
| 2961 | 2967 | prev_is_inline = prev_func.state == .inline_only; |
| 2962 | prev_func.deinit(gpa); | |
| 2963 | 2968 | } |
| 2964 | 2969 | decl.clearValues(gpa); |
| 2965 | 2970 | } |
| ... | ... | @@ -2969,6 +2974,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { |
| 2969 | 2974 | decl.align_val = try align_val.copy(&decl_arena.allocator); |
| 2970 | 2975 | decl.linksection_val = try linksection_val.copy(&decl_arena.allocator); |
| 2971 | 2976 | decl.has_tv = true; |
| 2977 | decl.owns_tv = fn_payload.data.owner_decl == decl; | |
| 2972 | 2978 | decl_arena_state.* = decl_arena.state; |
| 2973 | 2979 | decl.value_arena = decl_arena_state; |
| 2974 | 2980 | decl.analysis = .complete; |
| ... | ... | @@ -3004,6 +3010,25 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { |
| 3004 | 3010 | decl.clearValues(gpa); |
| 3005 | 3011 | } |
| 3006 | 3012 | |
| 3013 | decl.owns_tv = false; | |
| 3014 | var queue_linker_work = false; | |
| 3015 | if (decl_tv.val.castTag(.variable)) |payload| { | |
| 3016 | const variable = payload.data; | |
| 3017 | if (variable.owner_decl == decl) { | |
| 3018 | decl.owns_tv = true; | |
| 3019 | queue_linker_work = true; | |
| 3020 | ||
| 3021 | const copied_init = try variable.init.copy(&decl_arena.allocator); | |
| 3022 | variable.init = copied_init; | |
| 3023 | } | |
| 3024 | } else if (decl_tv.val.castTag(.extern_fn)) |payload| { | |
| 3025 | const owner_decl = payload.data; | |
| 3026 | if (decl == owner_decl) { | |
| 3027 | decl.owns_tv = true; | |
| 3028 | queue_linker_work = true; | |
| 3029 | } | |
| 3030 | } | |
| 3031 | ||
| 3007 | 3032 | decl.ty = try decl_tv.ty.copy(&decl_arena.allocator); |
| 3008 | 3033 | decl.val = try decl_tv.val.copy(&decl_arena.allocator); |
| 3009 | 3034 | decl.align_val = try align_val.copy(&decl_arena.allocator); |
| ... | ... | @@ -3014,13 +3039,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { |
| 3014 | 3039 | decl.analysis = .complete; |
| 3015 | 3040 | decl.generation = mod.generation; |
| 3016 | 3041 | |
| 3017 | if (decl.is_exported) { | |
| 3018 | const export_src = src; // TODO point to the export token | |
| 3019 | // The scope needs to have the decl in it. | |
| 3020 | try mod.analyzeExport(&block_scope.base, export_src, mem.spanZ(decl.name), decl); | |
| 3021 | } | |
| 3022 | ||
| 3023 | if (decl.val.tag() == .extern_fn) { | |
| 3042 | if (queue_linker_work and decl.ty.hasCodeGenBits()) { | |
| 3024 | 3043 | try mod.comp.bin_file.allocateDeclIndexes(decl); |
| 3025 | 3044 | try mod.comp.work_queue.writeItem(.{ .codegen_decl = decl }); |
| 3026 | 3045 | |
| ... | ... | @@ -3029,6 +3048,13 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { |
| 3029 | 3048 | } |
| 3030 | 3049 | } |
| 3031 | 3050 | |
| 3051 | if (decl.is_exported) { | |
| 3052 | const export_src = src; // TODO point to the export token | |
| 3053 | // The scope needs to have the decl in it. | |
| 3054 | try mod.analyzeExport(&block_scope.base, export_src, mem.spanZ(decl.name), decl); | |
| 3055 | } | |
| 3056 | ||
| 3057 | ||
| 3032 | 3058 | return type_changed; |
| 3033 | 3059 | } |
| 3034 | 3060 | } |
| ... | ... | @@ -3531,6 +3557,7 @@ fn allocateNewDecl(mod: *Module, namespace: *Scope.Namespace, src_node: ast.Node |
| 3531 | 3557 | .src_node = src_node, |
| 3532 | 3558 | .src_line = undefined, |
| 3533 | 3559 | .has_tv = false, |
| 3560 | .owns_tv = false, | |
| 3534 | 3561 | .ty = undefined, |
| 3535 | 3562 | .val = undefined, |
| 3536 | 3563 | .align_val = undefined, |
| ... | ... | @@ -3779,6 +3806,7 @@ pub fn createAnonymousDeclNamed( |
| 3779 | 3806 | new_decl.ty = typed_value.ty; |
| 3780 | 3807 | new_decl.val = typed_value.val; |
| 3781 | 3808 | new_decl.has_tv = true; |
| 3809 | new_decl.owns_tv = true; | |
| 3782 | 3810 | new_decl.analysis = .complete; |
| 3783 | 3811 | new_decl.generation = mod.generation; |
| 3784 | 3812 |
src/Sema.zig+1-1| ... | ... | @@ -5867,7 +5867,7 @@ fn zirVarExtended( |
| 5867 | 5867 | extra_index += 1; |
| 5868 | 5868 | const init_tv = try sema.resolveInstConst(block, init_src, init_ref); |
| 5869 | 5869 | break :blk init_tv.val; |
| 5870 | } else Value.initTag(.null_value); | |
| 5870 | } else Value.initTag(.unreachable_value); | |
| 5871 | 5871 | |
| 5872 | 5872 | if (!var_ty.isValidVarType(small.is_extern)) { |
| 5873 | 5873 | return sema.mod.fail(&block.base, mut_src, "variable of type '{}' must be const", .{ |
src/codegen/c.zig+51-4| ... | ... | @@ -30,6 +30,7 @@ pub const CValue = union(enum) { |
| 30 | 30 | arg: usize, |
| 31 | 31 | /// By-value |
| 32 | 32 | decl: *Decl, |
| 33 | decl_ref: *Decl, | |
| 33 | 34 | }; |
| 34 | 35 | |
| 35 | 36 | pub const CValueMap = std.AutoHashMap(*Inst, CValue); |
| ... | ... | @@ -117,6 +118,7 @@ pub const Object = struct { |
| 117 | 118 | .constant => |inst| return o.dg.renderValue(w, inst.ty, inst.value().?), |
| 118 | 119 | .arg => |i| return w.print("a{d}", .{i}), |
| 119 | 120 | .decl => |decl| return w.writeAll(mem.span(decl.name)), |
| 121 | .decl_ref => |decl| return w.print("&{s}", .{decl.name}), | |
| 120 | 122 | } |
| 121 | 123 | } |
| 122 | 124 | |
| ... | ... | @@ -528,13 +530,17 @@ pub const DeclGen = struct { |
| 528 | 530 | } |
| 529 | 531 | } |
| 530 | 532 | |
| 531 | fn functionIsGlobal(dg: *DeclGen, tv: TypedValue) bool { | |
| 533 | fn declIsGlobal(dg: *DeclGen, tv: TypedValue) bool { | |
| 532 | 534 | switch (tv.val.tag()) { |
| 533 | 535 | .extern_fn => return true, |
| 534 | 536 | .function => { |
| 535 | 537 | const func = tv.val.castTag(.function).?.data; |
| 536 | 538 | return dg.module.decl_exports.contains(func.owner_decl); |
| 537 | 539 | }, |
| 540 | .variable => { | |
| 541 | const variable = tv.val.castTag(.variable).?.data; | |
| 542 | return dg.module.decl_exports.contains(variable.owner_decl); | |
| 543 | }, | |
| 538 | 544 | else => unreachable, |
| 539 | 545 | } |
| 540 | 546 | } |
| ... | ... | @@ -549,7 +555,7 @@ pub fn genDecl(o: *Object) !void { |
| 549 | 555 | .val = o.dg.decl.val, |
| 550 | 556 | }; |
| 551 | 557 | if (tv.val.castTag(.function)) |func_payload| { |
| 552 | const is_global = o.dg.functionIsGlobal(tv); | |
| 558 | const is_global = o.dg.declIsGlobal(tv); | |
| 553 | 559 | const fwd_decl_writer = o.dg.fwd_decl.writer(); |
| 554 | 560 | if (is_global) { |
| 555 | 561 | try fwd_decl_writer.writeAll("ZIG_EXTERN_C "); |
| ... | ... | @@ -570,6 +576,30 @@ pub fn genDecl(o: *Object) !void { |
| 570 | 576 | try writer.writeAll("ZIG_EXTERN_C "); |
| 571 | 577 | try o.dg.renderFunctionSignature(writer, true); |
| 572 | 578 | try writer.writeAll(";\n"); |
| 579 | } else if (tv.val.castTag(.variable)) |var_payload| { | |
| 580 | const variable: *Module.Var = var_payload.data; | |
| 581 | const is_global = o.dg.declIsGlobal(tv); | |
| 582 | const fwd_decl_writer = o.dg.fwd_decl.writer(); | |
| 583 | if (is_global or variable.is_extern) { | |
| 584 | try fwd_decl_writer.writeAll("ZIG_EXTERN_C "); | |
| 585 | } | |
| 586 | if (variable.is_threadlocal) { | |
| 587 | try fwd_decl_writer.writeAll("zig_threadlocal "); | |
| 588 | } | |
| 589 | try o.dg.renderType(fwd_decl_writer, o.dg.decl.ty); | |
| 590 | const decl_name = mem.span(o.dg.decl.name); | |
| 591 | try fwd_decl_writer.print(" {s};\n", .{decl_name}); | |
| 592 | ||
| 593 | try o.indent_writer.insertNewline(); | |
| 594 | const w = o.writer(); | |
| 595 | try o.dg.renderType(w, o.dg.decl.ty); | |
| 596 | try w.print(" {s} = ", .{decl_name}); | |
| 597 | if (variable.init.tag() != .unreachable_value) { | |
| 598 | try o.dg.renderValue(w, tv.ty, variable.init); | |
| 599 | } | |
| 600 | try w.writeAll(";"); | |
| 601 | try o.indent_writer.insertNewline(); | |
| 602 | ||
| 573 | 603 | } else { |
| 574 | 604 | const writer = o.writer(); |
| 575 | 605 | try writer.writeAll("static "); |
| ... | ... | @@ -598,7 +628,7 @@ pub fn genHeader(dg: *DeclGen) error{ AnalysisFail, OutOfMemory }!void { |
| 598 | 628 | |
| 599 | 629 | switch (tv.ty.zigTypeTag()) { |
| 600 | 630 | .Fn => { |
| 601 | const is_global = dg.functionIsGlobal(tv); | |
| 631 | const is_global = dg.declIsGlobal(tv); | |
| 602 | 632 | if (is_global) { |
| 603 | 633 | try writer.writeAll("ZIG_EXTERN_C "); |
| 604 | 634 | } |
| ... | ... | @@ -696,7 +726,7 @@ pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!voi |
| 696 | 726 | .wrap_errunion_err => try genWrapErrUnionErr(o, inst.castTag(.wrap_errunion_err).?), |
| 697 | 727 | .br_block_flat => return o.dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement codegen for br_block_flat", .{}), |
| 698 | 728 | .ptrtoint => return o.dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement codegen for ptrtoint", .{}), |
| 699 | .varptr => return o.dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement codegen for varptr", .{}), | |
| 729 | .varptr => try genVarPtr(o, inst.castTag(.varptr).?), | |
| 700 | 730 | .floatcast => return o.dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement codegen for floatcast", .{}), |
| 701 | 731 | }; |
| 702 | 732 | switch (result_value) { |
| ... | ... | @@ -709,6 +739,10 @@ pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!voi |
| 709 | 739 | try writer.writeAll("}"); |
| 710 | 740 | } |
| 711 | 741 | |
| 742 | fn genVarPtr(o: *Object, inst: *Inst.VarPtr) !CValue { | |
| 743 | return CValue{ .decl_ref = inst.variable.owner_decl }; | |
| 744 | } | |
| 745 | ||
| 712 | 746 | fn genAlloc(o: *Object, alloc: *Inst.NoOp) !CValue { |
| 713 | 747 | const writer = o.writer(); |
| 714 | 748 | |
| ... | ... | @@ -743,6 +777,12 @@ fn genLoad(o: *Object, inst: *Inst.UnOp) !CValue { |
| 743 | 777 | try o.writeCValue(writer, wrapped); |
| 744 | 778 | try writer.writeAll(";\n"); |
| 745 | 779 | }, |
| 780 | .decl_ref => |decl| { | |
| 781 | const wrapped: CValue = .{ .decl = decl }; | |
| 782 | try writer.writeAll(" = "); | |
| 783 | try o.writeCValue(writer, wrapped); | |
| 784 | try writer.writeAll(";\n"); | |
| 785 | }, | |
| 746 | 786 | else => { |
| 747 | 787 | try writer.writeAll(" = *"); |
| 748 | 788 | try o.writeCValue(writer, operand); |
| ... | ... | @@ -791,6 +831,13 @@ fn genStore(o: *Object, inst: *Inst.BinOp) !CValue { |
| 791 | 831 | try o.writeCValue(writer, src_val); |
| 792 | 832 | try writer.writeAll(";\n"); |
| 793 | 833 | }, |
| 834 | .decl_ref => |decl| { | |
| 835 | const dest: CValue = .{ .decl = decl }; | |
| 836 | try o.writeCValue(writer, dest); | |
| 837 | try writer.writeAll(" = "); | |
| 838 | try o.writeCValue(writer, src_val); | |
| 839 | try writer.writeAll(";\n"); | |
| 840 | }, | |
| 794 | 841 | else => { |
| 795 | 842 | try writer.writeAll("*"); |
| 796 | 843 | try o.writeCValue(writer, dest_ptr); |
src/link/C/zig.h+18-14| ... | ... | @@ -1,25 +1,15 @@ |
| 1 | #if __STDC_VERSION__ >= 199901L | |
| 2 | #include <stdbool.h> | |
| 3 | #else | |
| 4 | #define bool unsigned char | |
| 5 | #define true 1 | |
| 6 | #define false 0 | |
| 7 | #endif | |
| 8 | ||
| 9 | 1 | #if __STDC_VERSION__ >= 201112L |
| 10 | 2 | #define zig_noreturn _Noreturn |
| 3 | #define zig_threadlocal thread_local | |
| 11 | 4 | #elif __GNUC__ |
| 12 | 5 | #define zig_noreturn __attribute__ ((noreturn)) |
| 6 | #define zig_threadlocal __thread | |
| 13 | 7 | #elif _MSC_VER |
| 14 | 8 | #define zig_noreturn __declspec(noreturn) |
| 9 | #define zig_threadlocal __declspec(thread) | |
| 15 | 10 | #else |
| 16 | 11 | #define zig_noreturn |
| 17 | #endif | |
| 18 | ||
| 19 | #if defined(__GNUC__) | |
| 20 | #define zig_unreachable() __builtin_unreachable() | |
| 21 | #else | |
| 22 | #define zig_unreachable() | |
| 12 | #define zig_threadlocal zig_threadlocal_unavailable | |
| 23 | 13 | #endif |
| 24 | 14 | |
| 25 | 15 | #if __STDC_VERSION__ >= 199901L |
| ... | ... | @@ -30,6 +20,20 @@ |
| 30 | 20 | #define ZIG_RESTRICT |
| 31 | 21 | #endif |
| 32 | 22 | |
| 23 | #if __STDC_VERSION__ >= 199901L | |
| 24 | #include <stdbool.h> | |
| 25 | #else | |
| 26 | #define bool unsigned char | |
| 27 | #define true 1 | |
| 28 | #define false 0 | |
| 29 | #endif | |
| 30 | ||
| 31 | #if defined(__GNUC__) | |
| 32 | #define zig_unreachable() __builtin_unreachable() | |
| 33 | #else | |
| 34 | #define zig_unreachable() | |
| 35 | #endif | |
| 36 | ||
| 33 | 37 | #ifdef __cplusplus |
| 34 | 38 | #define ZIG_EXTERN_C extern "C" |
| 35 | 39 | #else |
test/stage2/cbe.zig+1-1| ... | ... | @@ -517,7 +517,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 517 | 517 | \\} |
| 518 | 518 | , &.{ |
| 519 | 519 | ":3:21: error: missing struct field: x", |
| 520 | ":1:15: note: struct 'Point' declared here", | |
| 520 | ":1:15: note: struct 'test_case.Point' declared here", | |
| 521 | 521 | }); |
| 522 | 522 | case.addError( |
| 523 | 523 | \\const Point = struct { x: i32, y: i32 }; |