authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-02-01 16:47:26+00:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-02-22 21:01:44+00:00
logf0b331e95a45db5eb435c09c225471be0dac2b69
tree5bc692e50798a1c092138b42e06f93d93660ac21
parentdef7e2f20afa38417a195bb703179e0458ace274
signaturelock-open Commit is signed but in an unrecognized format.

langref: embrace the term "illegal behavior"

Also standardise the terms "safety-checked" and "unchecked".

4 files changed, 71 insertions(+), 55 deletions(-)

doc/langref.html.in+64-48
......@@ -1049,12 +1049,12 @@
10491049 {#header_close#}
10501050 {#header_open|Runtime Integer Values#}
10511051 <p>
1052 Integer literals have no size limitation, and if any undefined behavior occurs,
1052 Integer literals have no size limitation, and if any Illegal Behavior occurs,
10531053 the compiler catches it.
10541054 </p>
10551055 <p>
10561056 However, once an integer value is no longer known at compile-time, it must have a
1057 known size, and is vulnerable to undefined behavior.
1057 known size, and is vulnerable to safety-checked {#link|Illegal Behavior#}.
10581058 </p>
10591059 {#code|runtime_vs_comptime.zig#}
10601060
......@@ -1064,7 +1064,7 @@
10641064 {#link|Division by Zero#}.
10651065 </p>
10661066 <p>
1067 Operators such as {#syntax#}+{#endsyntax#} and {#syntax#}-{#endsyntax#} cause undefined behavior on
1067 Operators such as {#syntax#}+{#endsyntax#} and {#syntax#}-{#endsyntax#} cause {#link|Illegal Behavior#} on
10681068 integer overflow. Alternative operators are provided for wrapping and saturating arithmetic on all targets.
10691069 {#syntax#}+%{#endsyntax#} and {#syntax#}-%{#endsyntax#} perform wrapping arithmetic
10701070 while {#syntax#}+|{#endsyntax#} and {#syntax#}-|{#endsyntax#} perform saturating arithmetic.
......@@ -2029,7 +2029,7 @@ or
20292029 </p>
20302030 <p>
20312031 Slices have bounds checking and are therefore protected
2032 against this kind of undefined behavior. This is one reason
2032 against this kind of Illegal Behavior. This is one reason
20332033 we prefer slices to pointers.
20342034 </p>
20352035 {#code|test_slice_bounds.zig#}
......@@ -2048,7 +2048,7 @@ or
20482048
20492049 <p>
20502050 {#link|@ptrCast#} converts a pointer's element type to another. This
2051 creates a new pointer that can cause undetectable illegal behavior
2051 creates a new pointer that can cause undetectable Illegal Behavior
20522052 depending on the loads and stores that pass through it. Generally, other
20532053 kinds of type conversions are preferable to
20542054 {#syntax#}@ptrCast{#endsyntax#} if possible.
......@@ -2164,7 +2164,7 @@ or
21642164
21652165 <p>
21662166 Sentinel-terminated slicing asserts that the element in the sentinel position of the backing data is
2167 actually the sentinel value. If this is not the case, safety-protected {#link|Undefined Behavior#} results.
2167 actually the sentinel value. If this is not the case, safety-checked {#link|Illegal Behavior#} results.
21682168 </p>
21692169 {#code|test_sentinel_mismatch.zig#}
21702170
......@@ -2425,7 +2425,7 @@ or
24252425 or use an {#link|extern union#} or a {#link|packed union#} which have
24262426 guaranteed in-memory layout.
24272427 {#link|Accessing the non-active field|Wrong Union Field Access#} is
2428 safety-checked {#link|Undefined Behavior#}:
2428 safety-checked {#link|Illegal Behavior#}:
24292429 </p>
24302430 {#code|test_wrong_union_access.zig#}
24312431
......@@ -3023,11 +3023,11 @@ or
30233023 {#syntax#}const number = parseU64("1234", 10) catch unreachable;{#endsyntax#}
30243024 <p>
30253025 Here we know for sure that "1234" will parse successfully. So we put the
3026 {#syntax#}unreachable{#endsyntax#} value on the right hand side. {#syntax#}unreachable{#endsyntax#} generates
3027 a panic in {#link|Debug#} and {#link|ReleaseSafe#} modes and undefined behavior in
3028 {#link|ReleaseFast#} and {#link|ReleaseSmall#} modes. So, while we're debugging the
3029 application, if there <em>was</em> a surprise error here, the application would crash
3030 appropriately.
3026 {#syntax#}unreachable{#endsyntax#} value on the right hand side.
3027 {#syntax#}unreachable{#endsyntax#} invokes safety-checked {#link|Illegal Behavior#}, so
3028 in {#link|Debug#} and {#link|ReleaseSafe#}, triggers a safety panic by default. So, while
3029 we're debugging the application, if there <em>was</em> a surprise error here, the application
3030 would crash appropriately.
30313031 </p>
30323032 <p>
30333033 You may want to take a different action for every situation. For that, we combine
......@@ -4034,7 +4034,7 @@ fn performFn(start_value: i32) i32 {
40344034 </p>
40354035 <p>
40364036 Luckily, we used an unsigned integer, and so when we tried to subtract 1 from 0, it triggered
4037 undefined behavior, which is always a compile error if the compiler knows it happened.
4037 {#link|Illegal Behavior#}, which is always a compile error if the compiler knows it happened.
40384038 But what would have happened if we used a signed integer?
40394039 </p>
40404040 {#code|fibonacci_comptime_infinite_recursion.zig#}
......@@ -4239,7 +4239,7 @@ pub fn print(self: *Writer, arg0: []const u8, arg1: i32) !void {
42394239 </p>
42404240 <p>
42414241 Failure to declare the full set of clobbers for a given inline assembly
4242 expression is unchecked {#link|Undefined Behavior#}.
4242 expression is unchecked {#link|Illegal Behavior#}.
42434243 </p>
42444244 {#header_close#}
42454245
......@@ -4805,7 +4805,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
48054805 </p>
48064806 <p>
48074807 Attempting to convert an integer with no corresponding value in the enum invokes
4808 safety-checked {#link|Undefined Behavior#}.
4808 safety-checked {#link|Illegal Behavior#}.
48094809 Note that a {#link|non-exhaustive enum|Non-exhaustive enum#} has corresponding values for all
48104810 integers in the enum's integer tag type: the {#syntax#}_{#endsyntax#} value represents all
48114811 the remaining unnamed integers in the enum's tag type.
......@@ -4824,7 +4824,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
48244824 </p>
48254825 <p>
48264826 Attempting to convert an integer that does not correspond to any error results in
4827 safety-protected {#link|Undefined Behavior#}.
4827 safety-checked {#link|Illegal Behavior#}.
48284828 </p>
48294829 {#see_also|@intFromError#}
48304830 {#header_close#}
......@@ -4856,7 +4856,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
48564856 <p>
48574857 Converts an error set or error union value from one error set to another error set. The return type is the
48584858 inferred result type. Attempting to convert an error which is not in the destination error
4859 set results in safety-protected {#link|Undefined Behavior#}.
4859 set results in safety-checked {#link|Illegal Behavior#}.
48604860 </p>
48614861 {#header_close#}
48624862
......@@ -4912,7 +4912,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
49124912 </p>
49134913 <p>
49144914 If {#syntax#}field_ptr{#endsyntax#} does not point to the {#syntax#}field_name{#endsyntax#} field of an instance of
4915 the result type, and the result type has ill-defined layout, invokes unchecked {#link|Undefined Behavior#}.
4915 the result type, and the result type has ill-defined layout, invokes unchecked {#link|Illegal Behavior#}.
49164916 </p>
49174917 {#header_close#}
49184918
......@@ -5029,7 +5029,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
50295029 Converts an integer to another integer while keeping the same numerical value.
50305030 The return type is the inferred result type.
50315031 Attempting to convert a number which is out of range of the destination type results in
5032 safety-protected {#link|Undefined Behavior#}.
5032 safety-checked {#link|Illegal Behavior#}.
50335033 </p>
50345034 {#code|test_intCast_builtin.zig#}
50355035
......@@ -5090,7 +5090,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
50905090 </p>
50915091 <p>
50925092 If the integer part of the floating point number cannot fit in the destination type,
5093 it invokes safety-checked {#link|Undefined Behavior#}.
5093 it invokes safety-checked {#link|Illegal Behavior#}.
50945094 </p>
50955095 {#see_also|@floatFromInt#}
50965096 {#header_close#}
......@@ -5250,7 +5250,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
52505250 <p>
52515251 The {#syntax#}ptr{#endsyntax#} argument may be any pointer type and determines the memory
52525252 address to prefetch. This function does not dereference the pointer, it is perfectly legal
5253 to pass a pointer to invalid memory to this function and no illegal behavior will result.
5253 to pass a pointer to invalid memory to this function and no Illegal Behavior will result.
52545254 </p>
52555255 <p>{#syntax#}PrefetchOptions{#endsyntax#} can be found with {#syntax#}@import("std").builtin.PrefetchOptions{#endsyntax#}.</p>
52565256 {#header_close#}
......@@ -5262,7 +5262,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
52625262 </p>
52635263 <p>
52645264 {#link|Optional Pointers#} are allowed. Casting an optional pointer which is {#link|null#}
5265 to a non-optional pointer invokes safety-checked {#link|Undefined Behavior#}.
5265 to a non-optional pointer invokes safety-checked {#link|Illegal Behavior#}.
52665266 </p>
52675267 <p>
52685268 {#syntax#}@ptrCast{#endsyntax#} cannot be used for:
......@@ -5286,7 +5286,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
52865286 </p>
52875287 <p>
52885288 If the destination pointer type does not allow address zero and {#syntax#}address{#endsyntax#}
5289 is zero, this invokes safety-checked {#link|Undefined Behavior#}.
5289 is zero, this invokes safety-checked {#link|Illegal Behavior#}.
52905290 </p>
52915291 {#header_close#}
52925292
......@@ -5361,8 +5361,8 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
53615361 <li>
53625362 {#syntax#}Optimized{#endsyntax#} - Floating point operations may do all of the following:
53635363 <ul>
5364 <li>Assume the arguments and result are not NaN. Optimizations are required to retain defined behavior over NaNs, but the value of the result is undefined.</li>
5365 <li>Assume the arguments and result are not +/-Inf. Optimizations are required to retain defined behavior over +/-Inf, but the value of the result is undefined.</li>
5364 <li>Assume the arguments and result are not NaN. Optimizations are required to retain legal behavior over NaNs, but the value of the result is undefined.</li>
5365 <li>Assume the arguments and result are not +/-Inf. Optimizations are required to retain legal behavior over +/-Inf, but the value of the result is undefined.</li>
53665366 <li>Treat the sign of a zero argument or result as insignificant.</li>
53675367 <li>Use the reciprocal of an argument rather than perform division.</li>
53685368 <li>Perform floating-point contraction (e.g. fusing a multiply followed by an addition into a fused multiply-add).</li>
......@@ -5401,7 +5401,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
54015401 </p>
54025402 <p>
54035403 The type of {#syntax#}shift_amt{#endsyntax#} is an unsigned integer with {#syntax#}log2(@typeInfo(T).int.bits){#endsyntax#} bits.
5404 This is because {#syntax#}shift_amt >= @typeInfo(T).int.bits{#endsyntax#} is undefined behavior.
5404 This is because {#syntax#}shift_amt >= @typeInfo(T).int.bits{#endsyntax#} triggers safety-checked {#link|Illegal Behavior#}.
54055405 </p>
54065406 <p>
54075407 {#syntax#}comptime_int{#endsyntax#} is modeled as an integer with an infinite number of bits,
......@@ -5418,7 +5418,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
54185418 </p>
54195419 <p>
54205420 The type of {#syntax#}shift_amt{#endsyntax#} is an unsigned integer with {#syntax#}log2(@typeInfo(@TypeOf(a)).int.bits){#endsyntax#} bits.
5421 This is because {#syntax#}shift_amt >= @typeInfo(@TypeOf(a)).int.bits{#endsyntax#} is undefined behavior.
5421 This is because {#syntax#}shift_amt >= @typeInfo(@TypeOf(a)).int.bits{#endsyntax#} triggers safety-checked {#link|Illegal Behavior#}.
54225422 </p>
54235423 {#see_also|@shlExact|@shrExact#}
54245424 {#header_close#}
......@@ -5431,7 +5431,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
54315431 </p>
54325432 <p>
54335433 The type of {#syntax#}shift_amt{#endsyntax#} is an unsigned integer with {#syntax#}log2(@typeInfo(T).int.bits){#endsyntax#} bits.
5434 This is because {#syntax#}shift_amt >= @typeInfo(T).int.bits{#endsyntax#} is undefined behavior.
5434 This is because {#syntax#}shift_amt >= @typeInfo(T).int.bits{#endsyntax#} triggers safety-checked {#link|Illegal Behavior#}.
54355435 </p>
54365436 {#see_also|@shlExact|@shlWithOverflow#}
54375437 {#header_close#}
......@@ -5706,7 +5706,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
57065706 {#header_open|@tagName#}
57075707 <pre>{#syntax#}@tagName(value: anytype) [:0]const u8{#endsyntax#}</pre>
57085708 <p>
5709 Converts an enum value or union value to a string literal representing the name.</p><p>If the enum is non-exhaustive and the tag value does not map to a name, it invokes safety-checked {#link|Undefined Behavior#}.
5709 Converts an enum value or union value to a string literal representing the name.</p><p>If the enum is non-exhaustive and the tag value does not map to a name, it invokes safety-checked {#link|Illegal Behavior#}.
57105710 </p>
57115711 {#header_close#}
57125712
......@@ -5943,7 +5943,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
59435943 <li>Reproducible build</li>
59445944 </ul>
59455945 {#header_close#}
5946 {#see_also|Compile Variables|Zig Build System|Undefined Behavior#}
5946 {#see_also|Compile Variables|Zig Build System|Illegal Behavior#}
59475947 {#header_close#}
59485948
59495949 {#header_open|Single Threaded Builds#}
......@@ -5958,20 +5958,36 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
59585958 </ul>
59595959 {#header_close#}
59605960
5961 {#header_open|Undefined Behavior#}
5961 {#header_open|Illegal Behavior#}
59625962 <p>
5963 Zig has many instances of undefined behavior. If undefined behavior is
5964 detected at compile-time, Zig emits a compile error and refuses to continue.
5965 Most undefined behavior that cannot be detected at compile-time can be detected
5966 at runtime. In these cases, Zig has safety checks. Safety checks can be disabled
5967 on a per-block basis with {#link|@setRuntimeSafety#}. The {#link|ReleaseFast#}
5968 and {#link|ReleaseSmall#} build modes disable all safety checks (except where overridden
5969 by {#link|@setRuntimeSafety#}) in order to facilitate optimizations.
5963 Many operations in Zig trigger what is known as "Illegal Behavior" (IB). If Illegal Behavior is detected at
5964 compile-time, Zig emits a compile error and refuses to continue. Otherwise, when Illegal Behavior is not caught
5965 at compile-time, it falls into one of two categories.
59705966 </p>
59715967 <p>
5972 When a safety check fails, Zig crashes with a stack trace, like this:
5968 Some Illegal Behavior is <em>safety-checked</em>: this means that the compiler will insert "safety checks"
5969 anywhere that the Illegal Behavior may occur at runtime, to determine whether it is about to happen. If it
5970 is, the safety check "fails", which triggers a panic.
59735971 </p>
5974 {#code|test_undefined_behavior.zig#}
5972 <p>
5973 All other Illegal Behavior is <em>unchecked</em>, meaning the compiler is unable to insert safety checks for
5974 it. If Unchecked Illegal Behavior is invoked at runtime, anything can happen: usually that will be some kind of
5975 crash, but the optimizer is free to make Unchecked Illegal Behavior do anything, such as calling arbitrary functions
5976 or clobbering arbitrary data. This is similar to the concept of "undefined behavior" in some other languages. Note that
5977 Unchecked Illegal Behavior still always results in a compile error if evaluated at {#link|comptime#}, because the Zig
5978 compiler is able to perform more sophisticated checks at compile-time than at runtime.
5979 </p>
5980 <p>
5981 Most Illegal Behavior is safety-checked. However, to facilitate optimizations, safety checks are disabled by default
5982 in the {#link|ReleaseFast#} and {#link|ReleaseSmall#} optimization modes. Safety checks can also be enabled or disabled
5983 on a per-block basis, overriding the default for the current optimization mode, using {#link|@setRuntimeSafety#}. When
5984 safety checks are disabled, Safety-Checked Illegal Behavior behaves like Unchecked Illegal Behavior; that is, any behavior
5985 may result from invoking it.
5986 </p>
5987 <p>
5988 When a safety check fails, Zig's default panic handler crashes with a stack trace, like this:
5989 </p>
5990 {#code|test_illegal_behavior.zig#}
59755991
59765992 {#header_open|Reaching Unreachable Code#}
59775993 <p>At compile-time:</p>
......@@ -6337,7 +6353,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
63376353 <p>
63386354 {#syntax#}var{#endsyntax#} declarations inside functions are stored in the function's stack frame. Once a function returns,
63396355 any {#link|Pointers#} to variables in the function's stack frame become invalid references, and
6340 dereferencing them becomes unchecked {#link|Undefined Behavior#}.
6356 dereferencing them becomes unchecked {#link|Illegal Behavior#}.
63416357 </p>
63426358 <p>
63436359 {#syntax#}var{#endsyntax#} declarations at the top level or in {#link|struct#} declarations are stored in the global
......@@ -6445,7 +6461,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
64456461 The API documentation for functions and data structures should take great care to explain
64466462 the ownership and lifetime semantics of pointers. Ownership determines whose responsibility it
64476463 is to free the memory referenced by the pointer, and lifetime determines the point at which
6448 the memory becomes inaccessible (lest {#link|Undefined Behavior#} occur).
6464 the memory becomes inaccessible (lest {#link|Illegal Behavior#} occur).
64496465 </p>
64506466 {#header_close#}
64516467
......@@ -6733,10 +6749,10 @@ int foo(void) {
67336749 <li>Supports all the syntax of the other two pointer types ({#syntax#}*T{#endsyntax#}) and ({#syntax#}[*]T{#endsyntax#}).</li>
67346750 <li>Coerces to other pointer types, as well as {#link|Optional Pointers#}.
67356751 When a C pointer is coerced to a non-optional pointer, safety-checked
6736 {#link|Undefined Behavior#} occurs if the address is 0.
6752 {#link|Illegal Behavior#} occurs if the address is 0.
67376753 </li>
67386754 <li>Allows address 0. On non-freestanding targets, dereferencing address 0 is safety-checked
6739 {#link|Undefined Behavior#}. Optional C pointers introduce another bit to keep track of
6755 {#link|Illegal Behavior#}. Optional C pointers introduce another bit to keep track of
67406756 null, just like {#syntax#}?usize{#endsyntax#}. Note that creating an optional C pointer
67416757 is unnecessary as one can use normal {#link|Optional Pointers#}.
67426758 </li>
......@@ -7051,8 +7067,8 @@ fn readU32Be() u32 {}
70517067 <ul>
70527068 <li>Omit any information that is redundant based on the name of the thing being documented.</li>
70537069 <li>Duplicating information onto multiple similar functions is encouraged because it helps IDEs and other tools provide better help text.</li>
7054 <li>Use the word <strong>assume</strong> to indicate invariants that cause {#link|Undefined Behavior#} when violated.</li>
7055 <li>Use the word <strong>assert</strong> to indicate invariants that cause <em>safety-checked</em> {#link|Undefined Behavior#} when violated.</li>
7070 <li>Use the word <strong>assume</strong> to indicate invariants that cause <em>unchecked</em> {#link|Illegal Behavior#} when violated.</li>
7071 <li>Use the word <strong>assert</strong> to indicate invariants that cause <em>safety-checked</em> {#link|Illegal Behavior#} when violated.</li>
70567072 </ul>
70577073 {#header_close#}
70587074 {#header_close#}
......@@ -7448,8 +7464,8 @@ fn readU32Be() u32 {}
74487464 In particular, inside a {#syntax#}nosuspend{#endsyntax#} scope:
74497465 <ul>
74507466 <li>Using the {#syntax#}suspend{#endsyntax#} keyword results in a compile error.</li>
7451 <li>Using {#syntax#}await{#endsyntax#} on a function frame which hasn't completed yet results in safety-checked {#link|Undefined Behavior#}.</li>
7452 <li>Calling an async function may result in safety-checked {#link|Undefined Behavior#}, because it's equivalent to <code>await async some_async_fn()</code>, which contains an {#syntax#}await{#endsyntax#}.</li>
7467 <li>Using {#syntax#}await{#endsyntax#} on a function frame which hasn't completed yet results in safety-checked {#link|Illegal Behavior#}.</li>
7468 <li>Calling an async function may result in safety-checked {#link|Illegal Behavior#}, because it's equivalent to <code>await async some_async_fn()</code>, which contains an {#syntax#}await{#endsyntax#}.</li>
74537469 </ul>
74547470 Code inside a {#syntax#}nosuspend{#endsyntax#} scope does not cause the enclosing function to become an {#link|async function|Async Functions#}.
74557471 <ul>
doc/langref/test_illegal_behavior.zig created+5
......@@ -0,0 +1,5 @@
1test "safety check" {
2 unreachable;
3}
4
5// test_error=reached unreachable code
doc/langref/test_setRuntimeSafety_builtin.zig+2-2
......@@ -2,7 +2,7 @@ test "@setRuntimeSafety" {
22 // The builtin applies to the scope that it is called in. So here, integer overflow
33 // will not be caught in ReleaseFast and ReleaseSmall modes:
44 // var x: u8 = 255;
5 // x += 1; // undefined behavior in ReleaseFast/ReleaseSmall modes.
5 // x += 1; // Unchecked Illegal Behavior in ReleaseFast/ReleaseSmall modes.
66 {
77 // However this block has safety enabled, so safety checks happen here,
88 // even in ReleaseFast and ReleaseSmall modes.
......@@ -15,7 +15,7 @@ test "@setRuntimeSafety" {
1515 // would not be caught in any build mode.
1616 @setRuntimeSafety(false);
1717 // var x: u8 = 255;
18 // x += 1; // undefined behavior in all build modes.
18 // x += 1; // Unchecked Illegal Behavior in all build modes.
1919 }
2020 }
2121}
doc/langref/test_undefined_behavior.zig deleted-5
......@@ -1,5 +0,0 @@
1test "safety check" {
2 unreachable;
3}
4
5// test_error=reached unreachable code