镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions dev-guide/src/rules/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ When assigning rules to new paragraphs or modifying rule names, use the followin
- `intro`: The beginning paragraph of each section. It should explain the construct being defined overall.
- `syntax`: Syntax definitions or explanations when BNF syntax definitions are not used.
- `namespace`: For items only, specifies the namespace(s) the item introduces a name in. It may also be used elsewhere when defining a namespace (e.g., `r[attribute.diagnostic.namespace]`).
- `diverging`: The divergence behavior of a construct. Be sure to update the Divergence chapter, too.
6. When a rule doesn't fall under the above keywords, or for section rule IDs, name the subrule as follows:
- If the rule names a specific Rust language construct (e.g., an attribute, standard library type/function, or keyword-introduced concept), use the construct as named in the language, appropriately case-adjusted (but do not replace `_`s with `-`s).
- Other than Rust language concepts with `_`s in the name, use `-` characters to separate words within a "subrule".
Expand Down
185 changes: 181 additions & 4 deletions src/divergence.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,93 @@ fn example() {
}
```

See the following rules for specific expression divergence behavior:
See the following rules for specific divergence behavior:

- [asm.diverging.naked_asm] --- `naked_asm!`.
- [asm.diverging.noreturn] --- `noreturn` in `asm!`.
- [expr.arith-logic.diverging] --- Arithmetic and logical expressions.
- [expr.array.diverging] --- Array expressions.
- [expr.array.index.diverging] --- Index expressions.
- [expr.as.diverging] --- `as` expressions.
- [expr.assign.diverging] --- Assignment expressions.
- [expr.await.diverging] --- `.await` expressions.
- [expr.block.async.diverging] --- Async block expressions.
- [expr.block.diverging] --- Block expressions.
- [expr.bool-logic.diverging] --- Lazy boolean expressions.
- [expr.borrow.diverging] --- Borrow expressions.
- [expr.call.diverging] --- Call expressions.
- [expr.closure.diverging] --- Closure expressions.
- [expr.cmp.diverging] --- Comparison expressions.
- [expr.compound-assign.diverging] --- Compound assignment expressions.
- [expr.deref.diverging] --- Dereference expressions.
- [expr.field.diverging] --- Field expressions.
- [expr.if.chains.diverging] --- `if` condition chains.
- [expr.if.diverging] --- `if` expressions.
- [expr.if.let.diverging] --- `if let` conditions.
- [expr.literal.diverging] --- Literal expressions.
- [expr.loop.block-labels.type] --- Labeled block expressions with `break`.
- [expr.loop.break-value.diverging] --- `loop` expressions with `break`.
- [expr.loop.break.diverging] --- `break` expressions.
- [expr.loop.continue.diverging] --- `continue` expressions.
- [expr.loop.for.diverging] --- `for` loop expressions.
- [expr.loop.infinite.diverging] --- Infinite `loop` expressions.
- [expr.match.diverging] --- `match` expressions.
- [expr.loop.while.diverging] --- `while` loop expressions.
- [expr.match.arms.diverging] --- Arms of `match` expressions.
- [expr.match.empty] --- Empty `match` expressions.
- [expr.match.scrutinee.diverging] --- Scrutinees of `match` expressions.
- [expr.method.diverging] --- Method call expressions.
- [expr.negate.diverging] --- Negation expressions.
- [expr.paren.diverging] --- Parenthesized expressions.
- [expr.path.diverging] --- Path expressions.
- [expr.placeholder.diverging] --- Underscore expressions.
- [expr.range.diverging] --- Range expressions.
- [expr.return.diverging] --- `return` expressions.
- [expr.struct.diverging] --- Struct expressions.
- [expr.try.diverging] --- Try propagation expressions.
- [expr.tuple-index.diverging] --- Tuple indexing expressions.
- [expr.tuple.diverging] --- Tuple expressions.
- [statement.let.diverging] --- `let` statements.

> [!NOTE]
> The [`panic!`] macro and related panic-generating macros like [`unreachable!`] also have the type [`!`] and are diverging.

r[divergence.never]
Any expression of type [`!`] is a diverging expression. However, diverging expressions are not limited to type [`!`]; expressions of other types may also diverge (e.g., `Some(loop {})` has type `Option<!>`).
r[divergence.never-value]
[Value expressions] of type [`!`] are diverging expressions.

```rust
fn f() -> ! { let _: ! = loop {}; } // OK.
// ^^^^^^^ Diverging value expression.
```

r[divergence.never-place]
[Place expressions] of type [`!`] that are [read][divergence.place-read] are diverging expressions.

```rust
fn f(x: !) -> ! { let _x = x; }
// ^
// Reading this place expression of type `!` makes it diverge.
```

```rust,compile_fail,E0308
fn f(x: !) -> ! { let _: ! = x; } // ERROR: Mismatched types.
// ^
// This place expression is not read and does not diverge.
```

> [!NOTE]
> Other expressions, including expressions of other types, may also be diverging expressions when they contain diverging expressions.
>
> ```rust
> fn f1() -> ! { let _: Option<!> = Some(loop {}); } // OK.
> // ^^^^^^^^^^^^^
> // This value expression diverges despite not being of type `!`.
> fn f2() -> ! { let _: &! = &*&loop {}; } // OK.
> // ^^^^^^^^^
> // This place expression is not read but diverges anyway.
> fn f3() -> ! { *{loop {}; &mut 0u8} = 0u8; } // OK.
> // ^^^^^^^^^^^^^^^^^^^^
> // This assignee expression is diverging.
> ```

> [!NOTE]
> Though `!` is considered an uninhabited type, a type being uninhabited is not sufficient for it to diverge.
Expand All @@ -59,6 +128,102 @@ Any expression of type [`!`] is a diverging expression. However, diverging expre
> [!NOTE]
> Divergence can propagate to the surrounding block. See [expr.block.diverging].

r[divergence.place-read]
## Place expression reads for never types

r[divergence.place-read.intro]
When a [place expression] is used in a context that calls for a value, the place expression is *read*. But certain situations take the place itself without reading its value. In these situations, a place expression of the [never type] does not diverge only due to its type.

```rust,compile_fail,E0308
fn f(x: !) -> ! {
let _: &! = &x;
} // ERROR: Mismatched types.
```

```rust
fn f() -> ! {
let _: &! = &*&loop {};
} // OK.
```

r[divergence.place-read.value-expressions]
A [value expression] always constitutes a read for divergence calculation.

r[divergence.place-read.not-read]
A place expression constitutes a read for divergence calculation **except** for the following situations:

- As the operand of a [borrow expression].
- As the scrutinee of a [`match` expression], when not every arm's pattern constitutes a read (see [divergence.place-read.patterns]).
- As the [scrutinee] of a [`let` condition], when the pattern does not constitute a read.
- As the initializer of a [`let` statement], when the pattern does not constitute a read.

> [!EXAMPLE]
> The following compile-fail examples are structured around [the behavior of block divergence][expr.block.diverging]. Each function returns `!`. Examples that contain a diverging place expression that is **not** read from cause the final type of the body to be `()`, which fails to type check. If the diverging place expression is read from, then every code path is diverging, and the type of the body would be `!` and it would compile successfully.
>
> ```rust,compile_fail,E0308
> fn borrow_operand_not_read(x: !) -> ! {
> &x;
> // ERROR: expected `!`, found `()`
> }
> ```
>
> ```rust,compile_fail,E0308
> fn match_scrutinee_not_read_with_non_reading_patterns(x: !) -> ! {
> match x {
> _ => {}
> };
> // ERROR: expected `!`, found `()`
> }
> ```
>
> In contrast, when an arm uses a binding pattern (which constitutes a read), the scrutinee `x: !` is read, and reading an uninhabited place diverges. Note that the match is a statement here, just like the non-reading example above. The difference is that this statement is now considered diverging, so the block has the never type because the block diverges and [does not have a final expression][expr.block.value-diverges-no-trailing-expr]:
>
> ```rust
> fn match_scrutinee_read_with_reading_patterns(x: !) -> ! {
> match x {
> a => {}
> }; // OK: this statement diverges
> }
> ```

r[divergence.place-read.patterns]
A pattern *constitutes a read* of the value it is matched against in all cases except as follows:

- The [wildcard pattern] `_` does not constitute a read.
- An [or-pattern] `p1 | p2 | ...` constitutes a read only if *all* of its alternatives constitute a read.

```rust
fn pattern_is_read(x: !) -> ! {
// OK: Pattern is read, so the match expression diverges.
match x {
a => {}
};
}
```

```rust,compile_fail,E0308
fn wildcard_pattern_not_read(x: !) -> ! {
match x {
_ => {}
};
// ERROR: expected `!`, found `()`
}
```

```rust
fn or_pattern_read_in_all_patterns(x: !) -> ! {
// OK: This statement is diverging.
let (a | a) = x;
}
```

```rust,compile_fail,E0308
fn or_pattern_not_read_if_any_alternative_is_non_reading(x: !) -> ! {
let (_ | _) = x;
// ERROR: expected `!`, found `()`
}
```

r[divergence.fallback]
## Fallback

Expand Down Expand Up @@ -89,3 +254,15 @@ If a type to be inferred is only unified with diverging expressions, then that t
<!-- TODO: This last point should likely should be moved to a more general "type inference" section discussing generalization + unification. -->

[`!`]: type.never
[borrow expression]: expr.operator.borrow
[`let` condition]: expr.if.let
[`let` statement]: statement.let
[`match` expression]: expr.match
[never type]: type.never
[or-pattern]: patterns.or
[place expression]: expr.place-value.place-memory-location
[place expressions]: expr.place-value.place-memory-location
[scrutinee]: glossary.md#scrutinee
[value expression]: expr.place-value.value-result
[value expressions]: expr.place-value.value-result
[wildcard pattern]: patterns.wildcard.intro
45 changes: 45 additions & 0 deletions src/expressions/array-expr.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,16 @@ const EMPTY: Vec<i32> = Vec::new();
[EMPTY; 2];
```

