diff --git a/doc/manual/source/language/evaluation.md b/doc/manual/source/language/evaluation.md
index 980942c92..dff429776 100644
--- a/doc/manual/source/language/evaluation.md
+++ b/doc/manual/source/language/evaluation.md
@@ -74,4 +74,48 @@ in f { x = throw "error"; y = throw "error"; }
=> "ok"
```
+## Evaluation order
+
+The order in which expressions are evaluated is generally unspecified, because it does not affect successful evaluation outcomes.
+This allows more freedom for the evaluator to evolve and to evaluate efficiently.
+
+Data dependencies naturally impose some ordering constraints: a value cannot be used before it is computed.
+Beyond these constraints, the evaluator is free to choose any order.
+
+The order in which side effects such as [`builtins.trace`](@docroot@/language/builtins.md#builtins-trace) output occurs is not defined, but may be expected to follow data dependencies.
+
+In a lazy language, evaluation order is often opposite to expectations from strict languages.
+For example, in `let wrap = x: { wrapped = x; }; in wrap (1 + 2)`, the function body produces a result (`{ wrapped = ...; }`) *before* evaluating `x`.
+
+## Infinite recursion and stack overflow
+
+During evaluation, two types of errors can occur when expressions reference themselves or call functions too deeply:
+
+### Infinite recursion
+
+This error occurs when a value depends on itself through a cycle, making it impossible to compute.
+
+```nix
+let x = x; in x
+=> error: infinite recursion encountered
+```
+
+Infinite recursion happens at the value level when evaluating an expression requires evaluating the same expression again.
+
+Despite the name, infinite recursion is cheap to compute and does not involve a stack overflow.
+The cycle is finite and fairly easy to detect.
+
+### Stack overflow
+
+This error occurs when the call depth exceeds the maximum allowed limit.
+
+```nix
+let f = x: f (x + 1);
+in f 0
+=> error: stack overflow; max-call-depth exceeded
+```
+
+Stack overflow happens when too many function calls are nested without returning.
+The maximum call depth is controlled by the [`max-call-depth` setting](@docroot@/command-ref/conf-file.md#conf-max-call-depth).
+
[C API]: @docroot@/c-api.md
diff --git a/doc/manual/source/language/operators.md b/doc/manual/source/language/operators.md
index ab74e8a99..dad3e1e8d 100644
--- a/doc/manual/source/language/operators.md
+++ b/doc/manual/source/language/operators.md
@@ -23,8 +23,8 @@
| [Greater than or equal to][Comparison] | *expr* `>=` *expr* | none | 10 |
| [Equality] | *expr* `==` *expr* | none | 11 |
| Inequality | *expr* `!=` *expr* | none | 11 |
-| Logical conjunction (`AND`) | *bool* `&&` *bool* | left | 12 |
-| Logical disjunction (`OR`) | *bool* \|\| *bool* | left | 13 |
+| [Logical conjunction] (`AND`) | *bool* `&&` *bool* | left | [12](#precedence-and-disjunctive-normal-form) |
+| [Logical disjunction] (`OR`) | *bool* \|\| *bool* | left | [13](#precedence-and-disjunctive-normal-form) |
| [Logical implication] | *bool* `->` *bool* | right | 14 |
| [Pipe operator] (experimental) | *expr* `\|>` *func* | left | 15 |
| [Pipe operator] (experimental) | *func* `<\|` *expr* | right | 15 |
@@ -162,6 +162,9 @@ Update [attribute set] *attrset1* with names and values from *attrset2*.
The returned attribute set will have all of the attributes in *attrset1* and *attrset2*.
If an attribute name is present in both, the attribute value from the latter is taken.
+This operator is [strict](@docroot@/language/evaluation.md#strictness) in both *attrset1* and *attrset2*.
+That means that both arguments are evaluated to [weak head normal form](@docroot@/language/evaluation.md#values), so the attribute sets themselves are evaluated, but their attribute values are not evaluated.
+
[Update]: #update
## Comparison
@@ -185,18 +188,95 @@ All comparison operators are implemented in terms of `<`, and the following equi
## Equality
-- [Attribute sets][attribute set] and [lists][list] are compared recursively, and therefore are fully evaluated.
-- Comparison of [functions][function] always returns `false`.
+- [Attribute sets][attribute set] are compared first by attribute names and then by items until a difference is found.
+- [Lists][list] are compared first by length and then by items until a difference is found.
+- Comparison of distinct [functions][function] returns `false`, but identical functions may be subject to [value identity optimization](#value-identity-optimization).
- Numbers are type-compatible, see [arithmetic] operators.
- Floating point numbers only differ up to a limited precision.
+The `==` operator is [strict](@docroot@/language/evaluation.md#strictness) in both arguments; when comparing composite types ([attribute sets][attribute set] and [lists][list]), it is partially strict in their contained values: they are evaluated until a difference is found.
+
+### Value identity optimization
+
+Nix performs equality comparisons of nested values by pointer equality or more abstractly, _identity_.
+Nix semantics ideally do not assign a unique identity to values as they are created, but equality is an exception to this rule.
+The disputable benefit of this is that it is more efficient, and it allows cyclical structures to be compared, e.g. `let x = { x = x; }; in x == x` evaluates to `true`.
+However, as a consequence, it makes a function equal to itself when the comparison is made in a list or attribute set, in contradiction to a simple direct comparison.
+
[function]: ./syntax.md#functions
[Equality]: #equality
+## Logical conjunction
+
+> **Syntax**
+>
+> *bool1* `&&` *bool2*
+
+Logical AND. Equivalent to `if` *bool1* `then` *bool2* `else false`.
+
+This operator is [strict](@docroot@/language/evaluation.md#strictness) in *bool1*, but only evaluates *bool2* if *bool1* is `true`.
+
+> **Example**
+>
+> ```nix
+> true && false
+> => false
+>
+> false && throw "never evaluated"
+> => false
+> ```
+
+[Logical conjunction]: #logical-conjunction
+
+## Logical disjunction
+
+> **Syntax**
+>
+> *bool1* `||` *bool2*
+
+Logical OR. Equivalent to `if` *bool1* `then true` `else` *bool2*.
+
+This operator is [strict](@docroot@/language/evaluation.md#strictness) in *bool1*, but only evaluates *bool2* if *bool1* is `false`.
+
+> **Example**
+>
+> ```nix
+> true || false
+> => true
+>
+> true || throw "never evaluated"
+> => true
+> ```
+
+[Logical disjunction]: #logical-disjunction
+
+### Precedence and disjunctive normal form
+
+The precedence of `&&` and `||` aligns with disjunctive normal form.
+Without parentheses, an expression describes multiple "permissible situations" (connected by `||`), where each situation consists of multiple simultaneous conditions (connected by `&&`).
+
+For example, `A || B && C || D && E` is parsed as `A || (B && C) || (D && E)`, describing three permissible situations: A holds, or both B and C hold, or both D and E hold.
+
## Logical implication
-Equivalent to `!`*b1* `||` *b2* (or `if` *b1* `then` *b2* `else true`)
+> **Syntax**
+>
+> *bool1* `->` *bool2*
+
+Logical implication. Equivalent to `!`*bool1* `||` *bool2* (or `if` *bool1* `then` *bool2* `else true`).
+
+This operator is [strict](@docroot@/language/evaluation.md#strictness) in *bool1*, but only evaluates *bool2* if *bool1* is `true`.
+
+> **Example**
+>
+> ```nix
+> true -> false
+> => false
+>
+> false -> throw "never evaluated"
+> => true
+> ```
[Logical implication]: #logical-implication
diff --git a/tests/functional/lang/eval-okay-equal-function-attrset-distinct-similar.exp b/tests/functional/lang/eval-okay-equal-function-attrset-distinct-similar.exp
new file mode 100644
index 000000000..c508d5366
--- /dev/null
+++ b/tests/functional/lang/eval-okay-equal-function-attrset-distinct-similar.exp
@@ -0,0 +1 @@
+false
diff --git a/tests/functional/lang/eval-okay-equal-function-attrset-distinct-similar.nix b/tests/functional/lang/eval-okay-equal-function-attrset-distinct-similar.nix
new file mode 100644
index 000000000..13c30d9f7
--- /dev/null
+++ b/tests/functional/lang/eval-okay-equal-function-attrset-distinct-similar.nix
@@ -0,0 +1,3 @@
+# Distinct but not identical functions in attribute set compare as unequal
+# See https://nix.dev/manual/nix/latest/language/operators#equality
+{ a = (x: x); } == { a = (x: x); }
diff --git a/tests/functional/lang/eval-okay-equal-function-attrset-identical.exp b/tests/functional/lang/eval-okay-equal-function-attrset-identical.exp
new file mode 100644
index 000000000..27ba77dda
--- /dev/null
+++ b/tests/functional/lang/eval-okay-equal-function-attrset-identical.exp
@@ -0,0 +1 @@
+true
diff --git a/tests/functional/lang/eval-okay-equal-function-attrset-identical.nix b/tests/functional/lang/eval-okay-equal-function-attrset-identical.nix
new file mode 100644
index 000000000..830267c82
--- /dev/null
+++ b/tests/functional/lang/eval-okay-equal-function-attrset-identical.nix
@@ -0,0 +1,10 @@
+# Function comparison in attribute set uses value identity optimization
+# See https://nix.dev/manual/nix/latest/language/operators#value-identity-optimization
+let
+ f = x: x;
+in
+{
+ a = f;
+} == {
+ a = f;
+}
diff --git a/tests/functional/lang/eval-okay-equal-function-direct-distinct-similar.exp b/tests/functional/lang/eval-okay-equal-function-direct-distinct-similar.exp
new file mode 100644
index 000000000..c508d5366
--- /dev/null
+++ b/tests/functional/lang/eval-okay-equal-function-direct-distinct-similar.exp
@@ -0,0 +1 @@
+false
diff --git a/tests/functional/lang/eval-okay-equal-function-direct-distinct-similar.nix b/tests/functional/lang/eval-okay-equal-function-direct-distinct-similar.nix
new file mode 100644
index 000000000..f3a931c6b
--- /dev/null
+++ b/tests/functional/lang/eval-okay-equal-function-direct-distinct-similar.nix
@@ -0,0 +1,3 @@
+# Direct comparison of distinct but not identical functions returns false
+# See https://nix.dev/manual/nix/latest/language/operators#equality
+(x: x) == (x: x)
diff --git a/tests/functional/lang/eval-okay-equal-function-direct-identical.exp b/tests/functional/lang/eval-okay-equal-function-direct-identical.exp
new file mode 100644
index 000000000..c508d5366
--- /dev/null
+++ b/tests/functional/lang/eval-okay-equal-function-direct-identical.exp
@@ -0,0 +1 @@
+false
diff --git a/tests/functional/lang/eval-okay-equal-function-direct-identical.nix b/tests/functional/lang/eval-okay-equal-function-direct-identical.nix
new file mode 100644
index 000000000..f91a39fb8
--- /dev/null
+++ b/tests/functional/lang/eval-okay-equal-function-direct-identical.nix
@@ -0,0 +1,6 @@
+# Direct comparison of identical function returns false
+# See https://nix.dev/manual/nix/latest/language/operators#equality
+let
+ f = x: x;
+in
+f == f
diff --git a/tests/functional/lang/eval-okay-equal-function-list-distinct-similar.exp b/tests/functional/lang/eval-okay-equal-function-list-distinct-similar.exp
new file mode 100644
index 000000000..c508d5366
--- /dev/null
+++ b/tests/functional/lang/eval-okay-equal-function-list-distinct-similar.exp
@@ -0,0 +1 @@
+false
diff --git a/tests/functional/lang/eval-okay-equal-function-list-distinct-similar.nix b/tests/functional/lang/eval-okay-equal-function-list-distinct-similar.nix
new file mode 100644
index 000000000..cd6182770
--- /dev/null
+++ b/tests/functional/lang/eval-okay-equal-function-list-distinct-similar.nix
@@ -0,0 +1,3 @@
+# Distinct but not identical functions in list compare as unequal
+# See https://nix.dev/manual/nix/latest/language/operators#equality
+[ (x: x) ] == [ (x: x) ]
diff --git a/tests/functional/lang/eval-okay-equal-function-list-identical.exp b/tests/functional/lang/eval-okay-equal-function-list-identical.exp
new file mode 100644
index 000000000..27ba77dda
--- /dev/null
+++ b/tests/functional/lang/eval-okay-equal-function-list-identical.exp
@@ -0,0 +1 @@
+true
diff --git a/tests/functional/lang/eval-okay-equal-function-list-identical.nix b/tests/functional/lang/eval-okay-equal-function-list-identical.nix
new file mode 100644
index 000000000..5156ffc47
--- /dev/null
+++ b/tests/functional/lang/eval-okay-equal-function-list-identical.nix
@@ -0,0 +1,6 @@
+# Function comparison in list uses value identity optimization
+# See https://nix.dev/manual/nix/latest/language/operators#value-identity-optimization
+let
+ f = x: x;
+in
+[ f ] == [ f ]