authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2023-11-19 17:03:25+00:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2024-03-15 03:10:45+00:00
log5193da3422f7a63bb7ff4395da08db3782908e33
treed3da650f1e2dc1b5f6040e219b8f7f1e034845a8
parent6b387585940e06eb27d1f95c0f3259f13f84a674

langref: add basic documentation of RLS


1 files changed, 262 insertions(+), 1 deletions(-)

doc/langref.html.in+262-1
......@@ -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#}
6844const expectEqual = @import("std").testing.expectEqual;
6845test "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#}
6975const expect = @import("std").testing.expect;
6976test "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#}