authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-02-27 01:31:09-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2025-02-27 01:31:09-05:00
logdea72d15da4fba909dc3ccb2e9dc5286372ac023
treedaea2909e476467396347897bc12b13ae2a37ff7
parentab381933c87bcc744058d25a876cfdc0d23fc674
parent43a949ee95ce72003bb2d2ee22e1f4b5aea96739
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #22898 from kristoff-it/deprecated-proposal

Implement `@deprecated`

17 files changed, 251 insertions(+), 15 deletions(-)

doc/langref.html.in+49-13
...@@ -2288,7 +2288,7 @@ or...@@ -2288,7 +2288,7 @@ or
2288 {#code|test_aligned_struct_fields.zig#}2288 {#code|test_aligned_struct_fields.zig#}
22892289
2290 <p>2290 <p>
2291 Equating packed structs results in a comparison of the backing integer, 2291 Equating packed structs results in a comparison of the backing integer,
2292 and only works for the `==` and `!=` operators.2292 and only works for the `==` and `!=` operators.
2293 </p>2293 </p>
2294 {#code|test_packed_struct_equality.zig#}2294 {#code|test_packed_struct_equality.zig#}
...@@ -4086,7 +4086,7 @@ fn performFn(start_value: i32) i32 {...@@ -4086,7 +4086,7 @@ fn performFn(start_value: i32) i32 {
4086 special-case syntax.4086 special-case syntax.
4087 </p>4087 </p>
4088 <p>4088 <p>
4089 Here is an example of a generic {#syntax#}List{#endsyntax#} data structure.4089 Here is an example of a generic {#syntax#}List{#endsyntax#} data structure.
4090 </p>4090 </p>
4091 {#code|generic_data_structure.zig#}4091 {#code|generic_data_structure.zig#}
40924092
...@@ -4291,10 +4291,10 @@ pub fn print(self: *Writer, arg0: []const u8, arg1: i32) !void {...@@ -4291,10 +4291,10 @@ pub fn print(self: *Writer, arg0: []const u8, arg1: i32) !void {
4291 <pre>{#syntax#}@addrSpaceCast(ptr: anytype) anytype{#endsyntax#}</pre>4291 <pre>{#syntax#}@addrSpaceCast(ptr: anytype) anytype{#endsyntax#}</pre>
4292 <p>4292 <p>
4293 Converts a pointer from one address space to another. The new address space is inferred4293 Converts a pointer from one address space to another. The new address space is inferred
4294 based on the result type. Depending on the current target and address spaces, this cast4294 based on the result type. Depending on the current target and address spaces, this cast
4295 may be a no-op, a complex operation, or illegal. If the cast is legal, then the resulting4295 may be a no-op, a complex operation, or illegal. If the cast is legal, then the resulting
4296 pointer points to the same memory location as the pointer operand. It is always valid to4296 pointer points to the same memory location as the pointer operand. It is always valid to
4297 cast a pointer between the same address spaces.4297 cast a pointer between the same address spaces.
4298 </p>4298 </p>
4299 {#header_close#}4299 {#header_close#}
4300 {#header_open|@addWithOverflow#}4300 {#header_open|@addWithOverflow#}
...@@ -4307,7 +4307,7 @@ pub fn print(self: *Writer, arg0: []const u8, arg1: i32) !void {...@@ -4307,7 +4307,7 @@ pub fn print(self: *Writer, arg0: []const u8, arg1: i32) !void {
4307 <pre>{#syntax#}@alignCast(ptr: anytype) anytype{#endsyntax#}</pre>4307 <pre>{#syntax#}@alignCast(ptr: anytype) anytype{#endsyntax#}</pre>
4308 <p>4308 <p>
4309 {#syntax#}ptr{#endsyntax#} can be {#syntax#}*T{#endsyntax#}, {#syntax#}?*T{#endsyntax#}, or {#syntax#}[]T{#endsyntax#}.4309 {#syntax#}ptr{#endsyntax#} can be {#syntax#}*T{#endsyntax#}, {#syntax#}?*T{#endsyntax#}, or {#syntax#}[]T{#endsyntax#}.
4310 Changes the alignment of a pointer. The alignment to use is inferred based on the result type.4310 Changes the alignment of a pointer. The alignment to use is inferred based on the result type.
4311 </p>4311 </p>
4312 <p>A {#link|pointer alignment safety check|Incorrect Pointer Alignment#} is added4312 <p>A {#link|pointer alignment safety check|Incorrect Pointer Alignment#} is added
4313 to the generated code to make sure the pointer is aligned as promised.</p>4313 to the generated code to make sure the pointer is aligned as promised.</p>
...@@ -4384,7 +4384,7 @@ comptime {...@@ -4384,7 +4384,7 @@ comptime {
4384 <pre>{#syntax#}@bitCast(value: anytype) anytype{#endsyntax#}</pre>4384 <pre>{#syntax#}@bitCast(value: anytype) anytype{#endsyntax#}</pre>
4385 <p>4385 <p>
4386 Converts a value of one type to another type. The return type is the4386 Converts a value of one type to another type. The return type is the
4387 inferred result type.4387 inferred result type.
4388 </p>4388 </p>
4389 <p>4389 <p>
4390 Asserts that {#syntax#}@sizeOf(@TypeOf(value)) == @sizeOf(DestType){#endsyntax#}.4390 Asserts that {#syntax#}@sizeOf(@TypeOf(value)) == @sizeOf(DestType){#endsyntax#}.
...@@ -4741,6 +4741,42 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val...@@ -4741,6 +4741,42 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
4741 {#see_also|@cVaArg|@cVaCopy|@cVaEnd#}4741 {#see_also|@cVaArg|@cVaCopy|@cVaEnd#}
4742 {#header_close#}4742 {#header_close#}
47434743
4744 {#header_open|@deprecated#}
4745 <pre>{#syntax#}@deprecated(value: anytype) @TypeOf(value){#endsyntax#}</pre>
4746 <pre>{#syntax#}@deprecated() void{#endsyntax#}</pre>
4747 <p>
4748 Marks a given code path as scheduled for removal. Evaluates to the same
4749 value passed in as argument, or the {#syntax#}void{#endsyntax#} value
4750 when given none.
4751 </p>
4752 <p>
4753 When a public declaration has been moved to a new location, the old
4754 location can be marked {#syntax#}@deprecated{#endsyntax#}:
4755 </p>
4756 {#syntax_block|zig|root.zig#}
4757pub const fooToBar = @deprecated(bar.fromFoo); // moved
4758 {#end_syntax_block#}
4759 <p>
4760 By default deprecated code paths are disallowed in a module defined by
4761 the root package but allowed in modules defined by the rest of the
4762 dependency tree. This behavior can be overridden by passing
4763 <code>-fallow-deprecated</code> or <code>-fno-allow-deprecated</code> to
4764 <code>zig build</code>.
4765 </p>
4766 <p>
4767 The purpose of {#syntax#}@deprecated{#endsyntax#} is to provide at least
4768 one version (a "grace period") of a package that supports both old and new APIs
4769 simultaneously, while providing tooling for programmers to discover what needs
4770 to be upgraded to migrate to the new API. Such a grace period has the key property
4771 that it allows a project's dependency tree to be upgraded <em>one package at a time</em>.
4772 </p>
4773 <p>
4774 Using {#syntax#}@deprecated{#endsyntax#} without an argument can be
4775 useful inside of conditionally compiled blocks:
4776 </p>
4777 {#code|test_deprecated_builtin.zig#}
4778 {#header_close#}
4779
4744 {#header_open|@divExact#}4780 {#header_open|@divExact#}
4745 <pre>{#syntax#}@divExact(numerator: T, denominator: T) T{#endsyntax#}</pre>4781 <pre>{#syntax#}@divExact(numerator: T, denominator: T) T{#endsyntax#}</pre>
4746 <p>4782 <p>
...@@ -4855,8 +4891,8 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val...@@ -4855,8 +4891,8 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
4855 <pre>{#syntax#}@errorCast(value: anytype) anytype{#endsyntax#}</pre>4891 <pre>{#syntax#}@errorCast(value: anytype) anytype{#endsyntax#}</pre>
4856 <p>4892 <p>
4857 Converts an error set or error union value from one error set to another error set. The return type is the4893 Converts an error set or error union value from one error set to another error set. The return type is the
4858 inferred result type. Attempting to convert an error which is not in the destination error4894 inferred result type. Attempting to convert an error which is not in the destination error
4859 set results in safety-checked {#link|Illegal Behavior#}.4895 set results in safety-checked {#link|Illegal Behavior#}.
4860 </p>4896 </p>
4861 {#header_close#}4897 {#header_close#}
48624898
...@@ -4935,7 +4971,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val...@@ -4935,7 +4971,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
4935 <pre>{#syntax#}@floatFromInt(int: anytype) anytype{#endsyntax#}</pre>4971 <pre>{#syntax#}@floatFromInt(int: anytype) anytype{#endsyntax#}</pre>
4936 <p>4972 <p>
4937 Converts an integer to the closest floating point representation. The return type is the inferred result type.4973 Converts an integer to the closest floating point representation. The return type is the inferred result type.
4938 To convert the other way, use {#link|@intFromFloat#}. This operation is legal4974 To convert the other way, use {#link|@intFromFloat#}. This operation is legal
4939 for all values of all integer types.4975 for all values of all integer types.
4940 </p>4976 </p>
4941 {#header_close#}4977 {#header_close#}
...@@ -5027,7 +5063,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val...@@ -5027,7 +5063,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
5027 <pre>{#syntax#}@intCast(int: anytype) anytype{#endsyntax#}</pre>5063 <pre>{#syntax#}@intCast(int: anytype) anytype{#endsyntax#}</pre>
5028 <p>5064 <p>
5029 Converts an integer to another integer while keeping the same numerical value.5065 Converts an integer to another integer while keeping the same numerical value.
5030 The return type is the inferred result type.5066 The return type is the inferred result type.
5031 Attempting to convert a number which is out of range of the destination type results in5067 Attempting to convert a number which is out of range of the destination type results in
5032 safety-checked {#link|Illegal Behavior#}.5068 safety-checked {#link|Illegal Behavior#}.
5033 </p>5069 </p>
...@@ -5280,7 +5316,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val...@@ -5280,7 +5316,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
5280 <pre>{#syntax#}@ptrFromInt(address: usize) anytype{#endsyntax#}</pre>5316 <pre>{#syntax#}@ptrFromInt(address: usize) anytype{#endsyntax#}</pre>
5281 <p>5317 <p>
5282 Converts an integer to a {#link|pointer|Pointers#}. The return type is the inferred result type.5318 Converts an integer to a {#link|pointer|Pointers#}. The return type is the inferred result type.
5283 To convert the other way, use {#link|@intFromPtr#}. Casting an address of 0 to a destination type5319 To convert the other way, use {#link|@intFromPtr#}. Casting an address of 0 to a destination type
5284 which in not {#link|optional|Optional Pointers#} and does not have the {#syntax#}allowzero{#endsyntax#} attribute will result in a5320 which in not {#link|optional|Optional Pointers#} and does not have the {#syntax#}allowzero{#endsyntax#} attribute will result in a
5285 {#link|Pointer Cast Invalid Null#} panic when runtime safety checks are enabled.5321 {#link|Pointer Cast Invalid Null#} panic when runtime safety checks are enabled.
5286 </p>5322 </p>
doc/langref/test_deprecated_builtin.zig created+22
...@@ -0,0 +1,22 @@
1test "deprecated code path" {
2 compute(.greedy, false, 42);
3}
4
5const Strategy = enum { greedy, expensive, fast };
6fn compute(comptime strat: Strategy, comptime foo: bool, bar: usize) void {
7 switch (strat) {
8 .greedy => {
9 // This combination turned out to be ineffective.
10 if (!foo) @deprecated(); // use fast strategy when foo is false
11 runGreedy(foo, bar);
12 },
13 .expensive => runExpensive(foo, bar),
14 .fast => runFast(foo, bar),
15 }
16}
17
18extern fn runGreedy(foo: bool, bar: usize) void;
19extern fn runExpensive(foo: bool, bar: usize) void;
20extern fn runFast(foo: bool, bar: usize) void;
21
22// test_error=deprecated
lib/compiler/build_runner.zig+8
...@@ -80,6 +80,7 @@ pub fn main() !void {...@@ -80,6 +80,7 @@ pub fn main() !void {
80 .query = .{},80 .query = .{},
81 .result = try std.zig.system.resolveTargetQuery(.{}),81 .result = try std.zig.system.resolveTargetQuery(.{}),
82 },82 },
83 .root_builder = undefined, // populated below
83 };84 };
8485
85 graph.cache.addPrefix(.{ .path = null, .handle = std.fs.cwd() });86 graph.cache.addPrefix(.{ .path = null, .handle = std.fs.cwd() });
...@@ -94,6 +95,7 @@ pub fn main() !void {...@@ -94,6 +95,7 @@ pub fn main() !void {
94 local_cache_directory,95 local_cache_directory,
95 dependencies.root_deps,96 dependencies.root_deps,
96 );97 );
98 graph.root_builder = builder;
9799
98 var targets = ArrayList([]const u8).init(arena);100 var targets = ArrayList([]const u8).init(arena);
99 var debug_log_scopes = ArrayList([]const u8).init(arena);101 var debug_log_scopes = ArrayList([]const u8).init(arena);
...@@ -260,6 +262,10 @@ pub fn main() !void {...@@ -260,6 +262,10 @@ pub fn main() !void {
260 graph.incremental = true;262 graph.incremental = true;
261 } else if (mem.eql(u8, arg, "-fno-incremental")) {263 } else if (mem.eql(u8, arg, "-fno-incremental")) {
262 graph.incremental = false;264 graph.incremental = false;
265 } else if (mem.eql(u8, arg, "-fallow-deprecated")) {
266 graph.allow_deprecated = true;
267 } else if (mem.eql(u8, arg, "-fno-allow-deprecated")) {
268 graph.allow_deprecated = false;
263 } else if (mem.eql(u8, arg, "-fwine")) {269 } else if (mem.eql(u8, arg, "-fwine")) {
264 builder.enable_wine = true;270 builder.enable_wine = true;
265 } else if (mem.eql(u8, arg, "-fno-wine")) {271 } else if (mem.eql(u8, arg, "-fno-wine")) {
...@@ -1290,6 +1296,8 @@ fn usage(b: *std.Build, out_stream: anytype) !void {...@@ -1290,6 +1296,8 @@ fn usage(b: *std.Build, out_stream: anytype) !void {
1290 \\ new Omit cached steps1296 \\ new Omit cached steps
1291 \\ failures (Default) Only print failed steps1297 \\ failures (Default) Only print failed steps
1292 \\ none Do not print the build summary1298 \\ none Do not print the build summary
1299 \\ -fallow-deprecated Allow usage of deprecated code for the entire build graph
1300 \\ -fno-allow-deprecated Disallow usage of deprecated code for the entire build graph
1293 \\ -j<N> Limit concurrent jobs (default is to use all CPU cores)1301 \\ -j<N> Limit concurrent jobs (default is to use all CPU cores)
1294 \\ --maxrss <bytes> Limit memory usage (default is to use available memory)1302 \\ --maxrss <bytes> Limit memory usage (default is to use available memory)
1295 \\ --skip-oom-steps Instead of failing, skip steps that would exceed --maxrss1303 \\ --skip-oom-steps Instead of failing, skip steps that would exceed --maxrss
lib/std/Build.zig+2
...@@ -121,6 +121,8 @@ pub const Graph = struct {...@@ -121,6 +121,8 @@ pub const Graph = struct {
121 random_seed: u32 = 0,121 random_seed: u32 = 0,
122 dependency_cache: InitializedDepMap = .empty,122 dependency_cache: InitializedDepMap = .empty,
123 allow_so_scripts: ?bool = null,123 allow_so_scripts: ?bool = null,
124 allow_deprecated: ?bool = null,
125 root_builder: *std.Build,
124};126};
125127
126const AvailableDeps = []const struct { []const u8, []const u8 };128const AvailableDeps = []const struct { []const u8, []const u8 };
lib/std/Build/Module.zig+4
...@@ -557,6 +557,10 @@ pub fn appendZigProcessFlags(...@@ -557,6 +557,10 @@ pub fn appendZigProcessFlags(
557 try addFlag(zig_args, m.pic, "-fPIC", "-fno-PIC");557 try addFlag(zig_args, m.pic, "-fPIC", "-fno-PIC");
558 try addFlag(zig_args, m.red_zone, "-mred-zone", "-mno-red-zone");558 try addFlag(zig_args, m.red_zone, "-mred-zone", "-mno-red-zone");
559559
560 // -fno-allow-deprecated is the CLI default, and not inherited, so only pass the flag if true.
561 const allow_deprecated = m.owner.graph.allow_deprecated orelse (m.owner.graph.root_builder != m.owner);
562 if (allow_deprecated == true) try zig_args.append("-fallow-deprecated");
563
560 if (m.dwarf_format) |dwarf_format| {564 if (m.dwarf_format) |dwarf_format| {
561 try zig_args.append(switch (dwarf_format) {565 try zig_args.append(switch (dwarf_format) {
562 .@"32" => "-gdwarf32",566 .@"32" => "-gdwarf32",
lib/std/Build/Step/Options.zig+3
...@@ -514,6 +514,7 @@ test Options {...@@ -514,6 +514,7 @@ test Options {
514 .result = try std.zig.system.resolveTargetQuery(.{}),514 .result = try std.zig.system.resolveTargetQuery(.{}),
515 },515 },
516 .zig_lib_directory = std.Build.Cache.Directory.cwd(),516 .zig_lib_directory = std.Build.Cache.Directory.cwd(),
517 .root_builder = undefined,
517 };518 };
518519
519 var builder = try std.Build.create(520 var builder = try std.Build.create(
...@@ -523,6 +524,8 @@ test Options {...@@ -523,6 +524,8 @@ test Options {
523 &.{},524 &.{},
524 );525 );
525526
527 graph.root_builder = builder;
528
526 const options = builder.addOptions();529 const options = builder.addOptions();
527530
528 const KeywordEnum = enum {531 const KeywordEnum = enum {
lib/std/zig/AstGen.zig+13
...@@ -9695,6 +9695,19 @@ fn builtinCall(...@@ -9695,6 +9695,19 @@ fn builtinCall(
9695 .volatile_cast,9695 .volatile_cast,
9696 => return ptrCast(gz, scope, ri, node),9696 => return ptrCast(gz, scope, ri, node),
96979697
9698 .deprecated => {
9699 _ = try gz.addExtendedNodeSmall(.deprecated, node, 0);
9700 switch (params.len) {
9701 0 => return .void_value,
9702 1 => return expr(gz, scope, ri, params[0]),
9703 else => return astgen.failNode(
9704 node,
9705 "expected 0 or 1 argument, found {}",
9706 .{params.len},
9707 ),
9708 }
9709 },
9710
9698 // zig fmt: off9711 // zig fmt: off
9699 .has_decl => return hasDeclOrField(gz, scope, ri, node, params[0], params[1], .has_decl),9712 .has_decl => return hasDeclOrField(gz, scope, ri, node, params[0], params[1], .has_decl),
9700 .has_field => return hasDeclOrField(gz, scope, ri, node, params[0], params[1], .has_field),9713 .has_field => return hasDeclOrField(gz, scope, ri, node, params[0], params[1], .has_field),
lib/std/zig/AstRlAnnotate.zig+5-2
...@@ -817,8 +817,6 @@ fn blockExpr(astrl: *AstRlAnnotate, parent_block: ?*Block, ri: ResultInfo, node:...@@ -817,8 +817,6 @@ fn blockExpr(astrl: *AstRlAnnotate, parent_block: ?*Block, ri: ResultInfo, node:
817}817}
818818
819fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast.Node.Index, args: []const Ast.Node.Index) !bool {819fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast.Node.Index, args: []const Ast.Node.Index) !bool {
820 _ = ri; // Currently, no builtin consumes its result location.
821
822 const tree = astrl.tree;820 const tree = astrl.tree;
823 const main_tokens = tree.nodes.items(.main_token);821 const main_tokens = tree.nodes.items(.main_token);
824 const builtin_token = main_tokens[node];822 const builtin_token = main_tokens[node];
...@@ -828,6 +826,11 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast....@@ -828,6 +826,11 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast.
828 if (expected != args.len) return false;826 if (expected != args.len) return false;
829 }827 }
830 switch (info.tag) {828 switch (info.tag) {
829 .deprecated => if (args.len >= 1) {
830 return astrl.expr(args[0], block, ri);
831 } else {
832 return false;
833 },
831 .import => return false,834 .import => return false,
832 .branch_hint => {835 .branch_hint => {
833 _ = try astrl.expr(args[0], block, ResultInfo.type_only);836 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
lib/std/zig/BuiltinFn.zig+9
...@@ -121,6 +121,7 @@ pub const Tag = enum {...@@ -121,6 +121,7 @@ pub const Tag = enum {
121 work_item_id,121 work_item_id,
122 work_group_size,122 work_group_size,
123 work_group_id,123 work_group_id,
124 deprecated,
124};125};
125126
126pub const EvalToError = enum {127pub const EvalToError = enum {
...@@ -1016,6 +1017,14 @@ pub const list = list: {...@@ -1016,6 +1017,14 @@ pub const list = list: {
1016 .illegal_outside_function = true,1017 .illegal_outside_function = true,
1017 },1018 },
1018 },1019 },
1020 .{
1021 "@deprecated",
1022 .{
1023 .tag = .deprecated,
1024 .param_count = null,
1025 .eval_to_error = .maybe,
1026 },
1027 },
1019 });1028 });
1020};1029};
10211030
lib/std/zig/Zir.zig+4
...@@ -2112,6 +2112,9 @@ pub const Inst = struct {...@@ -2112,6 +2112,9 @@ pub const Inst = struct {
2112 /// any code may have gone here, avoiding false-positive "unreachable code" errors.2112 /// any code may have gone here, avoiding false-positive "unreachable code" errors.
2113 astgen_error,2113 astgen_error,
21142114
2115 /// `operand` is `src_node: i32`.
2116 deprecated,
2117
2115 pub const InstData = struct {2118 pub const InstData = struct {
2116 opcode: Extended,2119 opcode: Extended,
2117 small: u16,2120 small: u16,
...@@ -4363,6 +4366,7 @@ fn findTrackableInner(...@@ -4363,6 +4366,7 @@ fn findTrackableInner(
4363 .tuple_decl,4366 .tuple_decl,
4364 .dbg_empty_stmt,4367 .dbg_empty_stmt,
4365 .astgen_error,4368 .astgen_error,
4369 .deprecated,
4366 => return,4370 => return,
43674371
4368 // `@TypeOf` has a body.4372 // `@TypeOf` has a body.
src/Compilation.zig+1
...@@ -869,6 +869,7 @@ pub const cache_helpers = struct {...@@ -869,6 +869,7 @@ pub const cache_helpers = struct {
869 hh.add(mod.sanitize_c);869 hh.add(mod.sanitize_c);
870 hh.add(mod.sanitize_thread);870 hh.add(mod.sanitize_thread);
871 hh.add(mod.fuzz);871 hh.add(mod.fuzz);
872 hh.add(mod.allow_deprecated);
872 hh.add(mod.unwind_tables);873 hh.add(mod.unwind_tables);
873 hh.add(mod.structured_cfg);874 hh.add(mod.structured_cfg);
874 hh.add(mod.no_builtin);875 hh.add(mod.no_builtin);
src/Package/Module.zig+10
...@@ -27,6 +27,7 @@ red_zone: bool,...@@ -27,6 +27,7 @@ red_zone: bool,
27sanitize_c: bool,27sanitize_c: bool,
28sanitize_thread: bool,28sanitize_thread: bool,
29fuzz: bool,29fuzz: bool,
30allow_deprecated: bool,
30unwind_tables: std.builtin.UnwindTables,31unwind_tables: std.builtin.UnwindTables,
31cc_argv: []const []const u8,32cc_argv: []const []const u8,
32/// (SPIR-V) whether to generate a structured control flow graph or not33/// (SPIR-V) whether to generate a structured control flow graph or not
...@@ -95,6 +96,7 @@ pub const CreateOptions = struct {...@@ -95,6 +96,7 @@ pub const CreateOptions = struct {
95 sanitize_c: ?bool = null,96 sanitize_c: ?bool = null,
96 sanitize_thread: ?bool = null,97 sanitize_thread: ?bool = null,
97 fuzz: ?bool = null,98 fuzz: ?bool = null,
99 allow_deprecated: ?bool = null,
98 structured_cfg: ?bool = null,100 structured_cfg: ?bool = null,
99 no_builtin: ?bool = null,101 no_builtin: ?bool = null,
100 };102 };
...@@ -234,6 +236,11 @@ pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module {...@@ -234,6 +236,11 @@ pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module {
234 break :b false;236 break :b false;
235 };237 };
236238
239 const allow_deprecated = b: {
240 if (options.inherited.allow_deprecated) |x| break :b x;
241 break :b false;
242 };
243
237 const code_model = b: {244 const code_model = b: {
238 if (options.inherited.code_model) |x| break :b x;245 if (options.inherited.code_model) |x| break :b x;
239 if (options.parent) |p| break :b p.code_model;246 if (options.parent) |p| break :b p.code_model;
...@@ -380,6 +387,7 @@ pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module {...@@ -380,6 +387,7 @@ pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module {
380 .sanitize_c = sanitize_c,387 .sanitize_c = sanitize_c,
381 .sanitize_thread = sanitize_thread,388 .sanitize_thread = sanitize_thread,
382 .fuzz = fuzz,389 .fuzz = fuzz,
390 .allow_deprecated = allow_deprecated,
383 .unwind_tables = unwind_tables,391 .unwind_tables = unwind_tables,
384 .cc_argv = options.cc_argv,392 .cc_argv = options.cc_argv,
385 .structured_cfg = structured_cfg,393 .structured_cfg = structured_cfg,
...@@ -474,6 +482,7 @@ pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module {...@@ -474,6 +482,7 @@ pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module {
474 .sanitize_c = sanitize_c,482 .sanitize_c = sanitize_c,
475 .sanitize_thread = sanitize_thread,483 .sanitize_thread = sanitize_thread,
476 .fuzz = fuzz,484 .fuzz = fuzz,
485 .allow_deprecated = allow_deprecated,
477 .unwind_tables = unwind_tables,486 .unwind_tables = unwind_tables,
478 .cc_argv = &.{},487 .cc_argv = &.{},
479 .structured_cfg = structured_cfg,488 .structured_cfg = structured_cfg,
...@@ -532,6 +541,7 @@ pub fn createLimited(gpa: Allocator, options: LimitedOptions) Allocator.Error!*P...@@ -532,6 +541,7 @@ pub fn createLimited(gpa: Allocator, options: LimitedOptions) Allocator.Error!*P
532 .sanitize_c = undefined,541 .sanitize_c = undefined,
533 .sanitize_thread = undefined,542 .sanitize_thread = undefined,
534 .fuzz = undefined,543 .fuzz = undefined,
544 .allow_deprecated = undefined,
535 .unwind_tables = undefined,545 .unwind_tables = undefined,
536 .cc_argv = undefined,546 .cc_argv = undefined,
537 .structured_cfg = undefined,547 .structured_cfg = undefined,
src/Sema.zig+11
...@@ -1091,6 +1091,7 @@ fn analyzeBodyInner(...@@ -1091,6 +1091,7 @@ fn analyzeBodyInner(
1091 const map = &sema.inst_map;1091 const map = &sema.inst_map;
1092 const tags = sema.code.instructions.items(.tag);1092 const tags = sema.code.instructions.items(.tag);
1093 const datas = sema.code.instructions.items(.data);1093 const datas = sema.code.instructions.items(.data);
1094 const mod = block.ownerModule();
10941095
1095 var crash_info = crash_report.prepAnalyzeBody(sema, block, body);1096 var crash_info = crash_report.prepAnalyzeBody(sema, block, body);
1096 crash_info.push();1097 crash_info.push();
...@@ -1404,6 +1405,16 @@ fn analyzeBodyInner(...@@ -1404,6 +1405,16 @@ fn analyzeBodyInner(
1404 i += 1;1405 i += 1;
1405 continue;1406 continue;
1406 },1407 },
1408 .deprecated => {
1409 if (!mod.allow_deprecated) {
1410 const src_node: i32 = @bitCast(extended.operand);
1411 const src = block.nodeOffset(src_node);
1412 return sema.fail(block, src, "reached deprecated code", .{});
1413 }
1414
1415 i += 1;
1416 continue;
1417 },
1407 .disable_instrumentation => {1418 .disable_instrumentation => {
1408 try sema.zirDisableInstrumentation();1419 try sema.zirDisableInstrumentation();
1409 i += 1;1420 i += 1;
src/main.zig+6
...@@ -520,6 +520,8 @@ const usage_build_generic =...@@ -520,6 +520,8 @@ const usage_build_generic =
520 \\ -fno-sanitize-thread Disable Thread Sanitizer520 \\ -fno-sanitize-thread Disable Thread Sanitizer
521 \\ -ffuzz Enable fuzz testing instrumentation521 \\ -ffuzz Enable fuzz testing instrumentation
522 \\ -fno-fuzz Disable fuzz testing instrumentation522 \\ -fno-fuzz Disable fuzz testing instrumentation
523 \\ -fallow-deprecated Allow usage of deprecated code
524 \\ -fno-allow-deprecated Disallow usage of deprecated code
523 \\ -funwind-tables Always produce unwind table entries for all functions525 \\ -funwind-tables Always produce unwind table entries for all functions
524 \\ -fasync-unwind-tables Always produce asynchronous unwind table entries for all functions526 \\ -fasync-unwind-tables Always produce asynchronous unwind table entries for all functions
525 \\ -fno-unwind-tables Never produce unwind table entries527 \\ -fno-unwind-tables Never produce unwind table entries
...@@ -1454,6 +1456,10 @@ fn buildOutputType(...@@ -1454,6 +1456,10 @@ fn buildOutputType(
1454 mod_opts.fuzz = true;1456 mod_opts.fuzz = true;
1455 } else if (mem.eql(u8, arg, "-fno-fuzz")) {1457 } else if (mem.eql(u8, arg, "-fno-fuzz")) {
1456 mod_opts.fuzz = false;1458 mod_opts.fuzz = false;
1459 } else if (mem.eql(u8, arg, "-fallow-deprecated")) {
1460 mod_opts.allow_deprecated = true;
1461 } else if (mem.eql(u8, arg, "-fno-allow-deprecated")) {
1462 mod_opts.allow_deprecated = false;
1457 } else if (mem.eql(u8, arg, "-fllvm")) {1463 } else if (mem.eql(u8, arg, "-fllvm")) {
1458 create_module.opts.use_llvm = true;1464 create_module.opts.use_llvm = true;
1459 } else if (mem.eql(u8, arg, "-fno-llvm")) {1465 } else if (mem.eql(u8, arg, "-fno-llvm")) {
src/print_zir.zig+1
...@@ -535,6 +535,7 @@ const Writer = struct {...@@ -535,6 +535,7 @@ const Writer = struct {
535 .c_va_start,535 .c_va_start,
536 .in_comptime,536 .in_comptime,
537 .value_placeholder,537 .value_placeholder,
538 .deprecated,
538 => try self.writeExtNode(stream, extended),539 => try self.writeExtNode(stream, extended),
539540
540 .builtin_src => {541 .builtin_src => {
test/cases/compile_errors/deprecated.zig created+9
...@@ -0,0 +1,9 @@
1const bad = @deprecated(42);
2
3pub export fn foo() usize {
4 return bad;
5}
6
7// error
8//
9// :1:13: error: reached deprecated code
test/tests.zig+94
...@@ -1176,6 +1176,100 @@ pub fn addCliTests(b: *std.Build) *Step {...@@ -1176,6 +1176,100 @@ pub fn addCliTests(b: *std.Build) *Step {
1176 step.dependOn(&cleanup.step);1176 step.dependOn(&cleanup.step);
1177 }1177 }
11781178
1179 {
1180 // Test `zig build -fallow-deprecated`.
1181
1182 const deprecated_check: std.Build.Step.Run.StdIo.Check = .{
1183 .expect_stderr_match = "reached deprecated code",
1184 };
1185
1186 const tmp_path = b.makeTempPath();
1187
1188 // create custom main.zig file containing a deprecated decl
1189 {
1190 const new_main_src =
1191 \\const bad = @deprecated(42);
1192 \\
1193 \\pub fn main() u8 {
1194 \\ return bad;
1195 \\}
1196 \\
1197 \\test {
1198 \\ if (bad != 42) return error.Bad;
1199 \\}
1200 ;
1201
1202 var src_dir = std.fs.cwd().makeOpenPath(b.pathJoin(&.{ tmp_path, "src" }), .{}) catch @panic("unable to create tmp path");
1203 defer src_dir.close();
1204
1205 var main = src_dir.createFile("main.zig", .{}) catch @panic("unable to create main.zig");
1206 defer main.close();
1207
1208 main.writeAll(new_main_src) catch @panic("unable to write to main.zig");
1209 }
1210
1211 const init_exe = b.addSystemCommand(&.{ b.graph.zig_exe, "init" });
1212 init_exe.setCwd(.{ .cwd_relative = tmp_path });
1213 init_exe.setName("zig init");
1214 init_exe.expectStdOutEqual("");
1215 init_exe.expectStdErrEqual("info: created build.zig\n" ++
1216 "info: created build.zig.zon\n" ++
1217 "info: preserving already existing file: src" ++ s ++ "main.zig\n" ++
1218 "info: created src" ++ s ++ "root.zig\n");
1219
1220 const run_test_bad = b.addSystemCommand(&.{ b.graph.zig_exe, "build", "test", "--color", "off" });
1221 run_test_bad.setCwd(.{ .cwd_relative = tmp_path });
1222 run_test_bad.setName("zig build test");
1223 run_test_bad.expectExitCode(1);
1224 run_test_bad.expectStdOutEqual("");
1225 run_test_bad.addCheck(deprecated_check);
1226 run_test_bad.step.dependOn(&init_exe.step);
1227
1228 const run_test = b.addSystemCommand(&.{
1229 b.graph.zig_exe,
1230 "build",
1231 "test",
1232 "--color",
1233 "off",
1234 "-fallow-deprecated",
1235 });
1236 run_test.setCwd(.{ .cwd_relative = tmp_path });
1237 run_test.setName("zig build test");
1238 run_test.expectExitCode(0);
1239 run_test.expectStdOutEqual("");
1240 run_test.expectStdErrEqual("");
1241 run_test.step.dependOn(&init_exe.step);
1242
1243 const run_build_bad = b.addSystemCommand(&.{ b.graph.zig_exe, "build", "--color", "off" });
1244 run_build_bad.setCwd(.{ .cwd_relative = tmp_path });
1245 run_build_bad.setName("zig build test");
1246 run_build_bad.expectExitCode(1);
1247 run_build_bad.expectStdOutEqual("");
1248 run_build_bad.addCheck(deprecated_check);
1249 run_build_bad.step.dependOn(&init_exe.step);
1250
1251 const run_build = b.addSystemCommand(&.{
1252 b.graph.zig_exe,
1253 "build",
1254 "--color",
1255 "off",
1256 "-fallow-deprecated",
1257 });
1258 run_build.setCwd(.{ .cwd_relative = tmp_path });
1259 run_build.setName("zig build test");
1260 run_build.expectExitCode(0);
1261 run_build.expectStdOutEqual("");
1262 run_build.expectStdErrEqual("");
1263 run_build.step.dependOn(&init_exe.step);
1264
1265 const cleanup = b.addRemoveDirTree(.{ .cwd_relative = tmp_path });
1266 cleanup.step.dependOn(&run_test.step);
1267 cleanup.step.dependOn(&run_test_bad.step);
1268 cleanup.step.dependOn(&run_build.step);
1269 cleanup.step.dependOn(&run_build_bad.step);
1270
1271 step.dependOn(&cleanup.step);
1272 }
1179 // Test Godbolt API1273 // Test Godbolt API
1180 if (builtin.os.tag == .linux and builtin.cpu.arch == .x86_64) {1274 if (builtin.os.tag == .linux and builtin.cpu.arch == .x86_64) {
1181 const tmp_path = b.makeTempPath();1275 const tmp_path = b.makeTempPath();