Skip to content
Merged
62 changes: 49 additions & 13 deletions doc/langref.html.in
Original file line number Diff line number Diff line change
Expand Up @@ -2288,7 +2288,7 @@ or
{#code|test_aligned_struct_fields.zig#}

<p>
Equating packed structs results in a comparison of the backing integer,
Equating packed structs results in a comparison of the backing integer,
and only works for the `==` and `!=` operators.
</p>
{#code|test_packed_struct_equality.zig#}
Expand Down Expand Up @@ -4086,7 +4086,7 @@ fn performFn(start_value: i32) i32 {
special-case syntax.
</p>
<p>
Here is an example of a generic {#syntax#}List{#endsyntax#} data structure.
Here is an example of a generic {#syntax#}List{#endsyntax#} data structure.
</p>
{#code|generic_data_structure.zig#}

Expand Down Expand Up @@ -4291,10 +4291,10 @@ pub fn print(self: *Writer, arg0: []const u8, arg1: i32) !void {
<pre>{#syntax#}@addrSpaceCast(ptr: anytype) anytype{#endsyntax#}</pre>
<p>
Converts a pointer from one address space to another. The new address space is inferred
based on the result type. Depending on the current target and address spaces, this cast
may be a no-op, a complex operation, or illegal. If the cast is legal, then the resulting
pointer points to the same memory location as the pointer operand. It is always valid to
cast a pointer between the same address spaces.
based on the result type. Depending on the current target and address spaces, this cast
may be a no-op, a complex operation, or illegal. If the cast is legal, then the resulting
pointer points to the same memory location as the pointer operand. It is always valid to
cast a pointer between the same address spaces.
</p>
{#header_close#}
{#header_open|@addWithOverflow#}
Expand All @@ -4307,7 +4307,7 @@ pub fn print(self: *Writer, arg0: []const u8, arg1: i32) !void {
<pre>{#syntax#}@alignCast(ptr: anytype) anytype{#endsyntax#}</pre>
<p>
{#syntax#}ptr{#endsyntax#} can be {#syntax#}*T{#endsyntax#}, {#syntax#}?*T{#endsyntax#}, or {#syntax#}[]T{#endsyntax#}.
Changes the alignment of a pointer. The alignment to use is inferred based on the result type.
Changes the alignment of a pointer. The alignment to use is inferred based on the result type.
</p>
<p>A {#link|pointer alignment safety check|Incorrect Pointer Alignment#} is added
to the generated code to make sure the pointer is aligned as promised.</p>
Expand Down Expand Up @@ -4384,7 +4384,7 @@ comptime {
<pre>{#syntax#}@bitCast(value: anytype) anytype{#endsyntax#}</pre>
<p>
Converts a value of one type to another type. The return type is the
inferred result type.
inferred result type.
</p>
<p>
Asserts that {#syntax#}@sizeOf(@TypeOf(value)) == @sizeOf(DestType){#endsyntax#}.
Expand Down Expand Up @@ -4741,6 +4741,42 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
{#see_also|@cVaArg|@cVaCopy|@cVaEnd#}
{#header_close#}

{#header_open|@deprecated#}
<pre>{#syntax#}@deprecated(value: anytype) @TypeOf(value){#endsyntax#}</pre>
<pre>{#syntax#}@deprecated() void{#endsyntax#}</pre>
<p>
Marks a given code path as scheduled for removal. Evaluates to the same
value passed in as argument, or the {#syntax#}void{#endsyntax#} value
when given none.
</p>
<p>
When a public declaration has been moved to a new location, the old
location can be marked {#syntax#}@deprecated{#endsyntax#}:
</p>
{#syntax_block|zig|root.zig#}
pub const fooToBar = @deprecated(bar.fromFoo); // moved
{#end_syntax_block#}
<p>
By default deprecated code paths are disallowed in a module defined by
the root package but allowed in modules defined by the rest of the
dependency tree. This behavior can be overridden by passing
<code>-fallow-deprecated</code> or <code>-fno-allow-deprecated</code> to
<code>zig build</code>.
</p>
<p>
The purpose of {#syntax#}@deprecated{#endsyntax#} is to provide at least
one version (a "grace period") of a package that supports both old and new APIs
simultaneously, while providing tooling for programmers to discover what needs
to be upgraded to migrate to the new API. Such a grace period has the key property
that it allows a project's dependency tree to be upgraded <em>one package at a time</em>.
</p>
<p>
Using {#syntax#}@deprecated{#endsyntax#} without an argument can be
useful inside of conditionally compiled blocks:
</p>
{#code|test_deprecated_builtin.zig#}
{#header_close#}

{#header_open|@divExact#}
<pre>{#syntax#}@divExact(numerator: T, denominator: T) T{#endsyntax#}</pre>
<p>
Expand Down Expand Up @@ -4855,8 +4891,8 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
<pre>{#syntax#}@errorCast(value: anytype) anytype{#endsyntax#}</pre>
<p>
Converts an error set or error union value from one error set to another error set. The return type is the
inferred result type. Attempting to convert an error which is not in the destination error
set results in safety-checked {#link|Illegal Behavior#}.
inferred result type. Attempting to convert an error which is not in the destination error
set results in safety-checked {#link|Illegal Behavior#}.
</p>
{#header_close#}

Expand Down Expand Up @@ -4935,7 +4971,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
<pre>{#syntax#}@floatFromInt(int: anytype) anytype{#endsyntax#}</pre>
<p>
Converts an integer to the closest floating point representation. The return type is the inferred result type.
To convert the other way, use {#link|@intFromFloat#}. This operation is legal
To convert the other way, use {#link|@intFromFloat#}. This operation is legal
for all values of all integer types.
</p>
{#header_close#}
Expand Down Expand Up @@ -5027,7 +5063,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
<pre>{#syntax#}@intCast(int: anytype) anytype{#endsyntax#}</pre>
<p>
Converts an integer to another integer while keeping the same numerical value.
The return type is the inferred result type.
The return type is the inferred result type.
Attempting to convert a number which is out of range of the destination type results in
safety-checked {#link|Illegal Behavior#}.
</p>
Expand Down Expand Up @@ -5280,7 +5316,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
<pre>{#syntax#}@ptrFromInt(address: usize) anytype{#endsyntax#}</pre>
<p>
Converts an integer to a {#link|pointer|Pointers#}. The return type is the inferred result type.
To convert the other way, use {#link|@intFromPtr#}. Casting an address of 0 to a destination type
To convert the other way, use {#link|@intFromPtr#}. Casting an address of 0 to a destination type
which in not {#link|optional|Optional Pointers#} and does not have the {#syntax#}allowzero{#endsyntax#} attribute will result in a
{#link|Pointer Cast Invalid Null#} panic when runtime safety checks are enabled.
</p>
Expand Down
22 changes: 22 additions & 0 deletions doc/langref/test_deprecated_builtin.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
test "deprecated code path" {
compute(.greedy, false, 42);
}

const Strategy = enum { greedy, expensive, fast };
fn compute(comptime strat: Strategy, comptime foo: bool, bar: usize) void {
switch (strat) {
.greedy => {
// This combination turned out to be ineffective.
if (!foo) @deprecated(); // use fast strategy when foo is false
runGreedy(foo, bar);
},
.expensive => runExpensive(foo, bar),
.fast => runFast(foo, bar),
}
}

extern fn runGreedy(foo: bool, bar: usize) void;
extern fn runExpensive(foo: bool, bar: usize) void;
extern fn runFast(foo: bool, bar: usize) void;

// test_error=deprecated
8 changes: 8 additions & 0 deletions lib/compiler/build_runner.zig
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ pub fn main() !void {
.query = .{},
.result = try std.zig.system.resolveTargetQuery(.{}),
},
.root_builder = undefined, // populated below
};

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

var targets = ArrayList([]const u8).init(arena);
var debug_log_scopes = ArrayList([]const u8).init(arena);
Expand Down Expand Up @@ -260,6 +262,10 @@ pub fn main() !void {
graph.incremental = true;
} else if (mem.eql(u8, arg, "-fno-incremental")) {
graph.incremental = false;
} else if (mem.eql(u8, arg, "-fallow-deprecated")) {
graph.allow_deprecated = true;
} else if (mem.eql(u8, arg, "-fno-allow-deprecated")) {
graph.allow_deprecated = false;
} else if (mem.eql(u8, arg, "-fwine")) {
builder.enable_wine = true;
} else if (mem.eql(u8, arg, "-fno-wine")) {
Expand Down Expand Up @@ -1283,6 +1289,8 @@ fn usage(b: *std.Build, out_stream: anytype) !void {
\\ new Omit cached steps
\\ failures (Default) Only print failed steps
\\ none Do not print the build summary
\\ -fallow-deprecated Allow usage of deprecated code for the entire build graph
\\ -fno-allow-deprecated Disallow usage of deprecated code for the entire build graph
\\ -j<N> Limit concurrent jobs (default is to use all CPU cores)
\\ --maxrss <bytes> Limit memory usage (default is to use available memory)
\\ --skip-oom-steps Instead of failing, skip steps that would exceed --maxrss
Expand Down
2 changes: 2 additions & 0 deletions lib/std/Build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,8 @@ pub const Graph = struct {
random_seed: u32 = 0,
dependency_cache: InitializedDepMap = .empty,
allow_so_scripts: ?bool = null,
allow_deprecated: ?bool = null,
root_builder: *std.Build,
};

const AvailableDeps = []const struct { []const u8, []const u8 };
Expand Down
4 changes: 4 additions & 0 deletions lib/std/Build/Module.zig
Original file line number Diff line number Diff line change
Expand Up @@ -557,6 +557,10 @@ pub fn appendZigProcessFlags(
try addFlag(zig_args, m.pic, "-fPIC", "-fno-PIC");
try addFlag(zig_args, m.red_zone, "-mred-zone", "-mno-red-zone");

// -fno-allow-deprecated is the CLI default, and not inherited, so only pass the flag if true.
const allow_deprecated = m.owner.graph.allow_deprecated orelse (m.owner.graph.root_builder != m.owner);
if (allow_deprecated == true) try zig_args.append("-fallow-deprecated");

if (m.dwarf_format) |dwarf_format| {
try zig_args.append(switch (dwarf_format) {
.@"32" => "-gdwarf32",
Expand Down
3 changes: 3 additions & 0 deletions lib/std/Build/Step/Options.zig
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,7 @@ test Options {
.result = try std.zig.system.resolveTargetQuery(.{}),
},
.zig_lib_directory = std.Build.Cache.Directory.cwd(),
.root_builder = undefined,
};

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

graph.root_builder = builder;

const options = builder.addOptions();

const KeywordEnum = enum {
Expand Down
13 changes: 13 additions & 0 deletions lib/std/zig/AstGen.zig
Original file line number Diff line number Diff line change
Expand Up @@ -9695,6 +9695,19 @@ fn builtinCall(
.volatile_cast,
=> return ptrCast(gz, scope, ri, node),

.deprecated => {
_ = try gz.addExtendedNodeSmall(.deprecated, node, 0);
switch (params.len) {
0 => return .void_value,
1 => return expr(gz, scope, ri, params[0]),
else => return astgen.failNode(
node,
"expected 0 or 1 argument, found {}",
.{params.len},
),
}
},

// zig fmt: off
.has_decl => return hasDeclOrField(gz, scope, ri, node, params[0], params[1], .has_decl),
.has_field => return hasDeclOrField(gz, scope, ri, node, params[0], params[1], .has_field),
Expand Down
7 changes: 5 additions & 2 deletions lib/std/zig/AstRlAnnotate.zig
Original file line number Diff line number Diff line change
Expand Up @@ -817,8 +817,6 @@ fn blockExpr(astrl: *AstRlAnnotate, parent_block: ?*Block, ri: ResultInfo, node:
}

fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast.Node.Index, args: []const Ast.Node.Index) !bool {
_ = ri; // Currently, no builtin consumes its result location.

const tree = astrl.tree;
const main_tokens = tree.nodes.items(.main_token);
const builtin_token = main_tokens[node];
Expand All @@ -828,6 +826,11 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast.
if (expected != args.len) return false;
}
switch (info.tag) {
.deprecated => if (args.len >= 1) {
return astrl.expr(args[0], block, ri);
} else {
return false;
},
.import => return false,
.branch_hint => {
_ = try astrl.expr(args[0], block, ResultInfo.type_only);
Expand Down
9 changes: 9 additions & 0 deletions lib/std/zig/BuiltinFn.zig
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ pub const Tag = enum {
work_item_id,
work_group_size,
work_group_id,
deprecated,
};

pub const EvalToError = enum {
Expand Down Expand Up @@ -1016,6 +1017,14 @@ pub const list = list: {
.illegal_outside_function = true,
},
},
.{
"@deprecated",
.{
.tag = .deprecated,
.param_count = null,
.eval_to_error = .maybe,
},
},
});
};

Expand Down
4 changes: 4 additions & 0 deletions lib/std/zig/Zir.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2112,6 +2112,9 @@ pub const Inst = struct {
/// any code may have gone here, avoiding false-positive "unreachable code" errors.
astgen_error,

/// `operand` is `src_node: i32`.
deprecated,

pub const InstData = struct {
opcode: Extended,
small: u16,
Expand Down Expand Up @@ -4363,6 +4366,7 @@ fn findTrackableInner(
.tuple_decl,
.dbg_empty_stmt,
.astgen_error,
.deprecated,
=> return,

// `@TypeOf` has a body.
Expand Down
1 change: 1 addition & 0 deletions src/Compilation.zig
Original file line number Diff line number Diff line change
Expand Up @@ -869,6 +869,7 @@ pub const cache_helpers = struct {
hh.add(mod.sanitize_c);
hh.add(mod.sanitize_thread);
hh.add(mod.fuzz);
hh.add(mod.allow_deprecated);
hh.add(mod.unwind_tables);
hh.add(mod.structured_cfg);
hh.add(mod.no_builtin);
Expand Down
10 changes: 10 additions & 0 deletions src/Package/Module.zig
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ red_zone: bool,
sanitize_c: bool,
sanitize_thread: bool,
fuzz: bool,
allow_deprecated: bool,
unwind_tables: std.builtin.UnwindTables,
cc_argv: []const []const u8,
/// (SPIR-V) whether to generate a structured control flow graph or not
Expand Down Expand Up @@ -95,6 +96,7 @@ pub const CreateOptions = struct {
sanitize_c: ?bool = null,
sanitize_thread: ?bool = null,
fuzz: ?bool = null,
allow_deprecated: ?bool = null,
structured_cfg: ?bool = null,
no_builtin: ?bool = null,
};
Expand Down Expand Up @@ -234,6 +236,11 @@ pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module {
break :b false;
};

const allow_deprecated = b: {
if (options.inherited.allow_deprecated) |x| break :b x;
break :b false;
};

const code_model = b: {
if (options.inherited.code_model) |x| break :b x;
if (options.parent) |p| break :b p.code_model;
Expand Down Expand Up @@ -380,6 +387,7 @@ pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module {
.sanitize_c = sanitize_c,
.sanitize_thread = sanitize_thread,
.fuzz = fuzz,
.allow_deprecated = allow_deprecated,
.unwind_tables = unwind_tables,
.cc_argv = options.cc_argv,
.structured_cfg = structured_cfg,
Expand Down Expand Up @@ -474,6 +482,7 @@ pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module {
.sanitize_c = sanitize_c,
.sanitize_thread = sanitize_thread,
.fuzz = fuzz,
.allow_deprecated = allow_deprecated,
.unwind_tables = unwind_tables,
.cc_argv = &.{},
.structured_cfg = structured_cfg,
Expand Down Expand Up @@ -532,6 +541,7 @@ pub fn createLimited(gpa: Allocator, options: LimitedOptions) Allocator.Error!*P
.sanitize_c = undefined,
.sanitize_thread = undefined,
.fuzz = undefined,
.allow_deprecated = undefined,
.unwind_tables = undefined,
.cc_argv = undefined,
.structured_cfg = undefined,
Expand Down
11 changes: 11 additions & 0 deletions src/Sema.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1091,6 +1091,7 @@ fn analyzeBodyInner(
const map = &sema.inst_map;
const tags = sema.code.instructions.items(.tag);
const datas = sema.code.instructions.items(.data);
const mod = block.ownerModule();

var crash_info = crash_report.prepAnalyzeBody(sema, block, body);
crash_info.push();
Expand Down Expand Up @@ -1404,6 +1405,16 @@ fn analyzeBodyInner(
i += 1;
continue;
},
.deprecated => {
if (!mod.allow_deprecated) {
const src_node: i32 = @bitCast(extended.operand);
const src = block.nodeOffset(src_node);
return sema.fail(block, src, "reached deprecated code", .{});
}

i += 1;
continue;
},
.disable_instrumentation => {
try sema.zirDisableInstrumentation();
i += 1;
Expand Down
Loading