r[expr.array.diverging]
An array expression [diverges] if any of its operands diverges.

```rust
fn array_diverges(x: !) -> ! {
// OK, expression diverges.
[x];
}
```

r[expr.array.index]
## Array and slice indexing expressions

Expand Down Expand Up @@ -128,6 +138,39 @@ arr[10]; // warning: index out of bounds
r[expr.array.index.trait-impl]
The array index expression can be implemented for types other than arrays and slices by implementing the [Index] and [IndexMut] traits.

r[expr.array.index.diverging]
An index expression [diverges] if either of its operands diverges.

```rust
fn index_base_diverges(a: [i32; 1]) -> ! {
// OK, expression diverges.
({ loop {}; a })[0];
}

fn index_diverges(a: [i32; 1]) -> ! {
// OK, expression diverges.
a[{ loop {}; 0 }];
}
```

> [!NOTE]
> An index expression also diverges if the type of the indexed element is the [never type] and the value is [guaranteed to be read][divergence.place-read] (see [divergence.never-place]).
>
> ```rust
> fn diverging_place_read(x: [!; 1]) -> ! {
> // A read of a place expression produces a diverging block.
> let a = x[0];
> }
> ```
>
> ```rust,compile_fail,E0308
> fn diverging_place_no_read(x: [!; 1]) -> ! {
> // This does not constitute a read, and thus does not diverge.
> let _ = x[0];
> // ERROR: Expected type !, found ()
> }
> ```

