From bc9232183b90817ed41a369361265127a268f072 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 13 Dec 2022 18:55:00 -0800 Subject: [PATCH 01/18] Suggest constraining type parameter with `Clone` Fix #34896. --- .../src/fn_ctxt/suggestions.rs | 15 ++++++++++- ...on-unconstrained-borrowed-type-param.fixed | 8 ++++++ ...ne-on-unconstrained-borrowed-type-param.rs | 8 ++++++ ...n-unconstrained-borrowed-type-param.stderr | 25 +++++++++++++++++++ 4 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 src/test/ui/suggestions/clone-on-unconstrained-borrowed-type-param.fixed create mode 100644 src/test/ui/suggestions/clone-on-unconstrained-borrowed-type-param.rs create mode 100644 src/test/ui/suggestions/clone-on-unconstrained-borrowed-type-param.stderr diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 4f92477b5d87e..c98cf4f8a37a4 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -13,7 +13,9 @@ use rustc_hir_analysis::astconv::AstConv; use rustc_infer::infer; use rustc_infer::traits::{self, StatementAsExpression}; use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::{self, Binder, DefIdTree, IsSuggestable, ToPredicate, Ty}; +use rustc_middle::ty::{ + self, suggest_constraining_type_params, Binder, DefIdTree, IsSuggestable, ToPredicate, Ty, +}; use rustc_session::errors::ExprParenthesesNeeded; use rustc_span::symbol::sym; use rustc_span::Span; @@ -1295,6 +1297,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "`{expected_ty}` does not implement `Clone`, so `{found_ty}` was cloned instead" ), ); + let owner = self.tcx.hir().enclosing_body_owner(expr.hir_id); + if let ty::Param(param) = expected_ty.kind() + && let Some(generics) = self.tcx.hir().get_generics(owner) + { + suggest_constraining_type_params( + self.tcx, + generics, + diag, + vec![(param.name.as_str(), "Clone", Some(clone_trait_did))].into_iter(), + ); + } } } diff --git a/src/test/ui/suggestions/clone-on-unconstrained-borrowed-type-param.fixed b/src/test/ui/suggestions/clone-on-unconstrained-borrowed-type-param.fixed new file mode 100644 index 0000000000000..3e5ded6738b31 --- /dev/null +++ b/src/test/ui/suggestions/clone-on-unconstrained-borrowed-type-param.fixed @@ -0,0 +1,8 @@ +// run-rustfix +fn wat(t: &T) -> T { + t.clone() //~ ERROR E0308 +} + +fn main() { + wat(&42); +} diff --git a/src/test/ui/suggestions/clone-on-unconstrained-borrowed-type-param.rs b/src/test/ui/suggestions/clone-on-unconstrained-borrowed-type-param.rs new file mode 100644 index 0000000000000..1a5a38369ec52 --- /dev/null +++ b/src/test/ui/suggestions/clone-on-unconstrained-borrowed-type-param.rs @@ -0,0 +1,8 @@ +// run-rustfix +fn wat(t: &T) -> T { + t.clone() //~ ERROR E0308 +} + +fn main() { + wat(&42); +} diff --git a/src/test/ui/suggestions/clone-on-unconstrained-borrowed-type-param.stderr b/src/test/ui/suggestions/clone-on-unconstrained-borrowed-type-param.stderr new file mode 100644 index 0000000000000..01246955fae8a --- /dev/null +++ b/src/test/ui/suggestions/clone-on-unconstrained-borrowed-type-param.stderr @@ -0,0 +1,25 @@ +error[E0308]: mismatched types + --> $DIR/clone-on-unconstrained-borrowed-type-param.rs:3:5 + | +LL | fn wat(t: &T) -> T { + | - - expected `T` because of return type + | | + | this type parameter +LL | t.clone() + | ^^^^^^^^^ expected type parameter `T`, found `&T` + | + = note: expected type parameter `T` + found reference `&T` +note: `T` does not implement `Clone`, so `&T` was cloned instead + --> $DIR/clone-on-unconstrained-borrowed-type-param.rs:3:5 + | +LL | t.clone() + | ^ +help: consider restricting type parameter `T` + | +LL | fn wat(t: &T) -> T { + | +++++++ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. From 0fb8d848f9d3b427fea754a5d944375e049f2eb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 13 Dec 2022 19:52:42 -0800 Subject: [PATCH 02/18] Suggest `#[derive(Clone)]` --- .../src/fn_ctxt/suggestions.rs | 15 ++++++++------ .../rustc_hir_typeck/src/method/suggest.rs | 2 +- ...on-unconstrained-borrowed-type-param.fixed | 8 ++++++++ ...ne-on-unconstrained-borrowed-type-param.rs | 7 +++++++ ...n-unconstrained-borrowed-type-param.stderr | 20 ++++++++++++++++++- .../ui/typeck/explain_clone_autoref.stderr | 4 ++++ 6 files changed, 48 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index c98cf4f8a37a4..7ef2dabcce2a0 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -1278,17 +1278,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { && !results.expr_adjustments(callee_expr).iter().any(|adj| matches!(adj.kind, ty::adjustment::Adjust::Deref(..))) // Check that we're in fact trying to clone into the expected type && self.can_coerce(*pointee_ty, expected_ty) + && let predicate = ty::Binder::dummy(ty::TraitRef { + def_id: clone_trait_did, + substs: self.tcx.mk_substs([expected_ty.into()].iter()), + }) + .without_const() + .to_predicate(self.tcx) // And the expected type doesn't implement `Clone` && !self.predicate_must_hold_considering_regions(&traits::Obligation { cause: traits::ObligationCause::dummy(), param_env: self.param_env, recursion_depth: 0, - predicate: ty::Binder::dummy(ty::TraitRef { - def_id: clone_trait_did, - substs: self.tcx.mk_substs([expected_ty.into()].iter()), - }) - .without_const() - .to_predicate(self.tcx), + predicate, }) { diag.span_note( @@ -1307,6 +1308,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { diag, vec![(param.name.as_str(), "Clone", Some(clone_trait_did))].into_iter(), ); + } else { + self.suggest_derive(diag, &[(predicate, None, None)]); } } } diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 2d3b4663f06ce..9fe132fc94645 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -1843,7 +1843,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.suggest_derive(err, &preds); } - fn suggest_derive( + pub fn suggest_derive( &self, err: &mut Diagnostic, unsatisfied_predicates: &[( diff --git a/src/test/ui/suggestions/clone-on-unconstrained-borrowed-type-param.fixed b/src/test/ui/suggestions/clone-on-unconstrained-borrowed-type-param.fixed index 3e5ded6738b31..4f9e93a47ed1c 100644 --- a/src/test/ui/suggestions/clone-on-unconstrained-borrowed-type-param.fixed +++ b/src/test/ui/suggestions/clone-on-unconstrained-borrowed-type-param.fixed @@ -3,6 +3,14 @@ fn wat(t: &T) -> T { t.clone() //~ ERROR E0308 } +#[derive(Clone)] +struct Foo; + +fn wut(t: &Foo) -> Foo { + t.clone() //~ ERROR E0308 +} + fn main() { wat(&42); + wut(&Foo); } diff --git a/src/test/ui/suggestions/clone-on-unconstrained-borrowed-type-param.rs b/src/test/ui/suggestions/clone-on-unconstrained-borrowed-type-param.rs index 1a5a38369ec52..89b077d671a51 100644 --- a/src/test/ui/suggestions/clone-on-unconstrained-borrowed-type-param.rs +++ b/src/test/ui/suggestions/clone-on-unconstrained-borrowed-type-param.rs @@ -3,6 +3,13 @@ fn wat(t: &T) -> T { t.clone() //~ ERROR E0308 } +struct Foo; + +fn wut(t: &Foo) -> Foo { + t.clone() //~ ERROR E0308 +} + fn main() { wat(&42); + wut(&Foo); } diff --git a/src/test/ui/suggestions/clone-on-unconstrained-borrowed-type-param.stderr b/src/test/ui/suggestions/clone-on-unconstrained-borrowed-type-param.stderr index 01246955fae8a..26ab515d9b4b6 100644 --- a/src/test/ui/suggestions/clone-on-unconstrained-borrowed-type-param.stderr +++ b/src/test/ui/suggestions/clone-on-unconstrained-borrowed-type-param.stderr @@ -20,6 +20,24 @@ help: consider restricting type parameter `T` LL | fn wat(t: &T) -> T { | +++++++ -error: aborting due to previous error +error[E0308]: mismatched types + --> $DIR/clone-on-unconstrained-borrowed-type-param.rs:9:5 + | +LL | fn wut(t: &Foo) -> Foo { + | --- expected `Foo` because of return type +LL | t.clone() + | ^^^^^^^^^ expected struct `Foo`, found `&Foo` + | +note: `Foo` does not implement `Clone`, so `&Foo` was cloned instead + --> $DIR/clone-on-unconstrained-borrowed-type-param.rs:9:5 + | +LL | t.clone() + | ^ +help: consider annotating `Foo` with `#[derive(Clone)]` + | +LL | #[derive(Clone)] + | + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/typeck/explain_clone_autoref.stderr b/src/test/ui/typeck/explain_clone_autoref.stderr index faac680ea1931..ff36e18d28301 100644 --- a/src/test/ui/typeck/explain_clone_autoref.stderr +++ b/src/test/ui/typeck/explain_clone_autoref.stderr @@ -12,6 +12,10 @@ note: `NotClone` does not implement `Clone`, so `&NotClone` was cloned instead | LL | nc.clone() | ^^ +help: consider annotating `NotClone` with `#[derive(Clone)]` + | +LL | #[derive(Clone)] + | error: aborting due to previous error From 687b4d9bb2d1427d2ca9bb6225a706117e379350 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Wed, 14 Dec 2022 18:52:14 +0900 Subject: [PATCH 03/18] Add regression test for #104678 Signed-off-by: Yuki Okushi --- .../ui/async-await/in-trait/issue-104678.rs | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 src/test/ui/async-await/in-trait/issue-104678.rs diff --git a/src/test/ui/async-await/in-trait/issue-104678.rs b/src/test/ui/async-await/in-trait/issue-104678.rs new file mode 100644 index 0000000000000..e396df4e5d119 --- /dev/null +++ b/src/test/ui/async-await/in-trait/issue-104678.rs @@ -0,0 +1,31 @@ +// edition:2021 +// check-pass + +#![feature(async_fn_in_trait)] +#![allow(incomplete_features)] + +use std::future::Future; +pub trait Pool { + type Conn; + + async fn async_callback<'a, F: FnOnce(&'a Self::Conn) -> Fut, Fut: Future>( + &'a self, + callback: F, + ) -> (); +} + +pub struct PoolImpl; +pub struct ConnImpl; + +impl Pool for PoolImpl { + type Conn = ConnImpl; + + async fn async_callback<'a, F: FnOnce(&'a Self::Conn) -> Fut, Fut: Future>( + &'a self, + _callback: F, + ) -> () { + todo!() + } +} + +fn main() {} From 5f5ae17f4efbd54c0d53451101044422e9b728ad Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 12 Dec 2022 18:21:10 +0000 Subject: [PATCH 04/18] Consider discriminant fields that are ordered before variant fields --- compiler/rustc_ty_utils/src/layout.rs | 30 ++++++++++++++----- .../generator_discr_placement.rs | 25 ++++++++++++++++ .../generator_discr_placement.stdout | 11 +++++++ 3 files changed, 59 insertions(+), 7 deletions(-) create mode 100644 src/test/ui/print_type_sizes/generator_discr_placement.rs create mode 100644 src/test/ui/print_type_sizes/generator_discr_placement.stdout diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index f4672a70072b2..c15aba681da05 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -919,7 +919,7 @@ fn variant_info_for_generator<'tcx>( def_id: DefId, substs: ty::SubstsRef<'tcx>, ) -> (Vec, Option) { - let Variants::Multiple { tag, ref tag_encoding, .. } = layout.variants else { + let Variants::Multiple { tag, ref tag_encoding, tag_field, .. } = layout.variants else { return (vec![], None); }; @@ -975,12 +975,28 @@ fn variant_info_for_generator<'tcx>( if variant_size == Size::ZERO { variant_size = upvars_size; } - // We need to add the discriminant size back into min_size, since it is subtracted - // later during printing. - variant_size += match tag_encoding { - TagEncoding::Direct => tag.size(cx), - _ => Size::ZERO, - }; + + // This `if` deserves some explanation. + // + // The layout code has a choice of where to place the discriminant of this generator. + // If the discriminant of the generator is placed early in the layout (before the + // variant's own fields), then it'll implicitly be counted towards the size of the + // variant, since we use the maximum offset to calculate size. + // (side-note: I know this is a bit problematic given upvars placement, etc). + // + // This is important, since the layout printing code always subtracts this discriminant + // size from the variant size if the struct is "enum"-like, so failing to account for it + // will either lead to numerical underflow, or an underreported variant size... + // + // However, if the discriminant is placed past the end of the variant, then we need + // to factor in the size of the discriminant manually. This really should be refactored + // better, but this "works" for now. + if layout.fields.offset(tag_field) >= variant_size { + variant_size += match tag_encoding { + TagEncoding::Direct => tag.size(cx), + _ => Size::ZERO, + }; + } VariantInfo { name: Some(Symbol::intern(&ty::GeneratorSubsts::variant_name(variant_idx))), diff --git a/src/test/ui/print_type_sizes/generator_discr_placement.rs b/src/test/ui/print_type_sizes/generator_discr_placement.rs new file mode 100644 index 0000000000000..ddb5db56f9cb9 --- /dev/null +++ b/src/test/ui/print_type_sizes/generator_discr_placement.rs @@ -0,0 +1,25 @@ +// compile-flags: -Z print-type-sizes +// build-pass +// ignore-pass + +// Tests a generator that has its discriminant as the *final* field. + +// Avoid emitting panic handlers, like the rest of these tests... +#![feature(start, generators)] + +#[start] +fn start(_: isize, _: *const *const u8) -> isize { + let a = || { + { + let w: i32 = 4; + yield; + drop(w); + } + { + let z: i32 = 7; + yield; + drop(z); + } + }; + 0 +} diff --git a/src/test/ui/print_type_sizes/generator_discr_placement.stdout b/src/test/ui/print_type_sizes/generator_discr_placement.stdout new file mode 100644 index 0000000000000..7dfc8f0bd44f2 --- /dev/null +++ b/src/test/ui/print_type_sizes/generator_discr_placement.stdout @@ -0,0 +1,11 @@ +print-type-size type: `[generator@$DIR/generator_discr_placement.rs:12:13: 12:15]`: 8 bytes, alignment: 4 bytes +print-type-size discriminant: 1 bytes +print-type-size variant `Suspend0`: 7 bytes +print-type-size padding: 3 bytes +print-type-size field `.w`: 4 bytes, alignment: 4 bytes +print-type-size variant `Suspend1`: 7 bytes +print-type-size padding: 3 bytes +print-type-size field `.z`: 4 bytes, alignment: 4 bytes +print-type-size variant `Unresumed`: 0 bytes +print-type-size variant `Returned`: 0 bytes +print-type-size variant `Panicked`: 0 bytes From bdc3c4bf553ca10a1cbaeee8af061eef79417379 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 12 Dec 2022 20:09:34 +0000 Subject: [PATCH 05/18] Make print_type_sizes test not use feature(start) --- src/test/ui/print_type_sizes/async.rs | 12 ++-------- src/test/ui/print_type_sizes/async.stdout | 8 +++---- src/test/ui/print_type_sizes/generator.rs | 8 +++---- .../generator_discr_placement.rs | 8 +++---- .../generator_discr_placement.stdout | 2 +- src/test/ui/print_type_sizes/generics.rs | 24 ++----------------- .../ui/print_type_sizes/multiple_types.rs | 12 +--------- src/test/ui/print_type_sizes/niche-filling.rs | 10 +++----- src/test/ui/print_type_sizes/no_duplicates.rs | 8 ++----- src/test/ui/print_type_sizes/packed.rs | 20 ++++------------ src/test/ui/print_type_sizes/padding.rs | 8 +------ src/test/ui/print_type_sizes/repr-align.rs | 12 +++------- src/test/ui/print_type_sizes/repr_int_c.rs | 8 +------ src/test/ui/print_type_sizes/uninhabited.rs | 6 ++--- src/test/ui/print_type_sizes/variants.rs | 10 +------- .../ui/print_type_sizes/zero-sized-fields.rs | 8 ++----- 16 files changed, 36 insertions(+), 128 deletions(-) diff --git a/src/test/ui/print_type_sizes/async.rs b/src/test/ui/print_type_sizes/async.rs index 3491ad5afbc15..1598b0696913b 100644 --- a/src/test/ui/print_type_sizes/async.rs +++ b/src/test/ui/print_type_sizes/async.rs @@ -1,19 +1,11 @@ -// compile-flags: -Z print-type-sizes +// compile-flags: -Z print-type-sizes --crate-type lib // edition:2021 // build-pass // ignore-pass -#![feature(start)] - async fn wait() {} -async fn test(arg: [u8; 8192]) { +pub async fn test(arg: [u8; 8192]) { wait().await; drop(arg); } - -#[start] -fn start(_: isize, _: *const *const u8) -> isize { - let _ = test([0; 8192]); - 0 -} diff --git a/src/test/ui/print_type_sizes/async.stdout b/src/test/ui/print_type_sizes/async.stdout index 94ad09ef296d3..6e47bb4930dc5 100644 --- a/src/test/ui/print_type_sizes/async.stdout +++ b/src/test/ui/print_type_sizes/async.stdout @@ -1,4 +1,4 @@ -print-type-size type: `[async fn body@$DIR/async.rs:10:32: 13:2]`: 16386 bytes, alignment: 1 bytes +print-type-size type: `[async fn body@$DIR/async.rs:8:36: 11:2]`: 16386 bytes, alignment: 1 bytes print-type-size discriminant: 1 bytes print-type-size variant `Suspend0`: 16385 bytes print-type-size field `.arg`: 8192 bytes, offset: 0 bytes, alignment: 1 bytes @@ -16,14 +16,14 @@ print-type-size type: `std::mem::MaybeUninit<[u8; 8192]>`: 8192 bytes, alignment print-type-size variant `MaybeUninit`: 8192 bytes print-type-size field `.uninit`: 0 bytes print-type-size field `.value`: 8192 bytes -print-type-size type: `[async fn body@$DIR/async.rs:8:17: 8:19]`: 1 bytes, alignment: 1 bytes +print-type-size type: `[async fn body@$DIR/async.rs:6:17: 6:19]`: 1 bytes, alignment: 1 bytes print-type-size discriminant: 1 bytes print-type-size variant `Unresumed`: 0 bytes print-type-size variant `Returned`: 0 bytes print-type-size variant `Panicked`: 0 bytes -print-type-size type: `std::mem::ManuallyDrop<[async fn body@$DIR/async.rs:8:17: 8:19]>`: 1 bytes, alignment: 1 bytes +print-type-size type: `std::mem::ManuallyDrop<[async fn body@$DIR/async.rs:6:17: 6:19]>`: 1 bytes, alignment: 1 bytes print-type-size field `.value`: 1 bytes -print-type-size type: `std::mem::MaybeUninit<[async fn body@$DIR/async.rs:8:17: 8:19]>`: 1 bytes, alignment: 1 bytes +print-type-size type: `std::mem::MaybeUninit<[async fn body@$DIR/async.rs:6:17: 6:19]>`: 1 bytes, alignment: 1 bytes print-type-size variant `MaybeUninit`: 1 bytes print-type-size field `.uninit`: 0 bytes print-type-size field `.value`: 1 bytes diff --git a/src/test/ui/print_type_sizes/generator.rs b/src/test/ui/print_type_sizes/generator.rs index a46db6121046b..d1cd36274ef3e 100644 --- a/src/test/ui/print_type_sizes/generator.rs +++ b/src/test/ui/print_type_sizes/generator.rs @@ -1,8 +1,8 @@ -// compile-flags: -Z print-type-sizes +// compile-flags: -Z print-type-sizes --crate-type=lib // build-pass // ignore-pass -#![feature(start, generators, generator_trait)] +#![feature(generators, generator_trait)] use std::ops::Generator; @@ -13,8 +13,6 @@ fn generator(array: [u8; C]) -> impl Generator isize { +pub fn foo() { let _ = generator([0; 8192]); - 0 } diff --git a/src/test/ui/print_type_sizes/generator_discr_placement.rs b/src/test/ui/print_type_sizes/generator_discr_placement.rs index ddb5db56f9cb9..1a85fe95bb6f7 100644 --- a/src/test/ui/print_type_sizes/generator_discr_placement.rs +++ b/src/test/ui/print_type_sizes/generator_discr_placement.rs @@ -1,14 +1,13 @@ -// compile-flags: -Z print-type-sizes +// compile-flags: -Z print-type-sizes --crate-type lib // build-pass // ignore-pass // Tests a generator that has its discriminant as the *final* field. // Avoid emitting panic handlers, like the rest of these tests... -#![feature(start, generators)] +#![feature(generators)] -#[start] -fn start(_: isize, _: *const *const u8) -> isize { +pub fn foo() { let a = || { { let w: i32 = 4; @@ -21,5 +20,4 @@ fn start(_: isize, _: *const *const u8) -> isize { drop(z); } }; - 0 } diff --git a/src/test/ui/print_type_sizes/generator_discr_placement.stdout b/src/test/ui/print_type_sizes/generator_discr_placement.stdout index 7dfc8f0bd44f2..7f8f4ccae7c14 100644 --- a/src/test/ui/print_type_sizes/generator_discr_placement.stdout +++ b/src/test/ui/print_type_sizes/generator_discr_placement.stdout @@ -1,4 +1,4 @@ -print-type-size type: `[generator@$DIR/generator_discr_placement.rs:12:13: 12:15]`: 8 bytes, alignment: 4 bytes +print-type-size type: `[generator@$DIR/generator_discr_placement.rs:11:13: 11:15]`: 8 bytes, alignment: 4 bytes print-type-size discriminant: 1 bytes print-type-size variant `Suspend0`: 7 bytes print-type-size padding: 3 bytes diff --git a/src/test/ui/print_type_sizes/generics.rs b/src/test/ui/print_type_sizes/generics.rs index 3ef7b60db2cae..05097087d5a81 100644 --- a/src/test/ui/print_type_sizes/generics.rs +++ b/src/test/ui/print_type_sizes/generics.rs @@ -1,4 +1,4 @@ -// compile-flags: -Z print-type-sizes +// compile-flags: -Z print-type-sizes --crate-type=lib // build-pass // ignore-pass // ^-- needed because `--pass check` does not emit the output needed. @@ -8,24 +8,6 @@ // monomorphized, in the MIR of the original function in which they // occur, to have their size reported. -#![feature(start)] - -// In an ad-hoc attempt to avoid the injection of unwinding code -// (which clutters the output of `-Z print-type-sizes` with types from -// `unwind::libunwind`): -// -// * I am not using Default to build values because that seems to -// cause the injection of unwinding code. (Instead I just make `fn new` -// methods.) -// -// * Pair derive Copy to ensure that we don't inject -// unwinding code into generic uses of Pair when T itself is also -// Copy. -// -// (I suspect this reflect some naivety within the rust compiler -// itself; it should be checking for drop glue, i.e., a destructor -// somewhere in the monomorphized types. It should not matter whether -// the type is Copy.) #[derive(Copy, Clone)] pub struct Pair { _car: T, @@ -61,11 +43,9 @@ pub fn f1(x: T) { Pair::new(FiftyBytes::new(), FiftyBytes::new()); } -#[start] -fn start(_: isize, _: *const *const u8) -> isize { +pub fn start() { let _b: Pair = Pair::new(0, 0); let _s: Pair = Pair::new(SevenBytes::new(), SevenBytes::new()); let ref _z: ZeroSized = ZeroSized; f1::(SevenBytes::new()); - 0 } diff --git a/src/test/ui/print_type_sizes/multiple_types.rs b/src/test/ui/print_type_sizes/multiple_types.rs index f1ad27ec13137..9159038924719 100644 --- a/src/test/ui/print_type_sizes/multiple_types.rs +++ b/src/test/ui/print_type_sizes/multiple_types.rs @@ -1,11 +1,9 @@ -// compile-flags: -Z print-type-sizes +// compile-flags: -Z print-type-sizes --crate-type=lib // build-pass // This file illustrates that when multiple structural types occur in // a function, every one of them is included in the output. -#![feature(start)] - pub struct SevenBytes([u8; 7]); pub struct FiftyBytes([u8; 50]); @@ -13,11 +11,3 @@ pub enum Enum { Small(SevenBytes), Large(FiftyBytes), } - -#[start] -fn start(_: isize, _: *const *const u8) -> isize { - let _e: Enum; - let _f: FiftyBytes; - let _s: SevenBytes; - 0 -} diff --git a/src/test/ui/print_type_sizes/niche-filling.rs b/src/test/ui/print_type_sizes/niche-filling.rs index 0716cee21c662..5e620f248b65d 100644 --- a/src/test/ui/print_type_sizes/niche-filling.rs +++ b/src/test/ui/print_type_sizes/niche-filling.rs @@ -1,4 +1,4 @@ -// compile-flags: -Z print-type-sizes +// compile-flags: -Z print-type-sizes --crate-type=lib // build-pass // ignore-pass // ^-- needed because `--pass check` does not emit the output needed. @@ -14,7 +14,6 @@ // aligned (while on most it is 8-byte aligned) and so the resulting // padding and overall computed sizes can be quite different. -#![feature(start)] #![feature(rustc_attrs)] #![allow(dead_code)] @@ -56,7 +55,7 @@ pub struct NestedNonZero { impl Default for NestedNonZero { fn default() -> Self { - NestedNonZero { pre: 0, val: NonZeroU32::new(1).unwrap(), post: 0 } + NestedNonZero { pre: 0, val: unsafe { NonZeroU32::new_unchecked(1) }, post: 0 } } } @@ -76,8 +75,7 @@ pub union Union2 { b: B, } -#[start] -fn start(_: isize, _: *const *const u8) -> isize { +pub fn test() { let _x: MyOption = Default::default(); let _y: EmbeddedDiscr = Default::default(); let _z: MyOption = Default::default(); @@ -96,6 +94,4 @@ fn start(_: isize, _: *const *const u8) -> isize { // ...even when theoretically possible. let _j: MyOption> = Default::default(); let _k: MyOption> = Default::default(); - - 0 } diff --git a/src/test/ui/print_type_sizes/no_duplicates.rs b/src/test/ui/print_type_sizes/no_duplicates.rs index e45e4794a3526..2ec5d9e64bfbf 100644 --- a/src/test/ui/print_type_sizes/no_duplicates.rs +++ b/src/test/ui/print_type_sizes/no_duplicates.rs @@ -1,4 +1,4 @@ -// compile-flags: -Z print-type-sizes +// compile-flags: -Z print-type-sizes --crate-type=lib // build-pass // ignore-pass // ^-- needed because `--pass check` does not emit the output needed. @@ -8,16 +8,12 @@ // (even if multiple functions), it is only printed once in the // print-type-sizes output. -#![feature(start)] - pub struct SevenBytes([u8; 7]); pub fn f1() { let _s: SevenBytes = SevenBytes([0; 7]); } -#[start] -fn start(_: isize, _: *const *const u8) -> isize { +pub fn test() { let _s: SevenBytes = SevenBytes([0; 7]); - 0 } diff --git a/src/test/ui/print_type_sizes/packed.rs b/src/test/ui/print_type_sizes/packed.rs index 269cc3cc2825f..5ddfe4bf4dbb0 100644 --- a/src/test/ui/print_type_sizes/packed.rs +++ b/src/test/ui/print_type_sizes/packed.rs @@ -1,4 +1,4 @@ -// compile-flags: -Z print-type-sizes +// compile-flags: -Z print-type-sizes --crate-type=lib // build-pass // ignore-pass // ^-- needed because `--pass check` does not emit the output needed. @@ -13,11 +13,10 @@ // padding and overall computed sizes can be quite different. #![allow(dead_code)] -#![feature(start)] #[derive(Default)] #[repr(packed)] -struct Packed1 { +pub struct Packed1 { a: u8, b: u8, g: i32, @@ -28,7 +27,7 @@ struct Packed1 { #[derive(Default)] #[repr(packed(2))] -struct Packed2 { +pub struct Packed2 { a: u8, b: u8, g: i32, @@ -40,7 +39,7 @@ struct Packed2 { #[derive(Default)] #[repr(packed(2))] #[repr(C)] -struct Packed2C { +pub struct Packed2C { a: u8, b: u8, g: i32, @@ -50,7 +49,7 @@ struct Packed2C { } #[derive(Default)] -struct Padded { +pub struct Padded { a: u8, b: u8, g: i32, @@ -58,12 +57,3 @@ struct Padded { h: i16, d: u8, } - -#[start] -fn start(_: isize, _: *const *const u8) -> isize { - let _c: Packed1 = Default::default(); - let _d: Packed2 = Default::default(); - let _e: Packed2C = Default::default(); - let _f: Padded = Default::default(); - 0 -} diff --git a/src/test/ui/print_type_sizes/padding.rs b/src/test/ui/print_type_sizes/padding.rs index d1acad16d7e1d..f41c677dc6c08 100644 --- a/src/test/ui/print_type_sizes/padding.rs +++ b/src/test/ui/print_type_sizes/padding.rs @@ -1,4 +1,4 @@ -// compile-flags: -Z print-type-sizes +// compile-flags: -Z print-type-sizes --crate-type=lib // build-pass // This file illustrates how padding is handled: alignment @@ -9,7 +9,6 @@ // aligned (while on most it is 8-byte aligned) and so the resulting // padding and overall computed sizes can be quite different. -#![feature(start)] #![allow(dead_code)] struct S { @@ -27,8 +26,3 @@ enum E2 { A(i8, i32), B(S), } - -#[start] -fn start(_: isize, _: *const *const u8) -> isize { - 0 -} diff --git a/src/test/ui/print_type_sizes/repr-align.rs b/src/test/ui/print_type_sizes/repr-align.rs index 07544935b2f82..0bd11ebc95843 100644 --- a/src/test/ui/print_type_sizes/repr-align.rs +++ b/src/test/ui/print_type_sizes/repr-align.rs @@ -1,4 +1,4 @@ -// compile-flags: -Z print-type-sizes +// compile-flags: -Z print-type-sizes --crate-type=lib // build-pass // ignore-pass // ^-- needed because `--pass check` does not emit the output needed. @@ -11,7 +11,7 @@ // It avoids using u64/i64 because on some targets that is only 4-byte // aligned (while on most it is 8-byte aligned) and so the resulting // padding and overall computed sizes can be quite different. -#![feature(start)] + #![allow(dead_code)] #[repr(align(16))] @@ -24,15 +24,9 @@ enum E { } #[derive(Default)] -struct S { +pub struct S { a: i32, b: i32, c: A, d: i8, } - -#[start] -fn start(_: isize, _: *const *const u8) -> isize { - let _s: S = Default::default(); - 0 -} diff --git a/src/test/ui/print_type_sizes/repr_int_c.rs b/src/test/ui/print_type_sizes/repr_int_c.rs index b8067c112eef1..6b103776a30d3 100644 --- a/src/test/ui/print_type_sizes/repr_int_c.rs +++ b/src/test/ui/print_type_sizes/repr_int_c.rs @@ -1,10 +1,9 @@ -// compile-flags: -Z print-type-sizes +// compile-flags: -Z print-type-sizes --crate-type=lib // build-pass // This test makes sure that the tag is not grown for `repr(C)` or `repr(u8)` // variants (see https://github.com/rust-lang/rust/issues/50098 for the original bug). -#![feature(start)] #![allow(dead_code)] #[repr(C, u8)] @@ -18,8 +17,3 @@ enum Repru8 { A(u16), B, } - -#[start] -fn start(_: isize, _: *const *const u8) -> isize { - 0 -} diff --git a/src/test/ui/print_type_sizes/uninhabited.rs b/src/test/ui/print_type_sizes/uninhabited.rs index 06a62db4ebb0f..86fab7b500af0 100644 --- a/src/test/ui/print_type_sizes/uninhabited.rs +++ b/src/test/ui/print_type_sizes/uninhabited.rs @@ -1,14 +1,12 @@ -// compile-flags: -Z print-type-sizes +// compile-flags: -Z print-type-sizes --crate-type=lib // build-pass // ignore-pass // ^-- needed because `--pass check` does not emit the output needed. // FIXME: consider using an attribute instead of side-effects. #![feature(never_type)] -#![feature(start)] -#[start] -fn start(_: isize, _: *const *const u8) -> isize { +pub fn test() { let _x: Option = None; let _y: Result = Ok(42); let _z: Result = loop {}; diff --git a/src/test/ui/print_type_sizes/variants.rs b/src/test/ui/print_type_sizes/variants.rs index 6c8553cc23ded..5a3020520265d 100644 --- a/src/test/ui/print_type_sizes/variants.rs +++ b/src/test/ui/print_type_sizes/variants.rs @@ -1,4 +1,4 @@ -// compile-flags: -Z print-type-sizes +// compile-flags: -Z print-type-sizes --crate-type=lib // build-pass // This file illustrates two things: @@ -9,8 +9,6 @@ // 2. For an enum, the print-type-sizes output will also include the // size of each variant. -#![feature(start)] - pub struct SevenBytes([u8; 7]); pub struct FiftyBytes([u8; 50]); @@ -18,9 +16,3 @@ pub enum Enum { Small(SevenBytes), Large(FiftyBytes), } - -#[start] -fn start(_: isize, _: *const *const u8) -> isize { - let _e: Enum; - 0 -} diff --git a/src/test/ui/print_type_sizes/zero-sized-fields.rs b/src/test/ui/print_type_sizes/zero-sized-fields.rs index e02a33109e56a..09415824d5df0 100644 --- a/src/test/ui/print_type_sizes/zero-sized-fields.rs +++ b/src/test/ui/print_type_sizes/zero-sized-fields.rs @@ -1,12 +1,10 @@ -// compile-flags: -Z print-type-sizes +// compile-flags: -Z print-type-sizes --crate-type=lib // build-pass // ignore-pass // At one point, zero-sized fields such as those in this file were causing // incorrect output from `-Z print-type-sizes`. -#![feature(start)] - struct S1 { x: u32, y: u32, @@ -28,8 +26,7 @@ struct S5 { tagz: TagZ, } -#[start] -fn start(_: isize, _: *const *const u8) -> isize { +pub fn test() { let _s1: S1 = S1 { x: 0, y: 0, tag: () }; let _s5: S5<(), Empty> = S5 { @@ -43,5 +40,4 @@ fn start(_: isize, _: *const *const u8) -> isize { z: 4, tagz: Empty {}, }; - 0 } From 10368c6ceceb848fc47379ddecaa1dd8bb8b13b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 12 Dec 2022 17:39:08 -0800 Subject: [PATCH 06/18] Point at method chains on `E0271` errors --- .../src/traits/error_reporting/suggestions.rs | 209 ++++++++++-------- src/test/ui/issues/issue-31173.stderr | 12 + src/test/ui/issues/issue-33941.stderr | 7 + 3 files changed, 139 insertions(+), 89 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 55a05df763fe6..b4ec1a961fc3e 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -352,6 +352,14 @@ pub trait TypeErrCtxtExt<'tcx> { param_env: ty::ParamEnv<'tcx>, err: &mut Diagnostic, ); + fn probe_assoc_types_at_expr( + &self, + type_diffs: &[TypeError<'tcx>], + span: Span, + prev_ty: Ty<'tcx>, + body_id: hir::HirId, + param_env: ty::ParamEnv<'tcx>, + ) -> Vec))>>; } fn predicate_constraint(generics: &hir::Generics<'_>, pred: ty::Predicate<'_>) -> (Span, String) { @@ -3152,23 +3160,37 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { if let ObligationCauseCode::ExprBindingObligation(def_id, _, _, idx) = parent_code.deref() && let predicates = self.tcx.predicates_of(def_id).instantiate_identity(self.tcx) && let Some(pred) = predicates.predicates.get(*idx) - && let Ok(trait_pred) = pred.kind().try_map_bound(|pred| match pred { - ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred)) => Ok(trait_pred), - _ => Err(()), - }) { - let mut c = CollectAllMismatches { - infcx: self.infcx, - param_env, - errors: vec![], - }; - if let Ok(trait_predicate) = predicate.kind().try_map_bound(|pred| match pred { + if let Ok(trait_pred) = pred.kind().try_map_bound(|pred| match pred { ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred)) => Ok(trait_pred), _ => Err(()), - }) { + }) + && let Ok(trait_predicate) = predicate.kind().try_map_bound(|pred| match pred { + ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred)) => Ok(trait_pred), + _ => Err(()), + }) + { + let mut c = CollectAllMismatches { + infcx: self.infcx, + param_env, + errors: vec![], + }; if let Ok(_) = c.relate(trait_pred, trait_predicate) { type_diffs = c.errors; } + } else if let ty::PredicateKind::Clause( + ty::Clause::Projection(proj) + ) = pred.kind().skip_binder() + && let ty::PredicateKind::Clause( + ty::Clause::Projection(projection) + ) = predicate.kind().skip_binder() + { + type_diffs = vec![ + Sorts(ty::error::ExpectedFound { + expected: self.tcx.mk_ty(ty::Alias(ty::Projection, proj.projection_ty)), + found: projection.term.ty().unwrap(), + }), + ]; } } if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind @@ -3221,10 +3243,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let tcx = self.tcx; + let mut print_root_expr = true; let mut assocs = vec![]; - // We still want to point at the different methods even if there hasn't - // been a change of assoc type. - let mut call_spans = vec![]; let mut expr = expr; let mut prev_ty = self.resolve_vars_if_possible( typeck_results.expr_ty_adjusted_opt(expr).unwrap_or(tcx.ty_error()), @@ -3234,64 +3254,10 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // vec![1, 2, 3].iter().map(mapper).sum() // ^^^^^^ ^^^^^^^^^^^ expr = rcvr_expr; - let mut assocs_in_this_method = Vec::with_capacity(type_diffs.len()); - call_spans.push(span); - - let ocx = ObligationCtxt::new_in_snapshot(self.infcx); - for diff in &type_diffs { - let Sorts(expected_found) = diff else { continue; }; - let ty::Alias(ty::Projection, proj) = expected_found.expected.kind() else { continue; }; - - let origin = - TypeVariableOrigin { kind: TypeVariableOriginKind::TypeInference, span }; - let trait_def_id = proj.trait_def_id(self.tcx); - // Make `Self` be equivalent to the type of the call chain - // expression we're looking at now, so that we can tell what - // for example `Iterator::Item` is at this point in the chain. - let substs = InternalSubsts::for_item(self.tcx, trait_def_id, |param, _| { - match param.kind { - ty::GenericParamDefKind::Type { .. } => { - if param.index == 0 { - return prev_ty.into(); - } - } - ty::GenericParamDefKind::Lifetime - | ty::GenericParamDefKind::Const { .. } => {} - } - self.var_for_def(span, param) - }); - // This will hold the resolved type of the associated type, if the - // current expression implements the trait that associated type is - // in. For example, this would be what `Iterator::Item` is here. - let ty_var = self.infcx.next_ty_var(origin); - // This corresponds to `::Item = _`. - let trait_ref = ty::Binder::dummy(ty::PredicateKind::Clause( - ty::Clause::Projection(ty::ProjectionPredicate { - projection_ty: ty::AliasTy { substs, def_id: proj.def_id }, - term: ty_var.into(), - }), - )); - // Add `::Item = _` obligation. - ocx.register_obligation(Obligation::misc( - self.tcx, - span, - expr.hir_id, - param_env, - trait_ref, - )); - if ocx.select_where_possible().is_empty() { - // `ty_var` now holds the type that `Item` is for `ExprTy`. - let ty_var = self.resolve_vars_if_possible(ty_var); - assocs_in_this_method.push(Some((span, (proj.def_id, ty_var)))); - } else { - // `` didn't select, so likely we've - // reached the end of the iterator chain, like the originating - // `Vec<_>`. - // Keep the space consistent for later zipping. - assocs_in_this_method.push(None); - } - } + let assocs_in_this_method = + self.probe_assoc_types_at_expr(&type_diffs, span, prev_ty, expr.hir_id, param_env); assocs.push(assocs_in_this_method); + prev_ty = self.resolve_vars_if_possible( typeck_results.expr_ty_adjusted_opt(expr).unwrap_or(tcx.ty_error()), ); @@ -3300,17 +3266,32 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { && let hir::Path { res: hir::def::Res::Local(hir_id), .. } = path && let Some(hir::Node::Pat(binding)) = self.tcx.hir().find(*hir_id) && let parent_hir_id = self.tcx.hir().get_parent_node(binding.hir_id) - && let Some(hir::Node::Local(local)) = self.tcx.hir().find(parent_hir_id) - && let Some(binding_expr) = local.init + && let Some(parent) = self.tcx.hir().find(parent_hir_id) { - // We've reached the root of the method call chain and it is a - // binding. Get the binding creation and try to continue the chain. - expr = binding_expr; + // We've reached the root of the method call chain... + if let hir::Node::Local(local) = parent + && let Some(binding_expr) = local.init + { + // ...and it is a binding. Get the binding creation and continue the chain. + expr = binding_expr; + } + if let hir::Node::Param(param) = parent { + // ...and it is a an fn argument. + let prev_ty = self.resolve_vars_if_possible( + typeck_results.node_type_opt(param.hir_id).unwrap_or(tcx.ty_error()), + ); + let assocs_in_this_method = self.probe_assoc_types_at_expr(&type_diffs, param.ty_span, prev_ty, param.hir_id, param_env); + if assocs_in_this_method.iter().any(|a| a.is_some()) { + assocs.push(assocs_in_this_method); + print_root_expr = false; + } + break; + } } } // We want the type before deref coercions, otherwise we talk about `&[_]` // instead of `Vec<_>`. - if let Some(ty) = typeck_results.expr_ty_opt(expr) { + if let Some(ty) = typeck_results.expr_ty_opt(expr) && print_root_expr { let ty = with_forced_trimmed_paths!(self.ty_to_string(ty)); // Point at the root expression // vec![1, 2, 3].iter().map(mapper).sum() @@ -3324,7 +3305,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let Some(prev_assoc_in_method) = assocs.peek() else { for entry in assocs_in_method { let Some((span, (assoc, ty))) = entry else { continue; }; - if type_diffs.iter().any(|diff| { + if primary_spans.is_empty() || type_diffs.iter().any(|diff| { let Sorts(expected_found) = diff else { return false; }; self.can_eq(param_env, expected_found.found, ty).is_ok() }) { @@ -3380,13 +3361,6 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } } } - for span in call_spans { - if span_labels.iter().find(|(s, _)| *s == span).is_none() { - // Ensure we are showing the entire chain, even if the assoc types - // haven't changed. - span_labels.push((span, String::new())); - } - } if !primary_spans.is_empty() { let mut multi_span: MultiSpan = primary_spans.into(); for (span, label) in span_labels { @@ -3394,13 +3368,70 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } err.span_note( multi_span, - format!( - "the method call chain might not have had the expected \ - associated types", - ), + format!("the method call chain might not have had the expected associated types"), ); } } + + fn probe_assoc_types_at_expr( + &self, + type_diffs: &[TypeError<'tcx>], + span: Span, + prev_ty: Ty<'tcx>, + body_id: hir::HirId, + param_env: ty::ParamEnv<'tcx>, + ) -> Vec))>> { + let ocx = ObligationCtxt::new_in_snapshot(self.infcx); + let mut assocs_in_this_method = Vec::with_capacity(type_diffs.len()); + for diff in type_diffs { + let Sorts(expected_found) = diff else { continue; }; + let ty::Alias(ty::Projection, proj) = expected_found.expected.kind() else { continue; }; + + let origin = TypeVariableOrigin { kind: TypeVariableOriginKind::TypeInference, span }; + let trait_def_id = proj.trait_def_id(self.tcx); + // Make `Self` be equivalent to the type of the call chain + // expression we're looking at now, so that we can tell what + // for example `Iterator::Item` is at this point in the chain. + let substs = InternalSubsts::for_item(self.tcx, trait_def_id, |param, _| { + match param.kind { + ty::GenericParamDefKind::Type { .. } => { + if param.index == 0 { + return prev_ty.into(); + } + } + ty::GenericParamDefKind::Lifetime | ty::GenericParamDefKind::Const { .. } => {} + } + self.var_for_def(span, param) + }); + // This will hold the resolved type of the associated type, if the + // current expression implements the trait that associated type is + // in. For example, this would be what `Iterator::Item` is here. + let ty_var = self.infcx.next_ty_var(origin); + // This corresponds to `::Item = _`. + let trait_ref = ty::Binder::dummy(ty::PredicateKind::Clause(ty::Clause::Projection( + ty::ProjectionPredicate { + projection_ty: ty::AliasTy { substs, def_id: proj.def_id }, + term: ty_var.into(), + }, + ))); + // Add `::Item = _` obligation. + ocx.register_obligation(Obligation::misc( + self.tcx, span, body_id, param_env, trait_ref, + )); + if ocx.select_where_possible().is_empty() { + // `ty_var` now holds the type that `Item` is for `ExprTy`. + let ty_var = self.resolve_vars_if_possible(ty_var); + assocs_in_this_method.push(Some((span, (proj.def_id, ty_var)))); + } else { + // `` didn't select, so likely we've + // reached the end of the iterator chain, like the originating + // `Vec<_>`. + // Keep the space consistent for later zipping. + assocs_in_this_method.push(None); + } + } + assocs_in_this_method + } } /// Add a hint to add a missing borrow or remove an unnecessary one. diff --git a/src/test/ui/issues/issue-31173.stderr b/src/test/ui/issues/issue-31173.stderr index b667ae0a78937..cf05eb7d01032 100644 --- a/src/test/ui/issues/issue-31173.stderr +++ b/src/test/ui/issues/issue-31173.stderr @@ -6,6 +6,18 @@ LL | .cloned() | = note: expected reference `&_` found type `u8` +note: the method call chain might not have had the expected associated types + --> $DIR/issue-31173.rs:3:20 + | +LL | pub fn get_tok(it: &mut IntoIter) { + | ^^^^^^^^^^^^^^^^^ `Iterator::Item` is `u8` here +... +LL | .take_while(|&x| { + | __________- +LL | | found_e = true; +LL | | false +LL | | }) + | |__________- `Iterator::Item` remains `u8` here note: required by a bound in `cloned` --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL diff --git a/src/test/ui/issues/issue-33941.stderr b/src/test/ui/issues/issue-33941.stderr index 49702c4765863..f33d33308d756 100644 --- a/src/test/ui/issues/issue-33941.stderr +++ b/src/test/ui/issues/issue-33941.stderr @@ -6,6 +6,13 @@ LL | for _ in HashMap::new().iter().cloned() {} | = note: expected reference `&_` found tuple `(&_, &_)` +note: the method call chain might not have had the expected associated types + --> $DIR/issue-33941.rs:6:29 + | +LL | for _ in HashMap::new().iter().cloned() {} + | -------------- ^^^^^^ `Iterator::Item` is `(&_, &_)` here + | | + | this expression has type `HashMap<_, _>` note: required by a bound in `cloned` --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL From 36b60e7bfdce24fc44aa5dc352945fc7cbe2bd8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 13 Dec 2022 15:52:16 -0800 Subject: [PATCH 07/18] Use `with_forced_trimmed_paths` more --- .../src/traits/error_reporting/mod.rs | 37 +++++++++++-------- .../associated-types-overridden-binding-2.rs | 2 +- ...sociated-types-overridden-binding-2.stderr | 2 +- src/test/ui/issues/issue-31173.stderr | 2 +- src/test/ui/issues/issue-33941.rs | 6 +-- src/test/ui/issues/issue-33941.stderr | 6 +-- src/test/ui/traits/assoc-type-in-superbad.rs | 2 +- .../ui/traits/assoc-type-in-superbad.stderr | 2 +- .../ui/type-alias-impl-trait/issue-57961.rs | 2 +- .../type-alias-impl-trait/issue-57961.stderr | 2 +- 10 files changed, 34 insertions(+), 29 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index c68da3e24a190..b138ffdd0402a 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -35,7 +35,7 @@ use rustc_middle::traits::select::OverflowError; use rustc_middle::ty::abstract_const::NotConstEvaluatable; use rustc_middle::ty::error::ExpectedFound; use rustc_middle::ty::fold::{TypeFolder, TypeSuperFoldable}; -use rustc_middle::ty::print::{FmtPrinter, Print}; +use rustc_middle::ty::print::{with_forced_trimmed_paths, FmtPrinter, Print}; use rustc_middle::ty::{ self, SubtypePredicate, ToPolyTraitRef, ToPredicate, TraitRef, Ty, TyCtxt, TypeFoldable, TypeVisitable, @@ -1749,21 +1749,26 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let trait_def_id = pred.projection_ty.trait_def_id(self.tcx); let self_ty = pred.projection_ty.self_ty(); - if Some(pred.projection_ty.def_id) == self.tcx.lang_items().fn_once_output() { - Some(format!( - "expected `{self_ty}` to be a {fn_kind} that returns `{expected_ty}`, but it returns `{normalized_ty}`", - fn_kind = self_ty.prefix_string(self.tcx) - )) - } else if Some(trait_def_id) == self.tcx.lang_items().future_trait() { - Some(format!( - "expected `{self_ty}` to be a future that resolves to `{expected_ty}`, but it resolves to `{normalized_ty}`" - )) - } else if Some(trait_def_id) == self.tcx.get_diagnostic_item(sym::Iterator) { - Some(format!( - "expected `{self_ty}` to be an iterator that yields `{expected_ty}`, but it yields `{normalized_ty}`" - )) - } else { - None + with_forced_trimmed_paths! { + if Some(pred.projection_ty.def_id) == self.tcx.lang_items().fn_once_output() { + Some(format!( + "expected `{self_ty}` to be a {fn_kind} that returns `{expected_ty}`, but it \ + returns `{normalized_ty}`", + fn_kind = self_ty.prefix_string(self.tcx) + )) + } else if Some(trait_def_id) == self.tcx.lang_items().future_trait() { + Some(format!( + "expected `{self_ty}` to be a future that resolves to `{expected_ty}`, but it \ + resolves to `{normalized_ty}`" + )) + } else if Some(trait_def_id) == self.tcx.get_diagnostic_item(sym::Iterator) { + Some(format!( + "expected `{self_ty}` to be an iterator that yields `{expected_ty}`, but it \ + yields `{normalized_ty}`" + )) + } else { + None + } } } diff --git a/src/test/ui/associated-types/associated-types-overridden-binding-2.rs b/src/test/ui/associated-types/associated-types-overridden-binding-2.rs index 26b9f4b3a9266..fed60ccf089d0 100644 --- a/src/test/ui/associated-types/associated-types-overridden-binding-2.rs +++ b/src/test/ui/associated-types/associated-types-overridden-binding-2.rs @@ -4,5 +4,5 @@ trait I32Iterator = Iterator; fn main() { let _: &dyn I32Iterator = &vec![42].into_iter(); - //~^ ERROR expected `std::vec::IntoIter` to be an iterator that yields `i32`, but it yields `u32` + //~^ ERROR expected `IntoIter` to be an iterator that yields `i32`, but it yields `u32` } diff --git a/src/test/ui/associated-types/associated-types-overridden-binding-2.stderr b/src/test/ui/associated-types/associated-types-overridden-binding-2.stderr index 2d25f68de44ad..a28a0b74e4acc 100644 --- a/src/test/ui/associated-types/associated-types-overridden-binding-2.stderr +++ b/src/test/ui/associated-types/associated-types-overridden-binding-2.stderr @@ -1,4 +1,4 @@ -error[E0271]: expected `std::vec::IntoIter` to be an iterator that yields `i32`, but it yields `u32` +error[E0271]: expected `IntoIter` to be an iterator that yields `i32`, but it yields `u32` --> $DIR/associated-types-overridden-binding-2.rs:6:43 | LL | let _: &dyn I32Iterator = &vec![42].into_iter(); diff --git a/src/test/ui/issues/issue-31173.stderr b/src/test/ui/issues/issue-31173.stderr index cf05eb7d01032..695b30df6a407 100644 --- a/src/test/ui/issues/issue-31173.stderr +++ b/src/test/ui/issues/issue-31173.stderr @@ -1,4 +1,4 @@ -error[E0271]: expected `TakeWhile<&mut std::vec::IntoIter, [closure@$DIR/issue-31173.rs:7:21: 7:25]>` to be an iterator that yields `&_`, but it yields `u8` +error[E0271]: expected `TakeWhile<&mut IntoIter, [closure@$DIR/issue-31173.rs:7:21: 7:25]>` to be an iterator that yields `&_`, but it yields `u8` --> $DIR/issue-31173.rs:11:10 | LL | .cloned() diff --git a/src/test/ui/issues/issue-33941.rs b/src/test/ui/issues/issue-33941.rs index 8430e85df8715..e3b6dcf55a744 100644 --- a/src/test/ui/issues/issue-33941.rs +++ b/src/test/ui/issues/issue-33941.rs @@ -3,7 +3,7 @@ use std::collections::HashMap; fn main() { - for _ in HashMap::new().iter().cloned() {} //~ ERROR expected `std::collections::hash_map::Iter<'_, _, _>` to be an iterator that yields `&_`, but it yields `(&_, &_)` - //~^ ERROR expected `std::collections::hash_map::Iter<'_, _, _>` to be an iterator that yields `&_`, but it yields `(&_, &_)` - //~| ERROR expected `std::collections::hash_map::Iter<'_, _, _>` to be an iterator that yields `&_`, but it yields `(&_, &_)` + for _ in HashMap::new().iter().cloned() {} //~ ERROR expected `Iter<'_, _, _>` to be an iterator that yields `&_`, but it yields `(&_, &_)` + //~^ ERROR expected `Iter<'_, _, _>` to be an iterator that yields `&_`, but it yields `(&_, &_)` + //~| ERROR expected `Iter<'_, _, _>` to be an iterator that yields `&_`, but it yields `(&_, &_)` } diff --git a/src/test/ui/issues/issue-33941.stderr b/src/test/ui/issues/issue-33941.stderr index f33d33308d756..668eaabca4c46 100644 --- a/src/test/ui/issues/issue-33941.stderr +++ b/src/test/ui/issues/issue-33941.stderr @@ -1,4 +1,4 @@ -error[E0271]: expected `std::collections::hash_map::Iter<'_, _, _>` to be an iterator that yields `&_`, but it yields `(&_, &_)` +error[E0271]: expected `Iter<'_, _, _>` to be an iterator that yields `&_`, but it yields `(&_, &_)` --> $DIR/issue-33941.rs:6:36 | LL | for _ in HashMap::new().iter().cloned() {} @@ -16,7 +16,7 @@ LL | for _ in HashMap::new().iter().cloned() {} note: required by a bound in `cloned` --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL -error[E0271]: expected `std::collections::hash_map::Iter<'_, _, _>` to be an iterator that yields `&_`, but it yields `(&_, &_)` +error[E0271]: expected `Iter<'_, _, _>` to be an iterator that yields `&_`, but it yields `(&_, &_)` --> $DIR/issue-33941.rs:6:14 | LL | for _ in HashMap::new().iter().cloned() {} @@ -27,7 +27,7 @@ LL | for _ in HashMap::new().iter().cloned() {} = note: required for `Cloned>` to implement `Iterator` = note: required for `Cloned>` to implement `IntoIterator` -error[E0271]: expected `std::collections::hash_map::Iter<'_, _, _>` to be an iterator that yields `&_`, but it yields `(&_, &_)` +error[E0271]: expected `Iter<'_, _, _>` to be an iterator that yields `&_`, but it yields `(&_, &_)` --> $DIR/issue-33941.rs:6:14 | LL | for _ in HashMap::new().iter().cloned() {} diff --git a/src/test/ui/traits/assoc-type-in-superbad.rs b/src/test/ui/traits/assoc-type-in-superbad.rs index d7d6241ef708c..65340b2a2092f 100644 --- a/src/test/ui/traits/assoc-type-in-superbad.rs +++ b/src/test/ui/traits/assoc-type-in-superbad.rs @@ -10,7 +10,7 @@ pub trait Foo: Iterator::Key> { impl Foo for IntoIter { type Key = u32; - //~^ ERROR expected `std::vec::IntoIter` to be an iterator that yields `u32`, but it yields `i32` + //~^ ERROR expected `IntoIter` to be an iterator that yields `u32`, but it yields `i32` } fn main() {} diff --git a/src/test/ui/traits/assoc-type-in-superbad.stderr b/src/test/ui/traits/assoc-type-in-superbad.stderr index 3e2d9d9038aa7..7fa1d2c2eed67 100644 --- a/src/test/ui/traits/assoc-type-in-superbad.stderr +++ b/src/test/ui/traits/assoc-type-in-superbad.stderr @@ -1,4 +1,4 @@ -error[E0271]: expected `std::vec::IntoIter` to be an iterator that yields `u32`, but it yields `i32` +error[E0271]: expected `IntoIter` to be an iterator that yields `u32`, but it yields `i32` --> $DIR/assoc-type-in-superbad.rs:12:16 | LL | type Key = u32; diff --git a/src/test/ui/type-alias-impl-trait/issue-57961.rs b/src/test/ui/type-alias-impl-trait/issue-57961.rs index 24b3a045856a6..4aa5966ff25d4 100644 --- a/src/test/ui/type-alias-impl-trait/issue-57961.rs +++ b/src/test/ui/type-alias-impl-trait/issue-57961.rs @@ -8,7 +8,7 @@ trait Foo { impl Foo for () { type Bar = std::vec::IntoIter; - //~^ ERROR expected `std::vec::IntoIter` to be an iterator that yields `X`, but it yields `u32` + //~^ ERROR expected `IntoIter` to be an iterator that yields `X`, but it yields `u32` } fn incoherent() { diff --git a/src/test/ui/type-alias-impl-trait/issue-57961.stderr b/src/test/ui/type-alias-impl-trait/issue-57961.stderr index fb40895c49f15..8d11b48888947 100644 --- a/src/test/ui/type-alias-impl-trait/issue-57961.stderr +++ b/src/test/ui/type-alias-impl-trait/issue-57961.stderr @@ -1,4 +1,4 @@ -error[E0271]: expected `std::vec::IntoIter` to be an iterator that yields `X`, but it yields `u32` +error[E0271]: expected `IntoIter` to be an iterator that yields `X`, but it yields `u32` --> $DIR/issue-57961.rs:10:16 | LL | type X = impl Sized; From 50e269f4fe69d2159110f1dae9ca7a045b04f854 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 13 Dec 2022 16:34:04 -0800 Subject: [PATCH 08/18] Shorten trimmed display of closures When `with_forced_trimmed_paths` is used, only print filename and start of the closure's span, to reduce their verbosity. --- compiler/rustc_middle/src/ty/print/pretty.rs | 8 +++++++- compiler/rustc_span/src/lib.rs | 7 +++++++ compiler/rustc_span/src/source_map.rs | 15 +++++++++++---- .../issue-62203-hrtb-ice.stderr | 2 +- src/test/ui/issues/issue-31173.stderr | 2 +- .../fallback-closure-wrap.fallback.stderr | 2 +- 6 files changed, 28 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 6acc2dc65d369..3deea781e7240 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -16,6 +16,7 @@ use rustc_session::config::TrimmedDefPaths; use rustc_session::cstore::{ExternCrate, ExternCrateSource}; use rustc_session::Limit; use rustc_span::symbol::{kw, Ident, Symbol}; +use rustc_span::FileNameDisplayPreference; use rustc_target::abi::Size; use rustc_target::spec::abi::Abi; use smallvec::SmallVec; @@ -818,11 +819,16 @@ pub trait PrettyPrinter<'tcx>: p!("@", print_def_path(did.to_def_id(), substs)); } else { let span = self.tcx().def_span(did); + let preference = if FORCE_TRIMMED_PATH.with(|flag| flag.get()) { + FileNameDisplayPreference::Short + } else { + FileNameDisplayPreference::Remapped + }; p!(write( "@{}", // This may end up in stderr diagnostics but it may also be emitted // into MIR. Hence we use the remapped path if available - self.tcx().sess.source_map().span_to_embeddable_string(span) + self.tcx().sess.source_map().span_to_string(span, preference) )); } } else { diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 335bfc3302f27..d95b8742f0527 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -259,6 +259,10 @@ impl RealFileName { FileNameDisplayPreference::Remapped => { self.remapped_path_if_available().to_string_lossy() } + FileNameDisplayPreference::Short => self + .local_path_if_available() + .file_name() + .map_or_else(|| "".into(), |f| f.to_string_lossy()), } } } @@ -302,6 +306,9 @@ pub enum FileNameDisplayPreference { /// Display the path before the application of rewrite rules provided via `--remap-path-prefix`. /// This is appropriate for use in user-facing output (such as diagnostics). Local, + /// Display only the filename, as a way to reduce the verbosity of the output. + /// This is appropriate for use in user-facing output (such as diagnostics). + Short, } pub struct FileNameDisplay<'a> { diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs index a4e0f54d27670..1e4c7c711e453 100644 --- a/compiler/rustc_span/src/source_map.rs +++ b/compiler/rustc_span/src/source_map.rs @@ -438,7 +438,11 @@ impl SourceMap { } } - fn span_to_string(&self, sp: Span, filename_display_pref: FileNameDisplayPreference) -> String { + pub fn span_to_string( + &self, + sp: Span, + filename_display_pref: FileNameDisplayPreference, + ) -> String { if self.files.borrow().source_files.is_empty() || sp.is_dummy() { return "no-location".to_string(); } @@ -446,12 +450,15 @@ impl SourceMap { let lo = self.lookup_char_pos(sp.lo()); let hi = self.lookup_char_pos(sp.hi()); format!( - "{}:{}:{}: {}:{}", + "{}:{}:{}{}", lo.file.name.display(filename_display_pref), lo.line, lo.col.to_usize() + 1, - hi.line, - hi.col.to_usize() + 1, + if let FileNameDisplayPreference::Short = filename_display_pref { + String::new() + } else { + format!(": {}:{}", hi.line, hi.col.to_usize() + 1) + } ) } diff --git a/src/test/ui/higher-rank-trait-bounds/issue-62203-hrtb-ice.stderr b/src/test/ui/higher-rank-trait-bounds/issue-62203-hrtb-ice.stderr index ab5598e364fc4..fdd192f431370 100644 --- a/src/test/ui/higher-rank-trait-bounds/issue-62203-hrtb-ice.stderr +++ b/src/test/ui/higher-rank-trait-bounds/issue-62203-hrtb-ice.stderr @@ -30,7 +30,7 @@ LL | where LL | F: for<'r> T0<'r, (>::V,), O = >::V>, | ^^^^^^^^^^^^^^^^^^^^ required by this bound in `T1::m` -error[E0271]: expected `[closure@$DIR/issue-62203-hrtb-ice.rs:42:16: 42:19]` to be a closure that returns `Unit3`, but it returns `Unit4` +error[E0271]: expected `[closure@issue-62203-hrtb-ice.rs:42:16]` to be a closure that returns `Unit3`, but it returns `Unit4` --> $DIR/issue-62203-hrtb-ice.rs:39:9 | LL | let v = Unit2.m( diff --git a/src/test/ui/issues/issue-31173.stderr b/src/test/ui/issues/issue-31173.stderr index 695b30df6a407..bd99d66dcff2c 100644 --- a/src/test/ui/issues/issue-31173.stderr +++ b/src/test/ui/issues/issue-31173.stderr @@ -1,4 +1,4 @@ -error[E0271]: expected `TakeWhile<&mut IntoIter, [closure@$DIR/issue-31173.rs:7:21: 7:25]>` to be an iterator that yields `&_`, but it yields `u8` +error[E0271]: expected `TakeWhile<&mut IntoIter, [closure@issue-31173.rs:7:21]>` to be an iterator that yields `&_`, but it yields `u8` --> $DIR/issue-31173.rs:11:10 | LL | .cloned() diff --git a/src/test/ui/never_type/fallback-closure-wrap.fallback.stderr b/src/test/ui/never_type/fallback-closure-wrap.fallback.stderr index 45cf3723483f3..a0f790dba15ed 100644 --- a/src/test/ui/never_type/fallback-closure-wrap.fallback.stderr +++ b/src/test/ui/never_type/fallback-closure-wrap.fallback.stderr @@ -1,4 +1,4 @@ -error[E0271]: expected `[closure@$DIR/fallback-closure-wrap.rs:18:40: 18:47]` to be a closure that returns `()`, but it returns `!` +error[E0271]: expected `[closure@fallback-closure-wrap.rs:18:40]` to be a closure that returns `()`, but it returns `!` --> $DIR/fallback-closure-wrap.rs:18:31 | LL | let error = Closure::wrap(Box::new(move || { From e9ef36ae23164c8e29d7742743590375359410b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 13 Dec 2022 16:57:34 -0800 Subject: [PATCH 09/18] Trim paths in E0599 --- .../rustc_hir_typeck/src/method/suggest.rs | 22 +++++++++---- .../issue-33784.stderr | 4 +-- .../ui/empty/empty-struct-braces-expr.stderr | 8 ++--- .../fn-help-with-err.stderr | 4 +-- .../issue-30786.stderr | 8 ++--- .../no-method-suggested-traits.stderr | 32 +++++++++---------- src/test/ui/issues/issue-30123.stderr | 4 +-- src/test/ui/issues/issue-31173.stderr | 4 +-- src/test/ui/issues/issue-41880.stderr | 2 +- ...ethod-not-found-generic-arg-elision.stderr | 2 +- .../ui/mismatched_types/issue-36053-2.stderr | 4 +-- .../mut-borrow-needed-by-trait.stderr | 4 +-- ...ed-closures-static-call-wrong-trait.stderr | 4 +-- 13 files changed, 56 insertions(+), 46 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 41cd6bf314ebf..c2abbba91816d 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -25,7 +25,7 @@ use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKin use rustc_middle::traits::util::supertraits; use rustc_middle::ty::fast_reject::DeepRejectCtxt; use rustc_middle::ty::fast_reject::{simplify_type, TreatParams}; -use rustc_middle::ty::print::with_crate_prefix; +use rustc_middle::ty::print::{with_crate_prefix, with_forced_trimmed_paths}; use rustc_middle::ty::{self, DefIdTree, GenericArgKind, Ty, TyCtxt, TypeVisitable}; use rustc_middle::ty::{IsSuggestable, ToPolyTraitRef}; use rustc_span::symbol::{kw, sym, Ident}; @@ -270,7 +270,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let tcx = self.tcx; let rcvr_ty = self.resolve_vars_if_possible(rcvr_ty); - let ty_str = self.ty_to_string(rcvr_ty); + let ty_str = with_forced_trimmed_paths!(self.ty_to_string(rcvr_ty)); let is_method = mode == Mode::MethodCall; let item_kind = if is_method { "method" @@ -565,7 +565,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let term = pred.skip_binder().term; let obligation = format!("{} = {}", projection_ty, term); - let quiet = format!("{} = {}", quiet_projection_ty, term); + let quiet = with_forced_trimmed_paths!(format!( + "{} = {}", + quiet_projection_ty, term + )); bound_span_label(projection_ty.self_ty(), &obligation, &quiet); Some((obligation, projection_ty.self_ty())) @@ -575,7 +578,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let self_ty = p.self_ty(); let path = p.print_only_trait_path(); let obligation = format!("{}: {}", self_ty, path); - let quiet = format!("_: {}", path); + let quiet = with_forced_trimmed_paths!(format!("_: {}", path)); bound_span_label(self_ty, &obligation, &quiet); Some((obligation, self_ty)) } @@ -798,7 +801,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (None, None) }; let primary_message = primary_message.unwrap_or_else(|| format!( - "the {item_kind} `{item_name}` exists for {actual_prefix} `{ty_str}`, but its trait bounds were not satisfied" + "the {item_kind} `{item_name}` exists for {actual_prefix} `{ty_str}`, \ + but its trait bounds were not satisfied" )); err.set_primary_message(&primary_message); if let Some(label) = label { @@ -895,7 +899,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } } else { - err.span_label(span, format!("{item_kind} cannot be called on `{ty_str}` due to unsatisfied trait bounds")); + err.span_label( + span, + format!( + "{item_kind} cannot be called on `{ty_str}` due to unsatisfied \ + trait bounds", + ), + ); } }; diff --git a/src/test/ui/confuse-field-and-method/issue-33784.stderr b/src/test/ui/confuse-field-and-method/issue-33784.stderr index 34debb6831734..3906d64c94624 100644 --- a/src/test/ui/confuse-field-and-method/issue-33784.stderr +++ b/src/test/ui/confuse-field-and-method/issue-33784.stderr @@ -1,4 +1,4 @@ -error[E0599]: no method named `closure` found for reference `&Obj<[closure@$DIR/issue-33784.rs:25:43: 25:45]>` in the current scope +error[E0599]: no method named `closure` found for reference `&Obj<[closure@issue-33784.rs:25:43]>` in the current scope --> $DIR/issue-33784.rs:27:7 | LL | p.closure(); @@ -9,7 +9,7 @@ help: to call the function stored in `closure`, surround the field access with p LL | (p.closure)(); | + + -error[E0599]: no method named `fn_ptr` found for reference `&&Obj<[closure@$DIR/issue-33784.rs:25:43: 25:45]>` in the current scope +error[E0599]: no method named `fn_ptr` found for reference `&&Obj<[closure@issue-33784.rs:25:43]>` in the current scope --> $DIR/issue-33784.rs:29:7 | LL | q.fn_ptr(); diff --git a/src/test/ui/empty/empty-struct-braces-expr.stderr b/src/test/ui/empty/empty-struct-braces-expr.stderr index e1a7a02a56869..0e580aedeaa92 100644 --- a/src/test/ui/empty/empty-struct-braces-expr.stderr +++ b/src/test/ui/empty/empty-struct-braces-expr.stderr @@ -100,22 +100,22 @@ help: a unit struct with a similar name exists LL | let xe1 = XEmpty2(); | ~~~~~~~ -error[E0599]: no variant or associated item named `Empty3` found for enum `empty_struct::XE` in the current scope +error[E0599]: no variant or associated item named `Empty3` found for enum `XE` in the current scope --> $DIR/empty-struct-braces-expr.rs:25:19 | LL | let xe3 = XE::Empty3; | ^^^^^^ | | - | variant or associated item not found in `empty_struct::XE` + | variant or associated item not found in `XE` | help: there is a variant with a similar name: `XEmpty3` -error[E0599]: no variant or associated item named `Empty3` found for enum `empty_struct::XE` in the current scope +error[E0599]: no variant or associated item named `Empty3` found for enum `XE` in the current scope --> $DIR/empty-struct-braces-expr.rs:26:19 | LL | let xe3 = XE::Empty3(); | ^^^^^^ | | - | variant or associated item not found in `empty_struct::XE` + | variant or associated item not found in `XE` | help: there is a variant with a similar name: `XEmpty3` error[E0599]: no variant named `Empty1` found for enum `empty_struct::XE` diff --git a/src/test/ui/functions-closures/fn-help-with-err.stderr b/src/test/ui/functions-closures/fn-help-with-err.stderr index 2296666219eef..463ac7684ecdf 100644 --- a/src/test/ui/functions-closures/fn-help-with-err.stderr +++ b/src/test/ui/functions-closures/fn-help-with-err.stderr @@ -17,11 +17,11 @@ note: `Bar` defines an item `bar`, perhaps you need to implement it LL | trait Bar { | ^^^^^^^^^ -error[E0599]: no method named `bar` found for struct `Arc<[closure@$DIR/fn-help-with-err.rs:22:36: 22:38]>` in the current scope +error[E0599]: no method named `bar` found for struct `Arc<[closure@fn-help-with-err.rs:22:36]>` in the current scope --> $DIR/fn-help-with-err.rs:23:10 | LL | arc2.bar(); - | ^^^ method not found in `Arc<[closure@$DIR/fn-help-with-err.rs:22:36: 22:38]>` + | ^^^ method not found in `Arc<[closure@fn-help-with-err.rs:22:36]>` | = help: items from traits can only be used if the trait is implemented and in scope note: `Bar` defines an item `bar`, perhaps you need to implement it diff --git a/src/test/ui/higher-rank-trait-bounds/issue-30786.stderr b/src/test/ui/higher-rank-trait-bounds/issue-30786.stderr index c1e235441d65d..3c5a7ef404755 100644 --- a/src/test/ui/higher-rank-trait-bounds/issue-30786.stderr +++ b/src/test/ui/higher-rank-trait-bounds/issue-30786.stderr @@ -1,4 +1,4 @@ -error[E0599]: the method `filterx` exists for struct `Map`, but its trait bounds were not satisfied +error[E0599]: the method `filterx` exists for struct `Map`, but its trait bounds were not satisfied --> $DIR/issue-30786.rs:118:22 | LL | pub struct Map { @@ -8,7 +8,7 @@ LL | pub struct Map { | doesn't satisfy `_: StreamExt` ... LL | let filter = map.filterx(|x: &_| true); - | ^^^^^^^ method cannot be called on `Map` due to unsatisfied trait bounds + | ^^^^^^^ method cannot be called on `Map` due to unsatisfied trait bounds | note: the following trait bounds were not satisfied: `&'a mut &Map: Stream` @@ -19,7 +19,7 @@ note: the following trait bounds were not satisfied: LL | impl StreamExt for T where for<'a> &'a mut T: Stream {} | --------- - ^^^^^^ unsatisfied trait bound introduced here -error[E0599]: the method `countx` exists for struct `Filter fn(&'a u64) -> &'a u64 {identity::}>, [closure@$DIR/issue-30786.rs:129:30: 129:37]>`, but its trait bounds were not satisfied +error[E0599]: the method `countx` exists for struct `Filter fn(&'a u64) -> &'a u64 {identity::}>, [closure@issue-30786.rs:129:30]>`, but its trait bounds were not satisfied --> $DIR/issue-30786.rs:130:24 | LL | pub struct Filter { @@ -29,7 +29,7 @@ LL | pub struct Filter { | doesn't satisfy `_: StreamExt` ... LL | let count = filter.countx(); - | ^^^^^^ method cannot be called on `Filter fn(&'a u64) -> &'a u64 {identity::}>, [closure@$DIR/issue-30786.rs:129:30: 129:37]>` due to unsatisfied trait bounds + | ^^^^^^ method cannot be called on `Filter fn(&'a u64) -> &'a u64 {identity::}>, [closure@issue-30786.rs:129:30]>` due to unsatisfied trait bounds | note: the following trait bounds were not satisfied: `&'a mut &Filter fn(&'a u64) -> &'a u64 {identity::}>, [closure@$DIR/issue-30786.rs:129:30: 129:37]>: Stream` diff --git a/src/test/ui/impl-trait/no-method-suggested-traits.stderr b/src/test/ui/impl-trait/no-method-suggested-traits.stderr index 3d4ae11e57670..548c89d0a3871 100644 --- a/src/test/ui/impl-trait/no-method-suggested-traits.stderr +++ b/src/test/ui/impl-trait/no-method-suggested-traits.stderr @@ -145,11 +145,11 @@ note: `foo::Bar` defines an item `method2`, perhaps you need to implement it LL | pub trait Bar { | ^^^^^^^^^^^^^ -error[E0599]: no method named `method2` found for struct `no_method_suggested_traits::Foo` in the current scope +error[E0599]: no method named `method2` found for struct `Foo` in the current scope --> $DIR/no-method-suggested-traits.rs:50:37 | LL | no_method_suggested_traits::Foo.method2(); - | ^^^^^^^ method not found in `no_method_suggested_traits::Foo` + | ^^^^^^^ method not found in `Foo` | = help: items from traits can only be used if the trait is implemented and in scope note: `foo::Bar` defines an item `method2`, perhaps you need to implement it @@ -158,11 +158,11 @@ note: `foo::Bar` defines an item `method2`, perhaps you need to implement it LL | pub trait Bar { | ^^^^^^^^^^^^^ -error[E0599]: no method named `method2` found for struct `Rc<&mut Box<&no_method_suggested_traits::Foo>>` in the current scope +error[E0599]: no method named `method2` found for struct `Rc<&mut Box<&Foo>>` in the current scope --> $DIR/no-method-suggested-traits.rs:52:71 | LL | std::rc::Rc::new(&mut Box::new(&no_method_suggested_traits::Foo)).method2(); - | ^^^^^^^ method not found in `Rc<&mut Box<&no_method_suggested_traits::Foo>>` + | ^^^^^^^ method not found in `Rc<&mut Box<&Foo>>` | = help: items from traits can only be used if the trait is implemented and in scope note: `foo::Bar` defines an item `method2`, perhaps you need to implement it @@ -171,11 +171,11 @@ note: `foo::Bar` defines an item `method2`, perhaps you need to implement it LL | pub trait Bar { | ^^^^^^^^^^^^^ -error[E0599]: no method named `method2` found for enum `no_method_suggested_traits::Bar` in the current scope +error[E0599]: no method named `method2` found for enum `Bar` in the current scope --> $DIR/no-method-suggested-traits.rs:54:40 | LL | no_method_suggested_traits::Bar::X.method2(); - | ^^^^^^^ method not found in `no_method_suggested_traits::Bar` + | ^^^^^^^ method not found in `Bar` | = help: items from traits can only be used if the trait is implemented and in scope note: `foo::Bar` defines an item `method2`, perhaps you need to implement it @@ -184,11 +184,11 @@ note: `foo::Bar` defines an item `method2`, perhaps you need to implement it LL | pub trait Bar { | ^^^^^^^^^^^^^ -error[E0599]: no method named `method2` found for struct `Rc<&mut Box<&no_method_suggested_traits::Bar>>` in the current scope +error[E0599]: no method named `method2` found for struct `Rc<&mut Box<&Bar>>` in the current scope --> $DIR/no-method-suggested-traits.rs:56:74 | LL | std::rc::Rc::new(&mut Box::new(&no_method_suggested_traits::Bar::X)).method2(); - | ^^^^^^^ method not found in `Rc<&mut Box<&no_method_suggested_traits::Bar>>` + | ^^^^^^^ method not found in `Rc<&mut Box<&Bar>>` | = help: items from traits can only be used if the trait is implemented and in scope note: `foo::Bar` defines an item `method2`, perhaps you need to implement it @@ -255,29 +255,29 @@ error[E0599]: no method named `method3` found for struct `Rc<&mut Box<&usize>>` LL | std::rc::Rc::new(&mut Box::new(&1_usize)).method3(); | ^^^^^^^ method not found in `Rc<&mut Box<&usize>>` -error[E0599]: no method named `method3` found for struct `no_method_suggested_traits::Foo` in the current scope +error[E0599]: no method named `method3` found for struct `Foo` in the current scope --> $DIR/no-method-suggested-traits.rs:71:37 | LL | no_method_suggested_traits::Foo.method3(); - | ^^^^^^^ method not found in `no_method_suggested_traits::Foo` + | ^^^^^^^ method not found in `Foo` -error[E0599]: no method named `method3` found for struct `Rc<&mut Box<&no_method_suggested_traits::Foo>>` in the current scope +error[E0599]: no method named `method3` found for struct `Rc<&mut Box<&Foo>>` in the current scope --> $DIR/no-method-suggested-traits.rs:72:71 | LL | std::rc::Rc::new(&mut Box::new(&no_method_suggested_traits::Foo)).method3(); - | ^^^^^^^ method not found in `Rc<&mut Box<&no_method_suggested_traits::Foo>>` + | ^^^^^^^ method not found in `Rc<&mut Box<&Foo>>` -error[E0599]: no method named `method3` found for enum `no_method_suggested_traits::Bar` in the current scope +error[E0599]: no method named `method3` found for enum `Bar` in the current scope --> $DIR/no-method-suggested-traits.rs:74:40 | LL | no_method_suggested_traits::Bar::X.method3(); - | ^^^^^^^ method not found in `no_method_suggested_traits::Bar` + | ^^^^^^^ method not found in `Bar` -error[E0599]: no method named `method3` found for struct `Rc<&mut Box<&no_method_suggested_traits::Bar>>` in the current scope +error[E0599]: no method named `method3` found for struct `Rc<&mut Box<&Bar>>` in the current scope --> $DIR/no-method-suggested-traits.rs:75:74 | LL | std::rc::Rc::new(&mut Box::new(&no_method_suggested_traits::Bar::X)).method3(); - | ^^^^^^^ method not found in `Rc<&mut Box<&no_method_suggested_traits::Bar>>` + | ^^^^^^^ method not found in `Rc<&mut Box<&Bar>>` error: aborting due to 24 previous errors diff --git a/src/test/ui/issues/issue-30123.stderr b/src/test/ui/issues/issue-30123.stderr index e9d934332f171..7808cbf8aa108 100644 --- a/src/test/ui/issues/issue-30123.stderr +++ b/src/test/ui/issues/issue-30123.stderr @@ -1,8 +1,8 @@ -error[E0599]: no function or associated item named `new_undirected` found for struct `issue_30123_aux::Graph` in the current scope +error[E0599]: no function or associated item named `new_undirected` found for struct `Graph` in the current scope --> $DIR/issue-30123.rs:7:33 | LL | let ug = Graph::::new_undirected(); - | ^^^^^^^^^^^^^^ function or associated item not found in `issue_30123_aux::Graph` + | ^^^^^^^^^^^^^^ function or associated item not found in `Graph` | = note: the function or associated item was found for - `issue_30123_aux::Graph` diff --git a/src/test/ui/issues/issue-31173.stderr b/src/test/ui/issues/issue-31173.stderr index bd99d66dcff2c..c5382dd669a76 100644 --- a/src/test/ui/issues/issue-31173.stderr +++ b/src/test/ui/issues/issue-31173.stderr @@ -21,11 +21,11 @@ LL | | }) note: required by a bound in `cloned` --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL -error[E0599]: the method `collect` exists for struct `Cloned, [closure@$DIR/issue-31173.rs:7:21: 7:25]>>`, but its trait bounds were not satisfied +error[E0599]: the method `collect` exists for struct `Cloned, [closure@issue-31173.rs:7:21]>>`, but its trait bounds were not satisfied --> $DIR/issue-31173.rs:12:10 | LL | .collect(); - | ^^^^^^^ method cannot be called on `Cloned, [closure@$DIR/issue-31173.rs:7:21: 7:25]>>` due to unsatisfied trait bounds + | ^^^^^^^ method cannot be called on `Cloned, [closure@issue-31173.rs:7:21]>>` due to unsatisfied trait bounds --> $SRC_DIR/core/src/iter/adapters/take_while.rs:LL:COL | = note: doesn't satisfy `<_ as Iterator>::Item = &_` diff --git a/src/test/ui/issues/issue-41880.stderr b/src/test/ui/issues/issue-41880.stderr index fd694df30457e..00c375f8d2acf 100644 --- a/src/test/ui/issues/issue-41880.stderr +++ b/src/test/ui/issues/issue-41880.stderr @@ -5,7 +5,7 @@ LL | pub struct Iterate { | ------------------------ method `iter` not found for this struct ... LL | println!("{:?}", a.iter().take(10).collect::>()); - | ^^^^ method not found in `Iterate<{integer}, [closure@$DIR/issue-41880.rs:26:24: 26:27]>` + | ^^^^ method not found in `Iterate<{integer}, [closure@issue-41880.rs:26:24]>` error: aborting due to previous error diff --git a/src/test/ui/methods/method-not-found-generic-arg-elision.stderr b/src/test/ui/methods/method-not-found-generic-arg-elision.stderr index fc42d1a4dcd08..8846efba871b9 100644 --- a/src/test/ui/methods/method-not-found-generic-arg-elision.stderr +++ b/src/test/ui/methods/method-not-found-generic-arg-elision.stderr @@ -23,7 +23,7 @@ error[E0599]: no method named `extend` found for struct `Map` in the current sco --> $DIR/method-not-found-generic-arg-elision.rs:87:29 | LL | v.iter().map(|x| x * x).extend(std::iter::once(100)); - | ^^^^^^ method not found in `Map, [closure@$DIR/method-not-found-generic-arg-elision.rs:87:18: 87:21]>` + | ^^^^^^ method not found in `Map, [closure@method-not-found-generic-arg-elision.rs:87:18]>` error[E0599]: no method named `method` found for struct `Wrapper` in the current scope --> $DIR/method-not-found-generic-arg-elision.rs:90:13 diff --git a/src/test/ui/mismatched_types/issue-36053-2.stderr b/src/test/ui/mismatched_types/issue-36053-2.stderr index b91f75b97f84a..49a14839926c6 100644 --- a/src/test/ui/mismatched_types/issue-36053-2.stderr +++ b/src/test/ui/mismatched_types/issue-36053-2.stderr @@ -13,11 +13,11 @@ LL | once::<&str>("str").fuse().filter(|a: &str| true).count(); note: required by a bound in `filter` --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL -error[E0599]: the method `count` exists for struct `Filter>, [closure@$DIR/issue-36053-2.rs:7:39: 7:48]>`, but its trait bounds were not satisfied +error[E0599]: the method `count` exists for struct `Filter>, [closure@issue-36053-2.rs:7:39]>`, but its trait bounds were not satisfied --> $DIR/issue-36053-2.rs:7:55 | LL | once::<&str>("str").fuse().filter(|a: &str| true).count(); - | --------- ^^^^^ method cannot be called on `Filter>, [closure@$DIR/issue-36053-2.rs:7:39: 7:48]>` due to unsatisfied trait bounds + | --------- ^^^^^ method cannot be called on `Filter>, [closure@issue-36053-2.rs:7:39]>` due to unsatisfied trait bounds | | | doesn't satisfy `<_ as FnOnce<(&&str,)>>::Output = bool` | doesn't satisfy `_: FnMut<(&&str,)>` diff --git a/src/test/ui/suggestions/mut-borrow-needed-by-trait.stderr b/src/test/ui/suggestions/mut-borrow-needed-by-trait.stderr index 2cb53ecce1040..6910b77d9bc3f 100644 --- a/src/test/ui/suggestions/mut-borrow-needed-by-trait.stderr +++ b/src/test/ui/suggestions/mut-borrow-needed-by-trait.stderr @@ -20,11 +20,11 @@ LL | let fp = BufWriter::new(fp); note: required by a bound in `BufWriter` --> $SRC_DIR/std/src/io/buffered/bufwriter.rs:LL:COL -error[E0599]: the method `write_fmt` exists for struct `BufWriter<&dyn std::io::Write>`, but its trait bounds were not satisfied +error[E0599]: the method `write_fmt` exists for struct `BufWriter<&dyn Write>`, but its trait bounds were not satisfied --> $DIR/mut-borrow-needed-by-trait.rs:21:5 | LL | writeln!(fp, "hello world").unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ method cannot be called on `BufWriter<&dyn std::io::Write>` due to unsatisfied trait bounds + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ method cannot be called on `BufWriter<&dyn Write>` due to unsatisfied trait bounds --> $SRC_DIR/std/src/io/buffered/bufwriter.rs:LL:COL | = note: doesn't satisfy `BufWriter<&dyn std::io::Write>: std::io::Write` diff --git a/src/test/ui/unboxed-closures/unboxed-closures-static-call-wrong-trait.stderr b/src/test/ui/unboxed-closures/unboxed-closures-static-call-wrong-trait.stderr index e5ca0edd7a91c..99ec517834003 100644 --- a/src/test/ui/unboxed-closures/unboxed-closures-static-call-wrong-trait.stderr +++ b/src/test/ui/unboxed-closures/unboxed-closures-static-call-wrong-trait.stderr @@ -1,8 +1,8 @@ -error[E0599]: no method named `call` found for closure `[closure@$DIR/unboxed-closures-static-call-wrong-trait.rs:6:26: 6:29]` in the current scope +error[E0599]: no method named `call` found for closure `[closure@unboxed-closures-static-call-wrong-trait.rs:6:26]` in the current scope --> $DIR/unboxed-closures-static-call-wrong-trait.rs:7:10 | LL | mut_.call((0, )); - | ^^^^ method not found in `[closure@$DIR/unboxed-closures-static-call-wrong-trait.rs:6:26: 6:29]` + | ^^^^ method not found in `[closure@unboxed-closures-static-call-wrong-trait.rs:6:26]` error: aborting due to previous error From 7f8fdf4a3f6e66496e510730483222727b482ca4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 13 Dec 2022 18:00:24 -0800 Subject: [PATCH 10/18] Consider lifetimes when comparing assoc types in method chain Do not say "Type changed to X here" when the only difference is caused by lifetimes. --- .../src/traits/error_reporting/suggestions.rs | 2 +- .../ui/iterators/invalid-iterator-chain.rs | 12 +++++ .../iterators/invalid-iterator-chain.stderr | 44 +++++++++++++------ 3 files changed, 44 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index b4ec1a961fc3e..2586ef763a52b 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -3334,7 +3334,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let ty_str = with_forced_trimmed_paths!(self.ty_to_string(ty)); let assoc = with_forced_trimmed_paths!(self.tcx.def_path_str(assoc)); - if ty != *prev_ty { + if self.can_eq(param_env, ty, *prev_ty).is_err() { if type_diffs.iter().any(|diff| { let Sorts(expected_found) = diff else { return false; }; self.can_eq(param_env, expected_found.found, ty).is_ok() diff --git a/src/test/ui/iterators/invalid-iterator-chain.rs b/src/test/ui/iterators/invalid-iterator-chain.rs index 87116e4924570..ebdf33303c982 100644 --- a/src/test/ui/iterators/invalid-iterator-chain.rs +++ b/src/test/ui/iterators/invalid-iterator-chain.rs @@ -1,3 +1,11 @@ +use std::collections::hash_set::Iter; +use std::collections::HashSet; + +fn iter_to_vec<'b, X>(i: Iter<'b, X>) -> Vec { + let i = i.map(|x| x.clone()); + i.collect() //~ ERROR E0277 +} + fn main() { let scores = vec![(0, 0)] .iter() @@ -38,4 +46,8 @@ fn main() { }); let f = e.filter(|_| false); let g: Vec = f.collect(); //~ ERROR E0277 + + let mut s = HashSet::new(); + s.insert(1u8); + println!("{:?}", iter_to_vec(s.iter())); } diff --git a/src/test/ui/iterators/invalid-iterator-chain.stderr b/src/test/ui/iterators/invalid-iterator-chain.stderr index 84bac7833f67b..d76a4bfb7b3ea 100644 --- a/src/test/ui/iterators/invalid-iterator-chain.stderr +++ b/src/test/ui/iterators/invalid-iterator-chain.stderr @@ -1,5 +1,23 @@ +error[E0277]: a value of type `Vec` cannot be built from an iterator over elements of type `&X` + --> $DIR/invalid-iterator-chain.rs:6:7 + | +LL | i.collect() + | ^^^^^^^ value of type `Vec` cannot be built from `std::iter::Iterator` + | + = help: the trait `FromIterator<&X>` is not implemented for `Vec` + = help: the trait `FromIterator` is implemented for `Vec` +note: the method call chain might not have had the expected associated types + --> $DIR/invalid-iterator-chain.rs:4:26 + | +LL | fn iter_to_vec<'b, X>(i: Iter<'b, X>) -> Vec { + | ^^^^^^^^^^^ `Iterator::Item` is `&X` here +LL | let i = i.map(|x| x.clone()); + | ------------------ `Iterator::Item` remains `&X` here +note: required by a bound in `collect` + --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL + error[E0277]: a value of type `i32` cannot be made by summing an iterator over elements of type `()` - --> $DIR/invalid-iterator-chain.rs:7:27 + --> $DIR/invalid-iterator-chain.rs:15:27 | LL | println!("{}", scores.sum::()); | ^^^ value of type `i32` cannot be made by summing a `std::iter::Iterator` @@ -9,7 +27,7 @@ LL | println!("{}", scores.sum::()); > note: the method call chain might not have had the expected associated types - --> $DIR/invalid-iterator-chain.rs:4:10 + --> $DIR/invalid-iterator-chain.rs:12:10 | LL | let scores = vec![(0, 0)] | ------------ this expression has type `Vec<({integer}, {integer})>` @@ -24,7 +42,7 @@ note: required by a bound in `std::iter::Iterator::sum` --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL error[E0277]: a value of type `i32` cannot be made by summing an iterator over elements of type `()` - --> $DIR/invalid-iterator-chain.rs:18:14 + --> $DIR/invalid-iterator-chain.rs:26:14 | LL | .sum::(), | ^^^ value of type `i32` cannot be made by summing a `std::iter::Iterator` @@ -34,7 +52,7 @@ LL | .sum::(), > note: the method call chain might not have had the expected associated types - --> $DIR/invalid-iterator-chain.rs:12:14 + --> $DIR/invalid-iterator-chain.rs:20:14 | LL | vec![0, 1] | ---------- this expression has type `Vec<{integer}>` @@ -56,7 +74,7 @@ note: required by a bound in `std::iter::Iterator::sum` --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL error[E0277]: a value of type `i32` cannot be made by summing an iterator over elements of type `f64` - --> $DIR/invalid-iterator-chain.rs:28:14 + --> $DIR/invalid-iterator-chain.rs:36:14 | LL | .sum::(), | ^^^ value of type `i32` cannot be made by summing a `std::iter::Iterator` @@ -66,7 +84,7 @@ LL | .sum::(), > note: the method call chain might not have had the expected associated types - --> $DIR/invalid-iterator-chain.rs:24:14 + --> $DIR/invalid-iterator-chain.rs:32:14 | LL | vec![0, 1] | ---------- this expression has type `Vec<{integer}>` @@ -84,7 +102,7 @@ note: required by a bound in `std::iter::Iterator::sum` --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL error[E0277]: a value of type `i32` cannot be made by summing an iterator over elements of type `()` - --> $DIR/invalid-iterator-chain.rs:30:54 + --> $DIR/invalid-iterator-chain.rs:38:54 | LL | println!("{}", vec![0, 1].iter().map(|x| { x; }).sum::()); | ^^^ value of type `i32` cannot be made by summing a `std::iter::Iterator` @@ -94,7 +112,7 @@ LL | println!("{}", vec![0, 1].iter().map(|x| { x; }).sum::()); > note: the method call chain might not have had the expected associated types - --> $DIR/invalid-iterator-chain.rs:30:38 + --> $DIR/invalid-iterator-chain.rs:38:38 | LL | println!("{}", vec![0, 1].iter().map(|x| { x; }).sum::()); | ---------- ------ ^^^^^^^^^^^^^^^ `Iterator::Item` changed to `()` here @@ -105,7 +123,7 @@ note: required by a bound in `std::iter::Iterator::sum` --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL error[E0277]: a value of type `i32` cannot be made by summing an iterator over elements of type `&()` - --> $DIR/invalid-iterator-chain.rs:31:40 + --> $DIR/invalid-iterator-chain.rs:39:40 | LL | println!("{}", vec![(), ()].iter().sum::()); | ^^^ value of type `i32` cannot be made by summing a `std::iter::Iterator` @@ -115,7 +133,7 @@ LL | println!("{}", vec![(), ()].iter().sum::()); > note: the method call chain might not have had the expected associated types - --> $DIR/invalid-iterator-chain.rs:31:33 + --> $DIR/invalid-iterator-chain.rs:39:33 | LL | println!("{}", vec![(), ()].iter().sum::()); | ------------ ^^^^^^ `Iterator::Item` is `&()` here @@ -125,7 +143,7 @@ note: required by a bound in `std::iter::Iterator::sum` --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL error[E0277]: a value of type `Vec` cannot be built from an iterator over elements of type `()` - --> $DIR/invalid-iterator-chain.rs:40:25 + --> $DIR/invalid-iterator-chain.rs:48:25 | LL | let g: Vec = f.collect(); | ^^^^^^^ value of type `Vec` cannot be built from `std::iter::Iterator` @@ -133,7 +151,7 @@ LL | let g: Vec = f.collect(); = help: the trait `FromIterator<()>` is not implemented for `Vec` = help: the trait `FromIterator` is implemented for `Vec` note: the method call chain might not have had the expected associated types - --> $DIR/invalid-iterator-chain.rs:36:15 + --> $DIR/invalid-iterator-chain.rs:44:15 | LL | let a = vec![0]; | ------- this expression has type `Vec<{integer}>` @@ -153,6 +171,6 @@ LL | let f = e.filter(|_| false); note: required by a bound in `collect` --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL -error: aborting due to 6 previous errors +error: aborting due to 7 previous errors For more information about this error, try `rustc --explain E0277`. From ae60015e764fa0ba8e56c58373166a1c5d267e8e Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 12 Nov 2022 19:42:06 +0000 Subject: [PATCH 11/18] Use impl's def id when calculating type to specify UFCS --- .../src/traits/error_reporting/mod.rs | 9 +++++---- .../associated-types-unconstrained.stderr | 5 +++++ src/test/ui/suggestions/issue-104327.rs | 12 ++++++++++++ src/test/ui/suggestions/issue-104327.stderr | 17 +++++++++++++++++ src/test/ui/suggestions/issue-104328.rs | 12 ++++++++++++ src/test/ui/suggestions/issue-104328.stderr | 17 +++++++++++++++++ 6 files changed, 68 insertions(+), 4 deletions(-) create mode 100644 src/test/ui/suggestions/issue-104327.rs create mode 100644 src/test/ui/suggestions/issue-104327.stderr create mode 100644 src/test/ui/suggestions/issue-104328.rs create mode 100644 src/test/ui/suggestions/issue-104328.stderr diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index c68da3e24a190..31cc8d9ff6c65 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -2290,14 +2290,15 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let trait_impls = self.tcx.trait_impls_of(data.trait_ref.def_id); if trait_impls.blanket_impls().is_empty() - && let Some((impl_ty, _)) = trait_impls.non_blanket_impls().iter().next() - && let Some(impl_def_id) = impl_ty.def() { - let message = if trait_impls.non_blanket_impls().len() == 1 { + && let Some(impl_def_id) = trait_impls.non_blanket_impls().values().flatten().next() + { + let non_blanket_impl_count = trait_impls.non_blanket_impls().values().flatten().count(); + let message = if non_blanket_impl_count == 1 { "use the fully-qualified path to the only available implementation".to_string() } else { format!( "use a fully-qualified path to a specific available implementation ({} found)", - trait_impls.non_blanket_impls().len() + non_blanket_impl_count ) }; let mut suggestions = vec![( diff --git a/src/test/ui/associated-types/associated-types-unconstrained.stderr b/src/test/ui/associated-types/associated-types-unconstrained.stderr index e51a8f3bd1a3a..ef9b7cae01bbd 100644 --- a/src/test/ui/associated-types/associated-types-unconstrained.stderr +++ b/src/test/ui/associated-types/associated-types-unconstrained.stderr @@ -6,6 +6,11 @@ LL | fn bar() -> isize; ... LL | let x: isize = Foo::bar(); | ^^^^^^^^ cannot call associated function of trait + | +help: use the fully-qualified path to the only available implementation + | +LL | let x: isize = ::bar(); + | +++++++++ + error: aborting due to previous error diff --git a/src/test/ui/suggestions/issue-104327.rs b/src/test/ui/suggestions/issue-104327.rs new file mode 100644 index 0000000000000..dd621ae71008f --- /dev/null +++ b/src/test/ui/suggestions/issue-104327.rs @@ -0,0 +1,12 @@ +trait Bar {} + +trait Foo { + fn f() {} +} + +impl Foo for dyn Bar {} + +fn main() { + Foo::f(); + //~^ ERROR cannot call associated function on trait without specifying the corresponding `impl` type +} diff --git a/src/test/ui/suggestions/issue-104327.stderr b/src/test/ui/suggestions/issue-104327.stderr new file mode 100644 index 0000000000000..acec3a55d526e --- /dev/null +++ b/src/test/ui/suggestions/issue-104327.stderr @@ -0,0 +1,17 @@ +error[E0790]: cannot call associated function on trait without specifying the corresponding `impl` type + --> $DIR/issue-104327.rs:10:5 + | +LL | fn f() {} + | --------- `Foo::f` defined here +... +LL | Foo::f(); + | ^^^^^^ cannot call associated function of trait + | +help: use the fully-qualified path to the only available implementation + | +LL | <(dyn Bar + 'static) as Foo>::f(); + | +++++++++++++++++++++++ + + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0790`. diff --git a/src/test/ui/suggestions/issue-104328.rs b/src/test/ui/suggestions/issue-104328.rs new file mode 100644 index 0000000000000..c3707baf79fc3 --- /dev/null +++ b/src/test/ui/suggestions/issue-104328.rs @@ -0,0 +1,12 @@ +#![feature(object_safe_for_dispatch)] + +trait Foo { + fn f() {} +} + +impl Foo for dyn Sized {} + +fn main() { + Foo::f(); + //~^ ERROR cannot call associated function on trait without specifying the corresponding `impl` type +} diff --git a/src/test/ui/suggestions/issue-104328.stderr b/src/test/ui/suggestions/issue-104328.stderr new file mode 100644 index 0000000000000..b31b84781bacb --- /dev/null +++ b/src/test/ui/suggestions/issue-104328.stderr @@ -0,0 +1,17 @@ +error[E0790]: cannot call associated function on trait without specifying the corresponding `impl` type + --> $DIR/issue-104328.rs:10:5 + | +LL | fn f() {} + | --------- `Foo::f` defined here +... +LL | Foo::f(); + | ^^^^^^ cannot call associated function of trait + | +help: use the fully-qualified path to the only available implementation + | +LL | <(dyn Sized + 'static) as Foo>::f(); + | +++++++++++++++++++++++++ + + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0790`. From 1225a6538999f32a10157c6965e86816d3820fe8 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 12 Nov 2022 20:03:20 +0000 Subject: [PATCH 12/18] drive-by: Fix path spans --- .../src/traits/error_reporting/mod.rs | 2 +- src/test/ui/error-codes/E0790.stderr | 8 ++++---- src/test/ui/traits/static-method-generic-inference.stderr | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index 31cc8d9ff6c65..8ee31e4b498af 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -2302,7 +2302,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ) }; let mut suggestions = vec![( - trait_path_segment.ident.span.shrink_to_lo(), + path.span.shrink_to_lo(), format!("<{} as ", self.tcx.type_of(impl_def_id)) )]; if let Some(generic_arg) = trait_path_segment.args { diff --git a/src/test/ui/error-codes/E0790.stderr b/src/test/ui/error-codes/E0790.stderr index f68c0e7d220f3..fc025a3fca2bf 100644 --- a/src/test/ui/error-codes/E0790.stderr +++ b/src/test/ui/error-codes/E0790.stderr @@ -37,8 +37,8 @@ LL | inner::MyTrait::my_fn(); | help: use the fully-qualified path to the only available implementation | -LL | inner::::my_fn(); - | ++++++++++++ + +LL | ::my_fn(); + | ++++++++++++ + error[E0790]: cannot refer to the associated constant on trait without specifying the corresponding `impl` type --> $DIR/E0790.rs:30:13 @@ -51,8 +51,8 @@ LL | let _ = inner::MyTrait::MY_ASSOC_CONST; | help: use the fully-qualified path to the only available implementation | -LL | let _ = inner::::MY_ASSOC_CONST; - | ++++++++++++ + +LL | let _ = ::MY_ASSOC_CONST; + | ++++++++++++ + error[E0790]: cannot call associated function on trait without specifying the corresponding `impl` type --> $DIR/E0790.rs:50:5 diff --git a/src/test/ui/traits/static-method-generic-inference.stderr b/src/test/ui/traits/static-method-generic-inference.stderr index 5f74d0c3b9260..575ace2374ee6 100644 --- a/src/test/ui/traits/static-method-generic-inference.stderr +++ b/src/test/ui/traits/static-method-generic-inference.stderr @@ -9,8 +9,8 @@ LL | let _f: base::Foo = base::HasNew::new(); | help: use the fully-qualified path to the only available implementation | -LL | let _f: base::Foo = base::::new(); - | +++++++ + +LL | let _f: base::Foo = ::new(); + | +++++++ + error: aborting due to previous error From 7bf36de6abeba487dbe0328685f45b8034513927 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 12 Dec 2022 21:03:00 +0000 Subject: [PATCH 13/18] Make report_projection_error more term agnostic --- .../src/traits/error_reporting/mod.rs | 44 +++++++++++-------- .../const-projection-err.gce.stderr | 24 ++++++++++ .../const-projection-err.rs | 18 ++++++++ .../const-projection-err.stock.stderr | 17 +++++++ src/test/ui/issues/issue-105330.stderr | 8 +++- 5 files changed, 91 insertions(+), 20 deletions(-) create mode 100644 src/test/ui/associated-type-bounds/const-projection-err.gce.stderr create mode 100644 src/test/ui/associated-type-bounds/const-projection-err.rs create mode 100644 src/test/ui/associated-type-bounds/const-projection-err.stock.stderr diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index c68da3e24a190..fa94f16dc299a 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -1636,17 +1636,30 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { infer::LateBoundRegionConversionTime::HigherRankedType, bound_predicate.rebind(data), ); - let normalized_ty = ocx.normalize( - &obligation.cause, - obligation.param_env, - self.tcx.mk_projection(data.projection_ty.def_id, data.projection_ty.substs), - ); + let unnormalized_term = match data.term.unpack() { + ty::TermKind::Ty(_) => self + .tcx + .mk_projection(data.projection_ty.def_id, data.projection_ty.substs) + .into(), + ty::TermKind::Const(ct) => self + .tcx + .mk_const( + ty::UnevaluatedConst { + def: ty::WithOptConstParam::unknown(data.projection_ty.def_id), + substs: data.projection_ty.substs, + }, + ct.ty(), + ) + .into(), + }; + let normalized_term = + ocx.normalize(&obligation.cause, obligation.param_env, unnormalized_term); debug!(?obligation.cause, ?obligation.param_env); - debug!(?normalized_ty, data.ty = ?data.term); + debug!(?normalized_term, data.ty = ?data.term); - let is_normalized_ty_expected = !matches!( + let is_normalized_term_expected = !matches!( obligation.cause.code().peel_derives(), ObligationCauseCode::ItemObligation(_) | ObligationCauseCode::BindingObligation(_, _) @@ -1655,7 +1668,6 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { | ObligationCauseCode::ObjectCastObligation(..) | ObligationCauseCode::OpaqueType ); - let expected_ty = data.term.ty().unwrap_or_else(|| self.tcx.ty_error()); // constrain inference variables a bit more to nested obligations from normalize so // we can have more helpful errors. @@ -1664,11 +1676,11 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { if let Err(new_err) = ocx.eq_exp( &obligation.cause, obligation.param_env, - is_normalized_ty_expected, - normalized_ty, - expected_ty, + is_normalized_term_expected, + normalized_term, + data.term, ) { - (Some((data, is_normalized_ty_expected, normalized_ty, expected_ty)), new_err) + (Some((data, is_normalized_term_expected, normalized_term, data.term)), new_err) } else { (None, error.err) } @@ -1677,12 +1689,8 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { }; let msg = values - .and_then(|(predicate, _, normalized_ty, expected_ty)| { - self.maybe_detailed_projection_msg( - predicate, - normalized_ty.into(), - expected_ty.into(), - ) + .and_then(|(predicate, _, normalized_term, expected_term)| { + self.maybe_detailed_projection_msg(predicate, normalized_term, expected_term) }) .unwrap_or_else(|| format!("type mismatch resolving `{}`", predicate)); let mut diag = struct_span_err!(self.tcx.sess, obligation.cause.span, E0271, "{msg}"); diff --git a/src/test/ui/associated-type-bounds/const-projection-err.gce.stderr b/src/test/ui/associated-type-bounds/const-projection-err.gce.stderr new file mode 100644 index 0000000000000..0f1ec9ad0522f --- /dev/null +++ b/src/test/ui/associated-type-bounds/const-projection-err.gce.stderr @@ -0,0 +1,24 @@ +warning: the feature `generic_const_exprs` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/const-projection-err.rs:4:26 + | +LL | #![cfg_attr(gce, feature(generic_const_exprs))] + | ^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #76560 for more information + = note: `#[warn(incomplete_features)]` on by default + +error[E0271]: type mismatch resolving `::A == 1` + --> $DIR/const-projection-err.rs:14:11 + | +LL | foo::(); + | ^ expected `0`, found `1` + | +note: required by a bound in `foo` + --> $DIR/const-projection-err.rs:11:28 + | +LL | fn foo>() {} + | ^^^^^ required by this bound in `foo` + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0271`. diff --git a/src/test/ui/associated-type-bounds/const-projection-err.rs b/src/test/ui/associated-type-bounds/const-projection-err.rs new file mode 100644 index 0000000000000..bead85630016b --- /dev/null +++ b/src/test/ui/associated-type-bounds/const-projection-err.rs @@ -0,0 +1,18 @@ +// revisions: stock gce + +#![feature(associated_const_equality)] +#![cfg_attr(gce, feature(generic_const_exprs))] +//[gce]~^ WARN the feature `generic_const_exprs` is incomplete + +trait TraitWAssocConst { + const A: usize; +} + +fn foo>() {} + +fn bar>() { + foo::(); + //~^ ERROR type mismatch resolving `::A == 1` +} + +fn main() {} diff --git a/src/test/ui/associated-type-bounds/const-projection-err.stock.stderr b/src/test/ui/associated-type-bounds/const-projection-err.stock.stderr new file mode 100644 index 0000000000000..bf0824259a5a7 --- /dev/null +++ b/src/test/ui/associated-type-bounds/const-projection-err.stock.stderr @@ -0,0 +1,17 @@ +error[E0271]: type mismatch resolving `::A == 1` + --> $DIR/const-projection-err.rs:14:11 + | +LL | foo::(); + | ^ expected `1`, found `::A` + | + = note: expected constant `1` + found constant `::A` +note: required by a bound in `foo` + --> $DIR/const-projection-err.rs:11:28 + | +LL | fn foo>() {} + | ^^^^^ required by this bound in `foo` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0271`. diff --git a/src/test/ui/issues/issue-105330.stderr b/src/test/ui/issues/issue-105330.stderr index 92f2ccb6544b1..30c380152a5e6 100644 --- a/src/test/ui/issues/issue-105330.stderr +++ b/src/test/ui/issues/issue-105330.stderr @@ -55,8 +55,10 @@ error[E0271]: type mismatch resolving `::A == 32` --> $DIR/issue-105330.rs:12:11 | LL | foo::()(); - | ^^^^ types differ + | ^^^^ expected `32`, found `::A` | + = note: expected constant `32` + found constant `::A` note: required by a bound in `foo` --> $DIR/issue-105330.rs:11:28 | @@ -89,8 +91,10 @@ error[E0271]: type mismatch resolving `::A == 32` --> $DIR/issue-105330.rs:19:11 | LL | foo::(); - | ^^^^ types differ + | ^^^^ expected `32`, found `::A` | + = note: expected constant `32` + found constant `::A` note: required by a bound in `foo` --> $DIR/issue-105330.rs:11:28 | From cfa6a93a36d3cbb8e392f5d820e9e139f66c8a5a Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 12 Dec 2022 19:23:20 +0000 Subject: [PATCH 14/18] Auto traits in dyn are suggestable --- compiler/rustc_middle/src/ty/diagnostics.rs | 15 ++------------- .../display-is-suggestable.rs | 8 ++++++++ .../display-is-suggestable.stderr | 19 +++++++++++++++++++ 3 files changed, 29 insertions(+), 13 deletions(-) create mode 100644 src/test/ui/argument-suggestions/display-is-suggestable.rs create mode 100644 src/test/ui/argument-suggestions/display-is-suggestable.stderr diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs index d7880a32ea9cd..ceb17a59a63d1 100644 --- a/compiler/rustc_middle/src/ty/diagnostics.rs +++ b/compiler/rustc_middle/src/ty/diagnostics.rs @@ -3,8 +3,8 @@ use std::ops::ControlFlow; use crate::ty::{ - visit::TypeVisitable, AliasTy, Const, ConstKind, DefIdTree, ExistentialPredicate, InferConst, - InferTy, Opaque, PolyTraitPredicate, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor, + visit::TypeVisitable, AliasTy, Const, ConstKind, DefIdTree, InferConst, InferTy, Opaque, + PolyTraitPredicate, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor, }; use rustc_data_structures::fx::FxHashMap; @@ -469,17 +469,6 @@ impl<'tcx> TypeVisitor<'tcx> for IsSuggestableVisitor<'tcx> { } } - Dynamic(dty, _, _) => { - for pred in *dty { - match pred.skip_binder() { - ExistentialPredicate::Trait(_) | ExistentialPredicate::Projection(_) => { - // Okay - } - _ => return ControlFlow::Break(()), - } - } - } - Param(param) => { // FIXME: It would be nice to make this not use string manipulation, // but it's pretty hard to do this, since `ty::ParamTy` is missing diff --git a/src/test/ui/argument-suggestions/display-is-suggestable.rs b/src/test/ui/argument-suggestions/display-is-suggestable.rs new file mode 100644 index 0000000000000..d765bc4f74d33 --- /dev/null +++ b/src/test/ui/argument-suggestions/display-is-suggestable.rs @@ -0,0 +1,8 @@ +use std::fmt::Display; + +fn foo(x: &(dyn Display + Send)) {} + +fn main() { + foo(); + //~^ ERROR this function takes 1 argument but 0 arguments were supplied +} diff --git a/src/test/ui/argument-suggestions/display-is-suggestable.stderr b/src/test/ui/argument-suggestions/display-is-suggestable.stderr new file mode 100644 index 0000000000000..edd72b53eb3b6 --- /dev/null +++ b/src/test/ui/argument-suggestions/display-is-suggestable.stderr @@ -0,0 +1,19 @@ +error[E0061]: this function takes 1 argument but 0 arguments were supplied + --> $DIR/display-is-suggestable.rs:6:5 + | +LL | foo(); + | ^^^-- an argument of type `&dyn std::fmt::Display + Send` is missing + | +note: function defined here + --> $DIR/display-is-suggestable.rs:3:4 + | +LL | fn foo(x: &(dyn Display + Send)) {} + | ^^^ ------------------------ +help: provide the argument + | +LL | foo(/* &dyn std::fmt::Display + Send */); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0061`. From 34d194d41c8ffea79b8816a811b2fe626b1a6c39 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 15 Nov 2022 23:11:11 +0000 Subject: [PATCH 15/18] Highlight conflicting param-env candidates, again --- .../src/traits/error_reporting/ambiguity.rs | 79 +++++++++++++++---- .../src/traits/error_reporting/mod.rs | 48 ++++++++--- .../issue-72787.min.stderr | 26 +++++- src/test/ui/issues/issue-21974.stderr | 8 +- src/test/ui/issues/issue-24424.stderr | 6 +- src/test/ui/lifetimes/issue-34979.stderr | 12 ++- src/test/ui/traits/issue-85735.stderr | 12 ++- .../ui/type/type-check/issue-40294.stderr | 8 +- 8 files changed, 158 insertions(+), 41 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs index 752b53fbc3f9a..0c1717cff332c 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs @@ -1,52 +1,101 @@ use rustc_hir::def_id::DefId; -use rustc_infer::infer::InferCtxt; +use rustc_infer::infer::{InferCtxt, LateBoundRegionConversionTime}; +use rustc_infer::traits::util::elaborate_predicates_with_span; use rustc_infer::traits::{Obligation, ObligationCause, TraitObligation}; -use rustc_span::DUMMY_SP; +use rustc_middle::ty; +use rustc_span::{Span, DUMMY_SP}; use crate::traits::ObligationCtxt; +pub enum Ambiguity { + DefId(DefId), + ParamEnv(Span), +} + pub fn recompute_applicable_impls<'tcx>( infcx: &InferCtxt<'tcx>, obligation: &TraitObligation<'tcx>, -) -> Vec { +) -> Vec { let tcx = infcx.tcx; let param_env = obligation.param_env; - let dummy_cause = ObligationCause::dummy(); + let impl_may_apply = |impl_def_id| { let ocx = ObligationCtxt::new_in_snapshot(infcx); let placeholder_obligation = infcx.replace_bound_vars_with_placeholders(obligation.predicate); let obligation_trait_ref = - ocx.normalize(&dummy_cause, param_env, placeholder_obligation.trait_ref); + ocx.normalize(&ObligationCause::dummy(), param_env, placeholder_obligation.trait_ref); let impl_substs = infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id); let impl_trait_ref = tcx.bound_impl_trait_ref(impl_def_id).unwrap().subst(tcx, impl_substs); let impl_trait_ref = ocx.normalize(&ObligationCause::dummy(), param_env, impl_trait_ref); - if let Err(_) = ocx.eq(&dummy_cause, param_env, obligation_trait_ref, impl_trait_ref) { + if let Err(_) = + ocx.eq(&ObligationCause::dummy(), param_env, obligation_trait_ref, impl_trait_ref) + { return false; } let impl_predicates = tcx.predicates_of(impl_def_id).instantiate(tcx, impl_substs); - ocx.register_obligations( - impl_predicates - .predicates - .iter() - .map(|&predicate| Obligation::new(tcx, dummy_cause.clone(), param_env, predicate)), + ocx.register_obligations(impl_predicates.predicates.iter().map(|&predicate| { + Obligation::new(tcx, ObligationCause::dummy(), param_env, predicate) + })); + + ocx.select_where_possible().is_empty() + }; + + let param_env_candidate_may_apply = |poly_trait_predicate: ty::PolyTraitPredicate<'tcx>| { + let ocx = ObligationCtxt::new_in_snapshot(infcx); + let placeholder_obligation = + infcx.replace_bound_vars_with_placeholders(obligation.predicate); + let obligation_trait_ref = + ocx.normalize(&ObligationCause::dummy(), param_env, placeholder_obligation.trait_ref); + + let param_env_predicate = infcx.replace_bound_vars_with_fresh_vars( + DUMMY_SP, + LateBoundRegionConversionTime::HigherRankedType, + poly_trait_predicate, ); + let param_env_trait_ref = + ocx.normalize(&ObligationCause::dummy(), param_env, param_env_predicate.trait_ref); + + if let Err(_) = + ocx.eq(&ObligationCause::dummy(), param_env, obligation_trait_ref, param_env_trait_ref) + { + return false; + } ocx.select_where_possible().is_empty() }; - let mut impls = Vec::new(); + let mut ambiguities = Vec::new(); + tcx.for_each_relevant_impl( obligation.predicate.def_id(), obligation.predicate.skip_binder().trait_ref.self_ty(), |impl_def_id| { - if infcx.probe(move |_snapshot| impl_may_apply(impl_def_id)) { - impls.push(impl_def_id) + if infcx.probe(|_| impl_may_apply(impl_def_id)) { + ambiguities.push(Ambiguity::DefId(impl_def_id)) } }, ); - impls + + let predicates = + tcx.predicates_of(obligation.cause.body_id.owner.to_def_id()).instantiate_identity(tcx); + for obligation in + elaborate_predicates_with_span(tcx, std::iter::zip(predicates.predicates, predicates.spans)) + { + let kind = obligation.predicate.kind(); + if let ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred)) = kind.skip_binder() + && param_env_candidate_may_apply(kind.rebind(trait_pred)) + { + if kind.rebind(trait_pred.trait_ref) == ty::TraitRef::identity(tcx, trait_pred.def_id()) { + ambiguities.push(Ambiguity::ParamEnv(tcx.def_span(trait_pred.def_id()))) + } else { + ambiguities.push(Ambiguity::ParamEnv(obligation.cause.span)) + } + } + } + + ambiguities } diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index c68da3e24a190..dcbc2b332c25b 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -1487,7 +1487,7 @@ trait InferCtxtPrivExt<'tcx> { fn annotate_source_of_ambiguity( &self, err: &mut Diagnostic, - impls: &[DefId], + impls: &[ambiguity::Ambiguity], predicate: ty::Predicate<'tcx>, ); @@ -2180,13 +2180,22 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let mut selcx = SelectionContext::new(&self); match selcx.select_from_obligation(&obligation) { Ok(None) => { - let impls = ambiguity::recompute_applicable_impls(self.infcx, &obligation); + let ambiguities = + ambiguity::recompute_applicable_impls(self.infcx, &obligation); let has_non_region_infer = trait_ref.skip_binder().substs.types().any(|t| !t.is_ty_infer()); // It doesn't make sense to talk about applicable impls if there are more // than a handful of them. - if impls.len() > 1 && impls.len() < 10 && has_non_region_infer { - self.annotate_source_of_ambiguity(&mut err, &impls, predicate); + if ambiguities.len() > 1 && ambiguities.len() < 10 && has_non_region_infer { + if self.tainted_by_errors().is_some() && subst.is_none() { + // If `subst.is_none()`, then this is probably two param-env + // candidates or impl candidates that are equal modulo lifetimes. + // Therefore, if we've already emitted an error, just skip this + // one, since it's not particularly actionable. + err.cancel(); + return; + } + self.annotate_source_of_ambiguity(&mut err, &ambiguities, predicate); } else { if self.tainted_by_errors().is_some() { err.cancel(); @@ -2434,21 +2443,30 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { fn annotate_source_of_ambiguity( &self, err: &mut Diagnostic, - impls: &[DefId], + ambiguities: &[ambiguity::Ambiguity], predicate: ty::Predicate<'tcx>, ) { let mut spans = vec![]; let mut crates = vec![]; let mut post = vec![]; - for def_id in impls { - match self.tcx.span_of_impl(*def_id) { - Ok(span) => spans.push(span), - Err(name) => { - crates.push(name); - if let Some(header) = to_pretty_impl_header(self.tcx, *def_id) { - post.push(header); + let mut has_param_env = false; + for ambiguity in ambiguities { + match ambiguity { + ambiguity::Ambiguity::DefId(impl_def_id) => { + match self.tcx.span_of_impl(*impl_def_id) { + Ok(span) => spans.push(span), + Err(name) => { + crates.push(name); + if let Some(header) = to_pretty_impl_header(self.tcx, *impl_def_id) { + post.push(header); + } + } } } + ambiguity::Ambiguity::ParamEnv(span) => { + has_param_env = true; + spans.push(*span); + } } } let mut crate_names: Vec<_> = crates.iter().map(|n| format!("`{}`", n)).collect(); @@ -2472,7 +2490,11 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { return; } - let msg = format!("multiple `impl`s satisfying `{}` found", predicate); + let msg = format!( + "multiple `impl`s{} satisfying `{}` found", + if has_param_env { " or `where` clauses" } else { "" }, + predicate + ); let post = if post.len() > 1 || (post.len() == 1 && post[0].contains('\n')) { format!(":\n{}", post.iter().map(|p| format!("- {}", p)).collect::>().join("\n"),) } else if post.len() == 1 { diff --git a/src/test/ui/const-generics/generic_const_exprs/issue-72787.min.stderr b/src/test/ui/const-generics/generic_const_exprs/issue-72787.min.stderr index e0444042614bb..0af5493f81675 100644 --- a/src/test/ui/const-generics/generic_const_exprs/issue-72787.min.stderr +++ b/src/test/ui/const-generics/generic_const_exprs/issue-72787.min.stderr @@ -40,8 +40,17 @@ error[E0283]: type annotations needed: cannot satisfy `IsLessOrEqual: True LL | IsLessOrEqual: True, | ^^^^ | - = note: cannot satisfy `IsLessOrEqual: True` - = help: the trait `True` is implemented for `IsLessOrEqual` +note: multiple `impl`s or `where` clauses satisfying `IsLessOrEqual: True` found + --> $DIR/issue-72787.rs:10:1 + | +LL | impl True for IsLessOrEqual where + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | IsLessOrEqual: True, + | ^^^^ +... +LL | IsLessOrEqual<{ 8 - I }, { 8 - J }>: True, + | ^^^^ error[E0283]: type annotations needed: cannot satisfy `IsLessOrEqual: True` --> $DIR/issue-72787.rs:21:26 @@ -49,8 +58,17 @@ error[E0283]: type annotations needed: cannot satisfy `IsLessOrEqual: True LL | IsLessOrEqual: True, | ^^^^ | - = note: cannot satisfy `IsLessOrEqual: True` - = help: the trait `True` is implemented for `IsLessOrEqual` +note: multiple `impl`s or `where` clauses satisfying `IsLessOrEqual: True` found + --> $DIR/issue-72787.rs:10:1 + | +LL | impl True for IsLessOrEqual where + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | IsLessOrEqual: True, + | ^^^^ +... +LL | IsLessOrEqual<{ 8 - I }, { 8 - J }>: True, + | ^^^^ error: aborting due to 6 previous errors diff --git a/src/test/ui/issues/issue-21974.stderr b/src/test/ui/issues/issue-21974.stderr index 4e010a13653e7..2d60b18b1f208 100644 --- a/src/test/ui/issues/issue-21974.stderr +++ b/src/test/ui/issues/issue-21974.stderr @@ -4,7 +4,13 @@ error[E0283]: type annotations needed: cannot satisfy `&'a T: Foo` LL | where &'a T : Foo, | ^^^ | - = note: cannot satisfy `&'a T: Foo` +note: multiple `impl`s or `where` clauses satisfying `&'a T: Foo` found + --> $DIR/issue-21974.rs:11:19 + | +LL | where &'a T : Foo, + | ^^^ +LL | &'b T : Foo + | ^^^ error: aborting due to previous error diff --git a/src/test/ui/issues/issue-24424.stderr b/src/test/ui/issues/issue-24424.stderr index 8f3b2ac73199c..50d7f988e194c 100644 --- a/src/test/ui/issues/issue-24424.stderr +++ b/src/test/ui/issues/issue-24424.stderr @@ -4,7 +4,11 @@ error[E0283]: type annotations needed: cannot satisfy `T0: Trait0<'l0>` LL | impl <'l0, 'l1, T0> Trait1<'l0, T0> for bool where T0 : Trait0<'l0>, T0 : Trait0<'l1> {} | ^^^^^^^^^^^ | - = note: cannot satisfy `T0: Trait0<'l0>` +note: multiple `impl`s or `where` clauses satisfying `T0: Trait0<'l0>` found + --> $DIR/issue-24424.rs:4:57 + | +LL | impl <'l0, 'l1, T0> Trait1<'l0, T0> for bool where T0 : Trait0<'l0>, T0 : Trait0<'l1> {} + | ^^^^^^^^^^^ ^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/lifetimes/issue-34979.stderr b/src/test/ui/lifetimes/issue-34979.stderr index a332c6547b839..3d4208031cd06 100644 --- a/src/test/ui/lifetimes/issue-34979.stderr +++ b/src/test/ui/lifetimes/issue-34979.stderr @@ -4,8 +4,16 @@ error[E0283]: type annotations needed: cannot satisfy `&'a (): Foo` LL | &'a (): Foo, | ^^^ | - = note: cannot satisfy `&'a (): Foo` - = help: the trait `Foo` is implemented for `&'a T` +note: multiple `impl`s or `where` clauses satisfying `&'a (): Foo` found + --> $DIR/issue-34979.rs:2:1 + | +LL | impl<'a, T> Foo for &'a T {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | &'a (): Foo, + | ^^^ +LL | &'static (): Foo; + | ^^^ error: aborting due to previous error diff --git a/src/test/ui/traits/issue-85735.stderr b/src/test/ui/traits/issue-85735.stderr index 930708f9ad80c..9e80497ca6e92 100644 --- a/src/test/ui/traits/issue-85735.stderr +++ b/src/test/ui/traits/issue-85735.stderr @@ -4,10 +4,14 @@ error[E0283]: type annotations needed: cannot satisfy `T: FnMut<(&'a (),)>` LL | T: FnMut(&'a ()), | ^^^^^^^^^^^^^ | - = note: cannot satisfy `T: FnMut<(&'a (),)>` - = help: the following types implement trait `FnMut`: - &F - &mut F +note: multiple `impl`s or `where` clauses satisfying `T: FnMut<(&'a (),)>` found + --> $DIR/issue-85735.rs:7:8 + | +LL | T: FnMut(&'a ()), + | ^^^^^^^^^^^^^ +LL | +LL | T: FnMut(&'b ()), + | ^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/type/type-check/issue-40294.stderr b/src/test/ui/type/type-check/issue-40294.stderr index 75feb5698eb63..d15fd23418bb0 100644 --- a/src/test/ui/type/type-check/issue-40294.stderr +++ b/src/test/ui/type/type-check/issue-40294.stderr @@ -4,7 +4,13 @@ error[E0283]: type annotations needed: cannot satisfy `&'a T: Foo` LL | where &'a T : Foo, | ^^^ | - = note: cannot satisfy `&'a T: Foo` +note: multiple `impl`s or `where` clauses satisfying `&'a T: Foo` found + --> $DIR/issue-40294.rs:6:19 + | +LL | where &'a T : Foo, + | ^^^ +LL | &'b T : Foo + | ^^^ error: aborting due to previous error From d10f6b44e1243bf4d68ed4f0f5038049330a047e Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 5 Dec 2022 04:38:58 +0000 Subject: [PATCH 16/18] Add test --- src/test/ui/lifetimes/conflicting-bounds.rs | 11 +++++++++++ src/test/ui/lifetimes/conflicting-bounds.stderr | 14 ++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 src/test/ui/lifetimes/conflicting-bounds.rs create mode 100644 src/test/ui/lifetimes/conflicting-bounds.stderr diff --git a/src/test/ui/lifetimes/conflicting-bounds.rs b/src/test/ui/lifetimes/conflicting-bounds.rs new file mode 100644 index 0000000000000..f37f163dbb639 --- /dev/null +++ b/src/test/ui/lifetimes/conflicting-bounds.rs @@ -0,0 +1,11 @@ +//~ type annotations needed: cannot satisfy `Self: Gen<'source>` + +pub trait Gen<'source> { + type Output; + + fn gen(&self) -> T + where + Self: for<'s> Gen<'s, Output = T>; +} + +fn main() {} diff --git a/src/test/ui/lifetimes/conflicting-bounds.stderr b/src/test/ui/lifetimes/conflicting-bounds.stderr new file mode 100644 index 0000000000000..42aa393667dc2 --- /dev/null +++ b/src/test/ui/lifetimes/conflicting-bounds.stderr @@ -0,0 +1,14 @@ +error[E0283]: type annotations needed: cannot satisfy `Self: Gen<'source>` + | +note: multiple `impl`s or `where` clauses satisfying `Self: Gen<'source>` found + --> $DIR/conflicting-bounds.rs:3:1 + | +LL | pub trait Gen<'source> { + | ^^^^^^^^^^^^^^^^^^^^^^ +... +LL | Self: for<'s> Gen<'s, Output = T>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0283`. From c99f1b7413e8abccc07197838aafda6ce3788e26 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Wed, 14 Dec 2022 12:46:47 -0600 Subject: [PATCH 17/18] Run `x test tidy` sooner in mingw-check It takes less time to run than the other tests and is more likely to fail. `expand-yaml-anchors` is still run first to make sure the CI files are internally consistent. --- src/ci/docker/host-x86_64/mingw-check/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ci/docker/host-x86_64/mingw-check/Dockerfile b/src/ci/docker/host-x86_64/mingw-check/Dockerfile index 40caa7c50135d..720c5c4939482 100644 --- a/src/ci/docker/host-x86_64/mingw-check/Dockerfile +++ b/src/ci/docker/host-x86_64/mingw-check/Dockerfile @@ -40,10 +40,10 @@ COPY host-x86_64/mingw-check/validate-error-codes.sh /scripts/ ENV RUN_CHECK_WITH_PARALLEL_QUERIES 1 ENV SCRIPT python3 ../x.py --stage 2 test src/tools/expand-yaml-anchors && \ + python3 ../x.py test --stage 0 src/tools/tidy && \ python3 ../x.py check --target=i686-pc-windows-gnu --host=i686-pc-windows-gnu --all-targets && \ python3 ../x.py build --stage 0 src/tools/build-manifest && \ python3 ../x.py test --stage 0 src/tools/compiletest && \ - python3 ../x.py test --stage 2 src/tools/tidy && \ python3 ../x.py test --stage 0 core alloc std test proc_macro && \ # Build both public and internal documentation. RUSTDOCFLAGS="--document-private-items" python3 ../x.py doc --stage 0 library/test && \ From fc6d59dda201c1bf9b0db51d3310ed93d62a806c Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Wed, 14 Dec 2022 13:48:44 -0600 Subject: [PATCH 18/18] Make `RUN_CHECK_WITH_PARALLEL_QUERIES` the last thing to run This takes a long time and rarely fails. It also interferes with `retry make prepare`, the retry is unhelpful since `make prepare` turns into a no-op --- src/ci/run.sh | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/ci/run.sh b/src/ci/run.sh index 7de06ec35c36b..d0ec039a1fd12 100755 --- a/src/ci/run.sh +++ b/src/ci/run.sh @@ -157,13 +157,6 @@ trap datecheck EXIT # sccache server at the start of the build, but no need to worry if this fails. SCCACHE_IDLE_TIMEOUT=10800 sccache --start-server || true -if [ "$RUN_CHECK_WITH_PARALLEL_QUERIES" != "" ]; then - $SRC/configure --set rust.parallel-compiler - CARGO_INCREMENTAL=0 $PYTHON ../x.py check - rm -f config.toml - rm -rf build -fi - $SRC/configure $RUST_CONFIGURE_ARGS retry make prepare @@ -193,4 +186,11 @@ else do_make "$RUST_CHECK_TARGET" fi +if [ "$RUN_CHECK_WITH_PARALLEL_QUERIES" != "" ]; then + rm -f config.toml + rm -rf build + $SRC/configure --set rust.parallel-compiler + CARGO_INCREMENTAL=0 $PYTHON ../x.py check +fi + sccache --show-stats || true