Commit graph

303 commits

Author SHA1 Message Date
bors
f8463896a9 Auto merge of #150681 - meithecatte:always-discriminate, r=JonathanBrouwer,Nadrieril
Make operational semantics of pattern matching independent of crate and module

The question of "when does matching an enum against a pattern of one of its variants read its discriminant" is currently an underspecified part of the language, causing weird behavior around borrowck, drop order, and UB.

Of course, in the common cases, the discriminant must be read to distinguish the variant of the enum, but currently the following exceptions are implemented:

1. If the enum has only one variant, we currently skip the discriminant read.
     - This has the advantage that single-variant enums behave the same way as structs in this regard.
     - However, it means that if the discriminant exists in the layout, we can't say that this discriminant being invalid is UB. This makes me particularly uneasy in its interactions with niches – consider the following example ([playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=5904a6155cbdd39af4a2e7b1d32a9b1a)), where miri currently doesn't detect any UB (because the semantics don't specify any):

        <details><summary>Example 1</summary>

        ```rust
        #![allow(dead_code)]
        use core::mem::{size_of, transmute};
        
        #[repr(u8)]
        enum Inner {
            X(u8),
        }
        
        enum Outer {
            A(Inner),
            B(u8),
        }
        
        fn f(x: &Inner) {
            match x {
                Inner::X(v) => {
                    println!("{v}");
                }
            }
        }
        
        fn main() {
            assert_eq!(size_of::<Inner>(), 2);
            assert_eq!(size_of::<Outer>(), 2);
            let x = Outer::B(42);
            let y = &x;
            f(unsafe { transmute(y) });
        }
        ```

      </details>

2. For the purpose of the above, enums with marked with `#[non_exhaustive]` are always considered to have multiple variants when observed from foreign crates, but the actual number of variants is considered in the current crate.
    - This means that whether code has UB can depend on which crate it is in: https://github.com/rust-lang/rust/issues/147722
    - In another case of `#[non_exhaustive]` affecting the runtime semantics, its presence or absence can change what gets captured by a closure, and by extension, the drop order: https://github.com/rust-lang/rust/issues/147722#issuecomment-3674554872
    - Also at the above link, there is an example where removing `#[non_exhaustive]` can cause borrowck to suddenly start failing in another crate.
3. Moreover, we currently make a more specific check: we only read the discriminant if there is more than one *inhabited* variant in the enum.
    - This means that the semantics can differ between `foo<!>`, and a copy of `foo` where `T` was manually replaced with `!`: rust-lang/rust#146803
    - Moreover, due to the privacy rules for inhabitedness, it means that the semantics of code can depend on the *module* in which it is located.
    - Additionally, this inhabitedness rule is even uglier due to the fact that closure capture analysis needs to happen before we can determine whether types are uninhabited, which means that whether the discriminant read happens has a different answer specifically for capture analysis.
    - For the two above points, see the following example ([playground](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2024&gist=a07d8a3ec0b31953942e96e2130476d9)):

        <details><summary>Example 2</summary>

        ```rust
        #![allow(unused)]
        
        mod foo {
            enum Never {}
            struct PrivatelyUninhabited(Never);
            pub enum A {
                V(String, String),
                Y(PrivatelyUninhabited),
            }
            
            fn works(mut x: A) {
                let a = match x {
                    A::V(ref mut a, _) => a,
                    _ => unreachable!(),
                };
                
                let b = match x {
                    A::V(_, ref mut b) => b,
                    _ => unreachable!(),
                };
            
                a.len(); b.len();
            }
            
            fn fails(mut x: A) {
                let mut f = || match x {
                    A::V(ref mut a, _) => (),
                    _ => unreachable!(),
                };
                
                let mut g = || match x {
                    A::V(_, ref mut b) => (),
                    _ => unreachable!(),
                };
            
                f(); g();
            }
        }
        
        use foo::A;
        
        fn fails(mut x: A) {
            let a = match x {
                A::V(ref mut a, _) => a,
                _ => unreachable!(),
            };
            
            let b = match x {
                A::V(_, ref mut b) => b,
                _ => unreachable!(),
            };
        
            a.len(); b.len();
        }
        
        
        fn fails2(mut x: A) {
            let mut f = || match x {
                A::V(ref mut a, _) => (),
                _ => unreachable!(),
            };
            
            let mut g = || match x {
                A::V(_, ref mut b) => (),
                _ => unreachable!(),
            };
        
            f(); g();
        }
        ```

        </details>

In light of the above, and following the discussion at rust-lang/rust#138961 and rust-lang/rust#147722, this PR ~~makes it so that, operationally, matching on an enum *always* reads its discriminant.~~ introduces the following changes to this behavior:

 - matching on a `#[non_exhaustive]` enum will always introduce a discriminant read, regardless of whether the enum is from an external crate
 - uninhabited variants now count just like normal ones, and don't get skipped in the checks

As per the discussion below, the resolution for point (1) above is that it should land as part of a separate PR, so that the subtler decision can be more carefully considered.

Note that this is a breaking change, due to the aforementioned changes in borrow checking behavior, new UB (or at least UB newly detected by miri), as well as drop order around closure captures. However, it seems to me that the combination of this PR with rust-lang/rust#138961 should have smaller real-world impact than rust-lang/rust#138961 by itself.

Fixes rust-lang/rust#142394 
Fixes rust-lang/rust#146590
Fixes rust-lang/rust#146803 (though already marked as duplicate)
Fixes parts of rust-lang/rust#147722
Fixes rust-lang/miri#4778

r? @Nadrieril @RalfJung 

@rustbot label +A-closures +A-patterns +T-opsem +T-lang
2026-02-14 12:53:09 +00:00
Alan Egerton
c43a33eec7
Feed ErrorGuaranteed from late lifetime resolution to RBV
If late lifetime resolution fails for whatever reason, forward to RBV
the guarantee that an error was emitted - thereby eliminating the need
for a "hack" to suppress subsequent/superfluous error diagnostics.
2026-02-13 16:24:39 +00:00
Jonatan Lindh
fb55b5dcf3 diagnostics: fix ICE in closure signature mismatch
This fixes the ICE by renaming conflicting arguments in the diagnostic.
2026-02-08 13:32:50 +01:00
许杰友 Jieyou Xu (Joe)
7b821d1752
Rollup merge of #151278 - estebank:issue-108894, r=davidtwco
Provide more context on trait bounds being unmet due to imperfect derive

When encountering a value that has a borrow checker error where the type was previously moved, when suggesting cloning verify that it is not already being derived. If it is, explain why the `derive(Clone)` doesn't apply:

```
note: if `TypedAddress<T>` implemented `Clone`, you could clone the value
  --> $DIR/derive-clone-implicit-bound.rs:6:1
   |
LL | #[derive(Clone, Copy)]
   |          ----- derived `Clone` adds implicit bounds on type parameters
LL | pub struct TypedAddress<T>{
   | ^^^^^^^^^^^^^^^^^^^^^^^^-^
   | |                       |
   | |                       introduces an implicit `T: Clone` bound
   | consider manually implementing `Clone` for this type
...
LL |         let old = self.return_value(offset);
   |                                     ------ you could clone this value
```

When encountering a bound coming from a derive macro, suggest manual impl of the trait.

Use the span for the specific param when adding bounds in builtin derive macros, so the diagnostic will point at them as well as the derive macro itself.

```
note: required for `Id<SomeNode>` to implement `PartialEq`
  --> $DIR/derive-implicit-bound.rs:5:10
   |
LL | #[derive(PartialEq, Eq)]
   |          ^^^^^^^^^
LL | pub struct Id<T>(PhantomData<T>);
   |               - unsatisfied trait bound introduced in this `derive` macro
   = help: consider manually implementing `PartialEq` to avoid undesired bounds
```

Mention that the trait could be manually implemented in E0599.

Fix rust-lang/rust#108894. Address rust-lang/rust#143714. Address #rust-lang/rust#146515 (but ideally would also suggest constraining the fn bound correctly as well).
2026-02-06 10:25:43 +08:00
Stuart Cook
393ce4ba38
Rollup merge of #151895 - Delta17920:move-ui-tests-batch, r=Kivooeo
Move UI tests batch

moved few tests

r? @Kivooeo
2026-02-02 10:28:33 +11:00
Esteban Küber
dffec20dee Tweak help to unify formatting and wording 2026-02-01 18:20:31 +00:00
Esteban Küber
879633f97b Change note to help 2026-02-01 18:20:31 +00:00
delta17920
a7dea5504b rename various regression tests 2026-02-01 04:09:25 +00:00
zedddie
086dc0258b
clean up some tests 2026-01-31 14:01:54 +01:00
zedddie
4df307acc1
move some tests 2026-01-31 14:00:12 +01:00
delta17920
85ae47f83e moved 7 tests to organized locations 2026-01-31 06:44:24 +00:00
Esteban Küber
4a27be6972 Do not mention -Zmacro-backtrace for std macros that are a wrapper around a compiler intrinsic 2026-01-26 17:34:31 +00:00
Stuart Cook
bd909dd5c3
Rollup merge of #151207 - always-discriminate-prelim, r=Zalathar
Preliminary match/capture test cleanup for PR 150681

Review for rust-lang/rust#150681 requested that this cleanup gets extracted to a separate PR.

r? @Zalathar
2026-01-17 11:47:19 +11:00
Maja Kądziołka
ee1a6f4e88 match in closure: capture non_exhaustive even if defined in current crate 2026-01-15 18:35:11 +01:00
Maja Kądziołka
628bacac33 add tests for emergent behavior of partial captures 2026-01-15 17:53:55 +01:00
Maja Kądziołka
940a48966f non-exhaustive-match.rs: actually test what the comments say 2026-01-15 17:32:45 +01:00
Maja Kądziołka
4e090078b4 capture-enums.rs: get rid of feature gate noise 2026-01-15 17:24:35 +01:00
Maja Kądziołka
8536979cdb Move some match edge case tests
While these test cases were inspired by issue 137467, they aren't
directly related, and better fit into match-edge-cases_2.rs
2026-01-15 17:22:58 +01:00
Heath Dutton🕴️
afe76df79c Don't suggest replacing closure parameter with type name
When a closure has an inferred parameter type like `|ch|` and the
expected type differs in borrowing (e.g., `char` vs `&char`), the
suggestion code would incorrectly suggest `|char|` instead of the
valid `|ch: char|`.

This happened because the code couldn't walk explicit `&` references
in the HIR when the type is inferred, and fell back to replacing the
entire parameter span with the expected type name.

Fix by only emitting the suggestion when we can properly identify the
`&` syntax to remove.
2026-01-12 18:07:38 -05:00
reddevilmidzy
4578082361 Cleaned up some tests
add comment to closure-move-use-after-move-diagnostic.rs

add comment to missing-operator-after-float.rs

add comment to closure-array-break-length.rs

add comment to box-lifetime-argument-not-allowed.rs

add comment to const-return-outside-fn.rs

add comment to drop-conflicting-impls.rs

add comment to unbalanced-doublequote-2.rs

add comment to borrow-immutable-deref-box.rs

add comment to for-in-const-eval.rs

add comment to borrowck-annotated-static-lifetime.rs

cleaned up cast-rfc0401.rs

add comment to nll-anon-to-static.rs

add comment to cast-to-dyn-any.rs

add comment to missing-associated-items.rs

add comment to enum-discriminant-missing-variant.rs
2025-12-23 21:13:24 +09:00
reddevilmidzy
7ca2eb9c6f moved test 2025-12-23 20:33:37 +09:00
Maja Kądziołka
55bbe0531a
Re-bless tests 2025-12-17 20:47:49 +01:00
Maja Kądziołka
ae19274fb2
Add more variations from the PR thread 2025-12-17 20:47:48 +01:00
Maja Kądziołka
d05b1d76f3
Add a test for deref projections in new pattern capture behavior 2025-12-17 20:47:48 +01:00
Maja Kądziołka
3d5d1d72e0
Mark crash 140011 as fixed 2025-12-17 20:47:48 +01:00
Maja Kądziołka
8800f956f4
Avoid using #[derive] in test
As per code review, it is preferred to not use derives in tests that
aren't about them.
2025-12-17 20:47:48 +01:00
Maja Kądziołka
1cc7e7fe8b
Add test case for issue 138973 2025-12-17 20:47:48 +01:00
Maja Kądziołka
e25803acea
ExprUseVisitor: remove maybe_read_scrutinee
The split between walk_pat and maybe_read_scrutinee has now become
redundant.

Due to this change, one testcase within the testsuite has become similar
enough to a known ICE to also break. I am leaving this as future work,
as it requires feature(type_alias_impl_trait)
2025-12-17 20:47:47 +01:00
Maja Kądziołka
9cb47c6e8e
ExprUseVisitor: properly report discriminant reads
This solves the "can't find the upvar" ICEs that resulted from
`maybe_read_scrutinee` being unfit for purpose.
2025-12-17 20:47:47 +01:00
Jonathan Brouwer
f108cd7232
Rollup merge of #149767 - reddevilmidzy:t11, r=Kivooeo
Tidying up tests/ui/issues 33 tests [4/N]

> [!NOTE]
> Intermediate commits are intended to help review, but will be squashed add comment commit prior to merge.

part of rust-lang/rust#133895

`tests/ui/compile-flags` split it into `tests/ui/compile-flags/invalid/` and `tests/ui/compile-flags/run-pass/`

r? Kivooeo
2025-12-16 20:21:07 +01:00
reddevilmidzy
1bd997a452 Cleaned up some tests
Split invalid-compile-flags into run-pass & invalid

Update tests/ui/README.md
2025-12-16 02:10:08 +09:00
reddevilmidzy
92f21a806d moved tests 2025-12-10 09:23:50 +09:00
Esteban Küber
6cd44a472c Make typo in field and name suggestions verbose 2025-12-09 17:29:23 +00:00
reddevilmidzy
22a7457d12 Cleaned up some tests
Merged tests/ui/typeck/non-function-call-error-2 with
tests/ui/typeck/non-function-call-error

Add comment to
tests/ui/traits/normalize-associated-type-in-where-clause.rs

Merged tests/ui/privacy/private-item-simple-2.rs with
tests/ui/privacy/private-item-simple.rs

Merged tests/ui/str/str-add-operator-2.rs with
tests/ui/str/str-add-operator.rs

Add comment to tests/ui/imports/duplicate-empty-imports.rs

Add comment to tests/ui/for-loop-while/nested-loop-break-unit.rs

Add comment to tests/ui/match/match-ref-option-pattern.rs

Add comment to tests/ui/closures/simple-capture-and-call.rs

Add comment to tests/ui/type/never-type-inference-fail.rs

Add comment to tests/ui/match/match-stack-overflow-72933.rs
2025-12-08 07:06:13 +09:00
reddevilmidzy
c2e43fffbc moved tests 2025-12-05 20:41:51 +09:00
xonx4l
4b000cfacd Merge E0412 into E0425 2025-12-02 18:25:13 +00:00
joboet
912f7f9502
update references to thread implementation in tests 2025-11-29 15:59:11 +01:00
bors
e6edf3ae53 Auto merge of #147498 - ferrocene:pvdrz/edition-range-gating, r=jieyouxu,fmease
Gate tests with the right edition

This PR guarantees that `./x test --test-args="--edition XXXX" ui` runs correctly with the 2015, 2018 and 2021 editions.

I don't expect this PR to hold up over time but it helps to submit further updates to the `//@ edition` directives of tests where we can use the new range syntax to have a more robust testing across different editions

r? `@fmease`

---

try-job: aarch64-gnu
try-job: aarch64-apple
try-job: x86_64-msvc-1
try-job: i686-msvc-1
try-job: x86_64-mingw-1
try-job: test-various
try-job: armhf-gnu
2025-11-27 22:37:05 +00:00
Christian Poveda
7ae2823bc6
Gate 2018 UI tests 2025-11-27 14:13:58 -05:00
Matthias Krüger
d67a12dfff
Rollup merge of #148256 - lcnr:orphan-check, r=spastorino,WaffleLapkin
remove support for `typeof`

see https://github.com/rust-lang/compiler-team/issues/940 closes https://github.com/rust-lang/rust/issues/148700

This also enables checks for invariants previously broken by `typeof` again.

r? types
2025-11-27 15:59:11 +01:00
reddevilmidzy
941a17a15a Relocate 5 tests from tests/ui/issues
Relocate issues/issue-51154.rs to closures/box-generic-closure.rs

Relocate issues/issue-51515.rs to
borrowck/assignment-to-immutable-ref.rs

Relocate issues/issue-53348.rs to mismatched_types/deref-string-assign.rs

Relocate issues/issue-52717.rs to
pattern/match-enum-struct-variant-field-missing.rs

Relocate issues/issue-53300.rs to
type/cannot-find-wrapper-with-impl-trait.rs
2025-11-27 00:39:52 +09:00
lcnr
feb13036ef remove support for type-of 2025-11-25 10:19:44 +01:00
Stuart Cook
417bea36ef
Rollup merge of #148508 - estebank:issue-74617, r=nnethercote
Provide more context when mutably borrowing an imutably borrowed value

Point at statics and consts being mutable borrowed or written to:

```
error[E0594]: cannot assign to immutable static item `NUM`
  --> $DIR/E0594.rs:4:5
   |
LL | static NUM: i32 = 18;
   | --------------- this `static` cannot be written to
...
LL |     NUM = 20;
   |     ^^^^^^^^ cannot assign
```

Point at the expression that couldn't be mutably borrowed from a pattern:

```
error[E0596]: cannot borrow data in a `&` reference as mutable
  --> $DIR/mut-pattern-of-immutable-borrow.rs:19:14
   |
LL |     match &arg.field {
   |           ---------- this cannot be borrowed as mutable
LL |         Some(ref mut s) => s.push('a'),
   |              ^^^^^^^^^ cannot borrow as mutable
```

Partially address rust-lang/rust#74617.
2025-11-11 21:09:38 +11:00
Esteban Küber
71b0755a98 Provide more context when mutably borrowing an imutable borrow
Point at statics and consts being mutable borrowed or written to:

```
error[E0594]: cannot assign to immutable static item `NUM`
  --> $DIR/E0594.rs:4:5
   |
LL | static NUM: i32 = 18;
   | --------------- this `static` cannot be written to
...
LL |     NUM = 20;
   |     ^^^^^^^^ cannot assign
```

Point at the expression that couldn't be mutably borrowed from a pattern:

```
error[E0596]: cannot borrow data in a `&` reference as mutable
  --> $DIR/mut-pattern-of-immutable-borrow.rs:19:14
   |
LL |     match &arg.field {
   |           ---------- this cannot be borrowed as mutable
LL |         Some(ref mut s) => s.push('a'),
   |              ^^^^^^^^^ cannot borrow as mutable
```
2025-11-09 22:14:48 +00:00
21aslade
566a86b02f show packed alignment in mir_transform_unaligned_packed_ref 2025-11-07 13:57:37 -07:00
Esteban Küber
4ab1fc5127 Provide more context on Fn closure modifying binding
When modifying a binding from inside of an `Fn` closure, point at the binding definition and suggest using an `std::sync` type that would allow the code to compile.

```
error[E0594]: cannot assign to `counter`, as it is a captured variable in a `Fn` closure
 --> f703.rs:6:9
  |
4 |     let mut counter = 0;
  |         ----------- `counter` declared here, outside the closure
5 |     let x: Box<dyn Fn()> = Box::new(|| {
  |                                     -- in this closure
6 |         counter += 1;
  |         ^^^^^^^^^^^^ cannot assign
```
2025-11-03 20:26:18 +00:00
bors
ff6dc928c5 Auto merge of #142390 - cjgillot:mir-liveness, r=davidtwco
Perform unused assignment and unused variables lints on MIR.

Rebase of https://github.com/rust-lang/rust/pull/101500

Fixes https://github.com/rust-lang/rust/issues/51003.

The first commit moves detection of uninhabited types from the current liveness pass to MIR building.

In order to keep the same level of diagnostics, I had to instrument MIR a little more:
- keep for which original local a guard local is created;
- store in the `VarBindingForm` the list of introducer places and whether this was a shorthand pattern.

I am not very proud of the handling of self-assignments. The proposed scheme is in two parts: first detect probable self-assignments, by pattern matching on MIR, and second treat them specially during dataflow analysis. I welcome ideas.

Please review carefully the changes in tests. There are many small changes to behaviour, and I'm not sure all of them are desirable.
2025-10-12 13:00:04 +00:00
Matthias Krüger
f58eab74c6
Rollup merge of #145897 - Oneirical:uncountable-integer-11, r=jieyouxu
Rehome 30 `tests/ui/issues/` tests to other subdirectories under `tests/ui/` [#4 of Batch #2]

Part of rust-lang/rust#133895

Methodology:

1. Refer to the previously written `tests/ui/SUMMARY.md`
2. Find an appropriate category for the test, using the original issue thread and the test contents.
3. Add the issue URL at the bottom (not at the top, as that would mess up stderr line numbers)
4. Rename the tests to make their purpose clearer

Inspired by the methodology that `@Kivooeo` was using.

r? `@jieyouxu`
2025-10-12 10:13:13 +02:00
Oneirical
6ca69812cd Add test batch 4 2025-10-11 21:59:51 -04:00
Camille GILLOT
ca0379d6cd Diagnose liveness on MIR. 2025-10-11 20:50:21 +00:00