[`Copy`]: ../special-types-and-traits.md#copy
[IndexMut]: std::ops::IndexMut
[Index]: std::ops::Index
Expand All @@ -136,9 +179,11 @@ The array index expression can be implemented for types other than arrays and sl
[const block expression]: expr.block.const
[constant expression]: ../const_eval.md#constant-expressions
[constant item]: ../items/constant-items.md
[diverges]: divergence
[inferred const]: items.generics.const.inferred
[literal]: ../tokens.md#literals
[memory location]: ../expressions.md#place-expressions-and-value-expressions
[never type]: type.never
[panic]: ../panic.md
[path]: path-expr.md
[slice]: ../types/slice.md
Expand Down
22 changes: 22 additions & 0 deletions src/expressions/await-expr.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,26 @@ r[expr.await.edition2018]
> [!EDITION-2018]
> Await expressions are only available beginning with Rust 2018.

r[expr.await.diverging]
An await expression [diverges] if its future operand diverges.

```rust
async fn await_operand_diverges() -> ! {
// The future operand diverges, so the await expression diverges.
{ loop {}; async {} }.await;
} // OK.
```

> [!NOTE]
> If the output type of the future is the [never type], then the await expression has that type, and per [divergence.never-value] it diverges. For a future whose output type is left to inference, see [expr.block.async.diverging].
>
> ```rust,edition2024
> async fn await_output_never(x: impl Future<Output = !>) -> ! {
> // The output type is `!`, so the await expression diverges.
> x.await;
> } // OK.
> ```

