Skip to content

Commit dea72d1

Browse files
authored
Merge pull request #22898 from kristoff-it/deprecated-proposal
Implement `@deprecated`
2 parents ab38193 + 43a949e commit dea72d1

17 files changed

+251
-15
lines changed

doc/langref.html.in

Lines changed: 49 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2288,7 +2288,7 @@ or
22882288
{#code|test_aligned_struct_fields.zig#}
22892289

22902290
<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,
22922292
and only works for the `==` and `!=` operators.
22932293
</p>
22942294
{#code|test_packed_struct_equality.zig#}
@@ -4086,7 +4086,7 @@ fn performFn(start_value: i32) i32 {
40864086
special-case syntax.
40874087
</p>
40884088
<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.
40904090
</p>
40914091
{#code|generic_data_structure.zig#}
40924092

@@ -4291,10 +4291,10 @@ pub fn print(self: *Writer, arg0: []const u8, arg1: i32) !void {
42914291
<pre>{#syntax#}@addrSpaceCast(ptr: anytype) anytype{#endsyntax#}</pre>
42924292
<p>
42934293
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 cast
4295-
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 to
4297-
cast a pointer between the same address spaces.
4294+
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 resulting
4296+
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.
42984298
</p>
42994299
{#header_close#}
43004300
{#header_open|@addWithOverflow#}
@@ -4307,7 +4307,7 @@ pub fn print(self: *Writer, arg0: []const u8, arg1: i32) !void {
43074307
<pre>{#syntax#}@alignCast(ptr: anytype) anytype{#endsyntax#}</pre>
43084308
<p>
43094309
{#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.
43114311
</p>
43124312
<p>A {#link|pointer alignment safety check|Incorrect Pointer Alignment#} is added
43134313
to the generated code to make sure the pointer is aligned as promised.</p>
@@ -4384,7 +4384,7 @@ comptime {
43844384
<pre>{#syntax#}@bitCast(value: anytype) anytype{#endsyntax#}</pre>
43854385
<p>
43864386
Converts a value of one type to another type. The return type is the
4387-
inferred result type.
4387+
inferred result type.
43884388
</p>
43894389
<p>
43904390
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
47414741
{#see_also|@cVaArg|@cVaCopy|@cVaEnd#}
47424742
{#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#}
4757+
pub 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+
47444780
{#header_open|@divExact#}
47454781
<pre>{#syntax#}@divExact(numerator: T, denominator: T) T{#endsyntax#}</pre>
47464782
<p>
@@ -4855,8 +4891,8 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
48554891
<pre>{#syntax#}@errorCast(value: anytype) anytype{#endsyntax#}</pre>
48564892
<p>
48574893
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 error
4859-
set results in safety-checked {#link|Illegal Behavior#}.
4894+
inferred result type. Attempting to convert an error which is not in the destination error
4895+
set results in safety-checked {#link|Illegal Behavior#}.
48604896
</p>
48614897
{#header_close#}
48624898

@@ -4935,7 +4971,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
49354971
<pre>{#syntax#}@floatFromInt(int: anytype) anytype{#endsyntax#}</pre>
49364972
<p>
49374973
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 legal
4974+
To convert the other way, use {#link|@intFromFloat#}. This operation is legal
49394975
for all values of all integer types.
49404976
</p>
49414977
{#header_close#}
@@ -5027,7 +5063,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
50275063
<pre>{#syntax#}@intCast(int: anytype) anytype{#endsyntax#}</pre>
50285064
<p>
50295065
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.
50315067
Attempting to convert a number which is out of range of the destination type results in
50325068
safety-checked {#link|Illegal Behavior#}.
50335069
</p>
@@ -5280,7 +5316,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
52805316
<pre>{#syntax#}@ptrFromInt(address: usize) anytype{#endsyntax#}</pre>
52815317
<p>
52825318
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 type
5319+
To convert the other way, use {#link|@intFromPtr#}. Casting an address of 0 to a destination type
52845320
which in not {#link|optional|Optional Pointers#} and does not have the {#syntax#}allowzero{#endsyntax#} attribute will result in a
52855321
{#link|Pointer Cast Invalid Null#} panic when runtime safety checks are enabled.
52865322
</p>
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
test "deprecated code path" {
2+
compute(.greedy, false, 42);
3+
}
4+
5+
const Strategy = enum { greedy, expensive, fast };
6+
fn 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+
18+
extern fn runGreedy(foo: bool, bar: usize) void;
19+
extern fn runExpensive(foo: bool, bar: usize) void;
20+
extern fn runFast(foo: bool, bar: usize) void;
21+
22+
// test_error=deprecated

lib/compiler/build_runner.zig

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ pub fn main() !void {
8080
.query = .{},
8181
.result = try std.zig.system.resolveTargetQuery(.{}),
8282
},
83+
.root_builder = undefined, // populated below
8384
};
8485

8586
graph.cache.addPrefix(.{ .path = null, .handle = std.fs.cwd() });
@@ -94,6 +95,7 @@ pub fn main() !void {
9495
local_cache_directory,
9596
dependencies.root_deps,
9697
);
98+
graph.root_builder = builder;
9799

98100
var targets = ArrayList([]const u8).init(arena);
99101
var debug_log_scopes = ArrayList([]const u8).init(arena);
@@ -260,6 +262,10 @@ pub fn main() !void {
260262
graph.incremental = true;
261263
} else if (mem.eql(u8, arg, "-fno-incremental")) {
262264
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;
263269
} else if (mem.eql(u8, arg, "-fwine")) {
264270
builder.enable_wine = true;
265271
} else if (mem.eql(u8, arg, "-fno-wine")) {
@@ -1290,6 +1296,8 @@ fn usage(b: *std.Build, out_stream: anytype) !void {
12901296
\\ new Omit cached steps
12911297
\\ failures (Default) Only print failed steps
12921298
\\ 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
12931301
\\ -j<N> Limit concurrent jobs (default is to use all CPU cores)
12941302
\\ --maxrss <bytes> Limit memory usage (default is to use available memory)
12951303
\\ --skip-oom-steps Instead of failing, skip steps that would exceed --maxrss

lib/std/Build.zig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,8 @@ pub const Graph = struct {
121121
random_seed: u32 = 0,
122122
dependency_cache: InitializedDepMap = .empty,
123123
allow_so_scripts: ?bool = null,
124+
allow_deprecated: ?bool = null,
125+
root_builder: *std.Build,
124126
};
125127

126128
const AvailableDeps = []const struct { []const u8, []const u8 };

lib/std/Build/Module.zig

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -557,6 +557,10 @@ pub fn appendZigProcessFlags(
557557
try addFlag(zig_args, m.pic, "-fPIC", "-fno-PIC");
558558
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+
560564
if (m.dwarf_format) |dwarf_format| {
561565
try zig_args.append(switch (dwarf_format) {
562566
.@"32" => "-gdwarf32",

lib/std/Build/Step/Options.zig

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,7 @@ test Options {
514514
.result = try std.zig.system.resolveTargetQuery(.{}),
515515
},
516516
.zig_lib_directory = std.Build.Cache.Directory.cwd(),
517+
.root_builder = undefined,
517518
};
518519

519520
var builder = try std.Build.create(
@@ -523,6 +524,8 @@ test Options {
523524
&.{},
524525
);
525526

527+
graph.root_builder = builder;
528+
526529
const options = builder.addOptions();
527530

528531
const KeywordEnum = enum {

lib/std/zig/AstGen.zig

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9695,6 +9695,19 @@ fn builtinCall(
96959695
.volatile_cast,
96969696
=> 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+
96989711
// zig fmt: off
96999712
.has_decl => return hasDeclOrField(gz, scope, ri, node, params[0], params[1], .has_decl),
97009713
.has_field => return hasDeclOrField(gz, scope, ri, node, params[0], params[1], .has_field),

lib/std/zig/AstRlAnnotate.zig

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -817,8 +817,6 @@ fn blockExpr(astrl: *AstRlAnnotate, parent_block: ?*Block, ri: ResultInfo, node:
817817
}
818818

819819
fn 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-
822820
const tree = astrl.tree;
823821
const main_tokens = tree.nodes.items(.main_token);
824822
const builtin_token = main_tokens[node];
@@ -828,6 +826,11 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast.
828826
if (expected != args.len) return false;
829827
}
830828
switch (info.tag) {
829+
.deprecated => if (args.len >= 1) {
830+
return astrl.expr(args[0], block, ri);
831+
} else {
832+
return false;
833+
},
831834
.import => return false,
832835
.branch_hint => {
833836
_ = try astrl.expr(args[0], block, ResultInfo.type_only);

lib/std/zig/BuiltinFn.zig

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ pub const Tag = enum {
121121
work_item_id,
122122
work_group_size,
123123
work_group_id,
124+
deprecated,
124125
};
125126

126127
pub const EvalToError = enum {
@@ -1016,6 +1017,14 @@ pub const list = list: {
10161017
.illegal_outside_function = true,
10171018
},
10181019
},
1020+
.{
1021+
"@deprecated",
1022+
.{
1023+
.tag = .deprecated,
1024+
.param_count = null,
1025+
.eval_to_error = .maybe,
1026+
},
1027+
},
10191028
});
10201029
};
10211030

lib/std/zig/Zir.zig

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2112,6 +2112,9 @@ pub const Inst = struct {
21122112
/// any code may have gone here, avoiding false-positive "unreachable code" errors.
21132113
astgen_error,
21142114

2115+
/// `operand` is `src_node: i32`.
2116+
deprecated,
2117+
21152118
pub const InstData = struct {
21162119
opcode: Extended,
21172120
small: u16,
@@ -4363,6 +4366,7 @@ fn findTrackableInner(
43634366
.tuple_decl,
43644367
.dbg_empty_stmt,
43654368
.astgen_error,
4369+
.deprecated,
43664370
=> return,
43674371

43684372
// `@TypeOf` has a body.

src/Compilation.zig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -869,6 +869,7 @@ pub const cache_helpers = struct {
869869
hh.add(mod.sanitize_c);
870870
hh.add(mod.sanitize_thread);
871871
hh.add(mod.fuzz);
872+
hh.add(mod.allow_deprecated);
872873
hh.add(mod.unwind_tables);
873874
hh.add(mod.structured_cfg);
874875
hh.add(mod.no_builtin);

src/Package/Module.zig

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ red_zone: bool,
2727
sanitize_c: bool,
2828
sanitize_thread: bool,
2929
fuzz: bool,
30+
allow_deprecated: bool,
3031
unwind_tables: std.builtin.UnwindTables,
3132
cc_argv: []const []const u8,
3233
/// (SPIR-V) whether to generate a structured control flow graph or not
@@ -95,6 +96,7 @@ pub const CreateOptions = struct {
9596
sanitize_c: ?bool = null,
9697
sanitize_thread: ?bool = null,
9798
fuzz: ?bool = null,
99+
allow_deprecated: ?bool = null,
98100
structured_cfg: ?bool = null,
99101
no_builtin: ?bool = null,
100102
};
@@ -234,6 +236,11 @@ pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module {
234236
break :b false;
235237
};
236238

239+
const allow_deprecated = b: {
240+
if (options.inherited.allow_deprecated) |x| break :b x;
241+
break :b false;
242+
};
243+
237244
const code_model = b: {
238245
if (options.inherited.code_model) |x| break :b x;
239246
if (options.parent) |p| break :b p.code_model;
@@ -380,6 +387,7 @@ pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module {
380387
.sanitize_c = sanitize_c,
381388
.sanitize_thread = sanitize_thread,
382389
.fuzz = fuzz,
390+
.allow_deprecated = allow_deprecated,
383391
.unwind_tables = unwind_tables,
384392
.cc_argv = options.cc_argv,
385393
.structured_cfg = structured_cfg,
@@ -474,6 +482,7 @@ pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module {
474482
.sanitize_c = sanitize_c,
475483
.sanitize_thread = sanitize_thread,
476484
.fuzz = fuzz,
485+
.allow_deprecated = allow_deprecated,
477486
.unwind_tables = unwind_tables,
478487
.cc_argv = &.{},
479488
.structured_cfg = structured_cfg,
@@ -532,6 +541,7 @@ pub fn createLimited(gpa: Allocator, options: LimitedOptions) Allocator.Error!*P
532541
.sanitize_c = undefined,
533542
.sanitize_thread = undefined,
534543
.fuzz = undefined,
544+
.allow_deprecated = undefined,
535545
.unwind_tables = undefined,
536546
.cc_argv = undefined,
537547
.structured_cfg = undefined,

src/Sema.zig

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1091,6 +1091,7 @@ fn analyzeBodyInner(
10911091
const map = &sema.inst_map;
10921092
const tags = sema.code.instructions.items(.tag);
10931093
const datas = sema.code.instructions.items(.data);
1094+
const mod = block.ownerModule();
10941095

10951096
var crash_info = crash_report.prepAnalyzeBody(sema, block, body);
10961097
crash_info.push();
@@ -1404,6 +1405,16 @@ fn analyzeBodyInner(
14041405
i += 1;
14051406
continue;
14061407
},
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+
},
14071418
.disable_instrumentation => {
14081419
try sema.zirDisableInstrumentation();
14091420
i += 1;

0 commit comments

Comments
 (0)