| ... | @@ -6806,8 +6806,269 @@ fn foo() i32 { | ... | @@ -6806,8 +6806,269 @@ fn foo() i32 { |
| 6806 | | 6806 | |
| 6807 | {#header_open|Result Location Semantics#} | 6807 | {#header_open|Result Location Semantics#} |
| 6808 | <p> | 6808 | <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. |
| 6810 | </p> | 6815 | </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#} |
| 6811 | {#header_close#} | 7072 | {#header_close#} |
| 6812 | | 7073 | |
| 6813 | {#header_open|usingnamespace#} | 7074 | {#header_open|usingnamespace#} |