@@ -6806,8 +6806,269 @@ fn foo() i32 {
68066806
68076807 {#header_open|Result Location Semantics#}
68086808 <p>
6809- <a href="https://github.com/ziglang/zig/issues/2809">TODO add documentation for this</a>
6809+ During compilation, every Zig expression and sub-expression is assigned optional result location
6810+ information. This information dictates what type the expression should have (its result type), and
6811+ where the resulting value should be placed in memory (its result location). The information is
6812+ optional in the sense that not every expression has this information: assignment to
6813+ {#syntax#}_{#endsyntax#}, for instance, does not provide any information about the type of an
6814+ expression, nor does it provide a concrete memory location to place it in.
68106815 </p>
6816+ <p>
6817+ As a motivating example, consider the statement {#syntax#}const x: u32 = 42;{#endsyntax#}. The type
6818+ annotation here provides a result type of {#syntax#}u32{#endsyntax#} to the initialization expression
6819+ {#syntax#}42{#endsyntax#}, instructing the compiler to coerce this integer (initally of type
6820+ {#syntax#}comptime_int{#endsyntax#}) to this type. We will see more examples shortly.
6821+ </p>
6822+ <p>
6823+ This is not an implementation detail: the logic outlined above is codified into the Zig language
6824+ specification, and is the primary mechanism of type inference in the language. This system is
6825+ collectively referred to as "Result Location Semantics".
6826+ </p>
6827+ {#header_open|Result Types#}
6828+ <p>
6829+ Result types are propagated recursively through expressions where possible. For instance, if the
6830+ expression {#syntax#}&e{#endsyntax#} has result type {#syntax#}*u32{#endsyntax#}, then
6831+ {#syntax#}e{#endsyntax#} is given a result type of {#syntax#}u32{#endsyntax#}, allowing the
6832+ language to perform this coercion before taking a reference.
6833+ </p>
6834+ <p>
6835+ The result type mechanism is utilized by casting builtins such as {#syntax#}@intCast{#endsyntax#}.
6836+ Rather than taking as an argument the type to cast to, these builtins use their result type to
6837+ determine this information. The result type is often known from context; where it is not, the
6838+ {#syntax#}@as{#endsyntax#} builtin can be used to explicitly provide a result type.
6839+ </p>
6840+ <p>
6841+ We can break down the result types for each component of a simple expression as follows:
6842+ </p>
6843+ {#code_begin|test|result_type_propagation#}
6844+ const expectEqual = @import("std").testing.expectEqual;
6845+ test "result type propagates through struct initializer" {
6846+ const S = struct { x: u32 };
6847+ const val: u64 = 123;
6848+ const s: S = .{ .x = @intCast(val) };
6849+ // .{ .x = @intCast(val) } has result type `S` due to the type annotation
6850+ // @intCast(val) has result type `u32` due to the type of the field `S.x`
6851+ // val has no result type, as it is permitted to be any integer type
6852+ try expectEqual(@as(u32, 123), s.x);
6853+ }
6854+ {#code_end#}
6855+ <p>
6856+ This result type information is useful for the aforementioned cast builtins, as well as to avoid
6857+ the construction of pre-coercion values, and to avoid the need for explicit type coercions in some
6858+ cases. The following table details how some common expressions propagate result types, where
6859+ {#syntax#}x{#endsyntax#} and {#syntax#}y{#endsyntax#} are arbitrary sub-expressions.
6860+ </p>
6861+ <div class="table-wrapper">
6862+ <table>
6863+ <thead>
6864+ <tr>
6865+ <th scope="col">Expression</th>
6866+ <th scope="col">Parent Result Type</th>
6867+ <th scope="col">Sub-expression Result Type</th>
6868+ </tr>
6869+ </thead>
6870+ <tbody>
6871+ <tr>
6872+ <th scope="row">{#syntax#}const val: T = x{#endsyntax#}</th>
6873+ <td>-</td>
6874+ <td>{#syntax#}x{#endsyntax#} is a {#syntax#}T{#endsyntax#}</td>
6875+ </tr>
6876+ <tr>
6877+ <th scope="row">{#syntax#}var val: T = x{#endsyntax#}</th>
6878+ <td>-</td>
6879+ <td>{#syntax#}x{#endsyntax#} is a {#syntax#}T{#endsyntax#}</td>
6880+ </tr>
6881+ <tr>
6882+ <th scope="row">{#syntax#}val = x{#endsyntax#}</th>
6883+ <td>-</td>
6884+ <td>{#syntax#}x{#endsyntax#} is a {#syntax#}@TypeOf(val){#endsyntax#}</td>
6885+ </tr>
6886+ <tr>
6887+ <th scope="row">{#syntax#}@as(T, x){#endsyntax#}</th>
6888+ <td>-</td>
6889+ <td>{#syntax#}x{#endsyntax#} is a {#syntax#}T{#endsyntax#}</td>
6890+ </tr>
6891+ <tr>
6892+ <th scope="row">{#syntax#}&x{#endsyntax#}</th>
6893+ <td>{#syntax#}*T{#endsyntax#}</td>
6894+ <td>{#syntax#}x{#endsyntax#} is a {#syntax#}T{#endsyntax#}</td>
6895+ </tr>
6896+ <tr>
6897+ <th scope="row">{#syntax#}&x{#endsyntax#}</th>
6898+ <td>{#syntax#}[]T{#endsyntax#}</td>
6899+ <td>{#syntax#}x{#endsyntax#} is some array of {#syntax#}T{#endsyntax#}</td>
6900+ </tr>
6901+ <tr>
6902+ <th scope="row">{#syntax#}f(x){#endsyntax#}</th>
6903+ <td>-</td>
6904+ <td>{#syntax#}x{#endsyntax#} has the type of the first parameter of {#syntax#}f{#endsyntax#}</td>
6905+ </tr>
6906+ <tr>
6907+ <th scope="row">{#syntax#}.{x}{#endsyntax#}</th>
6908+ <td>{#syntax#}T{#endsyntax#}</td>
6909+ <td>{#syntax#}x{#endsyntax#} is a {#syntax#}std.meta.FieldType(T, .@"0"){#endsyntax#}</td>
6910+ </tr>
6911+ <tr>
6912+ <th scope="row">{#syntax#}.{ .a = x }{#endsyntax#}</th>
6913+ <td>{#syntax#}T{#endsyntax#}</td>
6914+ <td>{#syntax#}x{#endsyntax#} is a {#syntax#}std.meta.FieldType(T, .a){#endsyntax#}</td>
6915+ </tr>
6916+ <tr>
6917+ <th scope="row">{#syntax#}T{x}{#endsyntax#}</th>
6918+ <td>-</td>
6919+ <td>{#syntax#}x{#endsyntax#} is a {#syntax#}std.meta.FieldType(T, .@"0"){#endsyntax#}</td>
6920+ </tr>
6921+ <tr>
6922+ <th scope="row">{#syntax#}T{ .a = x }{#endsyntax#}</th>
6923+ <td>-</td>
6924+ <td>{#syntax#}x{#endsyntax#} is a {#syntax#}std.meta.FieldType(T, .a){#endsyntax#}</td>
6925+ </tr>
6926+ <tr>
6927+ <th scope="row">{#syntax#}@Type(x){#endsyntax#}</th>
6928+ <td>-</td>
6929+ <td>{#syntax#}x{#endsyntax#} is a {#syntax#}std.builtin.Type{#endsyntax#}</td>
6930+ </tr>
6931+ <tr>
6932+ <th scope="row">{#syntax#}@typeInfo(x){#endsyntax#}</th>
6933+ <td>-</td>
6934+ <td>{#syntax#}x{#endsyntax#} is a {#syntax#}type{#endsyntax#}</td>
6935+ </tr>
6936+ <tr>
6937+ <th scope="row">{#syntax#}x << y{#endsyntax#}</th>
6938+ <td>-</td>
6939+ <td>{#syntax#}y{#endsyntax#} is a {#syntax#}std.math.Log2IntCeil(@TypeOf(x)){#endsyntax#}</td>
6940+ </tr>
6941+ </tbody>
6942+ </table>
6943+ </div>
6944+ {#header_close#}
6945+ {#header_open|Result Locations#}
6946+ <p>
6947+ In addition to result type information, every expression may be optionally assigned a result
6948+ location: a pointer to which the value must be directly written. This system can be used to prevent
6949+ intermediate copies when initializing data structures, which can be important for types which must
6950+ have a fixed memory address ("pinned" types).
6951+ </p>
6952+ <p>
6953+ When compiling the simple assignment expression {#syntax#}x = e{#endsyntax#}, many languages would
6954+ create the temporary value {#syntax#}e{#endsyntax#} on the stack, and then assign it to
6955+ {#syntax#}x{#endsyntax#}, potentially performing a type coercion in the process. Zig approaches this
6956+ differently. The expression {#syntax#}e{#endsyntax#} is given a result type matching the type of
6957+ {#syntax#}x{#endsyntax#}, and a result location of {#syntax#}&x{#endsyntax#}. For many syntactic
6958+ forms of {#syntax#}e{#endsyntax#}, this has no practical impact. However, it can have important
6959+ semantic effects when working with more complex syntax forms.
6960+ </p>
6961+ <p>
6962+ For instance, if the expression {#syntax#}.{ .a = x, .b = y }{#endsyntax#} has a result location of
6963+ {#syntax#}ptr{#endsyntax#}, then {#syntax#}x{#endsyntax#} is given a result location of
6964+ {#syntax#}&ptr.a{#endsyntax#}, and {#syntax#}y{#endsyntax#} a result location of {#syntax#}&ptr.b{#endsyntax#}.
6965+ Without this system, this expression would construct a temporary struct value entirely on the stack, and
6966+ only then copy it to the destination address. In essence, Zig desugars the assignment
6967+ {#syntax#}foo = .{ .a = x, .b = y }{#endsyntax#} to the two statements {#syntax#}foo.a = x; foo.b = y;{#endsyntax#}.
6968+ </p>
6969+ <p>
6970+ This can sometimes be important when assigning an aggregate value where the initialization
6971+ expression depends on the previous value of the aggregate. The easiest way to demonstrate this is by
6972+ attempting to swap fields of a struct or array - the following logic looks sound, but in fact is not:
6973+ </p>
6974+ {#code_begin|test_err|result_location_interfering_with_swap#}
6975+ const expect = @import("std").testing.expect;
6976+ test "attempt to swap array elements with array initializer" {
6977+ var arr: [2]u32 = .{ 1, 2 };
6978+ arr = .{ arr[1], arr[0] };
6979+ // The previous line is equivalent to the following two lines:
6980+ // arr[0] = arr[1];
6981+ // arr[1] = arr[0];
6982+ // So this fails!
6983+ try expect(arr[0] == 2); // succeeds
6984+ try expect(arr[1] == 1); // fails
6985+ }
6986+ {#code_end#}
6987+ <p>
6988+ The following table details how some common expressions propagate result locations, where
6989+ {#syntax#}x{#endsyntax#} and {#syntax#}y{#endsyntax#} are arbitrary sub-expressions. Note that
6990+ some expressions cannot provide meaningful result locations to sub-expressions, even if they
6991+ themselves have a result location.
6992+ </p>
6993+ <div class="table-wrapper">
6994+ <table>
6995+ <thead>
6996+ <tr>
6997+ <th scope="col">Expression</th>
6998+ <th scope="col">Result Location</th>
6999+ <th scope="col">Sub-expression Result Locations</th>
7000+ </tr>
7001+ </thead>
7002+ <tbody>
7003+ <tr>
7004+ <th scope="row">{#syntax#}const val: T = x{#endsyntax#}</th>
7005+ <td>-</td>
7006+ <td>{#syntax#}x{#endsyntax#} has result location {#syntax#}&val{#endsyntax#}</td>
7007+ </tr>
7008+ <tr>
7009+ <th scope="row">{#syntax#}var val: T = x{#endsyntax#}</th>
7010+ <td>-</td>
7011+ <td>{#syntax#}x{#endsyntax#} has result location {#syntax#}&val{#endsyntax#}</td>
7012+ </tr>
7013+ <tr>
7014+ <th scope="row">{#syntax#}val = x{#endsyntax#}</th>
7015+ <td>-</td>
7016+ <td>{#syntax#}x{#endsyntax#} has result location {#syntax#}&val{#endsyntax#}</td>
7017+ </tr>
7018+ <tr>
7019+ <th scope="row">{#syntax#}@as(T, x){#endsyntax#}</th>
7020+ <td>{#syntax#}ptr{#endsyntax#}</td>
7021+ <td>{#syntax#}x{#endsyntax#} has no result location</td>
7022+ </tr>
7023+ <tr>
7024+ <th scope="row">{#syntax#}&x{#endsyntax#}</th>
7025+ <td>{#syntax#}ptr{#endsyntax#}</td>
7026+ <td>{#syntax#}x{#endsyntax#} has no result location</td>
7027+ </tr>
7028+ <tr>
7029+ <th scope="row">{#syntax#}f(x){#endsyntax#}</th>
7030+ <td>{#syntax#}ptr{#endsyntax#}</td>
7031+ <td>{#syntax#}x{#endsyntax#} has no result location</td>
7032+ </tr>
7033+ <tr>
7034+ <th scope="row">{#syntax#}.{x}{#endsyntax#}</th>
7035+ <td>{#syntax#}ptr{#endsyntax#}</td>
7036+ <td>{#syntax#}x{#endsyntax#} has result location {#syntax#}&ptr[0]{#endsyntax#}</td>
7037+ </tr>
7038+ <tr>
7039+ <th scope="row">{#syntax#}.{ .a = x }{#endsyntax#}</th>
7040+ <td>{#syntax#}ptr{#endsyntax#}</td>
7041+ <td>{#syntax#}x{#endsyntax#} has result location {#syntax#}&ptr.a{#endsyntax#}</td>
7042+ </tr>
7043+ <tr>
7044+ <th scope="row">{#syntax#}T{x}{#endsyntax#}</th>
7045+ <td>{#syntax#}ptr{#endsyntax#}</td>
7046+ <td>{#syntax#}x{#endsyntax#} has no result location (typed initializers do not propagate result locations)</td>
7047+ </tr>
7048+ <tr>
7049+ <th scope="row">{#syntax#}T{ .a = x }{#endsyntax#}</th>
7050+ <td>{#syntax#}ptr{#endsyntax#}</td>
7051+ <td>{#syntax#}x{#endsyntax#} has no result location (typed initializers do not propagate result locations)</td>
7052+ </tr>
7053+ <tr>
7054+ <th scope="row">{#syntax#}@Type(x){#endsyntax#}</th>
7055+ <td>{#syntax#}ptr{#endsyntax#}</td>
7056+ <td>{#syntax#}x{#endsyntax#} has no result location</td>
7057+ </tr>
7058+ <tr>
7059+ <th scope="row">{#syntax#}@typeInfo(x){#endsyntax#}</th>
7060+ <td>{#syntax#}ptr{#endsyntax#}</td>
7061+ <td>{#syntax#}x{#endsyntax#} has no result location</td>
7062+ </tr>
7063+ <tr>
7064+ <th scope="row">{#syntax#}x << y{#endsyntax#}</th>
7065+ <td>{#syntax#}ptr{#endsyntax#}</td>
7066+ <td>{#syntax#}x{#endsyntax#} and {#syntax#}y{#endsyntax#} do not have result locations</td>
7067+ </tr>
7068+ </tbody>
7069+ </table>
7070+ </div>
7071+ {#header_close#}
68117072 {#header_close#}
68127073
68137074 {#header_open|usingnamespace#}
0 commit comments