From d356c6804353f9130189fe72071481120f7bc232 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Sun, 7 Apr 2024 04:08:25 -0400 Subject: [PATCH 1/9] Update documentation for `hint::assert_unchecked` Rearrange the sections and add an example to `core::hint::assert_unchecked`. --- library/core/src/hint.rs | 93 +++++++++++++++++++++++++++++++--------- 1 file changed, 73 insertions(+), 20 deletions(-) diff --git a/library/core/src/hint.rs b/library/core/src/hint.rs index 6e2d88c6b8337..b8db4ad8237bd 100644 --- a/library/core/src/hint.rs +++ b/library/core/src/hint.rs @@ -111,36 +111,89 @@ pub const unsafe fn unreachable_unchecked() -> ! { /// Makes a *soundness* promise to the compiler that `cond` holds. /// -/// This may allow the optimizer to simplify things, -/// but it might also make the generated code slower. -/// Either way, calling it will most likely make compilation take longer. +/// This may allow the optimizer to simplify things, but it might also make the generated code +/// slower. Either way, calling it will most likely make compilation take longer. /// -/// This is a situational tool for micro-optimization, and is allowed to do nothing. -/// Any use should come with a repeatable benchmark to show the value -/// and allow removing it later should the optimizer get smarter and no longer need it. +/// You may know this from other places as +/// [`llvm.assume`](https://llvm.org/docs/LangRef.html#llvm-assume-intrinsic) or, in C, +/// [`__builtin_assume`](https://clang.llvm.org/docs/LanguageExtensions.html#builtin-assume). /// -/// The more complicated the condition the less likely this is to be fruitful. -/// For example, `assert_unchecked(foo.is_sorted())` is a complex enough value -/// that the compiler is unlikely to be able to take advantage of it. +/// This promotes a correctness requirement to a soundness requirement. Don't do that without +/// very good reason. /// -/// There's also no need to `assert_unchecked` basic properties of things. For -/// example, the compiler already knows the range of `count_ones`, so there's no -/// benefit to `let n = u32::count_ones(x); assert_unchecked(n <= u32::BITS);`. +/// # Usage /// -/// If ever you're tempted to write `assert_unchecked(false)`, then you're -/// actually looking for [`unreachable_unchecked()`]. +/// This is a situational tool for micro-optimization, and is allowed to do nothing. Any use +/// should come with a repeatable benchmark to show the value, with the expectation to drop it +/// later should the optimizer get smarter and no longer need it. /// -/// You may know this from other places -/// as [`llvm.assume`](https://llvm.org/docs/LangRef.html#llvm-assume-intrinsic) -/// or [`__builtin_assume`](https://clang.llvm.org/docs/LanguageExtensions.html#builtin-assume). +/// The more complicated the condition, the less likely this is to be useful. For example, +/// `assert_unchecked(foo.is_sorted())` is a complex enough value that the compiler is unlikely +/// to be able to take advantage of it. /// -/// This promotes a correctness requirement to a soundness requirement. -/// Don't do that without very good reason. +/// There's also no need to `assert_unchecked` basic properties of things. For example, the +/// compiler already knows the range of `count_ones`, so there is no benefit to +/// `let n = u32::count_ones(x); assert_unchecked(n <= u32::BITS);`. +/// +/// `assert_unchecked` is logically equivalent to `if !cond { unreachable_unchecked(); }`. If +/// ever you are tempted to write `assert_unchecked(false)`, you should instead use +/// [`unreachable_unchecked()`] directly. /// /// # Safety /// -/// `cond` must be `true`. It's immediate UB to call this with `false`. +/// `cond` must be `true`. It is immediate UB to call this with `false`. +/// +/// # Example +/// +/// ``` +/// #![feature(hint_assert_unchecked)] +/// +/// use core::hint; +/// +/// /// # Safety +/// /// +/// /// `p` must be nonnull and valid +/// pub unsafe fn next_value(p: *const i32) -> i32 { +/// // SAFETY: caller invariants guarantee that `p` is not null +/// unsafe { hint::assert_unchecked(!p.is_null()) } +/// +/// if p.is_null() { +/// return -1; +/// } else { +/// // SAFETY: caller invariants guarantee that `p` is valid +/// unsafe { *p + 1 } +/// } +/// } +/// ``` +/// +/// Without the `assert_unchecked`, the above function produces the following with optimizations +/// enabled: +/// +/// ```asm +/// next_value: +/// test rdi, rdi +/// je .LBB0_1 +/// mov eax, dword ptr [rdi] +/// inc eax +/// ret +/// .LBB0_1: +/// mov eax, -1 +/// ret +/// ``` +/// +/// Adding the assertion allows the optimizer to remove the extra check: +/// +/// ```asm +/// next_value: +/// mov eax, dword ptr [rdi] +/// inc eax +/// ret +/// ``` /// +/// This example is quite unlike anything that would be used in the real world: it is redundant +/// to put an an assertion right next to code that checks the same thing, and dereferencing a +/// pointer already has the builtin assumption that it is nonnull. However, it illustrates the +/// kind of changes the optimizer can make even when the behavior is less obviously related. #[inline(always)] #[doc(alias = "assume")] #[track_caller] From 5745c220e65a83436bb0bf855dcf83aebffe8c73 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Sun, 7 Apr 2024 04:30:49 -0400 Subject: [PATCH 2/9] Stabilize `hint_assert_unchecked` Make both `hint_assert_unchecked` and `const_hint_assert_unchecked` stable as `hint_assert_unchecked`. --- library/alloc/src/lib.rs | 1 - library/core/src/hint.rs | 8 +++----- library/core/src/intrinsics.rs | 2 +- library/core/src/lib.rs | 1 - library/std/src/lib.rs | 1 - tests/ui/consts/const-assert-unchecked-ub.rs | 6 +----- tests/ui/consts/const-assert-unchecked-ub.stderr | 2 +- 7 files changed, 6 insertions(+), 15 deletions(-) diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 895d1b8d59f2c..095e13ee98c98 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -126,7 +126,6 @@ #![feature(fmt_internals)] #![feature(fn_traits)] #![feature(hasher_prefixfree_extras)] -#![feature(hint_assert_unchecked)] #![feature(inplace_iteration)] #![feature(iter_advance_by)] #![feature(iter_next_chunk)] diff --git a/library/core/src/hint.rs b/library/core/src/hint.rs index b8db4ad8237bd..e637baf826ff2 100644 --- a/library/core/src/hint.rs +++ b/library/core/src/hint.rs @@ -146,8 +146,6 @@ pub const unsafe fn unreachable_unchecked() -> ! { /// # Example /// /// ``` -/// #![feature(hint_assert_unchecked)] -/// /// use core::hint; /// /// /// # Safety @@ -194,11 +192,11 @@ pub const unsafe fn unreachable_unchecked() -> ! { /// to put an an assertion right next to code that checks the same thing, and dereferencing a /// pointer already has the builtin assumption that it is nonnull. However, it illustrates the /// kind of changes the optimizer can make even when the behavior is less obviously related. +#[track_caller] #[inline(always)] #[doc(alias = "assume")] -#[track_caller] -#[unstable(feature = "hint_assert_unchecked", issue = "119131")] -#[rustc_const_unstable(feature = "const_hint_assert_unchecked", issue = "119131")] +#[stable(feature = "hint_assert_unchecked", since = "CURRENT_RUSTC_VERSION")] +#[rustc_const_stable(feature = "hint_assert_unchecked", since = "CURRENT_RUSTC_VERSION")] pub const unsafe fn assert_unchecked(cond: bool) { // SAFETY: The caller promised `cond` is true. unsafe { diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index 6b5054a9f0612..598b7ac0f82c4 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -959,7 +959,7 @@ extern "rust-intrinsic" { /// not be used if the invariant can be discovered by the optimizer on its /// own, or if it does not enable any significant optimizations. /// -/// This intrinsic does not have a stable counterpart. +/// The stabilized version of this intrinsic is [`core::hint::assert_unchecked`]. #[rustc_const_stable(feature = "const_assume", since = "1.77.0")] #[rustc_nounwind] #[unstable(feature = "core_intrinsics", issue = "none")] diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index ef28bc99c4fc8..abd8e1d1d0b2e 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -130,7 +130,6 @@ #![feature(const_fmt_arguments_new)] #![feature(const_hash)] #![feature(const_heap)] -#![feature(const_hint_assert_unchecked)] #![feature(const_index_range_slice_index)] #![feature(const_int_from_str)] #![feature(const_intrinsic_copy)] diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 1c226f9f08f10..1fcfbdcd12dfb 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -335,7 +335,6 @@ #![feature(fmt_internals)] #![feature(hasher_prefixfree_extras)] #![feature(hashmap_internals)] -#![feature(hint_assert_unchecked)] #![feature(ip)] #![feature(maybe_uninit_slice)] #![feature(maybe_uninit_uninit_array)] diff --git a/tests/ui/consts/const-assert-unchecked-ub.rs b/tests/ui/consts/const-assert-unchecked-ub.rs index 5c05b813048b8..ffc02eedcb7a7 100644 --- a/tests/ui/consts/const-assert-unchecked-ub.rs +++ b/tests/ui/consts/const-assert-unchecked-ub.rs @@ -1,10 +1,6 @@ -#![feature(hint_assert_unchecked)] -#![feature(const_hint_assert_unchecked)] - const _: () = unsafe { let n = u32::MAX.count_ones(); std::hint::assert_unchecked(n < 32); //~ ERROR evaluation of constant value failed }; -fn main() { -} +fn main() {} diff --git a/tests/ui/consts/const-assert-unchecked-ub.stderr b/tests/ui/consts/const-assert-unchecked-ub.stderr index 3957a3b1c246b..468f15f34728f 100644 --- a/tests/ui/consts/const-assert-unchecked-ub.stderr +++ b/tests/ui/consts/const-assert-unchecked-ub.stderr @@ -1,5 +1,5 @@ error[E0080]: evaluation of constant value failed - --> $DIR/const-assert-unchecked-ub.rs:6:5 + --> $DIR/const-assert-unchecked-ub.rs:3:5 | LL | std::hint::assert_unchecked(n < 32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `assume` called with `false` From cf6f6ca441e2efd3e196f45f36da253c49cec4c0 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 29 Jun 2024 12:02:44 +0200 Subject: [PATCH 3/9] unreferenced-used-static: run test everywhere --- .../linkage-attr/unreferenced-used-static-issue-127052.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/ui/linkage-attr/unreferenced-used-static-issue-127052.rs b/tests/ui/linkage-attr/unreferenced-used-static-issue-127052.rs index aa8236b74315c..0d34bf988efe6 100644 --- a/tests/ui/linkage-attr/unreferenced-used-static-issue-127052.rs +++ b/tests/ui/linkage-attr/unreferenced-used-static-issue-127052.rs @@ -1,9 +1,10 @@ -// This is a non-regression test for issue #127052 where unreferenced `#[used]` statics couldn't be -// removed by the MSVC linker, causing linking errors. +// This is a non-regression test for issue #127052 where unreferenced `#[used]` statics in the +// binary crate would be marked as "exported", but not be present in the binary, causing linking +// errors with the MSVC linker. //@ build-pass: needs linking -//@ only-msvc #[used] static FOO: u32 = 0; + fn main() {} From 860729ea39bc568aa57ce8cb152647138cfda995 Mon Sep 17 00:00:00 2001 From: dimi Date: Mon, 1 Jul 2024 14:14:22 +0200 Subject: [PATCH 4/9] Stabilize atomic_bool_fetch_not --- library/core/src/sync/atomic.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs index df108f5e0e4e5..efc07f38f68e0 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs @@ -1069,7 +1069,6 @@ impl AtomicBool { /// # Examples /// /// ``` - /// #![feature(atomic_bool_fetch_not)] /// use std::sync::atomic::{AtomicBool, Ordering}; /// /// let foo = AtomicBool::new(true); @@ -1081,7 +1080,7 @@ impl AtomicBool { /// assert_eq!(foo.load(Ordering::SeqCst), true); /// ``` #[inline] - #[unstable(feature = "atomic_bool_fetch_not", issue = "98485")] + #[stable(feature = "atomic_bool_fetch_not", since = "CURRENT_RUSTC_VERSION")] #[cfg(target_has_atomic = "8")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub fn fetch_not(&self, order: Ordering) -> bool { From 8c353cbc4688b50a0c950d9afc4bba5b1718278d Mon Sep 17 00:00:00 2001 From: Ana Hobden Date: Mon, 24 Jun 2024 12:54:43 -0700 Subject: [PATCH 5/9] Disable rmake test inaccessible-temp-dir on riscv64 --- tests/run-make/inaccessible-temp-dir/rmake.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/run-make/inaccessible-temp-dir/rmake.rs b/tests/run-make/inaccessible-temp-dir/rmake.rs index 6b3e9e0b29e38..b98e151e90696 100644 --- a/tests/run-make/inaccessible-temp-dir/rmake.rs +++ b/tests/run-make/inaccessible-temp-dir/rmake.rs @@ -13,13 +13,18 @@ // use a directory with non-existing parent like `/does-not-exist/output`. // See https://github.com/rust-lang/rust/issues/66530 +//@ ignore-riscv64 +// FIXME: The riscv build container runs as root, and can always write +// into `inaccessible/tmp`. Ideally, the riscv64-gnu docker container +// would use a non-root user, but this leads to issues with +// `mkfs.ext4 -d`, as well as mounting a loop device for the rootfs. //@ ignore-arm // Reason: linker error on `armhf-gnu` //@ ignore-windows // Reason: `set_readonly` has no effect on directories // and does not prevent modification. -use run_make_support::{fs_wrapper, rustc, target, test_while_readonly}; +use run_make_support::{fs_wrapper, rustc, test_while_readonly}; fn main() { // Create an inaccessible directory. @@ -28,7 +33,6 @@ fn main() { // Run rustc with `-Z temps-dir` set to a directory *inside* the inaccessible one, // so that it can't create `tmp`. rustc() - .target(target()) .input("program.rs") .arg("-Ztemps-dir=inaccessible/tmp") .run_fail() From 6c803ffefb8eba9c65afc9a122d94383f71ebfa2 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 2 Jul 2024 15:28:16 +0200 Subject: [PATCH 6/9] remove unnecessary ignore-endian-big from stack-overflow-trait-infer test --- tests/ui/sized/stack-overflow-trait-infer-98842.32bit.stderr | 4 ++-- tests/ui/sized/stack-overflow-trait-infer-98842.64bit.stderr | 4 ++-- tests/ui/sized/stack-overflow-trait-infer-98842.rs | 3 +-- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/tests/ui/sized/stack-overflow-trait-infer-98842.32bit.stderr b/tests/ui/sized/stack-overflow-trait-infer-98842.32bit.stderr index 6bbd81ae3e163..c01b2ccd1637c 100644 --- a/tests/ui/sized/stack-overflow-trait-infer-98842.32bit.stderr +++ b/tests/ui/sized/stack-overflow-trait-infer-98842.32bit.stderr @@ -3,14 +3,14 @@ error[E0391]: cycle detected when computing layout of `Foo` = note: ...which requires computing layout of `<&'static Foo as core::ops::deref::Deref>::Target`... = note: ...which again requires computing layout of `Foo`, completing the cycle note: cycle used when const-evaluating + checking `_` - --> $DIR/stack-overflow-trait-infer-98842.rs:15:1 + --> $DIR/stack-overflow-trait-infer-98842.rs:14:1 | LL | const _: *const Foo = 0 as _; | ^^^^^^^^^^^^^^^^^^^ = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information error[E0080]: evaluation of constant value failed - --> $DIR/stack-overflow-trait-infer-98842.rs:15:1 + --> $DIR/stack-overflow-trait-infer-98842.rs:14:1 | LL | const _: *const Foo = 0 as _; | ^^^^^^^^^^^^^^^^^^^ a cycle occurred during layout computation diff --git a/tests/ui/sized/stack-overflow-trait-infer-98842.64bit.stderr b/tests/ui/sized/stack-overflow-trait-infer-98842.64bit.stderr index 6bbd81ae3e163..c01b2ccd1637c 100644 --- a/tests/ui/sized/stack-overflow-trait-infer-98842.64bit.stderr +++ b/tests/ui/sized/stack-overflow-trait-infer-98842.64bit.stderr @@ -3,14 +3,14 @@ error[E0391]: cycle detected when computing layout of `Foo` = note: ...which requires computing layout of `<&'static Foo as core::ops::deref::Deref>::Target`... = note: ...which again requires computing layout of `Foo`, completing the cycle note: cycle used when const-evaluating + checking `_` - --> $DIR/stack-overflow-trait-infer-98842.rs:15:1 + --> $DIR/stack-overflow-trait-infer-98842.rs:14:1 | LL | const _: *const Foo = 0 as _; | ^^^^^^^^^^^^^^^^^^^ = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information error[E0080]: evaluation of constant value failed - --> $DIR/stack-overflow-trait-infer-98842.rs:15:1 + --> $DIR/stack-overflow-trait-infer-98842.rs:14:1 | LL | const _: *const Foo = 0 as _; | ^^^^^^^^^^^^^^^^^^^ a cycle occurred during layout computation diff --git a/tests/ui/sized/stack-overflow-trait-infer-98842.rs b/tests/ui/sized/stack-overflow-trait-infer-98842.rs index be4807b2e4aa7..8a958870b0e17 100644 --- a/tests/ui/sized/stack-overflow-trait-infer-98842.rs +++ b/tests/ui/sized/stack-overflow-trait-infer-98842.rs @@ -3,8 +3,7 @@ //@ check-fail //@ edition:2021 //@ stderr-per-bitwidth -//@ ignore-endian-big -//~^^^^^^ ERROR cycle detected when computing layout of `Foo` +//~^^^^^ ERROR cycle detected when computing layout of `Foo` // If the inner `Foo` is named through an associated type, // the "infinite size" error does not occur. From 8ce8c62f1e908ffb7edec993cb5e6cbafe4d620c Mon Sep 17 00:00:00 2001 From: Boxy Date: Tue, 2 Jul 2024 17:07:21 +0100 Subject: [PATCH 7/9] add test --- .../generic_const_exprs/adt_wf_hang.rs | 14 ++++++++++++++ .../generic_const_exprs/adt_wf_hang.stderr | 18 ++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 tests/ui/const-generics/generic_const_exprs/adt_wf_hang.rs create mode 100644 tests/ui/const-generics/generic_const_exprs/adt_wf_hang.stderr diff --git a/tests/ui/const-generics/generic_const_exprs/adt_wf_hang.rs b/tests/ui/const-generics/generic_const_exprs/adt_wf_hang.rs new file mode 100644 index 0000000000000..5d538d2679dd8 --- /dev/null +++ b/tests/ui/const-generics/generic_const_exprs/adt_wf_hang.rs @@ -0,0 +1,14 @@ +#![feature(generic_const_exprs)] +#![feature(adt_const_params)] +#![allow(incomplete_features)] +#![allow(dead_code)] + +#[derive(PartialEq, Eq)] +struct U; + +struct S() +where + S<{ U }>:; +//~^ ERROR: overflow evaluating the requirement `S<{ U }> well-formed` + +fn main() {} diff --git a/tests/ui/const-generics/generic_const_exprs/adt_wf_hang.stderr b/tests/ui/const-generics/generic_const_exprs/adt_wf_hang.stderr new file mode 100644 index 0000000000000..b244acb37dde0 --- /dev/null +++ b/tests/ui/const-generics/generic_const_exprs/adt_wf_hang.stderr @@ -0,0 +1,18 @@ +error[E0275]: overflow evaluating the requirement `S<{ U }> well-formed` + --> $DIR/adt_wf_hang.rs:11:5 + | +LL | S<{ U }>:; + | ^^^^^^^^ + | +note: required by a bound in `S` + --> $DIR/adt_wf_hang.rs:11:5 + | +LL | struct S() + | - required by a bound in this struct +LL | where +LL | S<{ U }>:; + | ^^^^^^^^ required by this bound in `S` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0275`. From 4b0b97f66ddf36525b844a9c0c775db9f980248f Mon Sep 17 00:00:00 2001 From: Ana Hobden Date: Tue, 2 Jul 2024 10:09:40 -0700 Subject: [PATCH 8/9] Give remote-test-client a longer timeout --- src/tools/remote-test-client/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/remote-test-client/src/main.rs b/src/tools/remote-test-client/src/main.rs index dd2c09c430b61..42de3caf5474d 100644 --- a/src/tools/remote-test-client/src/main.rs +++ b/src/tools/remote-test-client/src/main.rs @@ -71,7 +71,7 @@ fn spawn_emulator(target: &str, server: &Path, tmpdir: &Path, rootfs: Option Date: Wed, 12 Jun 2024 11:53:52 -0400 Subject: [PATCH 9/9] Actually report normalization-based type errors correctly for alias-relate obligations in new solver --- .../error_reporting/type_err_ctxt_ext.rs | 149 ++++++++++++------ .../as_expression.next.stderr | 5 +- .../issue-100222.nn.stderr | 5 +- .../issue-100222.ny.stderr | 5 +- .../issue-100222.yn.stderr | 5 +- .../issue-100222.yy.stderr | 5 +- tests/ui/traits/next-solver/async.fail.stderr | 6 +- tests/ui/traits/next-solver/async.rs | 2 +- .../next-solver/more-object-bound.stderr | 15 +- 9 files changed, 140 insertions(+), 57 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs index adf1076a7c90e..ff263eaac34fb 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs @@ -1586,60 +1586,113 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } self.probe(|_| { - let ocx = ObligationCtxt::new(self); - // try to find the mismatched types to report the error with. // // this can fail if the problem was higher-ranked, in which // cause I have no idea for a good error message. let bound_predicate = predicate.kind(); - let (values, err) = if let ty::PredicateKind::Clause(ty::ClauseKind::Projection(data)) = - bound_predicate.skip_binder() - { - let data = self.instantiate_binder_with_fresh_vars( - obligation.cause.span, - infer::BoundRegionConversionTime::HigherRankedType, - bound_predicate.rebind(data), - ); - let unnormalized_term = data.projection_term.to_term(self.tcx); - // FIXME(-Znext-solver): For diagnostic purposes, it would be nice - // to deeply normalize this type. - let normalized_term = - ocx.normalize(&obligation.cause, obligation.param_env, unnormalized_term); - - debug!(?obligation.cause, ?obligation.param_env); - - debug!(?normalized_term, data.ty = ?data.term); + let (values, err) = match bound_predicate.skip_binder() { + ty::PredicateKind::Clause(ty::ClauseKind::Projection(data)) => { + let ocx = ObligationCtxt::new(self); + + let data = self.instantiate_binder_with_fresh_vars( + obligation.cause.span, + infer::BoundRegionConversionTime::HigherRankedType, + bound_predicate.rebind(data), + ); + let unnormalized_term = data.projection_term.to_term(self.tcx); + // FIXME(-Znext-solver): For diagnostic purposes, it would be nice + // to deeply normalize this type. + let normalized_term = + ocx.normalize(&obligation.cause, obligation.param_env, unnormalized_term); + + let is_normalized_term_expected = !matches!( + obligation.cause.code().peel_derives(), + ObligationCauseCode::WhereClause(..) + | ObligationCauseCode::WhereClauseInExpr(..) + | ObligationCauseCode::Coercion { .. } + ); - let is_normalized_term_expected = !matches!( - obligation.cause.code().peel_derives(), - |ObligationCauseCode::WhereClause(..)| ObligationCauseCode::WhereClauseInExpr( - .. - ) | ObligationCauseCode::Coercion { .. } - ); + let (expected, actual) = if is_normalized_term_expected { + (normalized_term, data.term) + } else { + (data.term, normalized_term) + }; - let (expected, actual) = if is_normalized_term_expected { - (normalized_term, data.term) - } else { - (data.term, normalized_term) - }; + // constrain inference variables a bit more to nested obligations from normalize so + // we can have more helpful errors. + // + // we intentionally drop errors from normalization here, + // since the normalization is just done to improve the error message. + let _ = ocx.select_where_possible(); - // constrain inference variables a bit more to nested obligations from normalize so - // we can have more helpful errors. - // - // we intentionally drop errors from normalization here, - // since the normalization is just done to improve the error message. - let _ = ocx.select_where_possible(); + if let Err(new_err) = + ocx.eq(&obligation.cause, obligation.param_env, expected, actual) + { + ( + Some(( + data.projection_term, + is_normalized_term_expected, + self.resolve_vars_if_possible(normalized_term), + data.term, + )), + new_err, + ) + } else { + (None, error.err) + } + } + ty::PredicateKind::AliasRelate(lhs, rhs, _) => { + let derive_better_type_error = + |alias_term: ty::AliasTerm<'tcx>, expected_term: ty::Term<'tcx>| { + let ocx = ObligationCtxt::new(self); + let normalized_term = match expected_term.unpack() { + ty::TermKind::Ty(_) => self.next_ty_var(DUMMY_SP).into(), + ty::TermKind::Const(_) => self.next_const_var(DUMMY_SP).into(), + }; + ocx.register_obligation(Obligation::new( + self.tcx, + ObligationCause::dummy(), + obligation.param_env, + ty::PredicateKind::NormalizesTo(ty::NormalizesTo { + alias: alias_term, + term: normalized_term, + }), + )); + let _ = ocx.select_where_possible(); + if let Err(terr) = ocx.eq( + &ObligationCause::dummy(), + obligation.param_env, + expected_term, + normalized_term, + ) { + Some((terr, self.resolve_vars_if_possible(normalized_term))) + } else { + None + } + }; - if let Err(new_err) = - ocx.eq(&obligation.cause, obligation.param_env, expected, actual) - { - (Some((data, is_normalized_term_expected, normalized_term, data.term)), new_err) - } else { - (None, error.err) + if let Some(lhs) = lhs.to_alias_term() + && let Some((better_type_err, expected_term)) = + derive_better_type_error(lhs, rhs) + { + ( + Some((lhs, true, self.resolve_vars_if_possible(expected_term), rhs)), + better_type_err, + ) + } else if let Some(rhs) = rhs.to_alias_term() + && let Some((better_type_err, expected_term)) = + derive_better_type_error(rhs, lhs) + { + ( + Some((rhs, true, self.resolve_vars_if_possible(expected_term), lhs)), + better_type_err, + ) + } else { + (None, error.err) + } } - } else { - (None, error.err) + _ => (None, error.err), }; let msg = values @@ -1737,15 +1790,15 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { fn maybe_detailed_projection_msg( &self, - pred: ty::ProjectionPredicate<'tcx>, + projection_term: ty::AliasTerm<'tcx>, normalized_ty: ty::Term<'tcx>, expected_ty: ty::Term<'tcx>, ) -> Option { - let trait_def_id = pred.projection_term.trait_def_id(self.tcx); - let self_ty = pred.projection_term.self_ty(); + let trait_def_id = projection_term.trait_def_id(self.tcx); + let self_ty = projection_term.self_ty(); with_forced_trimmed_paths! { - if self.tcx.is_lang_item(pred.projection_term.def_id,LangItem::FnOnceOutput) { + if self.tcx.is_lang_item(projection_term.def_id, LangItem::FnOnceOutput) { let fn_kind = self_ty.prefix_string(self.tcx); let item = match self_ty.kind() { ty::FnDef(def, _) => self.tcx.item_name(*def).to_string(), diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.next.stderr b/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.next.stderr index d189d2dbdedc8..a686b913c55ef 100644 --- a/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.next.stderr +++ b/tests/ui/diagnostic_namespace/do_not_recommend/as_expression.next.stderr @@ -29,7 +29,10 @@ error[E0271]: type mismatch resolving `::SqlType == Tex --> $DIR/as_expression.rs:57:5 | LL | SelectInt.check("bar"); - | ^^^^^^^^^^^^^^^^^^^^^^ types differ + | ^^^^^^^^^^^^^^^^^^^^^^ expected `Integer`, found `Text` + | + = note: expected struct `Integer` + found struct `Text` error: aborting due to 3 previous errors diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-100222.nn.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-100222.nn.stderr index 4a949e90d8554..03536dca1e848 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-100222.nn.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-100222.nn.stderr @@ -2,7 +2,10 @@ error[E0271]: type mismatch resolving `<() as Index>::Output == &mut <() as Inde --> $DIR/issue-100222.rs:34:12 | LL | fn foo(&mut self, x: ::Output) -> ::Output - | ^^^^^^^^^ types differ + | ^^^^^^^^^ expected `()`, found `&mut <() as Index>::Output` + | + = note: expected unit type `()` + found mutable reference `&mut <() as Index>::Output` error: aborting due to 1 previous error diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-100222.ny.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-100222.ny.stderr index 1bfce48d26a2d..6a70a50360619 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-100222.ny.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-100222.ny.stderr @@ -2,7 +2,10 @@ error[E0271]: type mismatch resolving `<() as Index>::Output == &mut <() as Inde --> $DIR/issue-100222.rs:25:12 | LL | fn foo(&mut self, x: ::Output) -> ::Output - | ^^^^^^^^^ types differ + | ^^^^^^^^^ expected `()`, found `&mut <() as Index>::Output` + | + = note: expected unit type `()` + found mutable reference `&mut <() as Index>::Output` error: aborting due to 1 previous error diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-100222.yn.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-100222.yn.stderr index 4a949e90d8554..03536dca1e848 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-100222.yn.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-100222.yn.stderr @@ -2,7 +2,10 @@ error[E0271]: type mismatch resolving `<() as Index>::Output == &mut <() as Inde --> $DIR/issue-100222.rs:34:12 | LL | fn foo(&mut self, x: ::Output) -> ::Output - | ^^^^^^^^^ types differ + | ^^^^^^^^^ expected `()`, found `&mut <() as Index>::Output` + | + = note: expected unit type `()` + found mutable reference `&mut <() as Index>::Output` error: aborting due to 1 previous error diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-100222.yy.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-100222.yy.stderr index 1bfce48d26a2d..6a70a50360619 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-100222.yy.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-100222.yy.stderr @@ -2,7 +2,10 @@ error[E0271]: type mismatch resolving `<() as Index>::Output == &mut <() as Inde --> $DIR/issue-100222.rs:25:12 | LL | fn foo(&mut self, x: ::Output) -> ::Output - | ^^^^^^^^^ types differ + | ^^^^^^^^^ expected `()`, found `&mut <() as Index>::Output` + | + = note: expected unit type `()` + found mutable reference `&mut <() as Index>::Output` error: aborting due to 1 previous error diff --git a/tests/ui/traits/next-solver/async.fail.stderr b/tests/ui/traits/next-solver/async.fail.stderr index 83d520341bcc3..e47da338736f4 100644 --- a/tests/ui/traits/next-solver/async.fail.stderr +++ b/tests/ui/traits/next-solver/async.fail.stderr @@ -1,11 +1,13 @@ -error[E0271]: type mismatch resolving `<{async block@$DIR/async.rs:12:17: 12:22} as Future>::Output == i32` +error[E0271]: expected `{async block@$DIR/async.rs:12:17: 12:22}` to be a future that resolves to `i32`, but it resolves to `()` --> $DIR/async.rs:12:17 | LL | needs_async(async {}); - | ----------- ^^^^^^^^ types differ + | ----------- ^^^^^^^^ expected `()`, found `i32` | | | required by a bound introduced by this call | + = note: expected unit type `()` + found type `i32` note: required by a bound in `needs_async` --> $DIR/async.rs:8:31 | diff --git a/tests/ui/traits/next-solver/async.rs b/tests/ui/traits/next-solver/async.rs index 129e4cfaa028b..fded774354759 100644 --- a/tests/ui/traits/next-solver/async.rs +++ b/tests/ui/traits/next-solver/async.rs @@ -10,7 +10,7 @@ fn needs_async(_: impl Future) {} #[cfg(fail)] fn main() { needs_async(async {}); - //[fail]~^ ERROR type mismatch + //[fail]~^ ERROR expected `{async block@$DIR/async.rs:12:17: 12:22}` to be a future that resolves to `i32`, but it resolves to `()` } #[cfg(pass)] diff --git a/tests/ui/traits/next-solver/more-object-bound.stderr b/tests/ui/traits/next-solver/more-object-bound.stderr index 8cc2a51ee2b6c..043cbdff9ab9d 100644 --- a/tests/ui/traits/next-solver/more-object-bound.stderr +++ b/tests/ui/traits/next-solver/more-object-bound.stderr @@ -1,9 +1,22 @@ error[E0271]: type mismatch resolving ` as SuperTrait>::A == B` --> $DIR/more-object-bound.rs:12:5 | +LL | fn transmute(x: A) -> B { + | - - + | | | + | | expected type parameter + | | found type parameter + | found type parameter + | expected type parameter LL | foo::>(x) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ types differ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected type parameter `A`, found type parameter `B` | + = note: expected type parameter `A` + found type parameter `B` + = note: a type parameter was expected, but a different one was found; you might be missing a type parameter or trait bound + = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters + = note: a type parameter was expected, but a different one was found; you might be missing a type parameter or trait bound + = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters = note: required because it appears within the type `dyn Trait` note: required by a bound in `foo` --> $DIR/more-object-bound.rs:18:8