r[expr.await.task]
## Task context

Expand Down Expand Up @@ -63,6 +83,8 @@ where the `yield` pseudo-code returns `Poll::Pending` and, when re-invoked, resu
[`poll::Pending`]: std::task::Poll::Pending
[`poll::Ready`]: std::task::Poll::Ready
[async context]: ../expressions/block-expr.md#async-context
[diverges]: divergence
[future]: std::future::Future
[never type]: type.never
[`IntoFuture`]: std::future::IntoFuture
[`IntoFuture::into_future`]: std::future::IntoFuture::into_future
27 changes: 26 additions & 1 deletion src/expressions/block-expr.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,10 @@ fn f() -> ! { loop {}; } // Diverges and has no final operand.
> As a control flow expression, if a block expression is the outer expression of an expression statement, the expected type is `()` unless it is followed immediately by a semicolon.

r[expr.block.diverging]
A block is considered to be [diverging][divergence] if all reachable control flow paths contain a diverging expression, unless that expression is a [place expression] that is not read from.
A block is considered to be [diverging][divergence] if all reachable control flow paths contain a diverging expression.

> [!NOTE]
> A [place expression] of the [never type] that is not [read][divergence.place-read] does not diverge on account of its type, so a block whose only statement is `&x;`, with `x: !`, does not diverge.

```rust,no_run
fn no_control_flow() -> ! {
Expand Down Expand Up @@ -178,6 +181,27 @@ The actual data format for this type is unspecified.
> [!NOTE]
> The future type that rustc generates is roughly equivalent to an enum with one variant per `await` point, where each variant stores the data needed to resume from its corresponding point.

r[expr.block.async.diverging]
An async block expression does not [diverge].

```rust,compile_fail,E0308
async fn async_block_does_not_diverge() -> ! {
async { loop {} };
// ERROR: Expected type !, found ()
}
```

> [!NOTE]
> Evaluating the future such that the output type is the [never type] will result in a value whose type is the never type, and per [divergence.never-value] the expression diverges.
>
> ```rust
> async fn async_block_await_diverge() -> ! {
> // This explicitly specifies the type to be the never type because
> // otherwise the type inference would infer the output to be unit.
> let x: ! = async { loop {} }.await;
> }
> ```

r[expr.block.async.edition2018]
> [!EDITION-2018]
> Async blocks are only available beginning with Rust 2018.
Expand Down Expand Up @@ -339,6 +363,7 @@ fn is_unix_platform() -> bool {
[call expressions]: call-expr.md
[capture modes]: ../types/closure.md#capture-modes
[constant items]: ../items/constant-items.md
[diverge]: divergence
[diverges]: expr.block.diverging
[final operand]: expr.block.inner-attributes
[free item]: ../glossary.md#free-item
Expand Down
Loading
Loading