diff --git a/doc/langref.html.in b/doc/langref.html.in
index 959afdbf374e4eec44c150afa97616c564622dcc..df9af26b30173ad8dbf4aae164918fd14ee2781b 100644
--- a/doc/langref.html.in
+++ b/doc/langref.html.in
@@ -1894,20 +1894,77 @@ const B = error{Two};
{#header_close#}
{#header_open|Precedence#}
+
+ Zig features partial operator precedence: not every operator has a defined precedence with
+ every other operator. Mixing operators with ambiguous precedence is prohibited and parentheses
+ are mandatory. Consequently, valid code is typically auto-descriptive; when reading and
+ writing Zig code, there are very few precedence rules you'll need to remember.
+
+
+ {#code|operator_precedence.zig#}
+
+ {#code|ambiguous_precedence_orelse.zig#}
+
+
+ Simplified operator precedence tables are provided below, each tailored to a general group of
+ operators. For all of the groups shown below, the operators that have higher precedence are
+ the same, and themselves have the following precedence table:
+
+
{#syntax#}x() x[] x.y x.* x.?
a!b
x{}
-!x -x -%x ~x &x ?x try
-* / % *% *| ||
-+ - ++ +% -% +| -|
-<< >> <<|
-& ^ | orelse catch
-== != < > <= >=
-and
-or
-= *= *%= *|= /= %= += +%= +|= -= -%= -|= <<= <<|= >>= &= ^= |={#endsyntax#}
+!x -x -%x ~x &x ?x try{#endsyntax#}
+
+
+ For the tables below, the operators that have ambiguous precedence or semantic errors with the
+ given operators have been omitted, as have operators with higher precedence.
+ The operators colored red and
+ blue have ambiguous precedence with the other operators
+ on the same line. Those colored red also have ambiguous
+ precedence with themselves (they cannot be chained).
+
+
+
+ For arithmetic operators:
+
+
+ * / *% *| %
++ - +% -% +| -|
+== != < > <= >=
+and or
+
+
+ For bitwise operators:
+
+
+ << <<| >> & ^ |
+== != < > <= >=
+and or
+
+
+ For null and error recovery operators:
+
+
+ catch orelse
+
+
+ For error set operators:
+
+
+ {#syntax#}||{#endsyntax#}
+
+
+ For array concatenation operators:
+
+
+ {#syntax#}++{#endsyntax#}
+
+
+ The full precedence rules can be found in the {#link|Grammar#}, which also includes an {#link|Operator Precedence Flowchart#}.
+
+
{#header_close#}
-
{#header_close#}
{#header_open|Arrays#}
diff --git a/doc/langref/ambiguous_precedence_mul_bitand.zig b/doc/langref/ambiguous_precedence_mul_bitand.zig
deleted file mode 100644
index af3c28cc6c04729e2fd1cec8084c2a1b9afb8ae3..0000000000000000000000000000000000000000
--- a/doc/langref/ambiguous_precedence_mul_bitand.zig
+++ /dev/null
@@ -1,11 +0,0 @@
-test "ambiguous precedence" {
- const x: u8 = 1;
- const y: u8 = 2;
- const z: u8 = 3;
-
- // 1. Could be multiplication by a masked value: (x&y) *% z
- // 2. Or masking the result of multiplication: x & (y*%z)
- _ = x & y *% z;
-}
-
-// obj=ambiguous operator precedence with operators ... & ... *% ...; use parentheses to disambiguate
\ No newline at end of file
diff --git a/doc/langref/ambiguous_precedence_orelse.zig b/doc/langref/ambiguous_precedence_orelse.zig
new file mode 100644
index 0000000000000000000000000000000000000000..dcb9d4cab12f0992400e88c3eccf73711543947d
--- /dev/null
+++ b/doc/langref/ambiguous_precedence_orelse.zig
@@ -0,0 +1,12 @@
+test "ambiguous precedence" {
+ const x: ?bool = true;
+ const y: bool = false;
+ const z: bool = false;
+
+ // The following expression is always considered ambiguous,
+ // and, in this particular case, `(x orelse y) == z` would
+ // produce a different result than `x orelse (y == z)`.
+ _ = x orelse y == z;
+}
+
+// obj=ambiguous operator precedence with operators ... orelse ... == ...; use parentheses to disambiguate
diff --git a/doc/langref/ambiguous_precedence_orelse_ne.zig b/doc/langref/ambiguous_precedence_orelse_ne.zig
deleted file mode 100644
index b7be5d0c9bdff108e37183365fbcaa59705bb61a..0000000000000000000000000000000000000000
--- a/doc/langref/ambiguous_precedence_orelse_ne.zig
+++ /dev/null
@@ -1,18 +0,0 @@
-fn makeBool(x: u32) ?bool {
- if (x == 0) return false;
- if (x == 1) return true;
- return null;
-}
-
-test "ambiguous precedence" {
- const x: ?u32 = 0;
- const y: u8 = 0;
- const z: u8 = 0;
-
- // Without parentheses the compiler cannot distinguish between two conflicting interpretations:
- // 1. Compare on the value which may be recovered: (makeBool(x) orelse y) != z
- // 2. Recover to the result of the comparison: makeBool(x) orelse (y != z)
- _ = makeBool(x) orelse y != z;
-}
-
-// obj=ambiguous operator precedence with operators ... orelse ... != ...; use parentheses to disambiguate
\ No newline at end of file
diff --git a/doc/langref/ambiguous_precedence_shift_sub.zig b/doc/langref/ambiguous_precedence_shift_sub.zig
deleted file mode 100644
index 2410aa950cfcc25060f6d8d341129983f8fd51e3..0000000000000000000000000000000000000000
--- a/doc/langref/ambiguous_precedence_shift_sub.zig
+++ /dev/null
@@ -1,10 +0,0 @@
-test "ambiguous precedence" {
- const x: u64 = 1;
- const y: u6 = 63;
-
- // 1. Could mean shift then subtract: (x<> cannot be repeated without parenthesis nor mixed with other bitwise operators
- // The following lines would be compile errors
- // _ = x >> 1 >> 2;
- // _ = 1 << 16 | 1 << 8 | 1;
- // Instead parentheses should be used
- try expectEqual(63, (x >> 1) >> 2);
- try expectEqual(0x10101, (1 << 16) | (1 << 8) | 1);
-
- // Comparisons
- // < > <= >= == != cannot be chained
- // Binds lower than either bitwise operators or arithmetic operators
- // which allows one on one side and one on the other
- try expect(1 | 4 <= 1 + 4);
- // equivilent to this code
- try expect((1 | 4) <= (1 + 4));
-
- // Boolean Operators
- // `and` and `or` can be freely chained with themselves, shortcircuiting left to right
- // they may not be mixed with each other without parenthesis
- // bind looser than comparisons
- const false_expr: bool = 7 < 2;
- const true_expr: bool = 1 + 1 == 2;
-
- try expect(false_expr or false_expr or true_expr);
- try expect(7 < 2 or 7 < 2 or 1 + 1 == 2);
- try expect(((7 < 2) or (7 < 2)) or (1 + 1 == 2));
-
- try expect(true_expr and true_expr and true_expr);
- try expect(1 + 1 == 2 and 1 + 1 == 2 and 1 + 1 == 2);
- try expect(((1 + 1 == 2) and (1 + 1 == 2)) and (1 + 1 == 2));
-
- // The following line would be a compile error
- // _ = false_expr or true_expr and true_expr
- // Instead parentheses should be used
- try expect(false_expr or (true_expr and true_expr));
-
- // Orelse and Catch
- // Like Boolean Operators, may be chained with themselves, but not each other
- // bind looser than prefix expressions
- const some_null: ?u8 = null;
- try expectEqual(2, some_null orelse some_null orelse 2);
-
- const some_error: error{invalid}!u8 = error.invalid;
- try expectEqual(3, some_error catch some_error catch 3);
-
- // The following line would be a compile error
- // _ = some_error catch some_null orelse 1;
- // Instead parentheses should be used
- try expectEqual(1, (some_error catch some_null) orelse 1);
-}
-
-// obj
\ No newline at end of file
diff --git a/doc/langref/nonchaining_sibling_operators.zig b/doc/langref/nonchaining_sibling_operators.zig
deleted file mode 100644
index 9873620d783801578cdb5560a0541992be28ab4b..0000000000000000000000000000000000000000
--- a/doc/langref/nonchaining_sibling_operators.zig
+++ /dev/null
@@ -1,5 +0,0 @@
-fn foo(x: anytype, y: anytype, z: anytype) void {
- _ = x << y << z;
-}
-
-// obj=repetition of non-chainable operators ... << ... << ...; use parentheses to disambiguate
\ No newline at end of file
diff --git a/doc/langref/operator_precedence.zig b/doc/langref/operator_precedence.zig
index 9b5a2ed53323e20c44b01d5370de5c811b6ac10a..78d09e2441b8b78654893ff0d3dc2704d49d979b 100644
--- a/doc/langref/operator_precedence.zig
+++ b/doc/langref/operator_precedence.zig
@@ -1,23 +1,86 @@
-const assert = @import("std").debug.assert;
+const std = @import("std");
test "operator precedence" {
- const x = 1;
- const y = 2;
- const z = 3;
-
- assert(x + y * z == x + (y*z));
- assert(x * y + z == (x*y) + z);
-
- // Ambiguously related operators may exist in the same expression if they are not adjacent
- assert(x*y + x%y == (x*y) + (x%y));
- // Thus includes comparisons
- assert(x & y != x + y);
-
- // Or's and and's may be freely chained, but not with each other
- assert(false or x == y or x + y == z);
- assert(true and x != y and y != z);
- // The following line would be a compile error
- // assert(false or x != y and y != z);
+ // Arithmetic operators follow the conventional order of operations.
+ {
+ const a = 1 + 2 * 3 - 4 / 2;
+ const b = 1 + (2 * 3) - (4 / 2);
+ try std.testing.expectEqual(a, b);
+ }
+
+ // Remainder division is a special case. It still has a higher precedence than
+ // addition and subtraction, but it has ambiguous precedence with itself and
+ // other multiplication/division operators.
+ {
+ const a = 1 + 2 % 3;
+ const b = 1 + (2 % 3);
+ try std.testing.expectEqual(a, b);
+
+ // `1 % 2 % 3`, `1 % 2 / 3`, etc. would be a compile error,
+ // and parentheses would be required to disambiguate.
+ }
+
+ // Bitwise operators have ambiguous precedence with other bitwise operators.
+ // However, non-shifting bitwise operators (&, |, ^) can be chained with themselves.
+ {
+ const a = 1 ^ 2 ^ 3;
+ const b = (1 ^ 2) ^ 3;
+ try std.testing.expectEqual(a, b);
+
+ // `1 ^ 2 | 3`, `1 << 2 << 3`, etc. would be a compile error,
+ // and parentheses would be required to disambiguate.
+
+ // To mix bitwise operators, parentheses must be used.
+ try std.testing.expectEqual(0x10101, (1 << 16) | (1 << 8) | 1);
+ }
+
+ // Comparison operators have lower precedence than both arithmetic and bitwise
+ // operators. Comparisons cannot be chained.
+ {
+ const a = 1 | 4 <= 1 + 4;
+ const b = (1 | 4) <= (1 + 4);
+ try std.testing.expectEqual(a, b);
+
+ // `1 == 2 >= 3`, etc. would be a compile error,
+ // and parentheses would be required to disambiguate.
+ }
+
+ // Logical operators have lower precedence than comparison operators,
+ // and may only be chained homogeneously (`and` with `and`, but not
+ // with `or`, and vice versa).
+ {
+ const false_expr: bool = 7 < 2;
+ const true_expr: bool = 1 + 1 == 2;
+
+ const a = false_expr or false_expr or true_expr;
+ const b = 7 < 2 or 7 < 2 or 1 + 1 == 2;
+ const c = ((7 < 2) or (7 < 2)) or (1 + 1 == 2);
+ try std.testing.expect(a and b and c);
+
+ // `a or b and c`, etc. would be a compile error,
+ // and parentheses would be required to disambiguate.
+ }
+
+ // `orelse` and `catch` may be chained with themselves, but not each other.
+ {
+ const some_null: ?u8 = null;
+
+ const a = some_null orelse some_null orelse 2;
+ const b = (some_null orelse some_null) orelse 2;
+ try std.testing.expectEqual(a, b);
+
+ // `a catch b orelse c`, etc. would be a compile error,
+ // and parentheses would be required to disambiguate.
+
+ // Prefix operators like `try` have higher precedence than `orelse`/`catch`.
+ const c = try maybe() orelse 1;
+ const d = (try maybe()) orelse 1;
+ try std.testing.expectEqual(c, d);
+ }
+}
+
+fn maybe() error{Fail}!?u8 {
+ return null;
}
-// test
\ No newline at end of file
+// test