diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:11:28 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:11:28 +0000 |
commit | 94a0819fe3a0d679c3042a77bfe6a2afc505daea (patch) | |
tree | 2b827afe6a05f3538db3f7803a88c4587fe85648 /compiler/rustc_trait_selection | |
parent | Adding upstream version 1.64.0+dfsg1. (diff) | |
download | rustc-94a0819fe3a0d679c3042a77bfe6a2afc505daea.tar.xz rustc-94a0819fe3a0d679c3042a77bfe6a2afc505daea.zip |
Adding upstream version 1.66.0+dfsg1.upstream/1.66.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'compiler/rustc_trait_selection')
34 files changed, 2733 insertions, 1903 deletions
diff --git a/compiler/rustc_trait_selection/Cargo.toml b/compiler/rustc_trait_selection/Cargo.toml index 566f236f2..67613e1a4 100644 --- a/compiler/rustc_trait_selection/Cargo.toml +++ b/compiler/rustc_trait_selection/Cargo.toml @@ -4,7 +4,6 @@ version = "0.0.0" edition = "2021" [lib] -doctest = false [dependencies] rustc_parse_format = { path = "../rustc_parse_format" } diff --git a/compiler/rustc_trait_selection/src/autoderef.rs b/compiler/rustc_trait_selection/src/autoderef.rs index 8b7e8984a..61cfeec4b 100644 --- a/compiler/rustc_trait_selection/src/autoderef.rs +++ b/compiler/rustc_trait_selection/src/autoderef.rs @@ -1,6 +1,6 @@ +use crate::errors::AutoDerefReachedRecursionLimit; use crate::traits::query::evaluate_obligation::InferCtxtExt; use crate::traits::{self, TraitEngine}; -use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_infer::infer::InferCtxt; use rustc_middle::ty::{self, TraitRef, Ty, TyCtxt}; @@ -25,7 +25,7 @@ struct AutoderefSnapshot<'tcx> { pub struct Autoderef<'a, 'tcx> { // Meta infos: - infcx: &'a InferCtxt<'a, 'tcx>, + infcx: &'a InferCtxt<'tcx>, span: Span, overloaded_span: Span, body_id: hir::HirId, @@ -94,7 +94,7 @@ impl<'a, 'tcx> Iterator for Autoderef<'a, 'tcx> { impl<'a, 'tcx> Autoderef<'a, 'tcx> { pub fn new( - infcx: &'a InferCtxt<'a, 'tcx>, + infcx: &'a InferCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, body_id: hir::HirId, span: Span, @@ -222,19 +222,10 @@ pub fn report_autoderef_recursion_limit_error<'tcx>(tcx: TyCtxt<'tcx>, span: Spa Limit(0) => Limit(2), limit => limit * 2, }; - struct_span_err!( - tcx.sess, + tcx.sess.emit_err(AutoDerefReachedRecursionLimit { span, - E0055, - "reached the recursion limit while auto-dereferencing `{:?}`", - ty - ) - .span_label(span, "deref recursion limit reached") - .help(&format!( - "consider increasing the recursion limit by adding a \ - `#![recursion_limit = \"{}\"]` attribute to your crate (`{}`)", + ty, suggested_limit, - tcx.crate_name(LOCAL_CRATE), - )) - .emit(); + crate_name: tcx.crate_name(LOCAL_CRATE), + }); } diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs new file mode 100644 index 000000000..7f8705824 --- /dev/null +++ b/compiler/rustc_trait_selection/src/errors.rs @@ -0,0 +1,102 @@ +use rustc_errors::{fluent, ErrorGuaranteed, Handler, IntoDiagnostic}; +use rustc_macros::Diagnostic; +use rustc_middle::ty::{self, PolyTraitRef, Ty}; +use rustc_session::Limit; +use rustc_span::{Span, Symbol}; + +#[derive(Diagnostic)] +#[diag(trait_selection_dump_vtable_entries)] +pub struct DumpVTableEntries<'a> { + #[primary_span] + pub span: Span, + pub trait_ref: PolyTraitRef<'a>, + pub entries: String, +} + +#[derive(Diagnostic)] +#[diag(trait_selection_unable_to_construct_constant_value)] +pub struct UnableToConstructConstantValue<'a> { + #[primary_span] + pub span: Span, + pub unevaluated: ty::UnevaluatedConst<'a>, +} + +#[derive(Diagnostic)] +#[help] +#[diag(trait_selection_auto_deref_reached_recursion_limit, code = "E0055")] +pub struct AutoDerefReachedRecursionLimit<'a> { + #[primary_span] + #[label] + pub span: Span, + pub ty: Ty<'a>, + pub suggested_limit: Limit, + pub crate_name: Symbol, +} + +#[derive(Diagnostic)] +#[diag(trait_selection_empty_on_clause_in_rustc_on_unimplemented, code = "E0232")] +pub struct EmptyOnClauseInOnUnimplemented { + #[primary_span] + #[label] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(trait_selection_invalid_on_clause_in_rustc_on_unimplemented, code = "E0232")] +pub struct InvalidOnClauseInOnUnimplemented { + #[primary_span] + #[label] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(trait_selection_no_value_in_rustc_on_unimplemented, code = "E0232")] +#[note] +pub struct NoValueInOnUnimplemented { + #[primary_span] + #[label] + pub span: Span, +} + +pub struct NegativePositiveConflict<'a> { + pub impl_span: Span, + pub trait_desc: &'a str, + pub self_desc: &'a Option<String>, + pub negative_impl_span: Result<Span, Symbol>, + pub positive_impl_span: Result<Span, Symbol>, +} + +impl IntoDiagnostic<'_> for NegativePositiveConflict<'_> { + fn into_diagnostic( + self, + handler: &Handler, + ) -> rustc_errors::DiagnosticBuilder<'_, ErrorGuaranteed> { + let mut diag = handler.struct_err(fluent::trait_selection_negative_positive_conflict); + diag.set_arg("trait_desc", self.trait_desc); + diag.set_arg( + "self_desc", + self.self_desc.clone().map_or_else(|| String::from("none"), |ty| ty), + ); + diag.set_span(self.impl_span); + diag.code(rustc_errors::error_code!(E0751)); + match self.negative_impl_span { + Ok(span) => { + diag.span_label(span, fluent::negative_implementation_here); + } + Err(cname) => { + diag.note(fluent::negative_implementation_in_crate); + diag.set_arg("negative_impl_cname", cname.to_string()); + } + } + match self.positive_impl_span { + Ok(span) => { + diag.span_label(span, fluent::positive_implementation_here); + } + Err(cname) => { + diag.note(fluent::positive_implementation_in_crate); + diag.set_arg("positive_impl_cname", cname.to_string()); + } + } + diag + } +} diff --git a/compiler/rustc_trait_selection/src/infer.rs b/compiler/rustc_trait_selection/src/infer.rs index 9d30374f8..a335f8e06 100644 --- a/compiler/rustc_trait_selection/src/infer.rs +++ b/compiler/rustc_trait_selection/src/infer.rs @@ -24,6 +24,13 @@ pub trait InferCtxtExt<'tcx> { span: Span, ) -> bool; + fn type_is_sized_modulo_regions( + &self, + param_env: ty::ParamEnv<'tcx>, + ty: Ty<'tcx>, + span: Span, + ) -> bool; + fn partially_normalize_associated_types_in<T>( &self, cause: ObligationCause<'tcx>, @@ -52,7 +59,7 @@ pub trait InferCtxtExt<'tcx> { param_env: ty::ParamEnv<'tcx>, ) -> traits::EvaluationResult; } -impl<'cx, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'cx, 'tcx> { +impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { fn type_is_copy_modulo_regions( &self, param_env: ty::ParamEnv<'tcx>, @@ -62,7 +69,7 @@ impl<'cx, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'cx, 'tcx> { let ty = self.resolve_vars_if_possible(ty); if !(param_env, ty).needs_infer() { - return ty.is_copy_modulo_regions(self.tcx.at(span), param_env); + return ty.is_copy_modulo_regions(self.tcx, param_env); } let copy_def_id = self.tcx.require_lang_item(LangItem::Copy, None); @@ -74,6 +81,16 @@ impl<'cx, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'cx, 'tcx> { traits::type_known_to_meet_bound_modulo_regions(self, param_env, ty, copy_def_id, span) } + fn type_is_sized_modulo_regions( + &self, + param_env: ty::ParamEnv<'tcx>, + ty: Ty<'tcx>, + span: Span, + ) -> bool { + let lang_item = self.tcx.require_lang_item(LangItem::Sized, None); + traits::type_known_to_meet_bound_modulo_regions(self, param_env, ty, lang_item, span) + } + /// Normalizes associated types in `value`, potentially returning /// new obligations that must further be processed. fn partially_normalize_associated_types_in<T>( @@ -125,7 +142,7 @@ pub trait InferCtxtBuilderExt<'tcx> { fn enter_canonical_trait_query<K, R>( &mut self, canonical_key: &Canonical<'tcx, K>, - operation: impl FnOnce(&InferCtxt<'_, 'tcx>, &mut dyn TraitEngine<'tcx>, K) -> Fallible<R>, + operation: impl FnOnce(&InferCtxt<'tcx>, &mut dyn TraitEngine<'tcx>, K) -> Fallible<R>, ) -> Fallible<CanonicalizedQueryResponse<'tcx, R>> where K: TypeFoldable<'tcx>, @@ -153,25 +170,17 @@ impl<'tcx> InferCtxtBuilderExt<'tcx> for InferCtxtBuilder<'tcx> { fn enter_canonical_trait_query<K, R>( &mut self, canonical_key: &Canonical<'tcx, K>, - operation: impl FnOnce(&InferCtxt<'_, 'tcx>, &mut dyn TraitEngine<'tcx>, K) -> Fallible<R>, + operation: impl FnOnce(&InferCtxt<'tcx>, &mut dyn TraitEngine<'tcx>, K) -> Fallible<R>, ) -> Fallible<CanonicalizedQueryResponse<'tcx, R>> where K: TypeFoldable<'tcx>, R: Debug + TypeFoldable<'tcx>, Canonical<'tcx, QueryResponse<'tcx, R>>: ArenaAllocatable<'tcx>, { - self.enter_with_canonical( - DUMMY_SP, - canonical_key, - |ref infcx, key, canonical_inference_vars| { - let mut fulfill_cx = <dyn TraitEngine<'_>>::new(infcx.tcx); - let value = operation(infcx, &mut *fulfill_cx, key)?; - infcx.make_canonicalized_query_response( - canonical_inference_vars, - value, - &mut *fulfill_cx, - ) - }, - ) + let (ref infcx, key, canonical_inference_vars) = + self.build_with_canonical(DUMMY_SP, canonical_key); + let mut fulfill_cx = <dyn TraitEngine<'_>>::new(infcx.tcx); + let value = operation(infcx, &mut *fulfill_cx, key)?; + infcx.make_canonicalized_query_response(canonical_inference_vars, value, &mut *fulfill_cx) } } diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs index 282ee632c..5d52aa075 100644 --- a/compiler/rustc_trait_selection/src/lib.rs +++ b/compiler/rustc_trait_selection/src/lib.rs @@ -16,11 +16,10 @@ #![feature(control_flow_enum)] #![feature(drain_filter)] #![feature(hash_drain_filter)] -#![feature(label_break_value)] #![feature(let_chains)] -#![feature(let_else)] #![feature(if_let_guard)] #![feature(never_type)] +#![feature(type_alias_impl_trait)] #![recursion_limit = "512"] // For rustdoc #[macro_use] @@ -36,5 +35,6 @@ extern crate rustc_middle; extern crate smallvec; pub mod autoderef; +pub mod errors; pub mod infer; pub mod traits; diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs index 294c81d0b..ed34ab95a 100644 --- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs +++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs @@ -3,13 +3,14 @@ use super::*; +use crate::errors::UnableToConstructConstantValue; use crate::infer::region_constraints::{Constraint, RegionConstraintData}; use crate::infer::InferCtxt; use crate::traits::project::ProjectAndUnifyResult; use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::ty::fold::{TypeFolder, TypeSuperFoldable}; use rustc_middle::ty::visit::TypeVisitable; -use rustc_middle::ty::{Region, RegionVid, Term}; +use rustc_middle::ty::{PolyTraitRef, Region, RegionVid}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; @@ -65,13 +66,13 @@ impl<'tcx> AutoTraitFinder<'tcx> { /// struct Foo<T> { data: Box<T> } /// ``` /// - /// then this might return that Foo<T>: Send if T: Send (encoded in the AutoTraitResult type). - /// The analysis attempts to account for custom impls as well as other complex cases. This - /// result is intended for use by rustdoc and other such consumers. + /// then this might return that `Foo<T>: Send` if `T: Send` (encoded in the AutoTraitResult + /// type). The analysis attempts to account for custom impls as well as other complex cases. + /// This result is intended for use by rustdoc and other such consumers. /// /// (Note that due to the coinductive nature of Send, the full and correct result is actually /// quite simple to generate. That is, when a type has no custom impl, it is Send iff its field - /// types are all Send. So, in our example, we might have that Foo<T>: Send if Box<T>: Send. + /// types are all Send. So, in our example, we might have that `Foo<T>: Send` if `Box<T>: Send`. /// But this is often not the best way to present to the user.) /// /// Warning: The API should be considered highly unstable, and it may be refactored or removed @@ -89,145 +90,105 @@ impl<'tcx> AutoTraitFinder<'tcx> { let trait_pred = ty::Binder::dummy(trait_ref); - let bail_out = tcx.infer_ctxt().enter(|infcx| { - let mut selcx = SelectionContext::new(&infcx); - let result = selcx.select(&Obligation::new( - ObligationCause::dummy(), - orig_env, - trait_pred.to_poly_trait_predicate(), - )); - - match result { - Ok(Some(ImplSource::UserDefined(_))) => { - debug!( - "find_auto_trait_generics({:?}): \ - manual impl found, bailing out", - trait_ref - ); - return true; - } - _ => {} - } - - let result = selcx.select(&Obligation::new( - ObligationCause::dummy(), - orig_env, - trait_pred.to_poly_trait_predicate_negative_polarity(), - )); - - match result { - Ok(Some(ImplSource::UserDefined(_))) => { - debug!( - "find_auto_trait_generics({:?}): \ - manual impl found, bailing out", - trait_ref - ); - true - } - _ => false, + let infcx = tcx.infer_ctxt().build(); + let mut selcx = SelectionContext::new(&infcx); + for f in [ + PolyTraitRef::to_poly_trait_predicate, + PolyTraitRef::to_poly_trait_predicate_negative_polarity, + ] { + let result = + selcx.select(&Obligation::new(ObligationCause::dummy(), orig_env, f(&trait_pred))); + if let Ok(Some(ImplSource::UserDefined(_))) = result { + debug!( + "find_auto_trait_generics({:?}): \ + manual impl found, bailing out", + trait_ref + ); + // If an explicit impl exists, it always takes priority over an auto impl + return AutoTraitResult::ExplicitImpl; } - }); - - // If an explicit impl exists, it always takes priority over an auto impl - if bail_out { - return AutoTraitResult::ExplicitImpl; } - tcx.infer_ctxt().enter(|infcx| { - let mut fresh_preds = FxHashSet::default(); + let infcx = tcx.infer_ctxt().build(); + let mut fresh_preds = FxHashSet::default(); + + // Due to the way projections are handled by SelectionContext, we need to run + // evaluate_predicates twice: once on the original param env, and once on the result of + // the first evaluate_predicates call. + // + // The problem is this: most of rustc, including SelectionContext and traits::project, + // are designed to work with a concrete usage of a type (e.g., Vec<u8> + // fn<T>() { Vec<T> }. This information will generally never change - given + // the 'T' in fn<T>() { ... }, we'll never know anything else about 'T'. + // If we're unable to prove that 'T' implements a particular trait, we're done - + // there's nothing left to do but error out. + // + // However, synthesizing an auto trait impl works differently. Here, we start out with + // a set of initial conditions - the ParamEnv of the struct/enum/union we're dealing + // with - and progressively discover the conditions we need to fulfill for it to + // implement a certain auto trait. This ends up breaking two assumptions made by trait + // selection and projection: + // + // * We can always cache the result of a particular trait selection for the lifetime of + // an InfCtxt + // * Given a projection bound such as '<T as SomeTrait>::SomeItem = K', if 'T: + // SomeTrait' doesn't hold, then we don't need to care about the 'SomeItem = K' + // + // We fix the first assumption by manually clearing out all of the InferCtxt's caches + // in between calls to SelectionContext.select. This allows us to keep all of the + // intermediate types we create bound to the 'tcx lifetime, rather than needing to lift + // them between calls. + // + // We fix the second assumption by reprocessing the result of our first call to + // evaluate_predicates. Using the example of '<T as SomeTrait>::SomeItem = K', our first + // pass will pick up 'T: SomeTrait', but not 'SomeItem = K'. On our second pass, + // traits::project will see that 'T: SomeTrait' is in our ParamEnv, allowing + // SelectionContext to return it back to us. + + let Some((new_env, user_env)) = self.evaluate_predicates( + &infcx, + trait_did, + ty, + orig_env, + orig_env, + &mut fresh_preds, + false, + ) else { + return AutoTraitResult::NegativeImpl; + }; + + let (full_env, full_user_env) = self + .evaluate_predicates(&infcx, trait_did, ty, new_env, user_env, &mut fresh_preds, true) + .unwrap_or_else(|| { + panic!("Failed to fully process: {:?} {:?} {:?}", ty, trait_did, orig_env) + }); - // Due to the way projections are handled by SelectionContext, we need to run - // evaluate_predicates twice: once on the original param env, and once on the result of - // the first evaluate_predicates call. - // - // The problem is this: most of rustc, including SelectionContext and traits::project, - // are designed to work with a concrete usage of a type (e.g., Vec<u8> - // fn<T>() { Vec<T> }. This information will generally never change - given - // the 'T' in fn<T>() { ... }, we'll never know anything else about 'T'. - // If we're unable to prove that 'T' implements a particular trait, we're done - - // there's nothing left to do but error out. - // - // However, synthesizing an auto trait impl works differently. Here, we start out with - // a set of initial conditions - the ParamEnv of the struct/enum/union we're dealing - // with - and progressively discover the conditions we need to fulfill for it to - // implement a certain auto trait. This ends up breaking two assumptions made by trait - // selection and projection: - // - // * We can always cache the result of a particular trait selection for the lifetime of - // an InfCtxt - // * Given a projection bound such as '<T as SomeTrait>::SomeItem = K', if 'T: - // SomeTrait' doesn't hold, then we don't need to care about the 'SomeItem = K' - // - // We fix the first assumption by manually clearing out all of the InferCtxt's caches - // in between calls to SelectionContext.select. This allows us to keep all of the - // intermediate types we create bound to the 'tcx lifetime, rather than needing to lift - // them between calls. - // - // We fix the second assumption by reprocessing the result of our first call to - // evaluate_predicates. Using the example of '<T as SomeTrait>::SomeItem = K', our first - // pass will pick up 'T: SomeTrait', but not 'SomeItem = K'. On our second pass, - // traits::project will see that 'T: SomeTrait' is in our ParamEnv, allowing - // SelectionContext to return it back to us. - - let Some((new_env, user_env)) = self.evaluate_predicates( - &infcx, - trait_did, - ty, - orig_env, - orig_env, - &mut fresh_preds, - false, - ) else { - return AutoTraitResult::NegativeImpl; - }; - - let (full_env, full_user_env) = self - .evaluate_predicates( - &infcx, - trait_did, - ty, - new_env, - user_env, - &mut fresh_preds, - true, - ) - .unwrap_or_else(|| { - panic!("Failed to fully process: {:?} {:?} {:?}", ty, trait_did, orig_env) - }); - - debug!( - "find_auto_trait_generics({:?}): fulfilling \ - with {:?}", - trait_ref, full_env - ); - infcx.clear_caches(); - - // At this point, we already have all of the bounds we need. FulfillmentContext is used - // to store all of the necessary region/lifetime bounds in the InferContext, as well as - // an additional sanity check. - let mut fulfill = <dyn TraitEngine<'tcx>>::new(tcx); - fulfill.register_bound(&infcx, full_env, ty, trait_did, ObligationCause::dummy()); - let errors = fulfill.select_all_or_error(&infcx); - - if !errors.is_empty() { - panic!("Unable to fulfill trait {:?} for '{:?}': {:?}", trait_did, ty, errors); - } + debug!( + "find_auto_trait_generics({:?}): fulfilling \ + with {:?}", + trait_ref, full_env + ); + infcx.clear_caches(); + + // At this point, we already have all of the bounds we need. FulfillmentContext is used + // to store all of the necessary region/lifetime bounds in the InferContext, as well as + // an additional sanity check. + let errors = + super::fully_solve_bound(&infcx, ObligationCause::dummy(), full_env, ty, trait_did); + if !errors.is_empty() { + panic!("Unable to fulfill trait {:?} for '{:?}': {:?}", trait_did, ty, errors); + } - infcx.process_registered_region_obligations(&Default::default(), full_env); + infcx.process_registered_region_obligations(&Default::default(), full_env); - let region_data = infcx - .inner - .borrow_mut() - .unwrap_region_constraints() - .region_constraint_data() - .clone(); + let region_data = + infcx.inner.borrow_mut().unwrap_region_constraints().region_constraint_data().clone(); - let vid_to_region = self.map_vid_to_region(®ion_data); + let vid_to_region = self.map_vid_to_region(®ion_data); - let info = AutoTraitInfo { full_user_env, region_data, vid_to_region }; + let info = AutoTraitInfo { full_user_env, region_data, vid_to_region }; - AutoTraitResult::PositiveImpl(auto_trait_callback(info)) - }) + AutoTraitResult::PositiveImpl(auto_trait_callback(info)) } } @@ -273,7 +234,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { /// user. fn evaluate_predicates( &self, - infcx: &InferCtxt<'_, 'tcx>, + infcx: &InferCtxt<'tcx>, trait_did: DefId, ty: Ty<'tcx>, param_env: ty::ParamEnv<'tcx>, @@ -343,7 +304,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { } } - let obligations = impl_source.clone().nested_obligations().into_iter(); + let obligations = impl_source.borrow_nested_obligations().iter().cloned(); if !self.evaluate_nested_obligations( ty, @@ -613,7 +574,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { } fn is_self_referential_projection(&self, p: ty::PolyProjectionPredicate<'_>) -> bool { - if let Term::Ty(ty) = p.term().skip_binder() { + if let Some(ty) = p.term().skip_binder().ty() { matches!(ty.kind(), ty::Projection(proj) if proj == &p.skip_binder().projection_ty) } else { false @@ -832,8 +793,11 @@ impl<'tcx> AutoTraitFinder<'tcx> { Ok(None) => { let tcx = self.tcx; let def_id = unevaluated.def.did; - let reported = tcx.sess.struct_span_err(tcx.def_span(def_id), &format!("unable to construct a constant value for the unevaluated constant {:?}", unevaluated)).emit(); - + let reported = + tcx.sess.emit_err(UnableToConstructConstantValue { + span: tcx.def_span(def_id), + unevaluated: unevaluated, + }); Err(ErrorHandled::Reported(reported)) } Err(err) => Err(err), @@ -875,7 +839,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { pub fn clean_pred( &self, - infcx: &InferCtxt<'_, 'tcx>, + infcx: &InferCtxt<'tcx>, p: ty::Predicate<'tcx>, ) -> ty::Predicate<'tcx> { infcx.freshen(p) diff --git a/compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs b/compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs index 9ef7ac9a8..81e1d6449 100644 --- a/compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs @@ -28,7 +28,7 @@ impl FulfillmentContext<'_> { impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> { fn normalize_projection_type( &mut self, - infcx: &InferCtxt<'_, 'tcx>, + infcx: &InferCtxt<'tcx>, _param_env: ty::ParamEnv<'tcx>, projection_ty: ty::ProjectionTy<'tcx>, _cause: ObligationCause<'tcx>, @@ -38,7 +38,7 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> { fn register_predicate_obligation( &mut self, - infcx: &InferCtxt<'_, 'tcx>, + infcx: &InferCtxt<'tcx>, obligation: PredicateObligation<'tcx>, ) { assert!(!infcx.is_in_snapshot()); @@ -49,7 +49,7 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> { self.obligations.insert(obligation); } - fn select_all_or_error(&mut self, infcx: &InferCtxt<'_, 'tcx>) -> Vec<FulfillmentError<'tcx>> { + fn select_all_or_error(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>> { { let errors = self.select_where_possible(infcx); @@ -71,10 +71,7 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> { .collect() } - fn select_where_possible( - &mut self, - infcx: &InferCtxt<'_, 'tcx>, - ) -> Vec<FulfillmentError<'tcx>> { + fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>> { assert!(!infcx.is_in_snapshot()); let mut errors = Vec::new(); diff --git a/compiler/rustc_trait_selection/src/traits/codegen.rs b/compiler/rustc_trait_selection/src/traits/codegen.rs index c0700748c..8a62bf015 100644 --- a/compiler/rustc_trait_selection/src/traits/codegen.rs +++ b/compiler/rustc_trait_selection/src/traits/codegen.rs @@ -4,10 +4,12 @@ // general routines. use crate::infer::{DefiningAnchor, TyCtxtInferExt}; +use crate::traits::error_reporting::TypeErrCtxtExt; use crate::traits::{ ImplSource, Obligation, ObligationCause, SelectionContext, TraitEngine, TraitEngineExt, Unimplemented, }; +use rustc_infer::traits::FulfillmentErrorCode; use rustc_middle::traits::CodegenObligationError; use rustc_middle::ty::{self, TyCtxt}; @@ -18,8 +20,7 @@ use rustc_middle::ty::{self, TyCtxt}; /// obligations *could be* resolved if we wanted to. /// /// This also expects that `trait_ref` is fully normalized. -#[instrument(level = "debug", skip(tcx))] -pub fn codegen_fulfill_obligation<'tcx>( +pub fn codegen_select_candidate<'tcx>( tcx: TyCtxt<'tcx>, (param_env, trait_ref): (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>), ) -> Result<&'tcx ImplSource<'tcx, ()>, CodegenObligationError> { @@ -28,53 +29,61 @@ pub fn codegen_fulfill_obligation<'tcx>( // Do the initial selection for the obligation. This yields the // shallow result we are looking for -- that is, what specific impl. - let mut infcx_builder = - tcx.infer_ctxt().ignoring_regions().with_opaque_type_inference(DefiningAnchor::Bubble); - infcx_builder.enter(|infcx| { - //~^ HACK `Bubble` is required for - // this test to pass: type-alias-impl-trait/assoc-projection-ice.rs - let mut selcx = SelectionContext::new(&infcx); + let infcx = tcx + .infer_ctxt() + .ignoring_regions() + .with_opaque_type_inference(DefiningAnchor::Bubble) + .build(); + //~^ HACK `Bubble` is required for + // this test to pass: type-alias-impl-trait/assoc-projection-ice.rs + let mut selcx = SelectionContext::new(&infcx); - let obligation_cause = ObligationCause::dummy(); - let obligation = - Obligation::new(obligation_cause, param_env, trait_ref.to_poly_trait_predicate()); + let obligation_cause = ObligationCause::dummy(); + let obligation = + Obligation::new(obligation_cause, param_env, trait_ref.to_poly_trait_predicate()); - let selection = match selcx.select(&obligation) { - Ok(Some(selection)) => selection, - Ok(None) => return Err(CodegenObligationError::Ambiguity), - Err(Unimplemented) => return Err(CodegenObligationError::Unimplemented), - Err(e) => { - bug!("Encountered error `{:?}` selecting `{:?}` during codegen", e, trait_ref) - } - }; + let selection = match selcx.select(&obligation) { + Ok(Some(selection)) => selection, + Ok(None) => return Err(CodegenObligationError::Ambiguity), + Err(Unimplemented) => return Err(CodegenObligationError::Unimplemented), + Err(e) => { + bug!("Encountered error `{:?}` selecting `{:?}` during codegen", e, trait_ref) + } + }; - debug!(?selection); + debug!(?selection); - // Currently, we use a fulfillment context to completely resolve - // all nested obligations. This is because they can inform the - // inference of the impl's type parameters. - let mut fulfill_cx = <dyn TraitEngine<'tcx>>::new(tcx); - let impl_source = selection.map(|predicate| { - fulfill_cx.register_predicate_obligation(&infcx, predicate); - }); + // Currently, we use a fulfillment context to completely resolve + // all nested obligations. This is because they can inform the + // inference of the impl's type parameters. + let mut fulfill_cx = <dyn TraitEngine<'tcx>>::new(tcx); + let impl_source = selection.map(|predicate| { + fulfill_cx.register_predicate_obligation(&infcx, predicate); + }); - // In principle, we only need to do this so long as `impl_source` - // contains unbound type parameters. It could be a slight - // optimization to stop iterating early. - let errors = fulfill_cx.select_all_or_error(&infcx); - if !errors.is_empty() { - return Err(CodegenObligationError::FulfillmentError); + // In principle, we only need to do this so long as `impl_source` + // contains unbound type parameters. It could be a slight + // optimization to stop iterating early. + let errors = fulfill_cx.select_all_or_error(&infcx); + if !errors.is_empty() { + // `rustc_monomorphize::collector` assumes there are no type errors. + // Cycle errors are the only post-monomorphization errors possible; emit them now so + // `rustc_ty_utils::resolve_associated_item` doesn't return `None` post-monomorphization. + for err in errors { + if let FulfillmentErrorCode::CodeCycle(cycle) = err.code { + infcx.err_ctxt().report_overflow_error_cycle(&cycle); + } } + return Err(CodegenObligationError::FulfillmentError); + } - let impl_source = infcx.resolve_vars_if_possible(impl_source); - let impl_source = infcx.tcx.erase_regions(impl_source); + let impl_source = infcx.resolve_vars_if_possible(impl_source); + let impl_source = infcx.tcx.erase_regions(impl_source); - // Opaque types may have gotten their hidden types constrained, but we can ignore them safely - // as they will get constrained elsewhere, too. - // (ouz-a) This is required for `type-alias-impl-trait/assoc-projection-ice.rs` to pass - let _ = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types(); + // Opaque types may have gotten their hidden types constrained, but we can ignore them safely + // as they will get constrained elsewhere, too. + // (ouz-a) This is required for `type-alias-impl-trait/assoc-projection-ice.rs` to pass + let _ = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types(); - debug!("Cache miss: {trait_ref:?} => {impl_source:?}"); - Ok(&*tcx.arena.alloc(impl_source)) - }) + Ok(&*tcx.arena.alloc(impl_source)) } diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index 1c8cdf4ca..8aab75490 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -6,21 +6,22 @@ use crate::infer::outlives::env::OutlivesEnvironment; use crate::infer::{CombinedSnapshot, InferOk}; +use crate::traits::outlives_bounds::InferCtxtExt as _; use crate::traits::select::IntercrateAmbiguityCause; use crate::traits::util::impl_subject_and_oblig; use crate::traits::SkipLeakCheck; use crate::traits::{ - self, FulfillmentContext, Normalized, Obligation, ObligationCause, PredicateObligation, - PredicateObligations, SelectionContext, TraitEngineExt, + self, Normalized, Obligation, ObligationCause, ObligationCtxt, PredicateObligation, + PredicateObligations, SelectionContext, }; use rustc_data_structures::fx::FxIndexSet; use rustc_errors::Diagnostic; -use rustc_hir::def_id::{DefId, LOCAL_CRATE}; +use rustc_hir::def_id::{DefId, CRATE_DEF_ID, LOCAL_CRATE}; +use rustc_hir::CRATE_HIR_ID; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; -use rustc_infer::traits::{util, TraitEngine}; +use rustc_infer::traits::util; use rustc_middle::traits::specialization_graph::OverlapMode; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; -use rustc_middle::ty::subst::Subst; use rustc_middle::ty::visit::TypeVisitable; use rustc_middle::ty::{self, ImplSubject, Ty, TyCtxt, TypeVisitor}; use rustc_span::symbol::sym; @@ -59,23 +60,17 @@ pub fn add_placeholder_note(err: &mut Diagnostic) { ); } -/// If there are types that satisfy both impls, invokes `on_overlap` +/// If there are types that satisfy both impls, returns `Some` /// with a suitably-freshened `ImplHeader` with those types -/// substituted. Otherwise, invokes `no_overlap`. -#[instrument(skip(tcx, skip_leak_check, on_overlap, no_overlap), level = "debug")] -pub fn overlapping_impls<F1, F2, R>( +/// substituted. Otherwise, returns `None`. +#[instrument(skip(tcx, skip_leak_check), level = "debug")] +pub fn overlapping_impls( tcx: TyCtxt<'_>, impl1_def_id: DefId, impl2_def_id: DefId, skip_leak_check: SkipLeakCheck, overlap_mode: OverlapMode, - on_overlap: F1, - no_overlap: F2, -) -> R -where - F1: FnOnce(OverlapResult<'_>) -> R, - F2: FnOnce() -> R, -{ +) -> Option<OverlapResult<'_>> { // Before doing expensive operations like entering an inference context, do // a quick check via fast_reject to tell if the impl headers could possibly // unify. @@ -96,28 +91,24 @@ where if !may_overlap { // Some types involved are definitely different, so the impls couldn't possibly overlap. debug!("overlapping_impls: fast_reject early-exit"); - return no_overlap(); + return None; } - let overlaps = tcx.infer_ctxt().enter(|infcx| { - let selcx = &mut SelectionContext::intercrate(&infcx); - overlap(selcx, skip_leak_check, impl1_def_id, impl2_def_id, overlap_mode).is_some() - }); - + let infcx = tcx.infer_ctxt().build(); + let selcx = &mut SelectionContext::intercrate(&infcx); + let overlaps = + overlap(selcx, skip_leak_check, impl1_def_id, impl2_def_id, overlap_mode).is_some(); if !overlaps { - return no_overlap(); + return None; } // In the case where we detect an error, run the check again, but // this time tracking intercrate ambiguity causes for better // diagnostics. (These take time and can lead to false errors.) - tcx.infer_ctxt().enter(|infcx| { - let selcx = &mut SelectionContext::intercrate(&infcx); - selcx.enable_tracking_intercrate_ambiguity_causes(); - on_overlap( - overlap(selcx, skip_leak_check, impl1_def_id, impl2_def_id, overlap_mode).unwrap(), - ) - }) + let infcx = tcx.infer_ctxt().build(); + let selcx = &mut SelectionContext::intercrate(&infcx); + selcx.enable_tracking_intercrate_ambiguity_causes(); + Some(overlap(selcx, skip_leak_check, impl1_def_id, impl2_def_id, overlap_mode).unwrap()) } fn with_fresh_ty_vars<'cx, 'tcx>( @@ -166,7 +157,7 @@ fn overlap_within_probe<'cx, 'tcx>( impl1_def_id: DefId, impl2_def_id: DefId, overlap_mode: OverlapMode, - snapshot: &CombinedSnapshot<'_, 'tcx>, + snapshot: &CombinedSnapshot<'tcx>, ) -> Option<OverlapResult<'tcx>> { let infcx = selcx.infcx(); @@ -196,7 +187,7 @@ fn overlap_within_probe<'cx, 'tcx>( } } - // We disable the leak when when creating the `snapshot` by using + // We disable the leak when creating the `snapshot` by using // `infcx.probe_maybe_disable_leak_check`. if infcx.leak_check(true, snapshot).is_err() { debug!("overlap: leak check failed"); @@ -297,36 +288,41 @@ fn negative_impl<'cx, 'tcx>( let tcx = selcx.infcx().tcx; // Create an infcx, taking the predicates of impl1 as assumptions: - tcx.infer_ctxt().enter(|infcx| { - // create a parameter environment corresponding to a (placeholder) instantiation of impl1 - let impl_env = tcx.param_env(impl1_def_id); - let subject1 = match traits::fully_normalize( - &infcx, - FulfillmentContext::new(), - ObligationCause::dummy(), - impl_env, - tcx.impl_subject(impl1_def_id), - ) { - Ok(s) => s, - Err(err) => bug!("failed to fully normalize {:?}: {:?}", impl1_def_id, err), - }; + let infcx = tcx.infer_ctxt().build(); + // create a parameter environment corresponding to a (placeholder) instantiation of impl1 + let impl_env = tcx.param_env(impl1_def_id); + let subject1 = match traits::fully_normalize( + &infcx, + ObligationCause::dummy(), + impl_env, + tcx.impl_subject(impl1_def_id), + ) { + Ok(s) => s, + Err(err) => { + tcx.sess.delay_span_bug( + tcx.def_span(impl1_def_id), + format!("failed to fully normalize {:?}: {:?}", impl1_def_id, err), + ); + return false; + } + }; - // Attempt to prove that impl2 applies, given all of the above. - let selcx = &mut SelectionContext::new(&infcx); - let impl2_substs = infcx.fresh_substs_for_item(DUMMY_SP, impl2_def_id); - let (subject2, obligations) = - impl_subject_and_oblig(selcx, impl_env, impl2_def_id, impl2_substs); + // Attempt to prove that impl2 applies, given all of the above. + let selcx = &mut SelectionContext::new(&infcx); + let impl2_substs = infcx.fresh_substs_for_item(DUMMY_SP, impl2_def_id); + let (subject2, obligations) = + impl_subject_and_oblig(selcx, impl_env, impl2_def_id, impl2_substs); - !equate(&infcx, impl_env, subject1, subject2, obligations) - }) + !equate(&infcx, impl_env, subject1, subject2, obligations, impl1_def_id) } -fn equate<'cx, 'tcx>( - infcx: &InferCtxt<'cx, 'tcx>, +fn equate<'tcx>( + infcx: &InferCtxt<'tcx>, impl_env: ty::ParamEnv<'tcx>, subject1: ImplSubject<'tcx>, subject2: ImplSubject<'tcx>, obligations: impl Iterator<Item = PredicateObligation<'tcx>>, + body_def_id: DefId, ) -> bool { // do the impls unify? If not, not disjoint. let Ok(InferOk { obligations: more_obligations, .. }) = @@ -340,7 +336,7 @@ fn equate<'cx, 'tcx>( let opt_failing_obligation = obligations .into_iter() .chain(more_obligations) - .find(|o| negative_impl_exists(selcx, impl_env, o)); + .find(|o| negative_impl_exists(selcx, o, body_def_id)); if let Some(failing_obligation) = opt_failing_obligation { debug!("overlap: obligation unsatisfiable {:?}", failing_obligation); @@ -354,18 +350,16 @@ fn equate<'cx, 'tcx>( #[instrument(level = "debug", skip(selcx))] fn negative_impl_exists<'cx, 'tcx>( selcx: &SelectionContext<'cx, 'tcx>, - param_env: ty::ParamEnv<'tcx>, o: &PredicateObligation<'tcx>, + body_def_id: DefId, ) -> bool { - let infcx = &selcx.infcx().fork(); - - if resolve_negative_obligation(infcx, param_env, o) { + if resolve_negative_obligation(selcx.infcx().fork(), o, body_def_id) { return true; } // Try to prove a negative obligation exists for super predicates - for o in util::elaborate_predicates(infcx.tcx, iter::once(o.predicate)) { - if resolve_negative_obligation(infcx, param_env, &o) { + for o in util::elaborate_predicates(selcx.tcx(), iter::once(o.predicate)) { + if resolve_negative_obligation(selcx.infcx().fork(), &o, body_def_id) { return true; } } @@ -374,10 +368,10 @@ fn negative_impl_exists<'cx, 'tcx>( } #[instrument(level = "debug", skip(infcx))] -fn resolve_negative_obligation<'cx, 'tcx>( - infcx: &InferCtxt<'cx, 'tcx>, - param_env: ty::ParamEnv<'tcx>, +fn resolve_negative_obligation<'tcx>( + infcx: InferCtxt<'tcx>, o: &PredicateObligation<'tcx>, + body_def_id: DefId, ) -> bool { let tcx = infcx.tcx; @@ -385,17 +379,25 @@ fn resolve_negative_obligation<'cx, 'tcx>( return false; }; - let mut fulfillment_cx = <dyn TraitEngine<'tcx>>::new(infcx.tcx); - fulfillment_cx.register_predicate_obligation(infcx, o); - - let errors = fulfillment_cx.select_all_or_error(infcx); - - if !errors.is_empty() { + let param_env = o.param_env; + if !super::fully_solve_obligation(&infcx, o).is_empty() { return false; } - // FIXME -- also add "assumed to be well formed" types into the `outlives_env` - let outlives_env = OutlivesEnvironment::new(param_env); + let (body_id, body_def_id) = if let Some(body_def_id) = body_def_id.as_local() { + (tcx.hir().local_def_id_to_hir_id(body_def_id), body_def_id) + } else { + (CRATE_HIR_ID, CRATE_DEF_ID) + }; + + let ocx = ObligationCtxt::new(&infcx); + let wf_tys = ocx.assumed_wf_types(param_env, DUMMY_SP, body_def_id); + let outlives_env = OutlivesEnvironment::with_bounds( + param_env, + Some(&infcx), + infcx.implied_bounds_tys(param_env, body_id, wf_tys), + ); + infcx.process_registered_region_obligations(outlives_env.region_bound_pairs(), param_env); infcx.resolve_regions(&outlives_env).is_empty() @@ -404,12 +406,12 @@ fn resolve_negative_obligation<'cx, 'tcx>( pub fn trait_ref_is_knowable<'tcx>( tcx: TyCtxt<'tcx>, trait_ref: ty::TraitRef<'tcx>, -) -> Option<Conflict> { +) -> Result<(), Conflict> { debug!("trait_ref_is_knowable(trait_ref={:?})", trait_ref); if orphan_check_trait_ref(tcx, trait_ref, InCrate::Remote).is_ok() { // A downstream or cousin crate is allowed to implement some // substitution of this trait-ref. - return Some(Conflict::Downstream); + return Err(Conflict::Downstream); } if trait_ref_is_local_or_fundamental(tcx, trait_ref) { @@ -418,7 +420,7 @@ pub fn trait_ref_is_knowable<'tcx>( // allowed to implement a substitution of this trait ref, which // means impls could only come from dependencies of this crate, // which we already know about. - return None; + return Ok(()); } // This is a remote non-fundamental trait, so if another crate @@ -431,10 +433,10 @@ pub fn trait_ref_is_knowable<'tcx>( // we are an owner. if orphan_check_trait_ref(tcx, trait_ref, InCrate::Local).is_ok() { debug!("trait_ref_is_knowable: orphan check passed"); - None + Ok(()) } else { debug!("trait_ref_is_knowable: nonlocal, nonfundamental, unowned"); - Some(Conflict::Upstream) + Err(Conflict::Upstream) } } @@ -740,7 +742,21 @@ impl<'tcx> TypeVisitor<'tcx> for OrphanChecker<'tcx> { result } - // FIXME: Constants should participate in orphan checking. + /// All possible values for a constant parameter already exist + /// in the crate defining the trait, so they are always non-local[^1]. + /// + /// Because there's no way to have an impl where the first local + /// generic argument is a constant, we also don't have to fail + /// the orphan check when encountering a parameter or a generic constant. + /// + /// This means that we can completely ignore constants during the orphan check. + /// + /// See `src/test/ui/coherence/const-generics-orphan-check-ok.rs` for examples. + /// + /// [^1]: This might not hold for function pointers or trait objects in the future. + /// As these should be quite rare as const arguments and especially rare as impl + /// parameters, allowing uncovered const parameters in impls seems more useful + /// than allowing `impl<T> Trait<local_fn_ptr, T> for i32` to compile. fn visit_const(&mut self, _c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> { ControlFlow::CONTINUE } diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index 254bc4ab6..84038625f 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -9,14 +9,12 @@ //! `thir_abstract_const` which can then be checked for structural equality with other //! generic constants mentioned in the `caller_bounds` of the current environment. use rustc_errors::ErrorGuaranteed; -use rustc_hir::def::DefKind; use rustc_infer::infer::InferCtxt; use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::ty::abstract_const::{ walk_abstract_const, AbstractConst, FailureKind, Node, NotConstEvaluatable, }; use rustc_middle::ty::{self, TyCtxt, TypeVisitable}; -use rustc_session::lint; use rustc_span::Span; use std::iter; @@ -101,7 +99,7 @@ impl<'tcx> ConstUnifyCtxt<'tcx> { a_uv == b_uv } // FIXME(generic_const_exprs): We may want to either actually try - // to evaluate `a_ct` and `b_ct` if they are are fully concrete or something like + // to evaluate `a_ct` and `b_ct` if they are fully concrete or something like // this, for now we just return false here. _ => false, } @@ -138,7 +136,7 @@ impl<'tcx> ConstUnifyCtxt<'tcx> { #[instrument(skip(tcx), level = "debug")] pub fn try_unify_abstract_consts<'tcx>( tcx: TyCtxt<'tcx>, - (a, b): (ty::Unevaluated<'tcx, ()>, ty::Unevaluated<'tcx, ()>), + (a, b): (ty::UnevaluatedConst<'tcx>, ty::UnevaluatedConst<'tcx>), param_env: ty::ParamEnv<'tcx>, ) -> bool { (|| { @@ -159,13 +157,22 @@ pub fn try_unify_abstract_consts<'tcx>( /// Check if a given constant can be evaluated. #[instrument(skip(infcx), level = "debug")] -pub fn is_const_evaluatable<'cx, 'tcx>( - infcx: &InferCtxt<'cx, 'tcx>, - uv: ty::Unevaluated<'tcx, ()>, +pub fn is_const_evaluatable<'tcx>( + infcx: &InferCtxt<'tcx>, + ct: ty::Const<'tcx>, param_env: ty::ParamEnv<'tcx>, span: Span, ) -> Result<(), NotConstEvaluatable> { let tcx = infcx.tcx; + let uv = match ct.kind() { + ty::ConstKind::Unevaluated(uv) => uv, + ty::ConstKind::Param(_) + | ty::ConstKind::Bound(_, _) + | ty::ConstKind::Placeholder(_) + | ty::ConstKind::Value(_) + | ty::ConstKind::Error(_) => return Ok(()), + ty::ConstKind::Infer(_) => return Err(NotConstEvaluatable::MentionsInfer), + }; if tcx.features().generic_const_exprs { if let Some(ct) = AbstractConst::new(tcx, uv)? { @@ -183,7 +190,7 @@ pub fn is_const_evaluatable<'cx, 'tcx>( FailureKind::Concrete => {} } } - let concrete = infcx.const_eval_resolve(param_env, uv.expand(), Some(span)); + let concrete = infcx.const_eval_resolve(param_env, uv, Some(span)); match concrete { Err(ErrorHandled::TooGeneric) => { Err(NotConstEvaluatable::Error(infcx.tcx.sess.delay_span_bug( @@ -210,7 +217,7 @@ pub fn is_const_evaluatable<'cx, 'tcx>( // and hopefully soon change this to an error. // // See #74595 for more details about this. - let concrete = infcx.const_eval_resolve(param_env, uv.expand(), Some(span)); + let concrete = infcx.const_eval_resolve(param_env, uv, Some(span)); match concrete { // If we're evaluating a foreign constant, under a nightly compiler without generic @@ -235,39 +242,25 @@ pub fn is_const_evaluatable<'cx, 'tcx>( .emit() } - Err(ErrorHandled::TooGeneric) => Err(if uv.has_infer_types_or_consts() { - NotConstEvaluatable::MentionsInfer - } else if uv.has_param_types_or_consts() { - NotConstEvaluatable::MentionsParam - } else { - let guar = infcx.tcx.sess.delay_span_bug(span, format!("Missing value for constant, but no error reported?")); - NotConstEvaluatable::Error(guar) - }), + Err(ErrorHandled::TooGeneric) => { + let err = if uv.has_non_region_infer() { + NotConstEvaluatable::MentionsInfer + } else if uv.has_non_region_param() { + NotConstEvaluatable::MentionsParam + } else { + let guar = infcx.tcx.sess.delay_span_bug(span, format!("Missing value for constant, but no error reported?")); + NotConstEvaluatable::Error(guar) + }; + + Err(err) + }, Err(ErrorHandled::Linted) => { let reported = infcx.tcx.sess.delay_span_bug(span, "constant in type had error reported as lint"); Err(NotConstEvaluatable::Error(reported)) } Err(ErrorHandled::Reported(e)) => Err(NotConstEvaluatable::Error(e)), - Ok(_) => { - if uv.substs.has_param_types_or_consts() { - assert!(matches!(infcx.tcx.def_kind(uv.def.did), DefKind::AnonConst)); - let mir_body = infcx.tcx.mir_for_ctfe_opt_const_arg(uv.def); - - if mir_body.is_polymorphic { - let Some(local_def_id) = uv.def.did.as_local() else { return Ok(()) }; - tcx.struct_span_lint_hir( - lint::builtin::CONST_EVALUATABLE_UNCHECKED, - tcx.hir().local_def_id_to_hir_id(local_def_id), - span, - |err| { - err.build("cannot use constants which depend on generic parameters in types").emit(); - }) - } - } - - Ok(()) - }, + Ok(_) => Ok(()), } } } @@ -281,7 +274,7 @@ fn satisfied_from_param_env<'tcx>( for pred in param_env.caller_bounds() { match pred.kind().skip_binder() { ty::PredicateKind::ConstEvaluatable(uv) => { - if let Some(b_ct) = AbstractConst::new(tcx, uv)? { + if let Some(b_ct) = AbstractConst::from_const(tcx, uv)? { let const_unify_ctxt = ConstUnifyCtxt { tcx, param_env }; // Try to unify with each subtree in the AbstractConst to allow for diff --git a/compiler/rustc_trait_selection/src/traits/engine.rs b/compiler/rustc_trait_selection/src/traits/engine.rs index 6c177f638..e0c8deec9 100644 --- a/compiler/rustc_trait_selection/src/traits/engine.rs +++ b/compiler/rustc_trait_selection/src/traits/engine.rs @@ -3,7 +3,8 @@ use std::cell::RefCell; use super::TraitEngine; use super::{ChalkFulfillmentContext, FulfillmentContext}; use crate::infer::InferCtxtExt; -use rustc_hir::def_id::DefId; +use rustc_data_structures::fx::FxHashSet; +use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_infer::infer::{InferCtxt, InferOk}; use rustc_infer::traits::{ FulfillmentError, Obligation, ObligationCause, PredicateObligation, TraitEngineExt as _, @@ -12,9 +13,11 @@ use rustc_middle::ty::error::TypeError; use rustc_middle::ty::ToPredicate; use rustc_middle::ty::TypeFoldable; use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_span::Span; pub trait TraitEngineExt<'tcx> { fn new(tcx: TyCtxt<'tcx>) -> Box<Self>; + fn new_in_snapshot(tcx: TyCtxt<'tcx>) -> Box<Self>; } impl<'tcx> TraitEngineExt<'tcx> for dyn TraitEngine<'tcx> { @@ -25,20 +28,32 @@ impl<'tcx> TraitEngineExt<'tcx> for dyn TraitEngine<'tcx> { Box::new(FulfillmentContext::new()) } } + + fn new_in_snapshot(tcx: TyCtxt<'tcx>) -> Box<Self> { + if tcx.sess.opts.unstable_opts.chalk { + Box::new(ChalkFulfillmentContext::new()) + } else { + Box::new(FulfillmentContext::new_in_snapshot()) + } + } } /// Used if you want to have pleasant experience when dealing /// with obligations outside of hir or mir typeck. pub struct ObligationCtxt<'a, 'tcx> { - pub infcx: &'a InferCtxt<'a, 'tcx>, + pub infcx: &'a InferCtxt<'tcx>, engine: RefCell<Box<dyn TraitEngine<'tcx>>>, } impl<'a, 'tcx> ObligationCtxt<'a, 'tcx> { - pub fn new(infcx: &'a InferCtxt<'a, 'tcx>) -> Self { + pub fn new(infcx: &'a InferCtxt<'tcx>) -> Self { Self { infcx, engine: RefCell::new(<dyn TraitEngine<'_>>::new(infcx.tcx)) } } + pub fn new_in_snapshot(infcx: &'a InferCtxt<'tcx>) -> Self { + Self { infcx, engine: RefCell::new(<dyn TraitEngine<'_>>::new_in_snapshot(infcx.tcx)) } + } + pub fn register_obligation(&self, obligation: PredicateObligation<'tcx>) { self.engine.borrow_mut().register_predicate_obligation(self.infcx, obligation); } @@ -109,4 +124,34 @@ impl<'a, 'tcx> ObligationCtxt<'a, 'tcx> { pub fn select_all_or_error(&self) -> Vec<FulfillmentError<'tcx>> { self.engine.borrow_mut().select_all_or_error(self.infcx) } + + pub fn assumed_wf_types( + &self, + param_env: ty::ParamEnv<'tcx>, + span: Span, + def_id: LocalDefId, + ) -> FxHashSet<Ty<'tcx>> { + let tcx = self.infcx.tcx; + let assumed_wf_types = tcx.assumed_wf_types(def_id); + let mut implied_bounds = FxHashSet::default(); + let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); + let cause = ObligationCause::misc(span, hir_id); + for ty in assumed_wf_types { + // FIXME(@lcnr): rustc currently does not check wf for types + // pre-normalization, meaning that implied bounds are sometimes + // incorrect. See #100910 for more details. + // + // Not adding the unnormalized types here mostly fixes that, except + // that there are projections which are still ambiguous in the item definition + // but do normalize successfully when using the item, see #98543. + // + // Anyways, I will hopefully soon change implied bounds to make all of this + // sound and then uncomment this line again. + + // implied_bounds.insert(ty); + let normalized = self.normalize(cause.clone(), param_env, ty); + implied_bounds.insert(normalized); + } + implied_bounds + } } 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 e442c5c91..1217d264a 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -2,10 +2,10 @@ pub mod on_unimplemented; pub mod suggestions; use super::{ - EvaluationResult, FulfillmentContext, FulfillmentError, FulfillmentErrorCode, - MismatchedProjectionTypes, Obligation, ObligationCause, ObligationCauseCode, - OnUnimplementedDirective, OnUnimplementedNote, OutputTypeParameterMismatch, Overflow, - PredicateObligation, SelectionContext, SelectionError, TraitNotObjectSafe, + FulfillmentContext, FulfillmentError, FulfillmentErrorCode, MismatchedProjectionTypes, + Obligation, ObligationCause, ObligationCauseCode, OnUnimplementedDirective, + OnUnimplementedNote, OutputTypeParameterMismatch, Overflow, PredicateObligation, + SelectionContext, SelectionError, TraitNotObjectSafe, }; use crate::infer::error_reporting::{TyCategory, TypeAnnotationNeeded as ErrorCode}; @@ -22,6 +22,8 @@ use rustc_hir::intravisit::Visitor; use rustc_hir::GenericParam; use rustc_hir::Item; use rustc_hir::Node; +use rustc_infer::infer::error_reporting::TypeErrCtxt; +use rustc_infer::infer::TypeTrace; use rustc_infer::traits::TraitEngine; use rustc_middle::traits::select::OverflowError; use rustc_middle::ty::abstract_const::NotConstEvaluatable; @@ -31,6 +33,8 @@ use rustc_middle::ty::{ self, SubtypePredicate, ToPolyTraitRef, ToPredicate, TraitRef, Ty, TyCtxt, TypeFoldable, TypeVisitable, }; +use rustc_session::Limit; +use rustc_span::def_id::LOCAL_CRATE; use rustc_span::symbol::{kw, sym}; use rustc_span::{ExpnKind, Span, DUMMY_SP}; use std::fmt; @@ -40,8 +44,8 @@ use std::ops::ControlFlow; use crate::traits::query::evaluate_obligation::InferCtxtExt as _; use crate::traits::query::normalize::AtExt as _; use crate::traits::specialize::to_pretty_impl_header; -use on_unimplemented::InferCtxtExt as _; -use suggestions::InferCtxtExt as _; +use on_unimplemented::TypeErrCtxtExt as _; +use suggestions::TypeErrCtxtExt as _; pub use rustc_infer::traits::error_reporting::*; @@ -62,6 +66,37 @@ pub struct ImplCandidate<'tcx> { } pub trait InferCtxtExt<'tcx> { + /// Given some node representing a fn-like thing in the HIR map, + /// returns a span and `ArgKind` information that describes the + /// arguments it expects. This can be supplied to + /// `report_arg_count_mismatch`. + fn get_fn_like_arguments(&self, node: Node<'_>) -> Option<(Span, Vec<ArgKind>)>; + + /// Reports an error when the number of arguments needed by a + /// trait match doesn't match the number that the expression + /// provides. + fn report_arg_count_mismatch( + &self, + span: Span, + found_span: Option<Span>, + expected_args: Vec<ArgKind>, + found_args: Vec<ArgKind>, + is_closure: bool, + ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>; + + /// Checks if the type implements one of `Fn`, `FnMut`, or `FnOnce` + /// in that order, and returns the generic type corresponding to the + /// argument of that trait (corresponding to the closure arguments). + fn type_implements_fn_trait( + &self, + param_env: ty::ParamEnv<'tcx>, + ty: ty::Binder<'tcx, Ty<'tcx>>, + constness: ty::BoundConstness, + polarity: ty::ImplPolarity, + ) -> Result<(ty::ClosureKind, ty::Binder<'tcx, Ty<'tcx>>), ()>; +} + +pub trait TypeErrCtxtExt<'tcx> { fn report_fulfillment_errors( &self, errors: &[FulfillmentError<'tcx>], @@ -77,6 +112,8 @@ pub trait InferCtxtExt<'tcx> { where T: fmt::Display + TypeFoldable<'tcx>; + fn suggest_new_overflow_limit(&self, err: &mut Diagnostic); + fn report_overflow_error_cycle(&self, cycle: &[PredicateObligation<'tcx>]) -> !; /// The `root_obligation` parameter should be the `root_obligation` field @@ -89,12 +126,71 @@ pub trait InferCtxtExt<'tcx> { error: &SelectionError<'tcx>, fallback_has_occurred: bool, ); +} +impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { /// Given some node representing a fn-like thing in the HIR map, /// returns a span and `ArgKind` information that describes the /// arguments it expects. This can be supplied to /// `report_arg_count_mismatch`. - fn get_fn_like_arguments(&self, node: Node<'_>) -> Option<(Span, Vec<ArgKind>)>; + fn get_fn_like_arguments(&self, node: Node<'_>) -> Option<(Span, Vec<ArgKind>)> { + let sm = self.tcx.sess.source_map(); + let hir = self.tcx.hir(); + Some(match node { + Node::Expr(&hir::Expr { + kind: hir::ExprKind::Closure(&hir::Closure { body, fn_decl_span, .. }), + .. + }) => ( + fn_decl_span, + hir.body(body) + .params + .iter() + .map(|arg| { + if let hir::Pat { kind: hir::PatKind::Tuple(ref args, _), span, .. } = + *arg.pat + { + Some(ArgKind::Tuple( + Some(span), + args.iter() + .map(|pat| { + sm.span_to_snippet(pat.span) + .ok() + .map(|snippet| (snippet, "_".to_owned())) + }) + .collect::<Option<Vec<_>>>()?, + )) + } else { + let name = sm.span_to_snippet(arg.pat.span).ok()?; + Some(ArgKind::Arg(name, "_".to_owned())) + } + }) + .collect::<Option<Vec<ArgKind>>>()?, + ), + Node::Item(&hir::Item { kind: hir::ItemKind::Fn(ref sig, ..), .. }) + | Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(ref sig, _), .. }) + | Node::TraitItem(&hir::TraitItem { + kind: hir::TraitItemKind::Fn(ref sig, _), .. + }) => ( + sig.span, + sig.decl + .inputs + .iter() + .map(|arg| match arg.kind { + hir::TyKind::Tup(ref tys) => ArgKind::Tuple( + Some(arg.span), + vec![("_".to_owned(), "_".to_owned()); tys.len()], + ), + _ => ArgKind::empty(), + }) + .collect::<Vec<ArgKind>>(), + ), + Node::Ctor(ref variant_data) => { + let span = variant_data.ctor_hir_id().map_or(DUMMY_SP, |id| hir.span(id)); + (span, vec![ArgKind::empty(); variant_data.fields().len()]) + } + _ => panic!("non-FnLike node found: {:?}", node), + }) + } /// Reports an error when the number of arguments needed by a /// trait match doesn't match the number that the expression @@ -106,21 +202,175 @@ pub trait InferCtxtExt<'tcx> { expected_args: Vec<ArgKind>, found_args: Vec<ArgKind>, is_closure: bool, - ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>; + ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { + let kind = if is_closure { "closure" } else { "function" }; + + let args_str = |arguments: &[ArgKind], other: &[ArgKind]| { + let arg_length = arguments.len(); + let distinct = matches!(other, &[ArgKind::Tuple(..)]); + match (arg_length, arguments.get(0)) { + (1, Some(&ArgKind::Tuple(_, ref fields))) => { + format!("a single {}-tuple as argument", fields.len()) + } + _ => format!( + "{} {}argument{}", + arg_length, + if distinct && arg_length > 1 { "distinct " } else { "" }, + pluralize!(arg_length) + ), + } + }; + + let expected_str = args_str(&expected_args, &found_args); + let found_str = args_str(&found_args, &expected_args); + + let mut err = struct_span_err!( + self.tcx.sess, + span, + E0593, + "{} is expected to take {}, but it takes {}", + kind, + expected_str, + found_str, + ); + + err.span_label(span, format!("expected {} that takes {}", kind, expected_str)); + + if let Some(found_span) = found_span { + err.span_label(found_span, format!("takes {}", found_str)); + + // move |_| { ... } + // ^^^^^^^^-- def_span + // + // move |_| { ... } + // ^^^^^-- prefix + let prefix_span = self.tcx.sess.source_map().span_until_non_whitespace(found_span); + // move |_| { ... } + // ^^^-- pipe_span + let pipe_span = + if let Some(span) = found_span.trim_start(prefix_span) { span } else { found_span }; + + // Suggest to take and ignore the arguments with expected_args_length `_`s if + // found arguments is empty (assume the user just wants to ignore args in this case). + // For example, if `expected_args_length` is 2, suggest `|_, _|`. + if found_args.is_empty() && is_closure { + let underscores = vec!["_"; expected_args.len()].join(", "); + err.span_suggestion_verbose( + pipe_span, + &format!( + "consider changing the closure to take and ignore the expected argument{}", + pluralize!(expected_args.len()) + ), + format!("|{}|", underscores), + Applicability::MachineApplicable, + ); + } + + if let &[ArgKind::Tuple(_, ref fields)] = &found_args[..] { + if fields.len() == expected_args.len() { + let sugg = fields + .iter() + .map(|(name, _)| name.to_owned()) + .collect::<Vec<String>>() + .join(", "); + err.span_suggestion_verbose( + found_span, + "change the closure to take multiple arguments instead of a single tuple", + format!("|{}|", sugg), + Applicability::MachineApplicable, + ); + } + } + if let &[ArgKind::Tuple(_, ref fields)] = &expected_args[..] + && fields.len() == found_args.len() + && is_closure + { + let sugg = format!( + "|({}){}|", + found_args + .iter() + .map(|arg| match arg { + ArgKind::Arg(name, _) => name.to_owned(), + _ => "_".to_owned(), + }) + .collect::<Vec<String>>() + .join(", "), + // add type annotations if available + if found_args.iter().any(|arg| match arg { + ArgKind::Arg(_, ty) => ty != "_", + _ => false, + }) { + format!( + ": ({})", + fields + .iter() + .map(|(_, ty)| ty.to_owned()) + .collect::<Vec<String>>() + .join(", ") + ) + } else { + String::new() + }, + ); + err.span_suggestion_verbose( + found_span, + "change the closure to accept a tuple instead of individual arguments", + sugg, + Applicability::MachineApplicable, + ); + } + } + + err + } - /// Checks if the type implements one of `Fn`, `FnMut`, or `FnOnce` - /// in that order, and returns the generic type corresponding to the - /// argument of that trait (corresponding to the closure arguments). fn type_implements_fn_trait( &self, param_env: ty::ParamEnv<'tcx>, ty: ty::Binder<'tcx, Ty<'tcx>>, constness: ty::BoundConstness, polarity: ty::ImplPolarity, - ) -> Result<(ty::ClosureKind, ty::Binder<'tcx, Ty<'tcx>>), ()>; -} + ) -> Result<(ty::ClosureKind, ty::Binder<'tcx, Ty<'tcx>>), ()> { + self.commit_if_ok(|_| { + for trait_def_id in [ + self.tcx.lang_items().fn_trait(), + self.tcx.lang_items().fn_mut_trait(), + self.tcx.lang_items().fn_once_trait(), + ] { + let Some(trait_def_id) = trait_def_id else { continue }; + // Make a fresh inference variable so we can determine what the substitutions + // of the trait are. + let var = self.next_ty_var(TypeVariableOrigin { + span: DUMMY_SP, + kind: TypeVariableOriginKind::MiscVariable, + }); + let substs = self.tcx.mk_substs_trait(ty.skip_binder(), &[var.into()]); + let obligation = Obligation::new( + ObligationCause::dummy(), + param_env, + ty.rebind(ty::TraitPredicate { + trait_ref: ty::TraitRef::new(trait_def_id, substs), + constness, + polarity, + }) + .to_predicate(self.tcx), + ); + let mut fulfill_cx = FulfillmentContext::new_in_snapshot(); + fulfill_cx.register_predicate_obligation(self, obligation); + if fulfill_cx.select_all_or_error(self).is_empty() { + return Ok(( + ty::ClosureKind::from_def_id(self.tcx, trait_def_id) + .expect("expected to map DefId to ClosureKind"), + ty.rebind(self.resolve_vars_if_possible(var)), + )); + } + } -impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { + Err(()) + }) + } +} +impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { fn report_fulfillment_errors( &self, errors: &[FulfillmentError<'tcx>], @@ -250,6 +500,19 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { bug!(); } + fn suggest_new_overflow_limit(&self, err: &mut Diagnostic) { + let suggested_limit = match self.tcx.recursion_limit() { + Limit(0) => Limit(2), + limit => limit * 2, + }; + err.help(&format!( + "consider increasing the recursion limit by adding a \ + `#![recursion_limit = \"{}\"]` attribute to your crate (`{}`)", + suggested_limit, + self.tcx.crate_name(LOCAL_CRATE), + )); + } + /// Reports that a cycle was detected which led to overflow and halts /// compilation. This is equivalent to `report_overflow_error` except /// that we can give a more helpful error message (and, in particular, @@ -348,7 +611,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { message, label, note, - enclosing_scope, + parent_label, append_const_msg, } = self.on_unimplemented_note(trait_ref, &obligation); let have_alt_message = message.is_some() || label.is_some(); @@ -449,12 +712,27 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { { "consider using `()`, or a `Result`".to_owned() } else { - format!( - "{}the trait `{}` is not implemented for `{}`", - pre_message, - trait_predicate.print_modifiers_and_trait_path(), - trait_ref.skip_binder().self_ty(), - ) + let ty_desc = match trait_ref.skip_binder().self_ty().kind() { + ty::FnDef(_, _) => Some("fn item"), + ty::Closure(_, _) => Some("closure"), + _ => None, + }; + + match ty_desc { + Some(desc) => format!( + "{}the trait `{}` is not implemented for {} `{}`", + pre_message, + trait_predicate.print_modifiers_and_trait_path(), + desc, + trait_ref.skip_binder().self_ty(), + ), + None => format!( + "{}the trait `{}` is not implemented for `{}`", + pre_message, + trait_predicate.print_modifiers_and_trait_path(), + trait_ref.skip_binder().self_ty(), + ), + } }; if self.suggest_add_reference_to_arg( @@ -482,10 +760,11 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { } if let ObligationCauseCode::ObjectCastObligation(concrete_ty, obj_ty) = obligation.cause.code().peel_derives() && - Some(trait_ref.def_id()) == self.tcx.lang_items().sized_trait() { + Some(trait_ref.def_id()) == self.tcx.lang_items().sized_trait() { self.suggest_borrowing_for_object_cast(&mut err, &root_obligation, *concrete_ty, *obj_ty); } + let mut unsatisfied_const = false; if trait_predicate.is_const_if_const() && obligation.param_env.is_const() { let non_const_predicate = trait_ref.without_const(); let non_const_obligation = Obligation { @@ -495,6 +774,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { recursion_depth: obligation.recursion_depth, }; if self.predicate_may_hold(&non_const_obligation) { + unsatisfied_const = true; err.span_note( span, &format!( @@ -514,7 +794,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { // If it has a custom `#[rustc_on_unimplemented]` note, let's display it err.note(s.as_str()); } - if let Some(ref s) = enclosing_scope { + if let Some(ref s) = parent_label { let body = tcx .hir() .opt_local_def_id(obligation.cause.body_id) @@ -523,11 +803,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { hir_id: obligation.cause.body_id, }) }); - - let enclosing_scope_span = - tcx.hir().span_with_body(tcx.hir().local_def_id_to_hir_id(body)); - - err.span_label(enclosing_scope_span, s); + err.span_label(tcx.def_span(body), s); } self.suggest_floating_point_literal(&obligation, &mut err, &trait_ref); @@ -594,11 +870,11 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { // Try to report a help message if is_fn_trait && let Ok((implemented_kind, params)) = self.type_implements_fn_trait( - obligation.param_env, - trait_ref.self_ty(), - trait_predicate.skip_binder().constness, - trait_predicate.skip_binder().polarity, - ) + obligation.param_env, + trait_ref.self_ty(), + trait_predicate.skip_binder().constness, + trait_predicate.skip_binder().polarity, + ) { // If the type implements `Fn`, `FnMut`, or `FnOnce`, suppress the following // suggestion to add trait bounds for the type, since we only typically implement @@ -649,8 +925,8 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { ); } } - } else if !trait_ref.has_infer_types_or_consts() - && self.predicate_can_apply(obligation.param_env, trait_ref) + } else if !trait_ref.has_non_region_infer() + && self.predicate_can_apply(obligation.param_env, trait_predicate) { // If a where-clause may be useful, remind the // user that they can add it. @@ -665,7 +941,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { None, obligation.cause.body_id, ); - } else if !suggested { + } else if !suggested && !unsatisfied_const { // Can't show anything else useful, try to find similar impls. let impl_candidates = self.find_similar_impl_candidates(trait_predicate); if !self.report_similar_impl_candidates( @@ -828,12 +1104,11 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { // Additional context information explaining why the closure only implements // a particular trait. - if let Some(typeck_results) = self.in_progress_typeck_results { + if let Some(typeck_results) = &self.typeck_results { let hir_id = self .tcx .hir() .local_def_id_to_hir_id(closure_def_id.expect_local()); - let typeck_results = typeck_results.borrow(); match (found_kind, typeck_results.closure_kind_origins().get(hir_id)) { (ty::ClosureKind::FnOnce, Some((span, place))) => { err.span_label( @@ -859,8 +1134,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { } } - err.emit(); - return; + err } ty::PredicateKind::WellFormed(ty) => { @@ -941,9 +1215,14 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { self.reported_closure_mismatch.borrow_mut().insert((span, found_span)); + let mut not_tupled = false; + let found = match found_trait_ref.skip_binder().substs.type_at(1).kind() { ty::Tuple(ref tys) => vec![ArgKind::empty(); tys.len()], - _ => vec![ArgKind::empty()], + _ => { + not_tupled = true; + vec![ArgKind::empty()] + } }; let expected_ty = expected_trait_ref.skip_binder().substs.type_at(1); @@ -951,15 +1230,34 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { ty::Tuple(ref tys) => { tys.iter().map(|t| ArgKind::from_expected_ty(t, Some(span))).collect() } - _ => vec![ArgKind::Arg("_".to_owned(), expected_ty.to_string())], + _ => { + not_tupled = true; + vec![ArgKind::Arg("_".to_owned(), expected_ty.to_string())] + } }; - if found.len() == expected.len() { + // If this is a `Fn` family trait and either the expected or found + // is not tupled, then fall back to just a regular mismatch error. + // This shouldn't be common unless manually implementing one of the + // traits manually, but don't make it more confusing when it does + // happen. + if Some(expected_trait_ref.def_id()) != tcx.lang_items().gen_trait() && not_tupled { + self.report_and_explain_type_error( + TypeTrace::poly_trait_refs( + &obligation.cause, + true, + expected_trait_ref, + found_trait_ref, + ), + ty::error::TypeError::Mismatch, + ) + } else if found.len() == expected.len() { self.report_closure_arg_mismatch( span, found_span, found_trait_ref, expected_trait_ref, + obligation.cause.code(), ) } else { let (closure_span, found) = found_did @@ -1008,7 +1306,10 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { } match obligation.predicate.kind().skip_binder() { - ty::PredicateKind::ConstEvaluatable(uv) => { + ty::PredicateKind::ConstEvaluatable(ct) => { + let ty::ConstKind::Unevaluated(uv) = ct.kind() else { + bug!("const evaluatable failed for non-unevaluated const `{ct:?}`"); + }; let mut err = self.tcx.sess.struct_span_err(span, "unconstrained generic constant"); let const_span = self.tcx.def_span(uv.def.did); @@ -1054,250 +1355,9 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { err.emit(); } - - /// Given some node representing a fn-like thing in the HIR map, - /// returns a span and `ArgKind` information that describes the - /// arguments it expects. This can be supplied to - /// `report_arg_count_mismatch`. - fn get_fn_like_arguments(&self, node: Node<'_>) -> Option<(Span, Vec<ArgKind>)> { - let sm = self.tcx.sess.source_map(); - let hir = self.tcx.hir(); - Some(match node { - Node::Expr(&hir::Expr { - kind: hir::ExprKind::Closure(&hir::Closure { body, fn_decl_span, .. }), - .. - }) => ( - fn_decl_span, - hir.body(body) - .params - .iter() - .map(|arg| { - if let hir::Pat { kind: hir::PatKind::Tuple(ref args, _), span, .. } = - *arg.pat - { - Some(ArgKind::Tuple( - Some(span), - args.iter() - .map(|pat| { - sm.span_to_snippet(pat.span) - .ok() - .map(|snippet| (snippet, "_".to_owned())) - }) - .collect::<Option<Vec<_>>>()?, - )) - } else { - let name = sm.span_to_snippet(arg.pat.span).ok()?; - Some(ArgKind::Arg(name, "_".to_owned())) - } - }) - .collect::<Option<Vec<ArgKind>>>()?, - ), - Node::Item(&hir::Item { kind: hir::ItemKind::Fn(ref sig, ..), .. }) - | Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(ref sig, _), .. }) - | Node::TraitItem(&hir::TraitItem { - kind: hir::TraitItemKind::Fn(ref sig, _), .. - }) => ( - sig.span, - sig.decl - .inputs - .iter() - .map(|arg| match arg.kind { - hir::TyKind::Tup(ref tys) => ArgKind::Tuple( - Some(arg.span), - vec![("_".to_owned(), "_".to_owned()); tys.len()], - ), - _ => ArgKind::empty(), - }) - .collect::<Vec<ArgKind>>(), - ), - Node::Ctor(ref variant_data) => { - let span = variant_data.ctor_hir_id().map_or(DUMMY_SP, |id| hir.span(id)); - (span, vec![ArgKind::empty(); variant_data.fields().len()]) - } - _ => panic!("non-FnLike node found: {:?}", node), - }) - } - - /// Reports an error when the number of arguments needed by a - /// trait match doesn't match the number that the expression - /// provides. - fn report_arg_count_mismatch( - &self, - span: Span, - found_span: Option<Span>, - expected_args: Vec<ArgKind>, - found_args: Vec<ArgKind>, - is_closure: bool, - ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { - let kind = if is_closure { "closure" } else { "function" }; - - let args_str = |arguments: &[ArgKind], other: &[ArgKind]| { - let arg_length = arguments.len(); - let distinct = matches!(other, &[ArgKind::Tuple(..)]); - match (arg_length, arguments.get(0)) { - (1, Some(&ArgKind::Tuple(_, ref fields))) => { - format!("a single {}-tuple as argument", fields.len()) - } - _ => format!( - "{} {}argument{}", - arg_length, - if distinct && arg_length > 1 { "distinct " } else { "" }, - pluralize!(arg_length) - ), - } - }; - - let expected_str = args_str(&expected_args, &found_args); - let found_str = args_str(&found_args, &expected_args); - - let mut err = struct_span_err!( - self.tcx.sess, - span, - E0593, - "{} is expected to take {}, but it takes {}", - kind, - expected_str, - found_str, - ); - - err.span_label(span, format!("expected {} that takes {}", kind, expected_str)); - - if let Some(found_span) = found_span { - err.span_label(found_span, format!("takes {}", found_str)); - - // move |_| { ... } - // ^^^^^^^^-- def_span - // - // move |_| { ... } - // ^^^^^-- prefix - let prefix_span = self.tcx.sess.source_map().span_until_non_whitespace(found_span); - // move |_| { ... } - // ^^^-- pipe_span - let pipe_span = - if let Some(span) = found_span.trim_start(prefix_span) { span } else { found_span }; - - // Suggest to take and ignore the arguments with expected_args_length `_`s if - // found arguments is empty (assume the user just wants to ignore args in this case). - // For example, if `expected_args_length` is 2, suggest `|_, _|`. - if found_args.is_empty() && is_closure { - let underscores = vec!["_"; expected_args.len()].join(", "); - err.span_suggestion_verbose( - pipe_span, - &format!( - "consider changing the closure to take and ignore the expected argument{}", - pluralize!(expected_args.len()) - ), - format!("|{}|", underscores), - Applicability::MachineApplicable, - ); - } - - if let &[ArgKind::Tuple(_, ref fields)] = &found_args[..] { - if fields.len() == expected_args.len() { - let sugg = fields - .iter() - .map(|(name, _)| name.to_owned()) - .collect::<Vec<String>>() - .join(", "); - err.span_suggestion_verbose( - found_span, - "change the closure to take multiple arguments instead of a single tuple", - format!("|{}|", sugg), - Applicability::MachineApplicable, - ); - } - } - if let &[ArgKind::Tuple(_, ref fields)] = &expected_args[..] - && fields.len() == found_args.len() - && is_closure - { - let sugg = format!( - "|({}){}|", - found_args - .iter() - .map(|arg| match arg { - ArgKind::Arg(name, _) => name.to_owned(), - _ => "_".to_owned(), - }) - .collect::<Vec<String>>() - .join(", "), - // add type annotations if available - if found_args.iter().any(|arg| match arg { - ArgKind::Arg(_, ty) => ty != "_", - _ => false, - }) { - format!( - ": ({})", - fields - .iter() - .map(|(_, ty)| ty.to_owned()) - .collect::<Vec<String>>() - .join(", ") - ) - } else { - String::new() - }, - ); - err.span_suggestion_verbose( - found_span, - "change the closure to accept a tuple instead of individual arguments", - sugg, - Applicability::MachineApplicable, - ); - } - } - - err - } - - fn type_implements_fn_trait( - &self, - param_env: ty::ParamEnv<'tcx>, - ty: ty::Binder<'tcx, Ty<'tcx>>, - constness: ty::BoundConstness, - polarity: ty::ImplPolarity, - ) -> Result<(ty::ClosureKind, ty::Binder<'tcx, Ty<'tcx>>), ()> { - self.commit_if_ok(|_| { - for trait_def_id in [ - self.tcx.lang_items().fn_trait(), - self.tcx.lang_items().fn_mut_trait(), - self.tcx.lang_items().fn_once_trait(), - ] { - let Some(trait_def_id) = trait_def_id else { continue }; - // Make a fresh inference variable so we can determine what the substitutions - // of the trait are. - let var = self.next_ty_var(TypeVariableOrigin { - span: DUMMY_SP, - kind: TypeVariableOriginKind::MiscVariable, - }); - let substs = self.tcx.mk_substs_trait(ty.skip_binder(), &[var.into()]); - let obligation = Obligation::new( - ObligationCause::dummy(), - param_env, - ty.rebind(ty::TraitPredicate { - trait_ref: ty::TraitRef::new(trait_def_id, substs), - constness, - polarity, - }) - .to_predicate(self.tcx), - ); - let mut fulfill_cx = FulfillmentContext::new_in_snapshot(); - fulfill_cx.register_predicate_obligation(self, obligation); - if fulfill_cx.select_all_or_error(self).is_empty() { - return Ok(( - ty::ClosureKind::from_def_id(self.tcx, trait_def_id) - .expect("expected to map DefId to ClosureKind"), - ty.rebind(self.resolve_vars_if_possible(var)), - )); - } - } - - Err(()) - }) - } } -trait InferCtxtPrivExt<'hir, 'tcx> { +trait InferCtxtPrivExt<'tcx> { // returns if `cond` not occurring implies that `error` does not occur - i.e., that // `error` occurring implies that `cond` occurs. fn error_implies(&self, cond: ty::Predicate<'tcx>, error: ty::Predicate<'tcx>) -> bool; @@ -1315,6 +1375,13 @@ trait InferCtxtPrivExt<'hir, 'tcx> { error: &MismatchedProjectionTypes<'tcx>, ); + fn maybe_detailed_projection_msg( + &self, + pred: ty::ProjectionPredicate<'tcx>, + normalized_ty: ty::Term<'tcx>, + expected_ty: ty::Term<'tcx>, + ) -> Option<String>; + fn fuzzy_match_tys( &self, a: Ty<'tcx>, @@ -1371,7 +1438,7 @@ trait InferCtxtPrivExt<'hir, 'tcx> { fn predicate_can_apply( &self, param_env: ty::ParamEnv<'tcx>, - pred: ty::PolyTraitRef<'tcx>, + pred: ty::PolyTraitPredicate<'tcx>, ) -> bool; fn note_obligation_cause(&self, err: &mut Diagnostic, obligation: &PredicateObligation<'tcx>); @@ -1389,13 +1456,13 @@ trait InferCtxtPrivExt<'hir, 'tcx> { predicate: ty::Predicate<'tcx>, ); - fn maybe_suggest_unsized_generics(&self, err: &mut Diagnostic, span: Span, node: Node<'hir>); + fn maybe_suggest_unsized_generics(&self, err: &mut Diagnostic, span: Span, node: Node<'tcx>); fn maybe_indirection_for_unsized( &self, err: &mut Diagnostic, - item: &'hir Item<'hir>, - param: &'hir GenericParam<'hir>, + item: &'tcx Item<'tcx>, + param: &'tcx GenericParam<'tcx>, ) -> bool; fn is_recursive_obligation( @@ -1405,7 +1472,7 @@ trait InferCtxtPrivExt<'hir, 'tcx> { ) -> bool; } -impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { +impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // returns if `cond` not occurring implies that `error` does not occur - i.e., that // `error` occurring implies that `cond` occurs. fn error_implies(&self, cond: ty::Predicate<'tcx>, error: ty::Predicate<'tcx>) -> bool { @@ -1476,13 +1543,31 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { .emit(); } FulfillmentErrorCode::CodeConstEquateError(ref expected_found, ref err) => { - self.report_mismatched_consts( + let mut diag = self.report_mismatched_consts( &error.obligation.cause, expected_found.expected, expected_found.found, err.clone(), - ) - .emit(); + ); + let code = error.obligation.cause.code().peel_derives().peel_match_impls(); + if let ObligationCauseCode::BindingObligation(..) + | ObligationCauseCode::ItemObligation(..) + | ObligationCauseCode::ExprBindingObligation(..) + | ObligationCauseCode::ExprItemObligation(..) = code + { + self.note_obligation_cause_code( + &mut diag, + &error.obligation.predicate, + error.obligation.param_env, + code, + &mut vec![], + &mut Default::default(), + ); + } + diag.emit(); + } + FulfillmentErrorCode::CodeCycle(ref cycle) => { + self.report_overflow_error_cycle(cycle); } } } @@ -1500,8 +1585,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { } self.probe(|_| { - let err_buf; - let mut err = &error.err; + let mut err = error.err; let mut values = None; // try to find the mismatched types to report the error with. @@ -1534,31 +1618,28 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { obligation.cause.code().peel_derives(), ObligationCauseCode::ItemObligation(_) | ObligationCauseCode::BindingObligation(_, _) + | ObligationCauseCode::ExprItemObligation(..) + | ObligationCauseCode::ExprBindingObligation(..) | ObligationCauseCode::ObjectCastObligation(..) | ObligationCauseCode::OpaqueType ); - if let Err(error) = self.at(&obligation.cause, obligation.param_env).eq_exp( + if let Err(new_err) = self.at(&obligation.cause, obligation.param_env).eq_exp( is_normalized_ty_expected, normalized_ty, data.term, ) { - values = Some(infer::ValuePairs::Terms(ExpectedFound::new( - is_normalized_ty_expected, - normalized_ty, - data.term, - ))); - err_buf = error; - err = &err_buf; + values = Some((data, is_normalized_ty_expected, normalized_ty, data.term)); + err = new_err; } } - let mut diag = struct_span_err!( - self.tcx.sess, - obligation.cause.span, - E0271, - "type mismatch resolving `{}`", - predicate - ); + let msg = values + .and_then(|(predicate, _, normalized_ty, expected_ty)| { + self.maybe_detailed_projection_msg(predicate, normalized_ty, expected_ty) + }) + .unwrap_or_else(|| format!("type mismatch resolving `{}`", predicate)); + let mut diag = struct_span_err!(self.tcx.sess, obligation.cause.span, E0271, "{msg}"); + let secondary_span = match predicate.kind().skip_binder() { ty::PredicateKind::Projection(proj) => self .tcx @@ -1584,7 +1665,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { .. }) | hir::Node::ImplItem(hir::ImplItem { - kind: hir::ImplItemKind::TyAlias(ty), + kind: hir::ImplItemKind::Type(ty), .. }), ) => Some((ty.span, format!("type mismatch resolving `{}`", predicate))), @@ -1596,7 +1677,13 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { &mut diag, &obligation.cause, secondary_span, - values, + values.map(|(_, is_normalized_ty_expected, normalized_ty, term)| { + infer::ValuePairs::Terms(ExpectedFound::new( + is_normalized_ty_expected, + normalized_ty, + term, + )) + }), err, true, false, @@ -1606,6 +1693,33 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { }); } + fn maybe_detailed_projection_msg( + &self, + pred: ty::ProjectionPredicate<'tcx>, + normalized_ty: ty::Term<'tcx>, + expected_ty: ty::Term<'tcx>, + ) -> Option<String> { + 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.item_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 + } + } + fn fuzzy_match_tys( &self, mut a: Ty<'tcx>, @@ -1731,13 +1845,21 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { return false; } if candidates.len() == 1 { + let ty_desc = match candidates[0].self_ty().kind() { + ty::FnPtr(_) => Some("fn pointer"), + _ => None, + }; + let the_desc = match ty_desc { + Some(desc) => format!(" implemented for {} `", desc), + None => " implemented for `".to_string(), + }; err.highlighted_help(vec![ ( format!("the trait `{}` ", candidates[0].print_only_trait_path()), Style::NoStyle, ), ("is".to_string(), Style::Highlight), - (" implemented for `".to_string(), Style::NoStyle), + (the_desc, Style::NoStyle), (candidates[0].self_ty().to_string(), Style::Highlight), ("`".to_string(), Style::NoStyle), ]); @@ -1804,7 +1926,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { // types, too. self.tcx .visibility(def.did()) - .is_accessible_from(body_id.owner.to_def_id(), self.tcx) + .is_accessible_from(body_id.owner.def_id, self.tcx) } else { true } @@ -1814,16 +1936,11 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { } let normalize = |candidate| { - self.tcx.infer_ctxt().enter(|ref infcx| { - let normalized = infcx - .at(&ObligationCause::dummy(), ty::ParamEnv::empty()) - .normalize(candidate) - .ok(); - match normalized { - Some(normalized) => normalized.value, - None => candidate, - } - }) + let infcx = self.tcx.infer_ctxt().build(); + infcx + .at(&ObligationCause::dummy(), ty::ParamEnv::empty()) + .normalize(candidate) + .map_or(candidate, |normalized| normalized.value) }; // Sort impl candidates so that ordering is consistent for UI tests. @@ -1940,7 +2057,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { let predicate = self.resolve_vars_if_possible(obligation.predicate); let span = obligation.cause.span; - debug!(?predicate, obligation.cause.code = tracing::field::debug(&obligation.cause.code())); + debug!(?predicate, obligation.cause.code = ?obligation.cause.code()); // Ambiguity errors are often caused as fallout from earlier errors. // We ignore them if this `infcx` is tainted in some cases below. @@ -1997,7 +2114,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { // Pick the first substitution that still contains inference variables as the one // we're going to emit an error for. If there are none (see above), fall back to // a more general error. - let subst = data.trait_ref.substs.iter().find(|s| s.has_infer_types_or_consts()); + let subst = data.trait_ref.substs.iter().find(|s| s.has_non_region_infer()); let mut err = if let Some(subst) = subst { self.emit_inference_failure_err(body_id, span, subst, ErrorCode::E0283, true) @@ -2033,13 +2150,11 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { } } - if let ObligationCauseCode::ItemObligation(def_id) = *obligation.cause.code() { + if let ObligationCauseCode::ItemObligation(def_id) | ObligationCauseCode::ExprItemObligation(def_id, ..) = *obligation.cause.code() { self.suggest_fully_qualified_path(&mut err, def_id, span, trait_ref.def_id()); - } else if let ( - Ok(ref snippet), - &ObligationCauseCode::BindingObligation(def_id, _), - ) = - (self.tcx.sess.source_map().span_to_snippet(span), obligation.cause.code()) + } else if let Ok(snippet) = &self.tcx.sess.source_map().span_to_snippet(span) + && let ObligationCauseCode::BindingObligation(def_id, _) | ObligationCauseCode::ExprBindingObligation(def_id, ..) + = *obligation.cause.code() { let generics = self.tcx.generics_of(def_id); if generics.params.iter().any(|p| p.name != kw::SelfUpper) @@ -2119,12 +2234,12 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { && let [ .., trait_path_segment @ hir::PathSegment { - res: Some(rustc_hir::def::Res::Def(rustc_hir::def::DefKind::Trait, trait_id)), + res: rustc_hir::def::Res::Def(rustc_hir::def::DefKind::Trait, trait_id), .. }, hir::PathSegment { ident: assoc_item_name, - res: Some(rustc_hir::def::Res::Def(_, item_id)), + res: rustc_hir::def::Res::Def(_, item_id), .. } ] = path.segments @@ -2169,13 +2284,22 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { trait_impls.non_blanket_impls().len() ) }; - + let mut suggestions = vec![( + trait_path_segment.ident.span.shrink_to_lo(), + format!("<{} as ", self.tcx.type_of(impl_def_id)) + )]; + if let Some(generic_arg) = trait_path_segment.args { + let between_span = trait_path_segment.ident.span.between(generic_arg.span_ext); + // get rid of :: between Trait and <type> + // must be '::' between them, otherwise the parser won't accept the code + suggestions.push((between_span, "".to_string(),)); + suggestions.push((generic_arg.span_ext.shrink_to_hi(), format!(">"))); + } else { + suggestions.push((trait_path_segment.ident.span.shrink_to_hi(), format!(">"))); + } err.multipart_suggestion( message, - vec![ - (trait_path_segment.ident.span.shrink_to_lo(), format!("<{} as ", self.tcx.def_path(impl_def_id).to_string_no_crate_verbose())), - (trait_path_segment.ident.span.shrink_to_hi(), format!(">")) - ], + suggestions, Applicability::MaybeIncorrect ); } @@ -2220,7 +2344,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { .substs .iter() .chain(Some(data.term.into_arg())) - .find(|g| g.has_infer_types_or_consts()); + .find(|g| g.has_non_region_infer()); if let Some(subst) = subst { let mut err = self.emit_inference_failure_err( body_id, @@ -2249,7 +2373,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { if predicate.references_error() || self.is_tainted_by_errors() { return; } - let subst = data.substs.iter().find(|g| g.has_infer_types_or_consts()); + let subst = data.walk().find(|g| g.is_non_region_infer()); if let Some(subst) = subst { let err = self.emit_inference_failure_err( body_id, @@ -2389,10 +2513,10 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { fn predicate_can_apply( &self, param_env: ty::ParamEnv<'tcx>, - pred: ty::PolyTraitRef<'tcx>, + pred: ty::PolyTraitPredicate<'tcx>, ) -> bool { struct ParamToVarFolder<'a, 'tcx> { - infcx: &'a InferCtxt<'a, 'tcx>, + infcx: &'a InferCtxt<'tcx>, var_map: FxHashMap<Ty<'tcx>, Ty<'tcx>>, } @@ -2433,7 +2557,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { let obligation = Obligation::new( ObligationCause::dummy(), param_env, - cleaned_pred.without_const().to_predicate(selcx.tcx()), + cleaned_pred.to_predicate(selcx.tcx()), ); self.predicate_may_hold(&obligation) @@ -2462,15 +2586,10 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { err: &mut Diagnostic, obligation: &PredicateObligation<'tcx>, ) { - let ( - ty::PredicateKind::Trait(pred), - &ObligationCauseCode::BindingObligation(item_def_id, span), - ) = ( - obligation.predicate.kind().skip_binder(), - obligation.cause.code().peel_derives(), - ) else { - return; - }; + let ty::PredicateKind::Trait(pred) = obligation.predicate.kind().skip_binder() else { return; }; + let (ObligationCauseCode::BindingObligation(item_def_id, span) + | ObligationCauseCode::ExprBindingObligation(item_def_id, span, ..)) + = *obligation.cause.code().peel_derives() else { return; }; debug!(?pred, ?item_def_id, ?span); let (Some(node), true) = ( @@ -2483,12 +2602,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { } #[instrument(level = "debug", skip_all)] - fn maybe_suggest_unsized_generics<'hir>( - &self, - err: &mut Diagnostic, - span: Span, - node: Node<'hir>, - ) { + fn maybe_suggest_unsized_generics(&self, err: &mut Diagnostic, span: Span, node: Node<'tcx>) { let Some(generics) = node.generics() else { return; }; @@ -2539,11 +2653,11 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> { ); } - fn maybe_indirection_for_unsized<'hir>( + fn maybe_indirection_for_unsized( &self, err: &mut Diagnostic, - item: &'hir Item<'hir>, - param: &'hir GenericParam<'hir>, + item: &Item<'tcx>, + param: &GenericParam<'tcx>, ) -> bool { // Suggesting `T: ?Sized` is only valid in an ADT if `T` is only used in a // borrow. `struct S<'a, T: ?Sized>(&'a T);` is valid, `struct S<T: ?Sized>(T);` @@ -2643,82 +2757,6 @@ impl<'v> Visitor<'v> for FindTypeParam { } } -pub fn recursive_type_with_infinite_size_error<'tcx>( - tcx: TyCtxt<'tcx>, - type_def_id: DefId, - spans: Vec<(Span, Option<hir::HirId>)>, -) { - assert!(type_def_id.is_local()); - let span = tcx.def_span(type_def_id); - let path = tcx.def_path_str(type_def_id); - let mut err = - struct_span_err!(tcx.sess, span, E0072, "recursive type `{}` has infinite size", path); - err.span_label(span, "recursive type has infinite size"); - for &(span, _) in &spans { - err.span_label(span, "recursive without indirection"); - } - let msg = format!( - "insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `{}` representable", - path, - ); - if spans.len() <= 4 { - // FIXME(compiler-errors): This suggestion might be erroneous if Box is shadowed - err.multipart_suggestion( - &msg, - spans - .into_iter() - .flat_map(|(span, field_id)| { - if let Some(generic_span) = get_option_generic_from_field_id(tcx, field_id) { - // If we match an `Option` and can grab the span of the Option's generic, then - // suggest boxing the generic arg for a non-null niche optimization. - vec![ - (generic_span.shrink_to_lo(), "Box<".to_string()), - (generic_span.shrink_to_hi(), ">".to_string()), - ] - } else { - vec![ - (span.shrink_to_lo(), "Box<".to_string()), - (span.shrink_to_hi(), ">".to_string()), - ] - } - }) - .collect(), - Applicability::HasPlaceholders, - ); - } else { - err.help(&msg); - } - err.emit(); -} - -/// Extract the span for the generic type `T` of `Option<T>` in a field definition -fn get_option_generic_from_field_id(tcx: TyCtxt<'_>, field_id: Option<hir::HirId>) -> Option<Span> { - let node = tcx.hir().find(field_id?); - - // Expect a field from our field_id - let Some(hir::Node::Field(field_def)) = node - else { bug!("Expected HirId corresponding to FieldDef, found: {:?}", node) }; - - // Match a type that is a simple QPath with no Self - let hir::TyKind::Path(hir::QPath::Resolved(None, path)) = &field_def.ty.kind - else { return None }; - - // Check if the path we're checking resolves to Option - let hir::def::Res::Def(_, did) = path.res - else { return None }; - - // Bail if this path doesn't describe `::core::option::Option` - if !tcx.is_diagnostic_item(sym::Option, did) { - return None; - } - - // Match a single generic arg in the 0th path segment - let generic_arg = path.segments.last()?.args?.args.get(0)?; - - // Take the span out of the type, if it's a type - if let hir::GenericArg::Type(generic_ty) = generic_arg { Some(generic_ty.span) } else { None } -} - /// Summarizes information #[derive(Clone)] pub enum ArgKind { @@ -2763,3 +2801,8 @@ impl<'tcx> ty::TypeVisitor<'tcx> for HasNumericInferVisitor { } } } + +pub enum DefIdOrName { + DefId(DefId), + Name(&'static str), +} diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs index e6907637c..5eef54c63 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs @@ -1,17 +1,17 @@ use super::{ ObligationCauseCode, OnUnimplementedDirective, OnUnimplementedNote, PredicateObligation, }; -use crate::infer::InferCtxt; +use crate::infer::error_reporting::TypeErrCtxt; use rustc_hir as hir; use rustc_hir::def_id::DefId; -use rustc_middle::ty::subst::{Subst, SubstsRef}; +use rustc_middle::ty::SubstsRef; use rustc_middle::ty::{self, GenericParamDefKind}; use rustc_span::symbol::sym; use std::iter; use super::InferCtxtPrivExt; -pub trait InferCtxtExt<'tcx> { +pub trait TypeErrCtxtExt<'tcx> { /*private*/ fn impl_similar_to( &self, @@ -29,7 +29,7 @@ pub trait InferCtxtExt<'tcx> { ) -> OnUnimplementedNote; } -impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { +impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { fn impl_similar_to( &self, trait_ref: ty::PolyTraitRef<'tcx>, @@ -143,7 +143,9 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { } if let ObligationCauseCode::ItemObligation(item) - | ObligationCauseCode::BindingObligation(item, _) = *obligation.cause.code() + | ObligationCauseCode::BindingObligation(item, _) + | ObligationCauseCode::ExprItemObligation(item, ..) + | ObligationCauseCode::ExprBindingObligation(item, ..) = *obligation.cause.code() { // FIXME: maybe also have some way of handling methods // from other traits? That would require name resolution, @@ -162,6 +164,10 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { flags.push((sym::from_desugaring, Some(format!("{:?}", k)))); } + if let ObligationCauseCode::MainFunctionType = obligation.cause.code() { + flags.push((sym::cause, Some("MainFunctionType".to_string()))); + } + // Add all types without trimmed paths. ty::print::with_no_trimmed_paths!({ let generics = self.tcx.generics_of(def_id); @@ -254,7 +260,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { } } } - if let ty::Dynamic(traits, _) = self_ty.kind() { + if let ty::Dynamic(traits, _, _) = self_ty.kind() { for t in traits.iter() { if let ty::ExistentialPredicate::Trait(trait_ref) = t.skip_binder() { flags.push((sym::_Self, Some(self.tcx.def_path_str(trait_ref.def_id)))) 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 219413121..8c41d9d24 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -1,5 +1,5 @@ use super::{ - EvaluationResult, Obligation, ObligationCause, ObligationCauseCode, PredicateObligation, + DefIdOrName, Obligation, ObligationCause, ObligationCauseCode, PredicateObligation, SelectionContext, }; @@ -7,6 +7,7 @@ use crate::autoderef::Autoderef; use crate::infer::InferCtxt; use crate::traits::normalize_to; +use hir::def::CtorOf; use hir::HirId; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::stack::ensure_sufficient_stack; @@ -20,18 +21,17 @@ use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; use rustc_hir::lang_items::LangItem; use rustc_hir::{AsyncGeneratorKind, GeneratorKind, Node}; -use rustc_infer::infer::TyCtxtInferExt; +use rustc_infer::infer::error_reporting::TypeErrCtxt; +use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_infer::infer::LateBoundRegionConversionTime; use rustc_middle::hir::map; use rustc_middle::ty::{ self, suggest_arbitrary_trait_bound, suggest_constraining_type_param, AdtKind, DefIdTree, GeneratorDiagnosticData, GeneratorInteriorTypeCause, Infer, InferTy, IsSuggestable, - ProjectionPredicate, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, - TypeVisitable, + ToPredicate, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitable, }; use rustc_middle::ty::{TypeAndMut, TypeckResults}; -use rustc_session::Limit; -use rustc_span::def_id::LOCAL_CRATE; -use rustc_span::symbol::{kw, sym, Ident, Symbol}; +use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::{BytePos, DesugaringKind, ExpnKind, Span, DUMMY_SP}; use rustc_target::spec::abi; use std::fmt; @@ -63,7 +63,7 @@ impl<'tcx, 'a> GeneratorData<'tcx, 'a> { // meet an obligation fn try_get_upvar_span<F>( &self, - infer_context: &InferCtxt<'a, 'tcx>, + infer_context: &InferCtxt<'tcx>, generator_did: DefId, ty_matches: F, ) -> Option<GeneratorInteriorOrUpvar> @@ -169,12 +169,12 @@ impl<'tcx, 'a> GeneratorData<'tcx, 'a> { } // This trait is public to expose the diagnostics methods to clippy. -pub trait InferCtxtExt<'tcx> { +pub trait TypeErrCtxtExt<'tcx> { fn suggest_restricting_param_bound( &self, err: &mut Diagnostic, trait_pred: ty::PolyTraitPredicate<'tcx>, - proj_pred: Option<ty::PolyProjectionPredicate<'tcx>>, + associated_item: Option<(&'static str, Ty<'tcx>)>, body_id: hir::HirId, ); @@ -256,8 +256,15 @@ pub trait InferCtxtExt<'tcx> { found_span: Option<Span>, found: ty::PolyTraitRef<'tcx>, expected: ty::PolyTraitRef<'tcx>, + cause: &ObligationCauseCode<'tcx>, ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>; + fn note_conflicting_closure_bounds( + &self, + cause: &ObligationCauseCode<'tcx>, + err: &mut DiagnosticBuilder<'tcx, ErrorGuaranteed>, + ); + fn suggest_fully_qualified_path( &self, err: &mut Diagnostic, @@ -297,8 +304,6 @@ pub trait InferCtxtExt<'tcx> { ) where T: fmt::Display; - fn suggest_new_overflow_limit(&self, err: &mut Diagnostic); - /// Suggest to await before try: future? => future.await? fn suggest_await_before_try( &self, @@ -462,12 +467,12 @@ fn suggest_restriction<'tcx>( } } -impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { +impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { fn suggest_restricting_param_bound( &self, mut err: &mut Diagnostic, trait_pred: ty::PolyTraitPredicate<'tcx>, - proj_pred: Option<ty::PolyProjectionPredicate<'tcx>>, + associated_ty: Option<(&'static str, Ty<'tcx>)>, body_id: hir::HirId, ) { let trait_pred = self.resolve_numeric_literals_with_default(trait_pred); @@ -604,21 +609,18 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { trait_pred.print_modifiers_and_trait_path().to_string() ); - if let Some(proj_pred) = proj_pred { - let ProjectionPredicate { projection_ty, term } = proj_pred.skip_binder(); - let item = self.tcx.associated_item(projection_ty.item_def_id); - + if let Some((name, term)) = associated_ty { // FIXME: this case overlaps with code in TyCtxt::note_and_explain_type_err. // That should be extracted into a helper function. if constraint.ends_with('>') { constraint = format!( - "{}, {}={}>", + "{}, {} = {}>", &constraint[..constraint.len() - 1], - item.name, + name, term ); } else { - constraint.push_str(&format!("<{}={}>", item.name, term)); + constraint.push_str(&format!("<{} = {}>", name, term)); } } @@ -648,7 +650,13 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { .. }) if !param_ty => { // Missing generic type parameter bound. - if suggest_arbitrary_trait_bound(self.tcx, generics, &mut err, trait_pred) { + if suggest_arbitrary_trait_bound( + self.tcx, + generics, + &mut err, + trait_pred, + associated_ty, + ) { return; } } @@ -657,7 +665,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { _ => {} } - hir_id = self.tcx.hir().local_def_id_to_hir_id(self.tcx.hir().get_parent_item(hir_id)); + hir_id = self.tcx.hir().get_parent_item(hir_id).into(); } } @@ -671,11 +679,15 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { trait_pred: ty::PolyTraitPredicate<'tcx>, ) -> bool { // It only make sense when suggesting dereferences for arguments - let ObligationCauseCode::FunctionArgumentObligation { .. } = obligation.cause.code() else { - return false; - }; - let param_env = obligation.param_env; - let body_id = obligation.cause.body_id; + let ObligationCauseCode::FunctionArgumentObligation { arg_hir_id, .. } = obligation.cause.code() + else { return false; }; + let Some(typeck_results) = &self.typeck_results + else { return false; }; + let hir::Node::Expr(expr) = self.tcx.hir().get(*arg_hir_id) + else { return false; }; + let Some(arg_ty) = typeck_results.expr_ty_adjusted_opt(expr) + else { return false; }; + let span = obligation.cause.span; let mut real_trait_pred = trait_pred; let mut code = obligation.cause.code(); @@ -685,11 +697,25 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { real_trait_pred = parent_trait_pred; } - // Skipping binder here, remapping below - let real_ty = real_trait_pred.self_ty().skip_binder(); + let real_ty = real_trait_pred.self_ty(); + // We `erase_late_bound_regions` here because `make_subregion` does not handle + // `ReLateBound`, and we don't particularly care about the regions. + if self + .can_eq(obligation.param_env, self.tcx.erase_late_bound_regions(real_ty), arg_ty) + .is_err() + { + continue; + } - if let ty::Ref(region, base_ty, mutbl) = *real_ty.kind() { - let mut autoderef = Autoderef::new(self, param_env, body_id, span, base_ty, span); + if let ty::Ref(region, base_ty, mutbl) = *real_ty.skip_binder().kind() { + let mut autoderef = Autoderef::new( + self, + obligation.param_env, + obligation.cause.body_id, + span, + base_ty, + span, + ); if let Some(steps) = autoderef.find_map(|(ty, steps)| { // Re-add the `&` let ty = self.tcx.mk_ref(region, TypeAndMut { ty, mutbl }); @@ -697,24 +723,29 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { // Remapping bound vars here let real_trait_pred_and_ty = real_trait_pred.map_bound(|inner_trait_pred| (inner_trait_pred, ty)); - let obligation = self - .mk_trait_obligation_with_new_self_ty(param_env, real_trait_pred_and_ty); + let obligation = self.mk_trait_obligation_with_new_self_ty( + obligation.param_env, + real_trait_pred_and_ty, + ); Some(steps).filter(|_| self.predicate_may_hold(&obligation)) }) { if steps > 0 { - if let Ok(src) = self.tcx.sess.source_map().span_to_snippet(span) { - // Don't care about `&mut` because `DerefMut` is used less - // often and user will not expect autoderef happens. - if src.starts_with('&') && !src.starts_with("&mut ") { - let derefs = "*".repeat(steps); - err.span_suggestion( - span, - "consider dereferencing here", - format!("&{}{}", derefs, &src[1..]), - Applicability::MachineApplicable, - ); - return true; - } + // Don't care about `&mut` because `DerefMut` is used less + // often and user will not expect autoderef happens. + if let Some(hir::Node::Expr(hir::Expr { + kind: + hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, expr), + .. + })) = self.tcx.hir().find(*arg_hir_id) + { + let derefs = "*".repeat(steps); + err.span_suggestion_verbose( + expr.span.shrink_to_lo(), + "consider dereferencing here", + derefs, + Applicability::MachineApplicable, + ); + return true; } } } else if real_trait_pred != trait_pred { @@ -724,7 +755,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { let real_trait_pred_and_base_ty = real_trait_pred.map_bound(|inner_trait_pred| (inner_trait_pred, base_ty)); let obligation = self.mk_trait_obligation_with_new_self_ty( - param_env, + obligation.param_env, real_trait_pred_and_base_ty, ); if self.predicate_may_hold(&obligation) { @@ -750,7 +781,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { // Get the local name of this closure. This can be inaccurate because // of the possibility of reassignment, but this should be good enough. match &kind { - hir::PatKind::Binding(hir::BindingAnnotation::Unannotated, _, ident, None) => { + hir::PatKind::Binding(hir::BindingAnnotation::NONE, _, ident, None) => { Some(ident.name) } _ => { @@ -783,75 +814,138 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { err: &mut Diagnostic, trait_pred: ty::PolyTraitPredicate<'tcx>, ) -> bool { - // Skipping binder here, remapping below - let self_ty = trait_pred.self_ty().skip_binder(); - - let (def_id, output_ty, callable) = match *self_ty.kind() { - ty::Closure(def_id, substs) => (def_id, substs.as_closure().sig().output(), "closure"), - ty::FnDef(def_id, _) => (def_id, self_ty.fn_sig(self.tcx).output(), "function"), - _ => return false, - }; - let msg = format!("use parentheses to call the {}", callable); + if let ty::PredicateKind::Trait(trait_pred) = obligation.predicate.kind().skip_binder() + && Some(trait_pred.def_id()) == self.tcx.lang_items().sized_trait() + { + // Don't suggest calling to turn an unsized type into a sized type + return false; + } - // "We should really create a single list of bound vars from the combined vars - // from the predicate and function, but instead we just liberate the function bound vars" - let output_ty = self.tcx.liberate_late_bound_regions(def_id, output_ty); + // This is duplicated from `extract_callable_info` in typeck, which + // relies on autoderef, so we can't use it here. + let found = trait_pred.self_ty().skip_binder().peel_refs(); + let Some((def_id_or_name, output, inputs)) = (match *found.kind() + { + ty::FnPtr(fn_sig) => { + Some((DefIdOrName::Name("function pointer"), fn_sig.output(), fn_sig.inputs())) + } + ty::FnDef(def_id, _) => { + let fn_sig = found.fn_sig(self.tcx); + Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs())) + } + ty::Closure(def_id, substs) => { + let fn_sig = substs.as_closure().sig(); + Some(( + DefIdOrName::DefId(def_id), + fn_sig.output(), + fn_sig.inputs().map_bound(|inputs| &inputs[1..]), + )) + } + ty::Opaque(def_id, substs) => { + self.tcx.bound_item_bounds(def_id).subst(self.tcx, substs).iter().find_map(|pred| { + if let ty::PredicateKind::Projection(proj) = pred.kind().skip_binder() + && Some(proj.projection_ty.item_def_id) == self.tcx.lang_items().fn_once_output() + // args tuple will always be substs[1] + && let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind() + { + Some(( + DefIdOrName::DefId(def_id), + pred.kind().rebind(proj.term.ty().unwrap()), + pred.kind().rebind(args.as_slice()), + )) + } else { + None + } + }) + } + ty::Dynamic(data, _, ty::Dyn) => { + data.iter().find_map(|pred| { + if let ty::ExistentialPredicate::Projection(proj) = pred.skip_binder() + && Some(proj.item_def_id) == self.tcx.lang_items().fn_once_output() + // for existential projection, substs are shifted over by 1 + && let ty::Tuple(args) = proj.substs.type_at(0).kind() + { + Some(( + DefIdOrName::Name("trait object"), + pred.rebind(proj.term.ty().unwrap()), + pred.rebind(args.as_slice()), + )) + } else { + None + } + }) + } + ty::Param(_) => { + obligation.param_env.caller_bounds().iter().find_map(|pred| { + if let ty::PredicateKind::Projection(proj) = pred.kind().skip_binder() + && Some(proj.projection_ty.item_def_id) == self.tcx.lang_items().fn_once_output() + && proj.projection_ty.self_ty() == found + // args tuple will always be substs[1] + && let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind() + { + Some(( + DefIdOrName::Name("type parameter"), + pred.kind().rebind(proj.term.ty().unwrap()), + pred.kind().rebind(args.as_slice()), + )) + } else { + None + } + }) + } + _ => None, + }) else { return false; }; + let output = self.replace_bound_vars_with_fresh_vars( + obligation.cause.span, + LateBoundRegionConversionTime::FnCall, + output, + ); + let inputs = inputs.skip_binder().iter().map(|ty| { + self.replace_bound_vars_with_fresh_vars( + obligation.cause.span, + LateBoundRegionConversionTime::FnCall, + inputs.rebind(*ty), + ) + }); // Remapping bound vars here - let trait_pred_and_self = trait_pred.map_bound(|trait_pred| (trait_pred, output_ty)); + let trait_pred_and_self = trait_pred.map_bound(|trait_pred| (trait_pred, output)); let new_obligation = self.mk_trait_obligation_with_new_self_ty(obligation.param_env, trait_pred_and_self); - - match self.evaluate_obligation(&new_obligation) { - Ok( - EvaluationResult::EvaluatedToOk - | EvaluationResult::EvaluatedToOkModuloRegions - | EvaluationResult::EvaluatedToOkModuloOpaqueTypes - | EvaluationResult::EvaluatedToAmbig, - ) => {} - _ => return false, + if !self.predicate_must_hold_modulo_regions(&new_obligation) { + return false; } - let hir = self.tcx.hir(); + // Get the name of the callable and the arguments to be used in the suggestion. - let (snippet, sugg) = match hir.get_if_local(def_id) { - Some(hir::Node::Expr(hir::Expr { - kind: hir::ExprKind::Closure(hir::Closure { fn_decl, fn_decl_span, .. }), - .. - })) => { - err.span_label(*fn_decl_span, "consider calling this closure"); - let Some(name) = self.get_closure_name(def_id, err, &msg) else { - return false; - }; - let args = fn_decl.inputs.iter().map(|_| "_").collect::<Vec<_>>().join(", "); - let sugg = format!("({})", args); - (format!("{}{}", name, sugg), sugg) - } - Some(hir::Node::Item(hir::Item { - ident, - kind: hir::ItemKind::Fn(.., body_id), - .. - })) => { - err.span_label(ident.span, "consider calling this function"); - let body = hir.body(*body_id); - let args = body - .params - .iter() - .map(|arg| match &arg.pat.kind { - hir::PatKind::Binding(_, _, ident, None) - // FIXME: provide a better suggestion when encountering `SelfLower`, it - // should suggest a method call. - if ident.name != kw::SelfLower => ident.to_string(), - _ => "_".to_string(), - }) - .collect::<Vec<_>>() - .join(", "); - let sugg = format!("({})", args); - (format!("{}{}", ident, sugg), sugg) - } - _ => return false, + let hir = self.tcx.hir(); + + let msg = match def_id_or_name { + DefIdOrName::DefId(def_id) => match self.tcx.def_kind(def_id) { + DefKind::Ctor(CtorOf::Struct, _) => { + "use parentheses to construct this tuple struct".to_string() + } + DefKind::Ctor(CtorOf::Variant, _) => { + "use parentheses to construct this tuple variant".to_string() + } + kind => format!("use parentheses to call this {}", kind.descr(def_id)), + }, + DefIdOrName::Name(name) => format!("use parentheses to call this {name}"), }; + + let args = inputs + .map(|ty| { + if ty.is_suggestable(self.tcx, false) { + format!("/* {ty} */") + } else { + "/* value */".to_string() + } + }) + .collect::<Vec<_>>() + .join(", "); + if matches!(obligation.cause.code(), ObligationCauseCode::FunctionArgumentObligation { .. }) + && obligation.cause.span.can_be_used_for_suggestions() { // When the obligation error has been ensured to have been caused by // an argument, the `obligation.cause.span` points at the expression @@ -860,11 +954,36 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { err.span_suggestion_verbose( obligation.cause.span.shrink_to_hi(), &msg, - sugg, + format!("({args})"), Applicability::HasPlaceholders, ); - } else { - err.help(&format!("{}: `{}`", msg, snippet)); + } else if let DefIdOrName::DefId(def_id) = def_id_or_name { + let name = match hir.get_if_local(def_id) { + Some(hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::Closure(hir::Closure { fn_decl_span, .. }), + .. + })) => { + err.span_label(*fn_decl_span, "consider calling this closure"); + let Some(name) = self.get_closure_name(def_id, err, &msg) else { + return false; + }; + name.to_string() + } + Some(hir::Node::Item(hir::Item { ident, kind: hir::ItemKind::Fn(..), .. })) => { + err.span_label(ident.span, "consider calling this function"); + ident.to_string() + } + Some(hir::Node::Ctor(..)) => { + let name = self.tcx.def_path_str(def_id); + err.span_label( + self.tcx.def_span(def_id), + format!("consider calling the constructor for `{}`", name), + ); + name + } + _ => return false, + }; + err.help(&format!("{msg}: `{name}({args})`")); } true } @@ -882,6 +1001,10 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { obligation.cause.code() { &parent_code + } else if let ObligationCauseCode::ItemObligation(_) + | ObligationCauseCode::ExprItemObligation(..) = obligation.cause.code() + { + obligation.cause.code() } else if let ExpnKind::Desugaring(DesugaringKind::ForLoop) = span.ctxt().outer_expn_data().kind { @@ -906,102 +1029,121 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { let param_env = obligation.param_env; // Try to apply the original trait binding obligation by borrowing. - let mut try_borrowing = - |old_pred: ty::PolyTraitPredicate<'tcx>, blacklist: &[DefId]| -> bool { - if blacklist.contains(&old_pred.def_id()) { - return false; - } - // We map bounds to `&T` and `&mut T` - let trait_pred_and_imm_ref = old_pred.map_bound(|trait_pred| { - ( - trait_pred, - self.tcx.mk_imm_ref(self.tcx.lifetimes.re_static, trait_pred.self_ty()), - ) - }); - let trait_pred_and_mut_ref = old_pred.map_bound(|trait_pred| { + let mut try_borrowing = |old_pred: ty::PolyTraitPredicate<'tcx>, + blacklist: &[DefId]| + -> bool { + if blacklist.contains(&old_pred.def_id()) { + return false; + } + // We map bounds to `&T` and `&mut T` + let trait_pred_and_imm_ref = old_pred.map_bound(|trait_pred| { + ( + trait_pred, + self.tcx.mk_imm_ref(self.tcx.lifetimes.re_static, trait_pred.self_ty()), + ) + }); + let trait_pred_and_mut_ref = old_pred.map_bound(|trait_pred| { + ( + trait_pred, + self.tcx.mk_mut_ref(self.tcx.lifetimes.re_static, trait_pred.self_ty()), + ) + }); + + let mk_result = |trait_pred_and_new_ty| { + let obligation = + self.mk_trait_obligation_with_new_self_ty(param_env, trait_pred_and_new_ty); + self.predicate_must_hold_modulo_regions(&obligation) + }; + let imm_ref_self_ty_satisfies_pred = mk_result(trait_pred_and_imm_ref); + let mut_ref_self_ty_satisfies_pred = mk_result(trait_pred_and_mut_ref); + + let (ref_inner_ty_satisfies_pred, ref_inner_ty_mut) = + if let ObligationCauseCode::ItemObligation(_) | ObligationCauseCode::ExprItemObligation(..) = obligation.cause.code() + && let ty::Ref(_, ty, mutability) = old_pred.self_ty().skip_binder().kind() + { ( - trait_pred, - self.tcx.mk_mut_ref(self.tcx.lifetimes.re_static, trait_pred.self_ty()), + mk_result(old_pred.map_bound(|trait_pred| (trait_pred, *ty))), + matches!(mutability, hir::Mutability::Mut), ) - }); - - let mk_result = |trait_pred_and_new_ty| { - let obligation = - self.mk_trait_obligation_with_new_self_ty(param_env, trait_pred_and_new_ty); - self.predicate_must_hold_modulo_regions(&obligation) + } else { + (false, false) }; - let imm_result = mk_result(trait_pred_and_imm_ref); - let mut_result = mk_result(trait_pred_and_mut_ref); - - if imm_result || mut_result { - if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { - // We have a very specific type of error, where just borrowing this argument - // might solve the problem. In cases like this, the important part is the - // original type obligation, not the last one that failed, which is arbitrary. - // Because of this, we modify the error to refer to the original obligation and - // return early in the caller. - - let msg = format!("the trait bound `{}` is not satisfied", old_pred); - if has_custom_message { - err.note(&msg); - } else { - err.message = - vec![(rustc_errors::DiagnosticMessage::Str(msg), Style::NoStyle)]; - } - if snippet.starts_with('&') { - // This is already a literal borrow and the obligation is failing - // somewhere else in the obligation chain. Do not suggest non-sense. - return false; - } - err.span_label( - span, + + if imm_ref_self_ty_satisfies_pred + || mut_ref_self_ty_satisfies_pred + || ref_inner_ty_satisfies_pred + { + if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { + // We don't want a borrowing suggestion on the fields in structs, + // ``` + // struct Foo { + // the_foos: Vec<Foo> + // } + // ``` + if !matches!( + span.ctxt().outer_expn_data().kind, + ExpnKind::Root | ExpnKind::Desugaring(DesugaringKind::ForLoop) + ) { + return false; + } + if snippet.starts_with('&') { + // This is already a literal borrow and the obligation is failing + // somewhere else in the obligation chain. Do not suggest non-sense. + return false; + } + // We have a very specific type of error, where just borrowing this argument + // might solve the problem. In cases like this, the important part is the + // original type obligation, not the last one that failed, which is arbitrary. + // Because of this, we modify the error to refer to the original obligation and + // return early in the caller. + + let msg = format!("the trait bound `{}` is not satisfied", old_pred); + if has_custom_message { + err.note(&msg); + } else { + err.message = + vec![(rustc_errors::DiagnosticMessage::Str(msg), Style::NoStyle)]; + } + err.span_label( + span, + format!( + "the trait `{}` is not implemented for `{}`", + old_pred.print_modifiers_and_trait_path(), + old_pred.self_ty().skip_binder(), + ), + ); + + if imm_ref_self_ty_satisfies_pred && mut_ref_self_ty_satisfies_pred { + err.span_suggestions( + span.shrink_to_lo(), + "consider borrowing here", + ["&".to_string(), "&mut ".to_string()].into_iter(), + Applicability::MaybeIncorrect, + ); + } else { + let is_mut = mut_ref_self_ty_satisfies_pred || ref_inner_ty_mut; + err.span_suggestion_verbose( + span.shrink_to_lo(), &format!( - "expected an implementor of trait `{}`", - old_pred.print_modifiers_and_trait_path(), + "consider{} borrowing here", + if is_mut { " mutably" } else { "" } ), + format!("&{}", if is_mut { "mut " } else { "" }), + Applicability::MaybeIncorrect, ); - - // This if is to prevent a special edge-case - if matches!( - span.ctxt().outer_expn_data().kind, - ExpnKind::Root | ExpnKind::Desugaring(DesugaringKind::ForLoop) - ) { - // We don't want a borrowing suggestion on the fields in structs, - // ``` - // struct Foo { - // the_foos: Vec<Foo> - // } - // ``` - - if imm_result && mut_result { - err.span_suggestions( - span.shrink_to_lo(), - "consider borrowing here", - ["&".to_string(), "&mut ".to_string()].into_iter(), - Applicability::MaybeIncorrect, - ); - } else { - err.span_suggestion_verbose( - span.shrink_to_lo(), - &format!( - "consider{} borrowing here", - if mut_result { " mutably" } else { "" } - ), - format!("&{}", if mut_result { "mut " } else { "" }), - Applicability::MaybeIncorrect, - ); - } - } - return true; } + return true; } - return false; - }; + } + return false; + }; if let ObligationCauseCode::ImplDerivedObligation(cause) = &*code { try_borrowing(cause.derived.parent_trait_pred, &[]) } else if let ObligationCauseCode::BindingObligation(_, _) - | ObligationCauseCode::ItemObligation(_) = code + | ObligationCauseCode::ItemObligation(_) + | ObligationCauseCode::ExprItemObligation(..) + | ObligationCauseCode::ExprBindingObligation(..) = code { try_borrowing(poly_trait_pred, &never_suggest_borrow) } else { @@ -1017,7 +1159,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { self_ty: Ty<'tcx>, object_ty: Ty<'tcx>, ) { - let ty::Dynamic(predicates, _) = object_ty.kind() else { return; }; + let ty::Dynamic(predicates, _, ty::Dyn) = object_ty.kind() else { return; }; let self_ref_ty = self.tcx.mk_imm_ref(self.tcx.lifetimes.re_erased, self_ty); for predicate in predicates.iter() { @@ -1110,8 +1252,8 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { // and if not maybe suggest doing something else? If we kept the expression around we // could also check if it is an fn call (very likely) and suggest changing *that*, if // it is from the local crate. - err.span_suggestion_verbose( - expr.span.shrink_to_hi().with_hi(span.hi()), + err.span_suggestion( + span, "remove the `.await`", "", Applicability::MachineApplicable, @@ -1126,8 +1268,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { &format!("this call returns `{}`", pred.self_ty()), ); } - if let Some(typeck_results) = - self.in_progress_typeck_results.map(|t| t.borrow()) + if let Some(typeck_results) = &self.typeck_results && let ty = typeck_results.expr_ty_adjusted(base) && let ty::FnDef(def_id, _substs) = ty.kind() && let Some(hir::Node::Item(hir::Item { ident, span, vis_span, .. })) = @@ -1181,7 +1322,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { return; } let trait_pred = self.resolve_vars_if_possible(trait_pred); - if trait_pred.has_infer_types_or_consts() { + if trait_pred.has_non_region_infer() { // Do not ICE while trying to find if a reborrow would succeed on a trait with // unresolved bindings. return; @@ -1250,8 +1391,8 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { && let Some(stmt) = blk.stmts.last() && let hir::StmtKind::Semi(expr) = stmt.kind // Only suggest this if the expression behind the semicolon implements the predicate - && let Some(typeck_results) = self.in_progress_typeck_results - && let Some(ty) = typeck_results.borrow().expr_ty_opt(expr) + && let Some(typeck_results) = &self.typeck_results + && let Some(ty) = typeck_results.expr_ty_opt(expr) && self.predicate_may_hold(&self.mk_trait_obligation_with_new_self_ty( obligation.param_env, trait_pred.map_bound(|trait_pred| (trait_pred, ty)) )) @@ -1315,7 +1456,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { let trait_pred = self.resolve_vars_if_possible(trait_pred); let ty = trait_pred.skip_binder().self_ty(); let is_object_safe = match ty.kind() { - ty::Dynamic(predicates, _) => { + ty::Dynamic(predicates, _, ty::Dyn) => { // If the `dyn Trait` is not object safe, do not suggest `Box<dyn Trait>`. predicates .principal_def_id() @@ -1340,7 +1481,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { let mut visitor = ReturnsVisitor::default(); visitor.visit_body(&body); - let typeck_results = self.in_progress_typeck_results.map(|t| t.borrow()).unwrap(); + let typeck_results = self.typeck_results.as_ref().unwrap(); let Some(liberated_sig) = typeck_results.liberated_fn_sigs().get(fn_hir_id).copied() else { return false; }; let ret_types = visitor @@ -1375,7 +1516,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { let mut spans_and_needs_box = vec![]; match liberated_sig.output().kind() { - ty::Dynamic(predicates, _) => { + ty::Dynamic(predicates, _, ty::Dyn) => { let cause = ObligationCause::misc(ret_ty.span, fn_hir_id); let param_env = ty::ParamEnv::empty(); @@ -1523,7 +1664,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { // Point at all the `return`s in the function as they have failed trait bounds. let mut visitor = ReturnsVisitor::default(); visitor.visit_body(&body); - let typeck_results = self.in_progress_typeck_results.map(|t| t.borrow()).unwrap(); + let typeck_results = self.typeck_results.as_ref().unwrap(); for expr in &visitor.returns { if let Some(returned_ty) = typeck_results.node_type_opt(expr.hir_id) { let ty = self.resolve_vars_if_possible(returned_ty); @@ -1539,34 +1680,41 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { found_span: Option<Span>, found: ty::PolyTraitRef<'tcx>, expected: ty::PolyTraitRef<'tcx>, + cause: &ObligationCauseCode<'tcx>, ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { pub(crate) fn build_fn_sig_ty<'tcx>( - tcx: TyCtxt<'tcx>, + infcx: &InferCtxt<'tcx>, trait_ref: ty::PolyTraitRef<'tcx>, ) -> Ty<'tcx> { let inputs = trait_ref.skip_binder().substs.type_at(1); let sig = match inputs.kind() { ty::Tuple(inputs) - if tcx.fn_trait_kind_from_lang_item(trait_ref.def_id()).is_some() => + if infcx.tcx.fn_trait_kind_from_lang_item(trait_ref.def_id()).is_some() => { - tcx.mk_fn_sig( + infcx.tcx.mk_fn_sig( inputs.iter(), - tcx.mk_ty_infer(ty::TyVar(ty::TyVid::from_u32(0))), + infcx.next_ty_var(TypeVariableOrigin { + span: DUMMY_SP, + kind: TypeVariableOriginKind::MiscVariable, + }), false, hir::Unsafety::Normal, abi::Abi::Rust, ) } - _ => tcx.mk_fn_sig( + _ => infcx.tcx.mk_fn_sig( std::iter::once(inputs), - tcx.mk_ty_infer(ty::TyVar(ty::TyVid::from_u32(0))), + infcx.next_ty_var(TypeVariableOrigin { + span: DUMMY_SP, + kind: TypeVariableOriginKind::MiscVariable, + }), false, hir::Unsafety::Normal, abi::Abi::Rust, ), }; - tcx.mk_fn_ptr(trait_ref.rebind(sig)) + infcx.tcx.mk_fn_ptr(trait_ref.rebind(sig)) } let argument_kind = match expected.skip_binder().self_ty().kind() { @@ -1586,18 +1734,76 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { let found_span = found_span.unwrap_or(span); err.span_label(found_span, "found signature defined here"); - let expected = build_fn_sig_ty(self.tcx, expected); - let found = build_fn_sig_ty(self.tcx, found); + let expected = build_fn_sig_ty(self, expected); + let found = build_fn_sig_ty(self, found); - let (expected_str, found_str) = - self.tcx.infer_ctxt().enter(|infcx| infcx.cmp(expected, found)); + let (expected_str, found_str) = self.cmp(expected, found); let signature_kind = format!("{argument_kind} signature"); err.note_expected_found(&signature_kind, expected_str, &signature_kind, found_str); + self.note_conflicting_closure_bounds(cause, &mut err); + err } + // Add a note if there are two `Fn`-family bounds that have conflicting argument + // requirements, which will always cause a closure to have a type error. + fn note_conflicting_closure_bounds( + &self, + cause: &ObligationCauseCode<'tcx>, + err: &mut DiagnosticBuilder<'tcx, ErrorGuaranteed>, + ) { + // First, look for an `ExprBindingObligation`, which means we can get + // the unsubstituted predicate list of the called function. And check + // that the predicate that we failed to satisfy is a `Fn`-like trait. + if let ObligationCauseCode::ExprBindingObligation(def_id, _, _, idx) = cause + && let predicates = self.tcx.predicates_of(def_id).instantiate_identity(self.tcx) + && let Some(pred) = predicates.predicates.get(*idx) + && let ty::PredicateKind::Trait(trait_pred) = pred.kind().skip_binder() + && ty::ClosureKind::from_def_id(self.tcx, trait_pred.def_id()).is_some() + { + let expected_self = + self.tcx.anonymize_late_bound_regions(pred.kind().rebind(trait_pred.self_ty())); + let expected_substs = self + .tcx + .anonymize_late_bound_regions(pred.kind().rebind(trait_pred.trait_ref.substs)); + + // Find another predicate whose self-type is equal to the expected self type, + // but whose substs don't match. + let other_pred = std::iter::zip(&predicates.predicates, &predicates.spans) + .enumerate() + .find(|(other_idx, (pred, _))| match pred.kind().skip_binder() { + ty::PredicateKind::Trait(trait_pred) + if ty::ClosureKind::from_def_id(self.tcx, trait_pred.def_id()) + .is_some() + && other_idx != idx + // Make sure that the self type matches + // (i.e. constraining this closure) + && expected_self + == self.tcx.anonymize_late_bound_regions( + pred.kind().rebind(trait_pred.self_ty()), + ) + // But the substs don't match (i.e. incompatible args) + && expected_substs + != self.tcx.anonymize_late_bound_regions( + pred.kind().rebind(trait_pred.trait_ref.substs), + ) => + { + true + } + _ => false, + }); + // If we found one, then it's very likely the cause of the error. + if let Some((_, (_, other_pred_span))) = other_pred { + err.span_note( + *other_pred_span, + "closure inferred to have a different signature due to this bound", + ); + } + } + } + fn suggest_fully_qualified_path( &self, err: &mut Diagnostic, @@ -1786,12 +1992,11 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { let span = self.tcx.def_span(generator_did); - let in_progress_typeck_results = self.in_progress_typeck_results.map(|t| t.borrow()); let generator_did_root = self.tcx.typeck_root_def_id(generator_did); debug!( ?generator_did, ?generator_did_root, - in_progress_typeck_results.hir_owner = ?in_progress_typeck_results.as_ref().map(|t| t.hir_owner), + typeck_results.hir_owner = ?self.typeck_results.as_ref().map(|t| t.hir_owner), ?span, ); @@ -1846,7 +2051,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { // type-checking; otherwise, get them by performing a query. This is needed to avoid // cycles. If we can't use resolved types because the generator comes from another crate, // we still provide a targeted error but without all the relevant spans. - let generator_data: Option<GeneratorData<'tcx, '_>> = match &in_progress_typeck_results { + let generator_data: Option<GeneratorData<'tcx, '_>> = match &self.typeck_results { Some(t) if t.hir_owner.to_def_id() == generator_did_root => { Some(GeneratorData::Local(&t)) } @@ -2201,7 +2406,8 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { | ObligationCauseCode::QuestionMark | ObligationCauseCode::CheckAssociatedTypeBounds { .. } | ObligationCauseCode::LetElse - | ObligationCauseCode::BinOp { .. } => {} + | ObligationCauseCode::BinOp { .. } + | ObligationCauseCode::AscribeUserTypeProvePredicate(..) => {} ObligationCauseCode::SliceOrArrayElem => { err.note("slice and array elements must have `Sized` type"); } @@ -2223,11 +2429,13 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { region, object_ty, )); } - ObligationCauseCode::ItemObligation(_item_def_id) => { + ObligationCauseCode::ItemObligation(_) + | ObligationCauseCode::ExprItemObligation(..) => { // We hold the `DefId` of the item introducing the obligation, but displaying it // doesn't add user usable information. It always point at an associated item. } - ObligationCauseCode::BindingObligation(item_def_id, span) => { + ObligationCauseCode::BindingObligation(item_def_id, span) + | ObligationCauseCode::ExprBindingObligation(item_def_id, span, ..) => { let item_name = tcx.def_path_str(item_def_id); let mut multispan = MultiSpan::from(span); if let Some(ident) = tcx.opt_item_ident(item_def_id) { @@ -2537,9 +2745,9 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { parent_trait_pred.remap_constness_diag(param_env); let parent_def_id = parent_trait_pred.def_id(); let msg = format!( - "required because of the requirements on the impl of `{}` for `{}`", - parent_trait_pred.print_modifiers_and_trait_path(), - parent_trait_pred.skip_binder().self_ty() + "required for `{}` to implement `{}`", + parent_trait_pred.skip_binder().self_ty(), + parent_trait_pred.print_modifiers_and_trait_path() ); let mut is_auto_trait = false; match self.tcx.hir().get_if_local(data.impl_def_id) { @@ -2608,9 +2816,9 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { pluralize!(count) )); err.note(&format!( - "required because of the requirements on the impl of `{}` for `{}`", - parent_trait_pred.print_modifiers_and_trait_path(), - parent_trait_pred.skip_binder().self_ty() + "required for `{}` to implement `{}`", + parent_trait_pred.skip_binder().self_ty(), + parent_trait_pred.print_modifiers_and_trait_path() )); } // #74711: avoid a stack overflow @@ -2649,19 +2857,18 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { if let Some(Node::Expr(expr @ hir::Expr { kind: hir::ExprKind::Block(..), .. })) = hir.find(arg_hir_id) { - let in_progress_typeck_results = - self.in_progress_typeck_results.map(|t| t.borrow()); let parent_id = hir.get_parent_item(arg_hir_id); - let typeck_results: &TypeckResults<'tcx> = match &in_progress_typeck_results { + let typeck_results: &TypeckResults<'tcx> = match &self.typeck_results { Some(t) if t.hir_owner == parent_id => t, - _ => self.tcx.typeck(parent_id), + _ => self.tcx.typeck(parent_id.def_id), }; - let ty = typeck_results.expr_ty_adjusted(expr); - let span = expr.peel_blocks().span; + let expr = expr.peel_blocks(); + let ty = typeck_results.expr_ty_adjusted_opt(expr).unwrap_or(tcx.ty_error()); + let span = expr.span; if Some(span) != err.span.primary_span() { err.span_label( span, - &if ty.references_error() { + if ty.references_error() { String::new() } else { format!("this tail expression is of type `{:?}`", ty) @@ -2738,19 +2945,6 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { } } - fn suggest_new_overflow_limit(&self, err: &mut Diagnostic) { - let suggested_limit = match self.tcx.recursion_limit() { - Limit(0) => Limit(2), - limit => limit * 2, - }; - err.help(&format!( - "consider increasing the recursion limit by adding a \ - `#![recursion_limit = \"{}\"]` attribute to your crate (`{}`)", - suggested_limit, - self.tcx.crate_name(LOCAL_CRATE), - )); - } - #[instrument( level = "debug", skip(self, err), fields(trait_pred.self_ty = ?trait_pred.self_ty()) )] @@ -2832,19 +3026,15 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { ObligationCauseCode::BinOp { rhs_span: Some(span), is_lit, .. } if *is_lit => span, _ => return, }; - match ( - trait_ref.skip_binder().self_ty().kind(), - trait_ref.skip_binder().substs.type_at(1).kind(), - ) { - (ty::Float(_), ty::Infer(InferTy::IntVar(_))) => { - err.span_suggestion_verbose( - rhs_span.shrink_to_hi(), - "consider using a floating-point literal by writing it with `.0`", - ".0", - Applicability::MaybeIncorrect, - ); - } - _ => {} + if let ty::Float(_) = trait_ref.skip_binder().self_ty().kind() + && let ty::Infer(InferTy::IntVar(_)) = trait_ref.skip_binder().substs.type_at(1).kind() + { + err.span_suggestion_verbose( + rhs_span.shrink_to_hi(), + "consider using a floating-point literal by writing it with `.0`", + ".0", + Applicability::MaybeIncorrect, + ); } } diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 556ef466c..a417e1440 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -25,10 +25,9 @@ use super::Unimplemented; use super::{FulfillmentError, FulfillmentErrorCode}; use super::{ObligationCause, PredicateObligation}; -use crate::traits::error_reporting::InferCtxtExt as _; use crate::traits::project::PolyProjectionObligation; use crate::traits::project::ProjectionCacheKeyExt as _; -use crate::traits::query::evaluate_obligation::InferCtxtExt as _; +use crate::traits::query::evaluate_obligation::InferCtxtExt; impl<'tcx> ForestObligation for PendingPredicateObligation<'tcx> { /// Note that we include both the `ParamEnv` and the `Predicate`, @@ -103,7 +102,7 @@ impl<'a, 'tcx> FulfillmentContext<'tcx> { } /// Attempts to select obligations using `selcx`. - fn select(&mut self, selcx: &mut SelectionContext<'a, 'tcx>) -> Vec<FulfillmentError<'tcx>> { + fn select(&mut self, selcx: SelectionContext<'a, 'tcx>) -> Vec<FulfillmentError<'tcx>> { let span = debug_span!("select", obligation_forest_size = ?self.predicates.len()); let _enter = span.enter(); @@ -135,10 +134,10 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> { /// `SomeTrait` or a where-clause that lets us unify `$0` with /// something concrete. If this fails, we'll unify `$0` with /// `projection_ty` again. - #[tracing::instrument(level = "debug", skip(self, infcx, param_env, cause))] + #[instrument(level = "debug", skip(self, infcx, param_env, cause))] fn normalize_projection_type( &mut self, - infcx: &InferCtxt<'_, 'tcx>, + infcx: &InferCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, projection_ty: ty::ProjectionTy<'tcx>, cause: ObligationCause<'tcx>, @@ -166,7 +165,7 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> { fn register_predicate_obligation( &mut self, - infcx: &InferCtxt<'_, 'tcx>, + infcx: &InferCtxt<'tcx>, obligation: PredicateObligation<'tcx>, ) { // this helps to reduce duplicate errors, as well as making @@ -183,7 +182,7 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> { .register_obligation(PendingPredicateObligation { obligation, stalled_on: vec![] }); } - fn select_all_or_error(&mut self, infcx: &InferCtxt<'_, 'tcx>) -> Vec<FulfillmentError<'tcx>> { + fn select_all_or_error(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>> { { let errors = self.select_where_possible(infcx); if !errors.is_empty() { @@ -194,12 +193,9 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> { self.predicates.to_errors(CodeAmbiguity).into_iter().map(to_fulfillment_error).collect() } - fn select_where_possible( - &mut self, - infcx: &InferCtxt<'_, 'tcx>, - ) -> Vec<FulfillmentError<'tcx>> { - let mut selcx = SelectionContext::new(infcx); - self.select(&mut selcx) + fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>> { + let selcx = SelectionContext::new(infcx); + self.select(selcx) } fn pending_obligations(&self) -> Vec<PredicateObligation<'tcx>> { @@ -211,8 +207,8 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> { } } -struct FulfillProcessor<'a, 'b, 'tcx> { - selcx: &'a mut SelectionContext<'b, 'tcx>, +struct FulfillProcessor<'a, 'tcx> { + selcx: SelectionContext<'a, 'tcx>, } fn mk_pending(os: Vec<PredicateObligation<'_>>) -> Vec<PendingPredicateObligation<'_>> { @@ -221,9 +217,10 @@ fn mk_pending(os: Vec<PredicateObligation<'_>>) -> Vec<PendingPredicateObligatio .collect() } -impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { +impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { type Obligation = PendingPredicateObligation<'tcx>; type Error = FulfillmentErrorCode<'tcx>; + type OUT = Outcome<Self::Obligation, Self::Error>; /// Identifies whether a predicate obligation needs processing. /// @@ -279,7 +276,7 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { debug!(?obligation, "pre-resolve"); - if obligation.predicate.has_infer_types_or_consts() { + if obligation.predicate.has_non_region_infer() { obligation.predicate = self.selcx.infcx().resolve_vars_if_possible(obligation.predicate); } @@ -291,7 +288,7 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { if obligation.predicate.has_projections() { let mut obligations = Vec::new(); let predicate = crate::traits::project::try_normalize_with_depth_to( - self.selcx, + &mut self.selcx, obligation.param_env, obligation.cause.clone(), obligation.recursion_depth + 1, @@ -358,7 +355,7 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { } ty::PredicateKind::RegionOutlives(data) => { - if infcx.considering_regions || data.has_placeholders() { + if infcx.considering_regions { infcx.region_outlives_predicate(&obligation.cause, Binder::dummy(data)); } @@ -427,16 +424,14 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { obligation.param_env, Binder::dummy(subtype), ) { - None => { + Err((a, b)) => { // None means that both are unresolved. - pending_obligation.stalled_on = vec![ - TyOrConstInferVar::maybe_from_ty(subtype.a).unwrap(), - TyOrConstInferVar::maybe_from_ty(subtype.b).unwrap(), - ]; + pending_obligation.stalled_on = + vec![TyOrConstInferVar::Ty(a), TyOrConstInferVar::Ty(b)]; ProcessResult::Unchanged } - Some(Ok(ok)) => ProcessResult::Changed(mk_pending(ok.obligations)), - Some(Err(err)) => { + Ok(Ok(ok)) => ProcessResult::Changed(mk_pending(ok.obligations)), + Ok(Err(err)) => { let expected_found = ExpectedFound::new(subtype.a_is_expected, subtype.a, subtype.b); ProcessResult::Error(FulfillmentErrorCode::CodeSubtypeError( @@ -453,16 +448,14 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { obligation.param_env, Binder::dummy(coerce), ) { - None => { + Err((a, b)) => { // None means that both are unresolved. - pending_obligation.stalled_on = vec![ - TyOrConstInferVar::maybe_from_ty(coerce.a).unwrap(), - TyOrConstInferVar::maybe_from_ty(coerce.b).unwrap(), - ]; + pending_obligation.stalled_on = + vec![TyOrConstInferVar::Ty(a), TyOrConstInferVar::Ty(b)]; ProcessResult::Unchanged } - Some(Ok(ok)) => ProcessResult::Changed(mk_pending(ok.obligations)), - Some(Err(err)) => { + Ok(Ok(ok)) => ProcessResult::Changed(mk_pending(ok.obligations)), + Ok(Err(err)) => { let expected_found = ExpectedFound::new(false, coerce.a, coerce.b); ProcessResult::Error(FulfillmentErrorCode::CodeSubtypeError( expected_found, @@ -483,9 +476,7 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { Err(NotConstEvaluatable::MentionsInfer) => { pending_obligation.stalled_on.clear(); pending_obligation.stalled_on.extend( - uv.substs - .iter() - .filter_map(TyOrConstInferVar::maybe_from_generic_arg), + uv.walk().filter_map(TyOrConstInferVar::maybe_from_generic_arg), ); ProcessResult::Unchanged } @@ -499,23 +490,20 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { } ty::PredicateKind::ConstEquate(c1, c2) => { + assert!( + self.selcx.tcx().features().generic_const_exprs, + "`ConstEquate` without a feature gate: {c1:?} {c2:?}", + ); debug!(?c1, ?c2, "equating consts"); - let tcx = self.selcx.tcx(); - if tcx.features().generic_const_exprs { - // FIXME: we probably should only try to unify abstract constants - // if the constants depend on generic parameters. - // - // Let's just see where this breaks :shrug: - if let (ty::ConstKind::Unevaluated(a), ty::ConstKind::Unevaluated(b)) = - (c1.kind(), c2.kind()) - { - if infcx.try_unify_abstract_consts( - a.shrink(), - b.shrink(), - obligation.param_env, - ) { - return ProcessResult::Changed(vec![]); - } + // FIXME: we probably should only try to unify abstract constants + // if the constants depend on generic parameters. + // + // Let's just see where this breaks :shrug: + if let (ty::ConstKind::Unevaluated(a), ty::ConstKind::Unevaluated(b)) = + (c1.kind(), c2.kind()) + { + if infcx.try_unify_abstract_consts(a, b, obligation.param_env) { + return ProcessResult::Changed(vec![]); } } @@ -577,7 +565,7 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { ) } (Err(ErrorHandled::TooGeneric), _) | (_, Err(ErrorHandled::TooGeneric)) => { - if c1.has_infer_types_or_consts() || c2.has_infer_types_or_consts() { + if c1.has_non_region_infer() || c2.has_non_region_infer() { ProcessResult::Unchanged } else { // Two different constants using generic parameters ~> error. @@ -597,23 +585,26 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { } } + #[inline(never)] fn process_backedge<'c, I>( &mut self, cycle: I, _marker: PhantomData<&'c PendingPredicateObligation<'tcx>>, - ) where + ) -> Result<(), FulfillmentErrorCode<'tcx>> + where I: Clone + Iterator<Item = &'c PendingPredicateObligation<'tcx>>, { if self.selcx.coinductive_match(cycle.clone().map(|s| s.obligation.predicate)) { debug!("process_child_obligations: coinductive match"); + Ok(()) } else { let cycle: Vec<_> = cycle.map(|c| c.obligation.clone()).collect(); - self.selcx.infcx().report_overflow_error_cycle(&cycle); + Err(FulfillmentErrorCode::CodeCycle(cycle)) } } } -impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> { +impl<'a, 'tcx> FulfillProcessor<'a, 'tcx> { #[instrument(level = "debug", skip(self, obligation, stalled_on))] fn process_trait_obligation( &mut self, @@ -648,7 +639,7 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> { // information about the types in the trait. stalled_on.clear(); stalled_on.extend(substs_infer_vars( - self.selcx, + &self.selcx, trait_obligation.predicate.map_bound(|pred| pred.trait_ref.substs), )); @@ -700,12 +691,12 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> { } } - match project::poly_project_and_unify_type(self.selcx, &project_obligation) { + match project::poly_project_and_unify_type(&mut self.selcx, &project_obligation) { ProjectAndUnifyResult::Holds(os) => ProcessResult::Changed(mk_pending(os)), ProjectAndUnifyResult::FailedNormalization => { stalled_on.clear(); stalled_on.extend(substs_infer_vars( - self.selcx, + &self.selcx, project_obligation.predicate.map_bound(|pred| pred.projection_ty.substs), )); ProcessResult::Unchanged @@ -723,7 +714,7 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> { /// Returns the set of inference variables contained in `substs`. fn substs_infer_vars<'a, 'tcx>( - selcx: &mut SelectionContext<'a, 'tcx>, + selcx: &SelectionContext<'a, 'tcx>, substs: ty::Binder<'tcx, SubstsRef<'tcx>>, ) -> impl Iterator<Item = TyOrConstInferVar<'tcx>> { selcx @@ -731,11 +722,11 @@ fn substs_infer_vars<'a, 'tcx>( .resolve_vars_if_possible(substs) .skip_binder() // ok because this check doesn't care about regions .iter() - .filter(|arg| arg.has_infer_types_or_consts()) + .filter(|arg| arg.has_non_region_infer()) .flat_map(|arg| { let mut walker = arg.walk(); while let Some(c) = walker.next() { - if !c.has_infer_types_or_consts() { + if !c.has_non_region_infer() { walker.visited.remove(&c); walker.skip_current_subtree(); } diff --git a/compiler/rustc_trait_selection/src/traits/misc.rs b/compiler/rustc_trait_selection/src/traits/misc.rs index dd2769c71..be603c609 100644 --- a/compiler/rustc_trait_selection/src/traits/misc.rs +++ b/compiler/rustc_trait_selection/src/traits/misc.rs @@ -7,7 +7,7 @@ use rustc_hir as hir; use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitable}; -use crate::traits::error_reporting::InferCtxtExt; +use crate::traits::error_reporting::TypeErrCtxtExt; #[derive(Clone)] pub enum CopyImplementationError<'tcx> { @@ -23,66 +23,64 @@ pub fn can_type_implement_copy<'tcx>( parent_cause: ObligationCause<'tcx>, ) -> Result<(), CopyImplementationError<'tcx>> { // FIXME: (@jroesch) float this code up - tcx.infer_ctxt().enter(|infcx| { - let (adt, substs) = match self_type.kind() { - // These types used to have a builtin impl. - // Now libcore provides that impl. - ty::Uint(_) - | ty::Int(_) - | ty::Bool - | ty::Float(_) - | ty::Char - | ty::RawPtr(..) - | ty::Never - | ty::Ref(_, _, hir::Mutability::Not) - | ty::Array(..) => return Ok(()), + let infcx = tcx.infer_ctxt().build(); + let (adt, substs) = match self_type.kind() { + // These types used to have a builtin impl. + // Now libcore provides that impl. + ty::Uint(_) + | ty::Int(_) + | ty::Bool + | ty::Float(_) + | ty::Char + | ty::RawPtr(..) + | ty::Never + | ty::Ref(_, _, hir::Mutability::Not) + | ty::Array(..) => return Ok(()), - ty::Adt(adt, substs) => (adt, substs), + ty::Adt(adt, substs) => (adt, substs), - _ => return Err(CopyImplementationError::NotAnAdt), - }; + _ => return Err(CopyImplementationError::NotAnAdt), + }; - let mut infringing = Vec::new(); - for variant in adt.variants() { - for field in &variant.fields { - let ty = field.ty(tcx, substs); - if ty.references_error() { - continue; - } - let span = tcx.def_span(field.did); - // FIXME(compiler-errors): This gives us better spans for bad - // projection types like in issue-50480. - // If the ADT has substs, point to the cause we are given. - // If it does not, then this field probably doesn't normalize - // to begin with, and point to the bad field's span instead. - let cause = if field - .ty(tcx, traits::InternalSubsts::identity_for_item(tcx, adt.did())) - .has_param_types_or_consts() - { - parent_cause.clone() - } else { - ObligationCause::dummy_with_span(span) - }; - let ctx = traits::FulfillmentContext::new(); - match traits::fully_normalize(&infcx, ctx, cause, param_env, ty) { - Ok(ty) => { - if !infcx.type_is_copy_modulo_regions(param_env, ty, span) { - infringing.push((field, ty)); - } - } - Err(errors) => { - infcx.report_fulfillment_errors(&errors, None, false); - } - }; + let mut infringing = Vec::new(); + for variant in adt.variants() { + for field in &variant.fields { + let ty = field.ty(tcx, substs); + if ty.references_error() { + continue; } + let span = tcx.def_span(field.did); + // FIXME(compiler-errors): This gives us better spans for bad + // projection types like in issue-50480. + // If the ADT has substs, point to the cause we are given. + // If it does not, then this field probably doesn't normalize + // to begin with, and point to the bad field's span instead. + let cause = if field + .ty(tcx, traits::InternalSubsts::identity_for_item(tcx, adt.did())) + .has_non_region_param() + { + parent_cause.clone() + } else { + ObligationCause::dummy_with_span(span) + }; + match traits::fully_normalize(&infcx, cause, param_env, ty) { + Ok(ty) => { + if !infcx.type_is_copy_modulo_regions(param_env, ty, span) { + infringing.push((field, ty)); + } + } + Err(errors) => { + infcx.err_ctxt().report_fulfillment_errors(&errors, None, false); + } + }; } - if !infringing.is_empty() { - return Err(CopyImplementationError::InfrigingFields(infringing)); - } - if adt.has_dtor(tcx) { - return Err(CopyImplementationError::HasDestructor); - } + } + if !infringing.is_empty() { + return Err(CopyImplementationError::InfrigingFields(infringing)); + } + if adt.has_dtor(tcx) { + return Err(CopyImplementationError::HasDestructor); + } - Ok(()) - }) + Ok(()) } diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 9c6bb0731..0bf54c096 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -13,6 +13,7 @@ mod fulfill; pub mod misc; mod object_safety; mod on_unimplemented; +pub mod outlives_bounds; mod project; pub mod query; pub(crate) mod relationships; @@ -22,18 +23,22 @@ mod structural_match; mod util; pub mod wf; +use crate::errors::DumpVTableEntries; use crate::infer::outlives::env::OutlivesEnvironment; use crate::infer::{InferCtxt, TyCtxtInferExt}; -use crate::traits::error_reporting::InferCtxtExt as _; +use crate::traits::error_reporting::TypeErrCtxtExt as _; use crate::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_errors::ErrorGuaranteed; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; +use rustc_infer::traits::TraitEngineExt as _; use rustc_middle::ty::fold::TypeFoldable; -use rustc_middle::ty::subst::{InternalSubsts, SubstsRef}; use rustc_middle::ty::visit::TypeVisitable; -use rustc_middle::ty::{self, GenericParamDefKind, ToPredicate, Ty, TyCtxt, VtblEntry}; +use rustc_middle::ty::{ + self, DefIdTree, GenericParamDefKind, ToPredicate, Ty, TyCtxt, TypeSuperVisitable, VtblEntry, +}; +use rustc_middle::ty::{InternalSubsts, SubstsRef}; use rustc_span::{sym, Span}; use smallvec::SmallVec; @@ -113,11 +118,21 @@ pub enum TraitQueryMode { /// Creates predicate obligations from the generic bounds. pub fn predicates_for_generics<'tcx>( - cause: ObligationCause<'tcx>, + cause: impl Fn(usize, Span) -> ObligationCause<'tcx>, param_env: ty::ParamEnv<'tcx>, generic_bounds: ty::InstantiatedPredicates<'tcx>, ) -> impl Iterator<Item = PredicateObligation<'tcx>> { - util::predicates_for_generics(cause, 0, param_env, generic_bounds) + let generic_bounds = generic_bounds; + debug!("predicates_for_generics(generic_bounds={:?})", generic_bounds); + + std::iter::zip(generic_bounds.predicates, generic_bounds.spans).enumerate().map( + move |(idx, (predicate, span))| Obligation { + cause: cause(idx, span), + recursion_depth: 0, + param_env, + predicate, + }, + ) } /// Determines whether the type `ty` is known to meet `bound` and @@ -125,8 +140,8 @@ pub fn predicates_for_generics<'tcx>( /// `bound` or is not known to meet bound (note that this is /// conservative towards *no impl*, which is the opposite of the /// `evaluate` methods). -pub fn type_known_to_meet_bound_modulo_regions<'a, 'tcx>( - infcx: &InferCtxt<'a, 'tcx>, +pub fn type_known_to_meet_bound_modulo_regions<'tcx>( + infcx: &InferCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>, def_id: DefId, @@ -155,28 +170,26 @@ pub fn type_known_to_meet_bound_modulo_regions<'a, 'tcx>( result ); - if result && ty.has_infer_types_or_consts() { + if result && ty.has_non_region_infer() { // Because of inference "guessing", selection can sometimes claim // to succeed while the success requires a guess. To ensure // this function's result remains infallible, we must confirm // that guess. While imperfect, I believe this is sound. - // The handling of regions in this area of the code is terrible, - // see issue #29149. We should be able to improve on this with - // NLL. - let mut fulfill_cx = <dyn TraitEngine<'tcx>>::new(infcx.tcx); - // We can use a dummy node-id here because we won't pay any mind // to region obligations that arise (there shouldn't really be any // anyhow). let cause = ObligationCause::misc(span, hir::CRATE_HIR_ID); - fulfill_cx.register_bound(infcx, param_env, ty, def_id, cause); + // The handling of regions in this area of the code is terrible, + // see issue #29149. We should be able to improve on this with + // NLL. + let errors = fully_solve_bound(infcx, cause, param_env, ty, def_id); // Note: we only assume something is `Copy` if we can // *definitively* show that it implements `Copy`. Otherwise, // assume it is move; linear is always ok. - match fulfill_cx.select_all_or_error(infcx).as_slice() { + match &errors[..] { [] => { debug!( "type_known_to_meet_bound_modulo_regions: ty={:?} bound={} success", @@ -221,56 +234,51 @@ fn do_normalize_predicates<'tcx>( // by wfcheck anyway, so I'm not sure we have to check // them here too, and we will remove this function when // we move over to lazy normalization *anyway*. - tcx.infer_ctxt().ignoring_regions().enter(|infcx| { - let fulfill_cx = FulfillmentContext::new(); - let predicates = - match fully_normalize(&infcx, fulfill_cx, cause, elaborated_env, predicates) { - Ok(predicates) => predicates, - Err(errors) => { - let reported = infcx.report_fulfillment_errors(&errors, None, false); - return Err(reported); - } - }; + let infcx = tcx.infer_ctxt().ignoring_regions().build(); + let predicates = match fully_normalize(&infcx, cause, elaborated_env, predicates) { + Ok(predicates) => predicates, + Err(errors) => { + let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None, false); + return Err(reported); + } + }; + + debug!("do_normalize_predictes: normalized predicates = {:?}", predicates); - debug!("do_normalize_predictes: normalized predicates = {:?}", predicates); + // We can use the `elaborated_env` here; the region code only + // cares about declarations like `'a: 'b`. + let outlives_env = OutlivesEnvironment::new(elaborated_env); - // We can use the `elaborated_env` here; the region code only - // cares about declarations like `'a: 'b`. - let outlives_env = OutlivesEnvironment::new(elaborated_env); + // FIXME: It's very weird that we ignore region obligations but apparently + // still need to use `resolve_regions` as we need the resolved regions in + // the normalized predicates. + let errors = infcx.resolve_regions(&outlives_env); + if !errors.is_empty() { + tcx.sess.delay_span_bug( + span, + format!("failed region resolution while normalizing {elaborated_env:?}: {errors:?}"), + ); + } - // FIXME: It's very weird that we ignore region obligations but apparently - // still need to use `resolve_regions` as we need the resolved regions in - // the normalized predicates. - let errors = infcx.resolve_regions(&outlives_env); - if !errors.is_empty() { - tcx.sess.delay_span_bug( + match infcx.fully_resolve(predicates) { + Ok(predicates) => Ok(predicates), + Err(fixup_err) => { + // If we encounter a fixup error, it means that some type + // variable wound up unconstrained. I actually don't know + // if this can happen, and I certainly don't expect it to + // happen often, but if it did happen it probably + // represents a legitimate failure due to some kind of + // unconstrained variable. + // + // @lcnr: Let's still ICE here for now. I want a test case + // for that. + span_bug!( span, - format!( - "failed region resolution while normalizing {elaborated_env:?}: {errors:?}" - ), + "inference variables in normalized parameter environment: {}", + fixup_err ); } - - match infcx.fully_resolve(predicates) { - Ok(predicates) => Ok(predicates), - Err(fixup_err) => { - // If we encounter a fixup error, it means that some type - // variable wound up unconstrained. I actually don't know - // if this can happen, and I certainly don't expect it to - // happen often, but if it did happen it probably - // represents a legitimate failure due to some kind of - // unconstrained variable. - // - // @lcnr: Let's still ICE here for now. I want a test case - // for that. - span_bug!( - span, - "inference variables in normalized parameter environment: {}", - fixup_err - ); - } - } - }) + } } // FIXME: this is gonna need to be removed ... @@ -381,9 +389,9 @@ pub fn normalize_param_env_or_error<'tcx>( ) } -pub fn fully_normalize<'a, 'tcx, T>( - infcx: &InferCtxt<'a, 'tcx>, - mut fulfill_cx: FulfillmentContext<'tcx>, +/// Normalize a type and process all resulting obligations, returning any errors +pub fn fully_normalize<'tcx, T>( + infcx: &InferCtxt<'tcx>, cause: ObligationCause<'tcx>, param_env: ty::ParamEnv<'tcx>, value: T, @@ -399,8 +407,10 @@ where "fully_normalize: normalized_value={:?} obligations={:?}", normalized_value, obligations ); + + let mut fulfill_cx = FulfillmentContext::new(); for obligation in obligations { - fulfill_cx.register_predicate_obligation(selcx.infcx(), obligation); + fulfill_cx.register_predicate_obligation(infcx, obligation); } debug!("fully_normalize: select_all_or_error start"); @@ -414,6 +424,43 @@ where Ok(resolved_value) } +/// Process an obligation (and any nested obligations that come from it) to +/// completion, returning any errors +pub fn fully_solve_obligation<'tcx>( + infcx: &InferCtxt<'tcx>, + obligation: PredicateObligation<'tcx>, +) -> Vec<FulfillmentError<'tcx>> { + let mut engine = <dyn TraitEngine<'tcx>>::new(infcx.tcx); + engine.register_predicate_obligation(infcx, obligation); + engine.select_all_or_error(infcx) +} + +/// Process a set of obligations (and any nested obligations that come from them) +/// to completion +pub fn fully_solve_obligations<'tcx>( + infcx: &InferCtxt<'tcx>, + obligations: impl IntoIterator<Item = PredicateObligation<'tcx>>, +) -> Vec<FulfillmentError<'tcx>> { + let mut engine = <dyn TraitEngine<'tcx>>::new(infcx.tcx); + engine.register_predicate_obligations(infcx, obligations); + engine.select_all_or_error(infcx) +} + +/// Process a bound (and any nested obligations that come from it) to completion. +/// This is a convenience function for traits that have no generic arguments, such +/// as auto traits, and builtin traits like Copy or Sized. +pub fn fully_solve_bound<'tcx>( + infcx: &InferCtxt<'tcx>, + cause: ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ty: Ty<'tcx>, + bound: DefId, +) -> Vec<FulfillmentError<'tcx>> { + let mut engine = <dyn TraitEngine<'tcx>>::new(infcx.tcx); + engine.register_bound(infcx, param_env, ty, bound, cause); + engine.select_all_or_error(infcx) +} + /// Normalizes the predicates and checks whether they hold in an empty environment. If this /// returns true, then either normalize encountered an error or one of the predicates did not /// hold. Used when creating vtables to check for unsatisfiable methods. @@ -423,31 +470,20 @@ pub fn impossible_predicates<'tcx>( ) -> bool { debug!("impossible_predicates(predicates={:?})", predicates); - let result = tcx.infer_ctxt().enter(|infcx| { - // HACK: Set tainted by errors to gracefully exit in case of overflow. - infcx.set_tainted_by_errors(); - - let param_env = ty::ParamEnv::reveal_all(); - let mut selcx = SelectionContext::new(&infcx); - let mut fulfill_cx = FulfillmentContext::new(); - let cause = ObligationCause::dummy(); - let Normalized { value: predicates, obligations } = - normalize(&mut selcx, param_env, cause.clone(), predicates); - for obligation in obligations { - fulfill_cx.register_predicate_obligation(&infcx, obligation); - } - for predicate in predicates { - let obligation = Obligation::new(cause.clone(), param_env, predicate); - fulfill_cx.register_predicate_obligation(&infcx, obligation); - } - - let errors = fulfill_cx.select_all_or_error(&infcx); + let infcx = tcx.infer_ctxt().build(); + let param_env = ty::ParamEnv::reveal_all(); + let ocx = ObligationCtxt::new(&infcx); + let predicates = ocx.normalize(ObligationCause::dummy(), param_env, predicates); + for predicate in predicates { + let obligation = Obligation::new(ObligationCause::dummy(), param_env, predicate); + ocx.register_obligation(obligation); + } + let errors = ocx.select_all_or_error(); - // Clean up after ourselves - let _ = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types(); + // Clean up after ourselves + let _ = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types(); - !errors.is_empty() - }); + let result = !errors.is_empty(); debug!("impossible_predicates = {:?}", result); result } @@ -474,6 +510,82 @@ fn subst_and_check_impossible_predicates<'tcx>( result } +/// Checks whether a trait's method is impossible to call on a given impl. +/// +/// This only considers predicates that reference the impl's generics, and not +/// those that reference the method's generics. +fn is_impossible_method<'tcx>( + tcx: TyCtxt<'tcx>, + (impl_def_id, trait_item_def_id): (DefId, DefId), +) -> bool { + struct ReferencesOnlyParentGenerics<'tcx> { + tcx: TyCtxt<'tcx>, + generics: &'tcx ty::Generics, + trait_item_def_id: DefId, + } + impl<'tcx> ty::TypeVisitor<'tcx> for ReferencesOnlyParentGenerics<'tcx> { + type BreakTy = (); + fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { + // If this is a parameter from the trait item's own generics, then bail + if let ty::Param(param) = t.kind() + && let param_def_id = self.generics.type_param(param, self.tcx).def_id + && self.tcx.parent(param_def_id) == self.trait_item_def_id + { + return ControlFlow::BREAK; + } + t.super_visit_with(self) + } + fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> { + if let ty::ReEarlyBound(param) = r.kind() + && let param_def_id = self.generics.region_param(¶m, self.tcx).def_id + && self.tcx.parent(param_def_id) == self.trait_item_def_id + { + return ControlFlow::BREAK; + } + r.super_visit_with(self) + } + fn visit_const(&mut self, ct: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> { + if let ty::ConstKind::Param(param) = ct.kind() + && let param_def_id = self.generics.const_param(¶m, self.tcx).def_id + && self.tcx.parent(param_def_id) == self.trait_item_def_id + { + return ControlFlow::BREAK; + } + ct.super_visit_with(self) + } + } + + let generics = tcx.generics_of(trait_item_def_id); + let predicates = tcx.predicates_of(trait_item_def_id); + let impl_trait_ref = + tcx.impl_trait_ref(impl_def_id).expect("expected impl to correspond to trait"); + let param_env = tcx.param_env(impl_def_id); + + let mut visitor = ReferencesOnlyParentGenerics { tcx, generics, trait_item_def_id }; + let predicates_for_trait = predicates.predicates.iter().filter_map(|(pred, span)| { + if pred.visit_with(&mut visitor).is_continue() { + Some(Obligation::new( + ObligationCause::dummy_with_span(*span), + param_env, + ty::EarlyBinder(*pred).subst(tcx, impl_trait_ref.substs), + )) + } else { + None + } + }); + + let infcx = tcx.infer_ctxt().ignoring_regions().build(); + for obligation in predicates_for_trait { + // Ignore overflow error, to be conservative. + if let Ok(result) = infcx.evaluate_obligation(&obligation) + && !result.may_apply() + { + return true; + } + } + false +} + #[derive(Clone, Debug)] enum VtblSegment<'tcx> { MetadataDSA, @@ -645,16 +757,16 @@ fn dump_vtable_entries<'tcx>( trait_ref: ty::PolyTraitRef<'tcx>, entries: &[VtblEntry<'tcx>], ) { - let msg = format!("vtable entries for `{}`: {:#?}", trait_ref, entries); - tcx.sess.struct_span_err(sp, &msg).emit(); + tcx.sess.emit_err(DumpVTableEntries { + span: sp, + trait_ref, + entries: format!("{:#?}", entries), + }); } -fn own_existential_vtable_entries<'tcx>( - tcx: TyCtxt<'tcx>, - trait_ref: ty::PolyExistentialTraitRef<'tcx>, -) -> &'tcx [DefId] { +fn own_existential_vtable_entries<'tcx>(tcx: TyCtxt<'tcx>, trait_def_id: DefId) -> &'tcx [DefId] { let trait_methods = tcx - .associated_items(trait_ref.def_id()) + .associated_items(trait_def_id) .in_definition_order() .filter(|item| item.kind == ty::AssocKind::Fn); // Now list each method's DefId (for within its trait). @@ -663,7 +775,7 @@ fn own_existential_vtable_entries<'tcx>( let def_id = trait_method.def_id; // Some methods cannot be called on an object; skip those. - if !is_vtable_safe_method(tcx, trait_ref.def_id(), &trait_method) { + if !is_vtable_safe_method(tcx, trait_def_id, &trait_method) { debug!("own_existential_vtable_entry: not vtable safe"); return None; } @@ -695,7 +807,7 @@ fn vtable_entries<'tcx>( // Lookup the shape of vtable for the trait. let own_existential_entries = - tcx.own_existential_vtable_entries(existential_trait_ref); + tcx.own_existential_vtable_entries(existential_trait_ref.def_id()); let own_entries = own_existential_entries.iter().copied().map(|def_id| { debug!("vtable_entries: trait_method={:?}", def_id); @@ -831,10 +943,9 @@ pub fn vtable_trait_upcasting_coercion_new_vptr_slot<'tcx>( }), ); - let implsrc = tcx.infer_ctxt().enter(|infcx| { - let mut selcx = SelectionContext::new(&infcx); - selcx.select(&obligation).unwrap() - }); + let infcx = tcx.infer_ctxt().build(); + let mut selcx = SelectionContext::new(&infcx); + let implsrc = selcx.select(&obligation).unwrap(); let Some(ImplSource::TraitUpcasting(implsrc_traitcasting)) = implsrc else { bug!(); @@ -849,11 +960,12 @@ pub fn provide(providers: &mut ty::query::Providers) { *providers = ty::query::Providers { specialization_graph_of: specialize::specialization_graph_provider, specializes: specialize::specializes, - codegen_fulfill_obligation: codegen::codegen_fulfill_obligation, + codegen_select_candidate: codegen::codegen_select_candidate, own_existential_vtable_entries, vtable_entries, vtable_trait_upcasting_coercion_new_vptr_slot, subst_and_check_impossible_predicates, + is_impossible_method, try_unify_abstract_consts: |tcx, param_env_and| { let (param_env, (a, b)) = param_env_and.into_parts(); const_evaluatable::try_unify_abstract_consts(tcx, (a, b), param_env) diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs index 612f51309..0bb25a74d 100644 --- a/compiler/rustc_trait_selection/src/traits/object_safety.rs +++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs @@ -8,19 +8,20 @@ //! - not reference the erased type `Self` except for in this receiver; //! - not have generic type parameters. -use super::elaborate_predicates; +use super::{elaborate_predicates, elaborate_trait_ref}; use crate::infer::TyCtxtInferExt; use crate::traits::query::evaluate_obligation::InferCtxtExt; use crate::traits::{self, Obligation, ObligationCause}; -use rustc_errors::{FatalError, MultiSpan}; +use hir::def::DefKind; +use rustc_errors::{DelayDm, FatalError, MultiSpan}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_middle::ty::abstract_const::{walk_abstract_const, AbstractConst}; -use rustc_middle::ty::subst::{GenericArg, InternalSubsts, Subst}; use rustc_middle::ty::{ self, EarlyBinder, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, }; +use rustc_middle::ty::{GenericArg, InternalSubsts}; use rustc_middle::ty::{Predicate, ToPredicate}; use rustc_session::lint::builtin::WHERE_CLAUSES_OBJECT_SAFETY; use rustc_span::symbol::Symbol; @@ -163,37 +164,42 @@ fn lint_object_unsafe_trait( ) { // Using `CRATE_NODE_ID` is wrong, but it's hard to get a more precise id. // It's also hard to get a use site span, so we use the method definition span. - tcx.struct_span_lint_hir(WHERE_CLAUSES_OBJECT_SAFETY, hir::CRATE_HIR_ID, span, |lint| { - let mut err = lint.build(&format!( - "the trait `{}` cannot be made into an object", - tcx.def_path_str(trait_def_id) - )); - let node = tcx.hir().get_if_local(trait_def_id); - let mut spans = MultiSpan::from_span(span); - if let Some(hir::Node::Item(item)) = node { - spans.push_span_label(item.ident.span, "this trait cannot be made into an object..."); - spans.push_span_label(span, format!("...because {}", violation.error_msg())); - } else { - spans.push_span_label( - span, - format!( - "the trait cannot be made into an object because {}", - violation.error_msg() - ), + tcx.struct_span_lint_hir( + WHERE_CLAUSES_OBJECT_SAFETY, + hir::CRATE_HIR_ID, + span, + DelayDm(|| format!("the trait `{}` cannot be made into an object", tcx.def_path_str(trait_def_id))), + |err| { + let node = tcx.hir().get_if_local(trait_def_id); + let mut spans = MultiSpan::from_span(span); + if let Some(hir::Node::Item(item)) = node { + spans.push_span_label( + item.ident.span, + "this trait cannot be made into an object...", + ); + spans.push_span_label(span, format!("...because {}", violation.error_msg())); + } else { + spans.push_span_label( + span, + format!( + "the trait cannot be made into an object because {}", + violation.error_msg() + ), + ); + }; + err.span_note( + spans, + "for a trait to be \"object safe\" it needs to allow building a vtable to allow the \ + call to be resolvable dynamically; for more information visit \ + <https://doc.rust-lang.org/reference/items/traits.html#object-safety>", ); - }; - err.span_note( - spans, - "for a trait to be \"object safe\" it needs to allow building a vtable to allow the \ - call to be resolvable dynamically; for more information visit \ - <https://doc.rust-lang.org/reference/items/traits.html#object-safety>", - ); - if node.is_some() { - // Only provide the help if its a local trait, otherwise it's not - violation.solution(&mut err); - } - err.emit(); - }); + if node.is_some() { + // Only provide the help if its a local trait, otherwise it's not + violation.solution(err); + } + err + }, + ); } fn sized_trait_bound_spans<'tcx>( @@ -431,6 +437,9 @@ fn virtual_call_violation_for_method<'tcx>( if contains_illegal_self_type_reference(tcx, trait_def_id, sig.output()) { return Some(MethodViolationCode::ReferencesSelfOutput); } + if contains_illegal_impl_trait_in_trait(tcx, sig.output()) { + return Some(MethodViolationCode::ReferencesImplTraitInTrait); + } // We can't monomorphize things like `fn foo<A>(...)`. let own_counts = tcx.generics_of(method.def_id).own_counts(); @@ -438,19 +447,6 @@ fn virtual_call_violation_for_method<'tcx>( return Some(MethodViolationCode::Generic); } - if tcx - .predicates_of(method.def_id) - .predicates - .iter() - // A trait object can't claim to live more than the concrete type, - // so outlives predicates will always hold. - .cloned() - .filter(|(p, _)| p.to_opt_type_outlives().is_none()) - .any(|pred| contains_illegal_self_type_reference(tcx, trait_def_id, pred)) - { - return Some(MethodViolationCode::WhereClauseReferencesSelf); - } - let receiver_ty = tcx.liberate_late_bound_regions(method.def_id, sig.input(0)); // Until `unsized_locals` is fully implemented, `self: Self` can't be dispatched on. @@ -529,6 +525,21 @@ fn virtual_call_violation_for_method<'tcx>( } } + // NOTE: This check happens last, because it results in a lint, and not a + // hard error. + if tcx + .predicates_of(method.def_id) + .predicates + .iter() + // A trait object can't claim to live more than the concrete type, + // so outlives predicates will always hold. + .cloned() + .filter(|(p, _)| p.to_opt_type_outlives().is_none()) + .any(|pred| contains_illegal_self_type_reference(tcx, trait_def_id, pred)) + { + return Some(MethodViolationCode::WhereClauseReferencesSelf); + } + None } @@ -556,51 +567,44 @@ fn receiver_for_self_ty<'tcx>( /// Creates the object type for the current trait. For example, /// if the current trait is `Deref`, then this will be /// `dyn Deref<Target = Self::Target> + 'static`. +#[instrument(level = "trace", skip(tcx), ret)] fn object_ty_for_trait<'tcx>( tcx: TyCtxt<'tcx>, trait_def_id: DefId, lifetime: ty::Region<'tcx>, ) -> Ty<'tcx> { - debug!("object_ty_for_trait: trait_def_id={:?}", trait_def_id); - let trait_ref = ty::TraitRef::identity(tcx, trait_def_id); + debug!(?trait_ref); let trait_predicate = trait_ref.map_bound(|trait_ref| { ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref)) }); - - let mut associated_types = traits::supertraits(tcx, trait_ref) - .flat_map(|super_trait_ref| { - tcx.associated_items(super_trait_ref.def_id()) - .in_definition_order() - .map(move |item| (super_trait_ref, item)) - }) - .filter(|(_, item)| item.kind == ty::AssocKind::Type) - .collect::<Vec<_>>(); - - // existential predicates need to be in a specific order - associated_types.sort_by_cached_key(|(_, item)| tcx.def_path_hash(item.def_id)); - - let projection_predicates = associated_types.into_iter().map(|(super_trait_ref, item)| { - // We *can* get bound lifetimes here in cases like - // `trait MyTrait: for<'s> OtherTrait<&'s T, Output=bool>`. - super_trait_ref.map_bound(|super_trait_ref| { - ty::ExistentialPredicate::Projection(ty::ExistentialProjection { - term: tcx.mk_projection(item.def_id, super_trait_ref.substs).into(), - item_def_id: item.def_id, - substs: super_trait_ref.substs, - }) + debug!(?trait_predicate); + + let mut elaborated_predicates: Vec<_> = elaborate_trait_ref(tcx, trait_ref) + .filter_map(|obligation| { + debug!(?obligation); + let pred = obligation.predicate.to_opt_poly_projection_pred()?; + Some(pred.map_bound(|p| { + ty::ExistentialPredicate::Projection(ty::ExistentialProjection { + item_def_id: p.projection_ty.item_def_id, + substs: p.projection_ty.substs, + term: p.term, + }) + })) }) - }); + .collect(); + // NOTE: Since #37965, the existential predicates list has depended on the + // list of predicates to be sorted. This is mostly to enforce that the primary + // predicate comes first. + elaborated_predicates.sort_by(|a, b| a.skip_binder().stable_cmp(tcx, &b.skip_binder())); + elaborated_predicates.dedup(); let existential_predicates = tcx - .mk_poly_existential_predicates(iter::once(trait_predicate).chain(projection_predicates)); + .mk_poly_existential_predicates(iter::once(trait_predicate).chain(elaborated_predicates)); + debug!(?existential_predicates); - let object_ty = tcx.mk_dynamic(existential_predicates, lifetime); - - debug!("object_ty_for_trait: object_ty=`{}`", object_ty); - - object_ty + tcx.mk_dynamic(existential_predicates, lifetime, ty::Dyn) } /// Checks the method's receiver (the `self` argument) can be dispatched on when `Self` is a @@ -725,10 +729,9 @@ fn receiver_is_dispatchable<'tcx>( Obligation::new(ObligationCause::dummy(), param_env, predicate) }; - tcx.infer_ctxt().enter(|ref infcx| { - // the receiver is dispatchable iff the obligation holds - infcx.predicate_must_hold_modulo_regions(&obligation) - }) + let infcx = tcx.infer_ctxt().build(); + // the receiver is dispatchable iff the obligation holds + infcx.predicate_must_hold_modulo_regions(&obligation) } fn contains_illegal_self_type_reference<'tcx, T: TypeVisitable<'tcx>>( @@ -793,6 +796,12 @@ fn contains_illegal_self_type_reference<'tcx, T: TypeVisitable<'tcx>>( ControlFlow::CONTINUE } } + ty::Projection(ref data) + if self.tcx.def_kind(data.item_def_id) == DefKind::ImplTraitPlaceholder => + { + // We'll deny these later in their own pass + ControlFlow::CONTINUE + } ty::Projection(ref data) => { // This is a projected type `<Foo as SomeTrait>::X`. @@ -828,21 +837,14 @@ fn contains_illegal_self_type_reference<'tcx, T: TypeVisitable<'tcx>>( } } - fn visit_unevaluated(&mut self, uv: ty::Unevaluated<'tcx>) -> ControlFlow<Self::BreakTy> { + fn visit_const(&mut self, ct: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> { // Constants can only influence object safety if they reference `Self`. // This is only possible for unevaluated constants, so we walk these here. // - // If `AbstractConst::new` returned an error we already failed compilation + // If `AbstractConst::from_const` returned an error we already failed compilation // so we don't have to emit an additional error here. - // - // We currently recurse into abstract consts here but do not recurse in - // `is_const_evaluatable`. This means that the object safety check is more - // liberal than the const eval check. - // - // This shouldn't really matter though as we can't really use any - // constants which are not considered const evaluatable. use rustc_middle::ty::abstract_const::Node; - if let Ok(Some(ct)) = AbstractConst::new(self.tcx, uv.shrink()) { + if let Ok(Some(ct)) = AbstractConst::from_const(self.tcx, ct) { walk_abstract_const(self.tcx, ct, |node| match node.root(self.tcx) { Node::Leaf(leaf) => self.visit_const(leaf), Node::Cast(_, _, ty) => self.visit_ty(ty), @@ -851,7 +853,7 @@ fn contains_illegal_self_type_reference<'tcx, T: TypeVisitable<'tcx>>( } }) } else { - ControlFlow::CONTINUE + ct.super_visit_with(self) } } } @@ -861,6 +863,22 @@ fn contains_illegal_self_type_reference<'tcx, T: TypeVisitable<'tcx>>( .is_break() } +pub fn contains_illegal_impl_trait_in_trait<'tcx>( + tcx: TyCtxt<'tcx>, + ty: ty::Binder<'tcx, Ty<'tcx>>, +) -> bool { + // FIXME(RPITIT): Perhaps we should use a visitor here? + ty.skip_binder().walk().any(|arg| { + if let ty::GenericArgKind::Type(ty) = arg.unpack() + && let ty::Projection(proj) = ty.kind() + { + tcx.def_kind(proj.item_def_id) == DefKind::ImplTraitPlaceholder + } else { + false + } + }) +} + pub fn provide(providers: &mut ty::query::Providers) { *providers = ty::query::Providers { object_safety_violations, ..*providers }; } diff --git a/compiler/rustc_trait_selection/src/traits/on_unimplemented.rs b/compiler/rustc_trait_selection/src/traits/on_unimplemented.rs index 9227bbf01..4a4f34b76 100644 --- a/compiler/rustc_trait_selection/src/traits/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/traits/on_unimplemented.rs @@ -8,6 +8,10 @@ use rustc_parse_format::{ParseMode, Parser, Piece, Position}; use rustc_span::symbol::{kw, sym, Symbol}; use rustc_span::{Span, DUMMY_SP}; +use crate::errors::{ + EmptyOnClauseInOnUnimplemented, InvalidOnClauseInOnUnimplemented, NoValueInOnUnimplemented, +}; + #[derive(Clone, Debug)] pub struct OnUnimplementedFormatString(Symbol); @@ -18,7 +22,7 @@ pub struct OnUnimplementedDirective { pub message: Option<OnUnimplementedFormatString>, pub label: Option<OnUnimplementedFormatString>, pub note: Option<OnUnimplementedFormatString>, - pub enclosing_scope: Option<OnUnimplementedFormatString>, + pub parent_label: Option<OnUnimplementedFormatString>, pub append_const_msg: Option<Option<Symbol>>, } @@ -27,7 +31,7 @@ pub struct OnUnimplementedNote { pub message: Option<String>, pub label: Option<String>, pub note: Option<String>, - pub enclosing_scope: Option<String>, + pub parent_label: Option<String>, /// Append a message for `~const Trait` errors. `None` means not requested and /// should fallback to a generic message, `Some(None)` suggests using the default /// appended message, `Some(Some(s))` suggests use the `s` message instead of the @@ -35,21 +39,6 @@ pub struct OnUnimplementedNote { pub append_const_msg: Option<Option<Symbol>>, } -fn parse_error( - tcx: TyCtxt<'_>, - span: Span, - message: &str, - label: &str, - note: Option<&str>, -) -> ErrorGuaranteed { - let mut diag = struct_span_err!(tcx.sess, span, E0232, "{}", message); - diag.span_label(span, label); - if let Some(note) = note { - diag.note(note); - } - diag.emit() -} - impl<'tcx> OnUnimplementedDirective { fn parse( tcx: TyCtxt<'tcx>, @@ -70,25 +59,9 @@ impl<'tcx> OnUnimplementedDirective { } else { let cond = item_iter .next() - .ok_or_else(|| { - parse_error( - tcx, - span, - "empty `on`-clause in `#[rustc_on_unimplemented]`", - "empty on-clause here", - None, - ) - })? + .ok_or_else(|| tcx.sess.emit_err(EmptyOnClauseInOnUnimplemented { span }))? .meta_item() - .ok_or_else(|| { - parse_error( - tcx, - span, - "invalid `on`-clause in `#[rustc_on_unimplemented]`", - "invalid on-clause here", - None, - ) - })?; + .ok_or_else(|| tcx.sess.emit_err(InvalidOnClauseInOnUnimplemented { span }))?; attr::eval_condition(cond, &tcx.sess.parse_sess, Some(tcx.features()), &mut |cfg| { if let Some(value) = cfg.value && let Err(guar) = parse_value(value) { errored = Some(guar); @@ -101,7 +74,7 @@ impl<'tcx> OnUnimplementedDirective { let mut message = None; let mut label = None; let mut note = None; - let mut enclosing_scope = None; + let mut parent_label = None; let mut subcommands = vec![]; let mut append_const_msg = None; @@ -121,9 +94,9 @@ impl<'tcx> OnUnimplementedDirective { note = parse_value(note_)?; continue; } - } else if item.has_name(sym::enclosing_scope) && enclosing_scope.is_none() { - if let Some(enclosing_scope_) = item.value_str() { - enclosing_scope = parse_value(enclosing_scope_)?; + } else if item.has_name(sym::parent_label) && parent_label.is_none() { + if let Some(parent_label_) = item.value_str() { + parent_label = parse_value(parent_label_)?; continue; } } else if item.has_name(sym::on) @@ -150,13 +123,7 @@ impl<'tcx> OnUnimplementedDirective { } // nothing found - parse_error( - tcx, - item.span(), - "this attribute must have a valid value", - "expected value here", - Some(r#"eg `#[rustc_on_unimplemented(message="foo")]`"#), - ); + tcx.sess.emit_err(NoValueInOnUnimplemented { span: item.span() }); } if let Some(reported) = errored { @@ -168,7 +135,7 @@ impl<'tcx> OnUnimplementedDirective { message, label, note, - enclosing_scope, + parent_label, append_const_msg, }) } @@ -193,7 +160,7 @@ impl<'tcx> OnUnimplementedDirective { attr.span, )?), note: None, - enclosing_scope: None, + parent_label: None, append_const_msg: None, })) } else { @@ -214,7 +181,7 @@ impl<'tcx> OnUnimplementedDirective { let mut message = None; let mut label = None; let mut note = None; - let mut enclosing_scope = None; + let mut parent_label = None; let mut append_const_msg = None; info!("evaluate({:?}, trait_ref={:?}, options={:?})", self, trait_ref, options); @@ -250,8 +217,8 @@ impl<'tcx> OnUnimplementedDirective { note = Some(note_.clone()); } - if let Some(ref enclosing_scope_) = command.enclosing_scope { - enclosing_scope = Some(enclosing_scope_.clone()); + if let Some(ref parent_label_) = command.parent_label { + parent_label = Some(parent_label_.clone()); } append_const_msg = command.append_const_msg; @@ -261,7 +228,7 @@ impl<'tcx> OnUnimplementedDirective { label: label.map(|l| l.format(tcx, trait_ref, &options_map)), message: message.map(|m| m.format(tcx, trait_ref, &options_map)), note: note.map(|n| n.format(tcx, trait_ref, &options_map)), - enclosing_scope: enclosing_scope.map(|e_s| e_s.format(tcx, trait_ref, &options_map)), + parent_label: parent_label.map(|e_s| e_s.format(tcx, trait_ref, &options_map)), append_const_msg, } } diff --git a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs new file mode 100644 index 000000000..108dae092 --- /dev/null +++ b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs @@ -0,0 +1,115 @@ +use crate::infer::InferCtxt; +use crate::traits::query::type_op::{self, TypeOp, TypeOpOutput}; +use crate::traits::query::NoSolution; +use crate::traits::{ObligationCause, TraitEngine, TraitEngineExt}; +use rustc_data_structures::fx::FxHashSet; +use rustc_hir as hir; +use rustc_hir::HirId; +use rustc_middle::ty::{self, ParamEnv, Ty}; + +pub use rustc_middle::traits::query::OutlivesBound; + +type Bounds<'a, 'tcx: 'a> = impl Iterator<Item = OutlivesBound<'tcx>> + 'a; +pub trait InferCtxtExt<'a, 'tcx> { + fn implied_outlives_bounds( + &self, + param_env: ty::ParamEnv<'tcx>, + body_id: hir::HirId, + ty: Ty<'tcx>, + ) -> Vec<OutlivesBound<'tcx>>; + + fn implied_bounds_tys( + &'a self, + param_env: ty::ParamEnv<'tcx>, + body_id: hir::HirId, + tys: FxHashSet<Ty<'tcx>>, + ) -> Bounds<'a, 'tcx>; +} + +impl<'a, 'tcx: 'a> InferCtxtExt<'a, 'tcx> for InferCtxt<'tcx> { + /// Implied bounds are region relationships that we deduce + /// automatically. The idea is that (e.g.) a caller must check that a + /// function's argument types are well-formed immediately before + /// calling that fn, and hence the *callee* can assume that its + /// argument types are well-formed. This may imply certain relationships + /// between generic parameters. For example: + /// ``` + /// fn foo<'a,T>(x: &'a T) {} + /// ``` + /// can only be called with a `'a` and `T` such that `&'a T` is WF. + /// For `&'a T` to be WF, `T: 'a` must hold. So we can assume `T: 'a`. + /// + /// # Parameters + /// + /// - `param_env`, the where-clauses in scope + /// - `body_id`, the body-id to use when normalizing assoc types. + /// Note that this may cause outlives obligations to be injected + /// into the inference context with this body-id. + /// - `ty`, the type that we are supposed to assume is WF. + #[instrument(level = "debug", skip(self, param_env, body_id), ret)] + fn implied_outlives_bounds( + &self, + param_env: ty::ParamEnv<'tcx>, + body_id: hir::HirId, + ty: Ty<'tcx>, + ) -> Vec<OutlivesBound<'tcx>> { + let span = self.tcx.hir().span(body_id); + let result = param_env + .and(type_op::implied_outlives_bounds::ImpliedOutlivesBounds { ty }) + .fully_perform(self); + let result = match result { + Ok(r) => r, + Err(NoSolution) => { + self.tcx.sess.delay_span_bug( + span, + "implied_outlives_bounds failed to solve all obligations", + ); + return vec![]; + } + }; + + let TypeOpOutput { output, constraints, .. } = result; + + if let Some(constraints) = constraints { + debug!(?constraints); + // Instantiation may have produced new inference variables and constraints on those + // variables. Process these constraints. + let mut fulfill_cx = <dyn TraitEngine<'tcx>>::new(self.tcx); + let cause = ObligationCause::misc(span, body_id); + for &constraint in &constraints.outlives { + let obligation = self.query_outlives_constraint_to_obligation( + constraint, + cause.clone(), + param_env, + ); + fulfill_cx.register_predicate_obligation(self, obligation); + } + if !constraints.member_constraints.is_empty() { + span_bug!(span, "{:#?}", constraints.member_constraints); + } + let errors = fulfill_cx.select_all_or_error(self); + if !errors.is_empty() { + self.tcx.sess.delay_span_bug( + span, + "implied_outlives_bounds failed to solve obligations from instantiation", + ); + } + }; + + output + } + + fn implied_bounds_tys( + &'a self, + param_env: ParamEnv<'tcx>, + body_id: HirId, + tys: FxHashSet<Ty<'tcx>>, + ) -> Bounds<'a, 'tcx> { + tys.into_iter() + .map(move |ty| { + let ty = self.resolve_vars_if_possible(ty); + self.implied_outlives_bounds(param_env, body_id, ty) + }) + .flatten() + } +} diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index c4e80e1ba..e4284b9d3 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -18,7 +18,7 @@ use super::{Normalized, NormalizedTy, ProjectionCacheEntry, ProjectionCacheKey}; use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use crate::infer::{InferCtxt, InferOk, LateBoundRegionConversionTime}; -use crate::traits::error_reporting::InferCtxtExt as _; +use crate::traits::error_reporting::TypeErrCtxtExt as _; use crate::traits::query::evaluate_obligation::InferCtxtExt as _; use crate::traits::select::ProjectionMatchesProjection; use rustc_data_structures::sso::SsoHashSet; @@ -30,8 +30,8 @@ use rustc_hir::lang_items::LangItem; use rustc_infer::infer::resolve::OpportunisticRegionResolver; use rustc_middle::traits::select::OverflowError; use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; -use rustc_middle::ty::subst::Subst; use rustc_middle::ty::visit::{MaxUniverse, TypeVisitable}; +use rustc_middle::ty::DefIdTree; use rustc_middle::ty::{self, Term, ToPredicate, Ty, TyCtxt}; use rustc_span::symbol::sym; @@ -62,7 +62,8 @@ enum ProjectionCandidate<'tcx> { /// From a where-clause in the env or object type ParamEnv(ty::PolyProjectionPredicate<'tcx>), - /// From the definition of `Trait` when you have something like <<A as Trait>::B as Trait2>::C + /// From the definition of `Trait` when you have something like + /// `<<A as Trait>::B as Trait2>::C`. TraitDef(ty::PolyProjectionPredicate<'tcx>), /// Bounds specified on an object type @@ -70,6 +71,16 @@ enum ProjectionCandidate<'tcx> { /// From an "impl" (or a "pseudo-impl" returned by select) Select(Selection<'tcx>), + + ImplTraitInTrait(ImplTraitInTraitCandidate<'tcx>), +} + +#[derive(PartialEq, Eq, Debug)] +enum ImplTraitInTraitCandidate<'tcx> { + // The `impl Trait` from a trait function's default body + Trait, + // A concrete type provided from a trait's `impl Trait` from an impl + Impl(ImplSourceUserDefinedData<'tcx, PredicateObligation<'tcx>>), } enum ProjectionCandidateSet<'tcx> { @@ -231,7 +242,7 @@ pub(super) fn poly_project_and_unify_type<'cx, 'tcx>( /// If successful, this may result in additional obligations. /// /// See [poly_project_and_unify_type] for an explanation of the return value. -#[tracing::instrument(level = "debug", skip(selcx))] +#[instrument(level = "debug", skip(selcx))] fn project_and_unify_type<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionObligation<'tcx>, @@ -253,7 +264,7 @@ fn project_and_unify_type<'cx, 'tcx>( }; debug!(?normalized, ?obligations, "project_and_unify_type result"); let actual = obligation.predicate.term; - // For an example where this is neccessary see src/test/ui/impl-trait/nested-return-type2.rs + // For an example where this is necessary see src/test/ui/impl-trait/nested-return-type2.rs // This allows users to omit re-mentioning all bounds on an associated type and just use an // `impl Trait` for the assoc type to add more bounds. let InferOk { value: actual, obligations: new } = @@ -511,7 +522,7 @@ impl<'a, 'b, 'tcx> TypeFolder<'tcx> for AssocTypeNormalizer<'a, 'b, 'tcx> { self.param_env, ty, ); - self.selcx.infcx().report_overflow_error(&obligation, true); + self.selcx.infcx().err_ctxt().report_overflow_error(&obligation, true); } let substs = substs.fold_with(self); @@ -552,7 +563,7 @@ impl<'a, 'b, 'tcx> TypeFolder<'tcx> for AssocTypeNormalizer<'a, 'b, 'tcx> { ) .ok() .flatten() - .unwrap_or_else(|| ty::Term::Ty(ty.super_fold_with(self))) + .unwrap_or_else(|| ty.super_fold_with(self).into()) }; debug!( ?self.depth, @@ -620,19 +631,33 @@ impl<'a, 'b, 'tcx> TypeFolder<'tcx> for AssocTypeNormalizer<'a, 'b, 'tcx> { #[instrument(skip(self), level = "debug")] fn fold_const(&mut self, constant: ty::Const<'tcx>) -> ty::Const<'tcx> { - if self.selcx.tcx().lazy_normalization() || !self.eager_inference_replacement { + let tcx = self.selcx.tcx(); + if tcx.lazy_normalization() { constant } else { let constant = constant.super_fold_with(self); - debug!(?constant); - debug!("self.param_env: {:?}", self.param_env); - constant.eval(self.selcx.tcx(), self.param_env) + debug!(?constant, ?self.param_env); + with_replaced_escaping_bound_vars( + self.selcx.infcx(), + &mut self.universes, + constant, + |constant| constant.eval(tcx, self.param_env), + ) + } + } + + #[inline] + fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> { + if p.allow_normalization() && needs_normalization(&p, self.param_env.reveal()) { + p.super_fold_with(self) + } else { + p } } } pub struct BoundVarReplacer<'me, 'tcx> { - infcx: &'me InferCtxt<'me, 'tcx>, + infcx: &'me InferCtxt<'tcx>, // These three maps track the bound variable that were replaced by placeholders. It might be // nice to remove these since we already have the `kind` in the placeholder; we really just need // the `var` (but we *could* bring that into scope if we were to track them as we pass them). @@ -647,11 +672,46 @@ pub struct BoundVarReplacer<'me, 'tcx> { universe_indices: &'me mut Vec<Option<ty::UniverseIndex>>, } +/// Executes `f` on `value` after replacing all escaping bound variables with placeholders +/// and then replaces these placeholders with the original bound variables in the result. +/// +/// In most places, bound variables should be replaced right when entering a binder, making +/// this function unnecessary. However, normalization currently does not do that, so we have +/// to do this lazily. +/// +/// You should not add any additional uses of this function, at least not without first +/// discussing it with t-types. +/// +/// FIXME(@lcnr): We may even consider experimenting with eagerly replacing bound vars during +/// normalization as well, at which point this function will be unnecessary and can be removed. +pub fn with_replaced_escaping_bound_vars<'a, 'tcx, T: TypeFoldable<'tcx>, R: TypeFoldable<'tcx>>( + infcx: &'a InferCtxt<'tcx>, + universe_indices: &'a mut Vec<Option<ty::UniverseIndex>>, + value: T, + f: impl FnOnce(T) -> R, +) -> R { + if value.has_escaping_bound_vars() { + let (value, mapped_regions, mapped_types, mapped_consts) = + BoundVarReplacer::replace_bound_vars(infcx, universe_indices, value); + let result = f(value); + PlaceholderReplacer::replace_placeholders( + infcx, + mapped_regions, + mapped_types, + mapped_consts, + universe_indices, + result, + ) + } else { + f(value) + } +} + impl<'me, 'tcx> BoundVarReplacer<'me, 'tcx> { /// Returns `Some` if we *were* able to replace bound vars. If there are any bound vars that /// use a binding level above `universe_indices.len()`, we fail. pub fn replace_bound_vars<T: TypeFoldable<'tcx>>( - infcx: &'me InferCtxt<'me, 'tcx>, + infcx: &'me InferCtxt<'tcx>, universe_indices: &'me mut Vec<Option<ty::UniverseIndex>>, value: T, ) -> ( @@ -771,7 +831,7 @@ impl<'tcx> TypeFolder<'tcx> for BoundVarReplacer<'_, 'tcx> { // The inverse of `BoundVarReplacer`: replaces placeholders with the bound vars from which they came. pub struct PlaceholderReplacer<'me, 'tcx> { - infcx: &'me InferCtxt<'me, 'tcx>, + infcx: &'me InferCtxt<'tcx>, mapped_regions: BTreeMap<ty::PlaceholderRegion, ty::BoundRegion>, mapped_types: BTreeMap<ty::PlaceholderType, ty::BoundTy>, mapped_consts: BTreeMap<ty::PlaceholderConst<'tcx>, ty::BoundVar>, @@ -781,7 +841,7 @@ pub struct PlaceholderReplacer<'me, 'tcx> { impl<'me, 'tcx> PlaceholderReplacer<'me, 'tcx> { pub fn replace_placeholders<T: TypeFoldable<'tcx>>( - infcx: &'me InferCtxt<'me, 'tcx>, + infcx: &'me InferCtxt<'tcx>, mapped_regions: BTreeMap<ty::PlaceholderRegion, ty::BoundRegion>, mapped_types: BTreeMap<ty::PlaceholderType, ty::BoundTy>, mapped_consts: BTreeMap<ty::PlaceholderConst<'tcx>, ty::BoundVar>, @@ -1182,7 +1242,7 @@ impl<'tcx> Progress<'tcx> { /// /// IMPORTANT: /// - `obligation` must be fully normalized -#[tracing::instrument(level = "info", skip(selcx))] +#[instrument(level = "info", skip(selcx))] fn project<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionTyObligation<'tcx>, @@ -1201,6 +1261,8 @@ fn project<'cx, 'tcx>( let mut candidates = ProjectionCandidateSet::None; + assemble_candidate_for_impl_trait_in_trait(selcx, obligation, &mut candidates); + // Make sure that the following procedures are kept in order. ParamEnv // needs to be first because it has highest priority, and Select checks // the return value of push_candidate which assumes it's ran at last. @@ -1239,6 +1301,63 @@ fn project<'cx, 'tcx>( } } +/// If the predicate's item is an `ImplTraitPlaceholder`, we do a select on the +/// corresponding trait ref. If this yields an `impl`, then we're able to project +/// to a concrete type, since we have an `impl`'s method to provide the RPITIT. +fn assemble_candidate_for_impl_trait_in_trait<'cx, 'tcx>( + selcx: &mut SelectionContext<'cx, 'tcx>, + obligation: &ProjectionTyObligation<'tcx>, + candidate_set: &mut ProjectionCandidateSet<'tcx>, +) { + let tcx = selcx.tcx(); + if tcx.def_kind(obligation.predicate.item_def_id) == DefKind::ImplTraitPlaceholder { + let trait_fn_def_id = tcx.impl_trait_in_trait_parent(obligation.predicate.item_def_id); + // If we are trying to project an RPITIT with trait's default `Self` parameter, + // then we must be within a default trait body. + if obligation.predicate.self_ty() + == ty::InternalSubsts::identity_for_item(tcx, obligation.predicate.item_def_id) + .type_at(0) + && tcx.associated_item(trait_fn_def_id).defaultness(tcx).has_value() + { + candidate_set.push_candidate(ProjectionCandidate::ImplTraitInTrait( + ImplTraitInTraitCandidate::Trait, + )); + return; + } + + let trait_def_id = tcx.parent(trait_fn_def_id); + let trait_substs = + obligation.predicate.substs.truncate_to(tcx, tcx.generics_of(trait_def_id)); + // FIXME(named-returns): Binders + let trait_predicate = + ty::Binder::dummy(ty::TraitRef { def_id: trait_def_id, substs: trait_substs }) + .to_poly_trait_predicate(); + + let _ = + selcx.infcx().commit_if_ok(|_| match selcx.select(&obligation.with(trait_predicate)) { + Ok(Some(super::ImplSource::UserDefined(data))) => { + candidate_set.push_candidate(ProjectionCandidate::ImplTraitInTrait( + ImplTraitInTraitCandidate::Impl(data), + )); + Ok(()) + } + Ok(None) => { + candidate_set.mark_ambiguous(); + return Err(()); + } + Ok(Some(_)) => { + // Don't know enough about the impl to provide a useful signature + return Err(()); + } + Err(e) => { + debug!(error = ?e, "selection error"); + candidate_set.mark_error(e); + return Err(()); + } + }); + } +} + /// The first thing we have to do is scan through the parameter /// environment to see whether there are any projection predicates /// there that can answer this question. @@ -1257,7 +1376,7 @@ fn assemble_candidates_from_param_env<'cx, 'tcx>( ); } -/// In the case of a nested projection like <<A as Foo>::FooT as Bar>::BarT, we may find +/// In the case of a nested projection like `<<A as Foo>::FooT as Bar>::BarT`, we may find /// that the definition of `Foo` has some clues: /// /// ```ignore (illustrative) @@ -1344,7 +1463,7 @@ fn assemble_candidates_from_object_ty<'cx, 'tcx>( ); } -#[tracing::instrument( +#[instrument( level = "debug", skip(selcx, candidate_set, ctor, env_predicates, potentially_unnormalized_candidates) )] @@ -1378,7 +1497,7 @@ fn assemble_candidates_from_predicates<'cx, 'tcx>( candidate_set.push_candidate(ctor(data)); if potentially_unnormalized_candidates - && !obligation.predicate.has_infer_types_or_consts() + && !obligation.predicate.has_non_region_infer() { // HACK: Pick the first trait def candidate for a fully // inferred predicate. This is to allow duplicates that @@ -1395,12 +1514,17 @@ fn assemble_candidates_from_predicates<'cx, 'tcx>( } } -#[tracing::instrument(level = "debug", skip(selcx, obligation, candidate_set))] +#[instrument(level = "debug", skip(selcx, obligation, candidate_set))] fn assemble_candidates_from_impls<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionTyObligation<'tcx>, candidate_set: &mut ProjectionCandidateSet<'tcx>, ) { + // Can't assemble candidate from impl for RPITIT + if selcx.tcx().def_kind(obligation.predicate.item_def_id) == DefKind::ImplTraitPlaceholder { + return; + } + // If we are resolving `<T as TraitRef<...>>::Item == Type`, // start out by selecting the predicate `T as TraitRef<...>`: let poly_trait_ref = ty::Binder::dummy(obligation.predicate.trait_ref(selcx.tcx())); @@ -1676,6 +1800,18 @@ fn confirm_candidate<'cx, 'tcx>( ProjectionCandidate::Select(impl_source) => { confirm_select_candidate(selcx, obligation, impl_source) } + ProjectionCandidate::ImplTraitInTrait(ImplTraitInTraitCandidate::Impl(data)) => { + confirm_impl_trait_in_trait_candidate(selcx, obligation, data) + } + // If we're projecting an RPITIT for a default trait body, that's just + // the same def-id, but as an opaque type (with regular RPIT semantics). + ProjectionCandidate::ImplTraitInTrait(ImplTraitInTraitCandidate::Trait) => Progress { + term: selcx + .tcx() + .mk_opaque(obligation.predicate.item_def_id, obligation.predicate.substs) + .into(), + obligations: vec![], + }, }; // When checking for cycle during evaluation, we compare predicates with @@ -2021,15 +2157,15 @@ fn confirm_impl_candidate<'cx, 'tcx>( let identity_substs = crate::traits::InternalSubsts::identity_for_item(tcx, assoc_ty.item.def_id); let did = ty::WithOptConstParam::unknown(assoc_ty.item.def_id); - let kind = ty::ConstKind::Unevaluated(ty::Unevaluated::new(did, identity_substs)); + let kind = ty::ConstKind::Unevaluated(ty::UnevaluatedConst::new(did, identity_substs)); ty.map_bound(|ty| tcx.mk_const(ty::ConstS { ty, kind }).into()) } else { ty.map_bound(|ty| ty.into()) }; - if substs.len() != tcx.generics_of(assoc_ty.item.def_id).count() { + if !check_substs_compatible(tcx, &assoc_ty.item, substs) { let err = tcx.ty_error_with_message( obligation.cause.span, - "impl item and trait item have different parameter counts", + "impl item and trait item have different parameters", ); Progress { term: err.into(), obligations: nested } } else { @@ -2038,10 +2174,125 @@ fn confirm_impl_candidate<'cx, 'tcx>( } } +// Verify that the trait item and its implementation have compatible substs lists +fn check_substs_compatible<'tcx>( + tcx: TyCtxt<'tcx>, + assoc_ty: &ty::AssocItem, + substs: ty::SubstsRef<'tcx>, +) -> bool { + fn check_substs_compatible_inner<'tcx>( + tcx: TyCtxt<'tcx>, + generics: &'tcx ty::Generics, + args: &'tcx [ty::GenericArg<'tcx>], + ) -> bool { + if generics.count() != args.len() { + return false; + } + + let (parent_args, own_args) = args.split_at(generics.parent_count); + + if let Some(parent) = generics.parent + && let parent_generics = tcx.generics_of(parent) + && !check_substs_compatible_inner(tcx, parent_generics, parent_args) { + return false; + } + + for (param, arg) in std::iter::zip(&generics.params, own_args) { + match (¶m.kind, arg.unpack()) { + (ty::GenericParamDefKind::Type { .. }, ty::GenericArgKind::Type(_)) + | (ty::GenericParamDefKind::Lifetime, ty::GenericArgKind::Lifetime(_)) + | (ty::GenericParamDefKind::Const { .. }, ty::GenericArgKind::Const(_)) => {} + _ => return false, + } + } + + true + } + + check_substs_compatible_inner(tcx, tcx.generics_of(assoc_ty.def_id), substs.as_slice()) +} + +fn confirm_impl_trait_in_trait_candidate<'tcx>( + selcx: &mut SelectionContext<'_, 'tcx>, + obligation: &ProjectionTyObligation<'tcx>, + data: ImplSourceUserDefinedData<'tcx, PredicateObligation<'tcx>>, +) -> Progress<'tcx> { + let tcx = selcx.tcx(); + let mut obligations = data.nested; + + let trait_fn_def_id = tcx.impl_trait_in_trait_parent(obligation.predicate.item_def_id); + let Ok(leaf_def) = assoc_def(selcx, data.impl_def_id, trait_fn_def_id) else { + return Progress { term: tcx.ty_error().into(), obligations }; + }; + if !leaf_def.item.defaultness(tcx).has_value() { + return Progress { term: tcx.ty_error().into(), obligations }; + } + + // Use the default `impl Trait` for the trait, e.g., for a default trait body + if leaf_def.item.container == ty::AssocItemContainer::TraitContainer { + return Progress { + term: tcx + .mk_opaque(obligation.predicate.item_def_id, obligation.predicate.substs) + .into(), + obligations, + }; + } + + let impl_fn_def_id = leaf_def.item.def_id; + // Rebase from {trait}::{fn}::{opaque} to {impl}::{fn}::{opaque}, + // since `data.substs` are the impl substs. + let impl_fn_substs = + obligation.predicate.substs.rebase_onto(tcx, tcx.parent(trait_fn_def_id), data.substs); + + let cause = ObligationCause::new( + obligation.cause.span, + obligation.cause.body_id, + super::ItemObligation(impl_fn_def_id), + ); + let predicates = normalize_with_depth_to( + selcx, + obligation.param_env, + cause.clone(), + obligation.recursion_depth + 1, + tcx.predicates_of(impl_fn_def_id).instantiate(tcx, impl_fn_substs), + &mut obligations, + ); + obligations.extend(std::iter::zip(predicates.predicates, predicates.spans).map( + |(pred, span)| { + Obligation::with_depth( + ObligationCause::new( + obligation.cause.span, + obligation.cause.body_id, + if span.is_dummy() { + super::ItemObligation(impl_fn_def_id) + } else { + super::BindingObligation(impl_fn_def_id, span) + }, + ), + obligation.recursion_depth + 1, + obligation.param_env, + pred, + ) + }, + )); + + let ty = super::normalize_to( + selcx, + obligation.param_env, + cause.clone(), + tcx.bound_trait_impl_trait_tys(impl_fn_def_id) + .map_bound(|tys| { + tys.map_or_else(|_| tcx.ty_error(), |tys| tys[&obligation.predicate.item_def_id]) + }) + .subst(tcx, impl_fn_substs), + &mut obligations, + ); + + Progress { term: ty.into(), obligations } +} + // Get obligations corresponding to the predicates from the where-clause of the // associated type itself. -// Note: `feature(generic_associated_types)` is required to write such -// predicates, even for non-generic associated types. fn assoc_ty_own_obligations<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionTyObligation<'tcx>, diff --git a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs index 32669e23d..c84f128dd 100644 --- a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs +++ b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs @@ -31,7 +31,7 @@ pub trait InferCtxtExt<'tcx> { ) -> EvaluationResult; } -impl<'cx, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'cx, 'tcx> { +impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { /// Evaluates whether the predicate can be satisfied (by any means) /// in the given `ParamEnv`. fn predicate_may_hold(&self, obligation: &PredicateObligation<'tcx>) -> bool { diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs index 449d7a7b4..58e4597b7 100644 --- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -5,17 +5,16 @@ use crate::infer::at::At; use crate::infer::canonical::OriginalQueryValues; use crate::infer::{InferCtxt, InferOk}; -use crate::traits::error_reporting::InferCtxtExt; -use crate::traits::project::needs_normalization; +use crate::traits::error_reporting::TypeErrCtxtExt; +use crate::traits::project::{needs_normalization, BoundVarReplacer, PlaceholderReplacer}; use crate::traits::{Obligation, ObligationCause, PredicateObligation, Reveal}; use rustc_data_structures::sso::SsoHashMap; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_infer::traits::Normalized; -use rustc_middle::mir; use rustc_middle::ty::fold::{FallibleTypeFolder, TypeFoldable, TypeSuperFoldable}; -use rustc_middle::ty::subst::Subst; use rustc_middle::ty::visit::{TypeSuperVisitable, TypeVisitable}; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitor}; +use rustc_span::DUMMY_SP; use std::ops::ControlFlow; @@ -48,10 +47,11 @@ impl<'cx, 'tcx> AtExt<'tcx> for At<'cx, 'tcx> { T: TypeFoldable<'tcx>, { debug!( - "normalize::<{}>(value={:?}, param_env={:?})", + "normalize::<{}>(value={:?}, param_env={:?}, cause={:?})", std::any::type_name::<T>(), value, self.param_env, + self.cause, ); if !needs_normalization(&value, self.param_env.reveal()) { return Ok(Normalized { value, obligations: vec![] }); @@ -154,7 +154,7 @@ impl<'tcx> TypeVisitor<'tcx> for MaxEscapingBoundVarVisitor { } struct QueryNormalizer<'cx, 'tcx> { - infcx: &'cx InferCtxt<'cx, 'tcx>, + infcx: &'cx InferCtxt<'tcx>, cause: &'cx ObligationCause<'tcx>, param_env: ty::ParamEnv<'tcx>, obligations: Vec<PredicateObligation<'tcx>>, @@ -213,7 +213,7 @@ impl<'cx, 'tcx> FallibleTypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> { self.param_env, ty, ); - self.infcx.report_overflow_error(&obligation, true); + self.infcx.err_ctxt().report_overflow_error(&obligation, true); } let generic_ty = self.tcx().bound_type_of(def_id); @@ -254,7 +254,15 @@ impl<'cx, 'tcx> FallibleTypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> { let result = tcx.normalize_projection_ty(c_data)?; // We don't expect ambiguity. if result.is_ambiguous() { - bug!("unexpected ambiguity: {:?} {:?}", c_data, result); + // Rustdoc normalizes possibly not well-formed types, so only + // treat this as a bug if we're not in rustdoc. + if !tcx.sess.opts.actually_rustdoc { + tcx.sess.delay_span_bug( + DUMMY_SP, + format!("unexpected ambiguity: {:?} {:?}", c_data, result), + ); + } + return Err(NoSolution); } let InferOk { value: result, obligations } = self.infcx.instantiate_query_response_and_region_obligations( @@ -266,7 +274,15 @@ impl<'cx, 'tcx> FallibleTypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> { debug!("QueryNormalizer: result = {:#?}", result); debug!("QueryNormalizer: obligations = {:#?}", obligations); self.obligations.extend(obligations); - Ok(result.normalized_ty) + + let res = result.normalized_ty; + // `tcx.normalize_projection_ty` may normalize to a type that still has + // unevaluated consts, so keep normalizing here if that's the case. + if res != ty && res.has_type_flags(ty::TypeFlags::HAS_CT_PROJECTION) { + Ok(res.try_super_fold_with(self)?) + } else { + Ok(res) + } } ty::Projection(data) => { @@ -275,11 +291,7 @@ impl<'cx, 'tcx> FallibleTypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> { let tcx = self.infcx.tcx; let infcx = self.infcx; let (data, mapped_regions, mapped_types, mapped_consts) = - crate::traits::project::BoundVarReplacer::replace_bound_vars( - infcx, - &mut self.universes, - data, - ); + BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, data); let data = data.try_fold_with(self)?; let mut orig_values = OriginalQueryValues::default(); @@ -293,7 +305,15 @@ impl<'cx, 'tcx> FallibleTypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> { let result = tcx.normalize_projection_ty(c_data)?; // We don't expect ambiguity. if result.is_ambiguous() { - bug!("unexpected ambiguity: {:?} {:?}", c_data, result); + // Rustdoc normalizes possibly not well-formed types, so only + // treat this as a bug if we're not in rustdoc. + if !tcx.sess.opts.actually_rustdoc { + tcx.sess.delay_span_bug( + DUMMY_SP, + format!("unexpected ambiguity: {:?} {:?}", c_data, result), + ); + } + return Err(NoSolution); } let InferOk { value: result, obligations } = self.infcx.instantiate_query_response_and_region_obligations( @@ -305,18 +325,26 @@ impl<'cx, 'tcx> FallibleTypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> { debug!("QueryNormalizer: result = {:#?}", result); debug!("QueryNormalizer: obligations = {:#?}", obligations); self.obligations.extend(obligations); - Ok(crate::traits::project::PlaceholderReplacer::replace_placeholders( + let res = PlaceholderReplacer::replace_placeholders( infcx, mapped_regions, mapped_types, mapped_consts, &self.universes, result.normalized_ty, - )) + ); + // `tcx.normalize_projection_ty` may normalize to a type that still has + // unevaluated consts, so keep normalizing here if that's the case. + if res != ty && res.has_type_flags(ty::TypeFlags::HAS_CT_PROJECTION) { + Ok(res.try_super_fold_with(self)?) + } else { + Ok(res) + } } _ => ty.try_super_fold_with(self), })()?; + self.cache.insert(ty, res); Ok(res) } @@ -326,29 +354,24 @@ impl<'cx, 'tcx> FallibleTypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> { constant: ty::Const<'tcx>, ) -> Result<ty::Const<'tcx>, Self::Error> { let constant = constant.try_super_fold_with(self)?; - Ok(constant.eval(self.infcx.tcx, self.param_env)) + debug!(?constant, ?self.param_env); + Ok(crate::traits::project::with_replaced_escaping_bound_vars( + self.infcx, + &mut self.universes, + constant, + |constant| constant.eval(self.infcx.tcx, self.param_env), + )) } - fn try_fold_mir_const( + #[inline] + fn try_fold_predicate( &mut self, - constant: mir::ConstantKind<'tcx>, - ) -> Result<mir::ConstantKind<'tcx>, Self::Error> { - Ok(match constant { - mir::ConstantKind::Ty(c) => { - let const_folded = c.try_super_fold_with(self)?; - match const_folded.kind() { - ty::ConstKind::Value(valtree) => { - let tcx = self.infcx.tcx; - let ty = const_folded.ty(); - let const_val = tcx.valtree_to_const_val((ty, valtree)); - debug!(?ty, ?valtree, ?const_val); - - mir::ConstantKind::Val(const_val, ty) - } - _ => mir::ConstantKind::Ty(const_folded), - } - } - mir::ConstantKind::Val(_, _) => constant.try_super_fold_with(self)?, - }) + p: ty::Predicate<'tcx>, + ) -> Result<ty::Predicate<'tcx>, Self::Error> { + if p.allow_normalization() && needs_normalization(&p, self.param_env.reveal()) { + p.try_super_fold_with(self) + } else { + Ok(p) + } } } diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs index c99564936..6bf3ed0d0 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs @@ -1,11 +1,9 @@ use crate::infer::canonical::query_response; use crate::infer::{InferCtxt, InferOk}; -use crate::traits::engine::TraitEngineExt as _; +use crate::traits; use crate::traits::query::type_op::TypeOpOutput; use crate::traits::query::Fallible; -use crate::traits::TraitEngine; use rustc_infer::infer::region_constraints::RegionConstraintData; -use rustc_infer::traits::TraitEngineExt as _; use rustc_span::source_map::DUMMY_SP; use std::fmt; @@ -18,16 +16,16 @@ pub struct CustomTypeOp<F, G> { impl<F, G> CustomTypeOp<F, G> { pub fn new<'tcx, R>(closure: F, description: G) -> Self where - F: FnOnce(&InferCtxt<'_, 'tcx>) -> Fallible<InferOk<'tcx, R>>, + F: FnOnce(&InferCtxt<'tcx>) -> Fallible<InferOk<'tcx, R>>, G: Fn() -> String, { CustomTypeOp { closure, description } } } -impl<'tcx, F, R, G> super::TypeOp<'tcx> for CustomTypeOp<F, G> +impl<'tcx, F, R: fmt::Debug, G> super::TypeOp<'tcx> for CustomTypeOp<F, G> where - F: for<'a, 'cx> FnOnce(&'a InferCtxt<'cx, 'tcx>) -> Fallible<InferOk<'tcx, R>>, + F: for<'a, 'cx> FnOnce(&'a InferCtxt<'tcx>) -> Fallible<InferOk<'tcx, R>>, G: Fn() -> String, { type Output = R; @@ -38,7 +36,7 @@ where /// Processes the operation and all resulting obligations, /// returning the final result along with any region constraints /// (they will be given over to the NLL region solver). - fn fully_perform(self, infcx: &InferCtxt<'_, 'tcx>) -> Fallible<TypeOpOutput<'tcx, Self>> { + fn fully_perform(self, infcx: &InferCtxt<'tcx>) -> Fallible<TypeOpOutput<'tcx, Self>> { if cfg!(debug_assertions) { info!("fully_perform({:?})", self); } @@ -59,11 +57,9 @@ where /// Executes `op` and then scrapes out all the "old style" region /// constraints that result, creating query-region-constraints. pub fn scrape_region_constraints<'tcx, Op: super::TypeOp<'tcx, Output = R>, R>( - infcx: &InferCtxt<'_, 'tcx>, + infcx: &InferCtxt<'tcx>, op: impl FnOnce() -> Fallible<InferOk<'tcx, R>>, ) -> Fallible<(TypeOpOutput<'tcx, Op>, RegionConstraintData<'tcx>)> { - let mut fulfill_cx = <dyn TraitEngine<'_>>::new(infcx.tcx); - // During NLL, we expect that nobody will register region // obligations **except** as part of a custom type op (and, at the // end of each custom type op, we scrape out the region @@ -77,8 +73,7 @@ pub fn scrape_region_constraints<'tcx, Op: super::TypeOp<'tcx, Output = R>, R>( ); let InferOk { value, obligations } = infcx.commit_if_ok(|_| op())?; - fulfill_cx.register_predicate_obligations(infcx, obligations); - let errors = fulfill_cx.select_all_or_error(infcx); + let errors = traits::fully_solve_obligations(infcx, obligations); if !errors.is_empty() { infcx.tcx.sess.diagnostic().delay_span_bug( DUMMY_SP, @@ -94,8 +89,8 @@ pub fn scrape_region_constraints<'tcx, Op: super::TypeOp<'tcx, Output = R>, R>( infcx.tcx, region_obligations .iter() - .map(|r_o| (r_o.sup_type, r_o.sub_region)) - .map(|(ty, r)| (infcx.resolve_vars_if_possible(ty), r)), + .map(|r_o| (r_o.sup_type, r_o.sub_region, r_o.origin.to_constraint_category())) + .map(|(ty, r, cc)| (infcx.resolve_vars_if_possible(ty), r, cc)), ®ion_constraint_data, ); diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs index 578e1d00c..29ae8ae6b 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs @@ -26,13 +26,13 @@ pub use rustc_middle::traits::query::type_op::*; /// extract out the resulting region constraints (or an error if it /// cannot be completed). pub trait TypeOp<'tcx>: Sized + fmt::Debug { - type Output; + type Output: fmt::Debug; type ErrorInfo; /// Processes the operation and all resulting obligations, /// returning the final result along with any region constraints /// (they will be given over to the NLL region solver). - fn fully_perform(self, infcx: &InferCtxt<'_, 'tcx>) -> Fallible<TypeOpOutput<'tcx, Self>>; + fn fully_perform(self, infcx: &InferCtxt<'tcx>) -> Fallible<TypeOpOutput<'tcx, Self>>; } /// The output from performing a type op @@ -78,7 +78,7 @@ pub trait QueryTypeOp<'tcx>: fmt::Debug + Copy + TypeFoldable<'tcx> + 'tcx { fn fully_perform_into( query_key: ParamEnvAnd<'tcx, Self>, - infcx: &InferCtxt<'_, 'tcx>, + infcx: &InferCtxt<'tcx>, output_query_region_constraints: &mut QueryRegionConstraints<'tcx>, ) -> Fallible<( Self::QueryResponse, @@ -120,7 +120,7 @@ where type Output = Q::QueryResponse; type ErrorInfo = Canonical<'tcx, ParamEnvAnd<'tcx, Q>>; - fn fully_perform(self, infcx: &InferCtxt<'_, 'tcx>) -> Fallible<TypeOpOutput<'tcx, Self>> { + fn fully_perform(self, infcx: &InferCtxt<'tcx>) -> Fallible<TypeOpOutput<'tcx, Self>> { let mut region_constraints = QueryRegionConstraints::default(); let (output, error_info, mut obligations, _) = Q::fully_perform_into(self, infcx, &mut region_constraints)?; diff --git a/compiler/rustc_trait_selection/src/traits/relationships.rs b/compiler/rustc_trait_selection/src/traits/relationships.rs index 8148e2b78..8cf500a46 100644 --- a/compiler/rustc_trait_selection/src/traits/relationships.rs +++ b/compiler/rustc_trait_selection/src/traits/relationships.rs @@ -6,7 +6,7 @@ use rustc_middle::ty::{self, ToPredicate}; pub(crate) fn update<'tcx, T>( engine: &mut T, - infcx: &InferCtxt<'_, 'tcx>, + infcx: &InferCtxt<'tcx>, obligation: &PredicateObligation<'tcx>, ) where T: TraitEngine<'tcx>, diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index a60ce0f34..4c5bc3339 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -6,9 +6,10 @@ //! //! [rustc dev guide]:https://rustc-dev-guide.rust-lang.org/traits/resolution.html#candidate-assembly use hir::LangItem; +use rustc_errors::DelayDm; use rustc_hir as hir; use rustc_hir::def_id::DefId; -use rustc_infer::traits::TraitEngine; +use rustc_infer::traits::ObligationCause; use rustc_infer::traits::{Obligation, SelectionError, TraitObligation}; use rustc_lint_defs::builtin::DEREF_INTO_DYN_SUPERTRAIT; use rustc_middle::ty::print::with_no_trimmed_paths; @@ -28,7 +29,7 @@ use super::SelectionCandidate::{self, *}; use super::{EvaluatedCandidate, SelectionCandidateSet, SelectionContext, TraitObligationStack}; impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { - #[instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip(self), ret)] pub(super) fn candidate_from_obligation<'o>( &mut self, stack: &TraitObligationStack<'o, 'tcx>, @@ -48,7 +49,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { if let Some(c) = self.check_candidate_cache(stack.obligation.param_env, cache_fresh_trait_pred) { - debug!(candidate = ?c, "CACHE HIT"); + debug!("CACHE HIT"); return c; } @@ -61,7 +62,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let (candidate, dep_node) = self.in_task(|this| this.candidate_from_obligation_no_cache(stack)); - debug!(?candidate, "CACHE MISS"); + debug!("CACHE MISS"); self.insert_candidate_cache( stack.obligation.param_env, cache_fresh_trait_pred, @@ -75,7 +76,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut self, stack: &TraitObligationStack<'o, 'tcx>, ) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> { - if let Some(conflict) = self.is_knowable(stack) { + if let Err(conflict) = self.is_knowable(stack) { debug!("coherence stage: not knowable"); if self.intercrate_ambiguity_causes.is_some() { debug!("evaluate_stack: intercrate_ambiguity_causes is some"); @@ -173,7 +174,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { debug!(?stack, ?candidates, "winnowed to {} candidates", candidates.len()); - let needs_infer = stack.obligation.predicate.has_infer_types_or_consts(); + let needs_infer = stack.obligation.predicate.has_non_region_infer(); // If there are STILL multiple candidates, we can further // reduce the list by dropping duplicates -- including @@ -309,6 +310,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // User-defined transmutability impls are permitted. self.assemble_candidates_from_impls(obligation, &mut candidates); self.assemble_candidates_for_transmutability(obligation, &mut candidates); + } else if lang_items.tuple_trait() == Some(def_id) { + self.assemble_candidate_for_tuple(obligation, &mut candidates); } else { if lang_items.clone_trait() == Some(def_id) { // Same builtin conditions as `Copy`, i.e., every type which has builtin support @@ -337,7 +340,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { Ok(candidates) } - #[tracing::instrument(level = "debug", skip(self, candidates))] + #[instrument(level = "debug", skip(self, candidates))] fn assemble_candidates_from_projected_tys( &mut self, obligation: &TraitObligation<'tcx>, @@ -360,14 +363,16 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { .infcx .probe(|_| self.match_projection_obligation_against_definition_bounds(obligation)); - candidates.vec.extend(result.into_iter().map(ProjectionCandidate)); + candidates + .vec + .extend(result.into_iter().map(|(idx, constness)| ProjectionCandidate(idx, constness))); } /// Given an obligation like `<SomeTrait for T>`, searches the obligations that the caller /// supplied to find out whether it is listed among them. /// /// Never affects the inference environment. - #[tracing::instrument(level = "debug", skip(self, stack, candidates))] + #[instrument(level = "debug", skip(self, stack, candidates))] fn assemble_candidates_from_caller_bounds<'o>( &mut self, stack: &TraitObligationStack<'o, 'tcx>, @@ -620,7 +625,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } - _ => candidates.vec.push(AutoImplCandidate(def_id)), + _ => candidates.vec.push(AutoImplCandidate), } } } @@ -706,8 +711,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { fn need_migrate_deref_output_trait_object( &mut self, ty: Ty<'tcx>, - cause: &traits::ObligationCause<'tcx>, param_env: ty::ParamEnv<'tcx>, + cause: &ObligationCause<'tcx>, ) -> Option<(Ty<'tcx>, DefId)> { let tcx = self.tcx(); if tcx.features().trait_upcasting { @@ -729,24 +734,27 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { return None; } - let mut fulfillcx = traits::FulfillmentContext::new_in_snapshot(); - let normalized_ty = fulfillcx.normalize_projection_type( - &self.infcx, + let ty = traits::normalize_projection_type( + self, param_env, ty::ProjectionTy { item_def_id: tcx.lang_items().deref_target()?, substs: trait_ref.substs, }, cause.clone(), - ); - - let ty::Dynamic(data, ..) = normalized_ty.kind() else { - return None; - }; - - let def_id = data.principal_def_id()?; - - return Some((normalized_ty, def_id)); + 0, + // We're *intentionally* throwing these away, + // since we don't actually use them. + &mut vec![], + ) + .ty() + .unwrap(); + + if let ty::Dynamic(data, ..) = ty.kind() { + Some((ty, data.principal_def_id()?)) + } else { + None + } } /// Searches for unsizing that might apply to `obligation`. @@ -809,8 +817,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { if let Some((deref_output_ty, deref_output_trait_did)) = self .need_migrate_deref_output_trait_object( source, - &obligation.cause, obligation.param_env, + &obligation.cause, ) { if deref_output_trait_did == target_trait_did { @@ -818,13 +826,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { DEREF_INTO_DYN_SUPERTRAIT, obligation.cause.body_id, obligation.cause.span, - |lint| { - lint.build(&format!( - "`{}` implements `Deref` with supertrait `{}` as output", - source, - deref_output_ty - )).emit(); - }, + DelayDm(|| format!( + "`{}` implements `Deref` with supertrait `{}` as output", + source, deref_output_ty + )), + |lint| lint, ); return; } @@ -877,17 +883,17 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { }; } - #[tracing::instrument(level = "debug", skip(self, obligation, candidates))] + #[instrument(level = "debug", skip(self, obligation, candidates))] fn assemble_candidates_for_transmutability( &mut self, obligation: &TraitObligation<'tcx>, candidates: &mut SelectionCandidateSet<'tcx>, ) { - if obligation.has_param_types_or_consts() { + if obligation.has_non_region_param() { return; } - if obligation.has_infer_types_or_consts() { + if obligation.has_non_region_infer() { candidates.ambiguous = true; return; } @@ -895,7 +901,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { candidates.vec.push(TransmutabilityCandidate); } - #[tracing::instrument(level = "debug", skip(self, obligation, candidates))] + #[instrument(level = "debug", skip(self, obligation, candidates))] fn assemble_candidates_for_trait_alias( &mut self, obligation: &TraitObligation<'tcx>, @@ -908,13 +914,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let def_id = obligation.predicate.def_id(); if self.tcx().is_trait_alias(def_id) { - candidates.vec.push(TraitAliasCandidate(def_id)); + candidates.vec.push(TraitAliasCandidate); } } /// Assembles the trait which are built-in to the language itself: /// `Copy`, `Clone` and `Sized`. - #[tracing::instrument(level = "debug", skip(self, candidates))] + #[instrument(level = "debug", skip(self, candidates))] fn assemble_builtin_bound_candidates( &mut self, conditions: BuiltinImplConditions<'tcx>, @@ -1006,4 +1012,46 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } } + + fn assemble_candidate_for_tuple( + &mut self, + obligation: &TraitObligation<'tcx>, + candidates: &mut SelectionCandidateSet<'tcx>, + ) { + let self_ty = self.infcx().shallow_resolve(obligation.self_ty().skip_binder()); + match self_ty.kind() { + ty::Tuple(_) => { + candidates.vec.push(BuiltinCandidate { has_nested: false }); + } + ty::Infer(ty::TyVar(_)) => { + candidates.ambiguous = true; + } + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Adt(_, _) + | ty::Foreign(_) + | ty::Str + | ty::Array(_, _) + | ty::Slice(_) + | ty::RawPtr(_) + | ty::Ref(_, _, _) + | ty::FnDef(_, _) + | ty::FnPtr(_) + | ty::Dynamic(_, _, _) + | ty::Closure(_, _) + | ty::Generator(_, _, _) + | ty::GeneratorWitness(_) + | ty::Never + | ty::Projection(_) + | ty::Opaque(_, _) + | ty::Param(_) + | ty::Bound(_, _) + | ty::Error(_) + | ty::Infer(_) + | ty::Placeholder(_) => {} + } + } } diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 2a1099fc8..ed22058c6 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -11,9 +11,10 @@ use rustc_hir::lang_items::LangItem; use rustc_index::bit_set::GrowableBitSet; use rustc_infer::infer::InferOk; use rustc_infer::infer::LateBoundRegionConversionTime::HigherRankedType; -use rustc_middle::ty::subst::{GenericArg, GenericArgKind, InternalSubsts, Subst, SubstsRef}; -use rustc_middle::ty::{self, GenericParamDefKind, Ty, TyCtxt}; -use rustc_middle::ty::{ToPolyTraitRef, ToPredicate}; +use rustc_middle::ty::{ + self, GenericArg, GenericArgKind, GenericParamDefKind, InternalSubsts, SubstsRef, + ToPolyTraitRef, ToPredicate, Ty, TyCtxt, +}; use rustc_span::def_id::DefId; use crate::traits::project::{normalize_with_depth, normalize_with_depth_to}; @@ -63,15 +64,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ImplSource::UserDefined(self.confirm_impl_candidate(obligation, impl_def_id)) } - AutoImplCandidate(trait_def_id) => { - let data = self.confirm_auto_impl_candidate(obligation, trait_def_id); + AutoImplCandidate => { + let data = self.confirm_auto_impl_candidate(obligation); ImplSource::AutoImpl(data) } - ProjectionCandidate(idx) => { + ProjectionCandidate(idx, constness) => { let obligations = self.confirm_projection_candidate(obligation, idx)?; - // FIXME(jschievink): constness - ImplSource::Param(obligations, ty::BoundConstness::NotConst) + ImplSource::Param(obligations, constness) } ObjectCandidate(idx) => { @@ -100,8 +100,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { PointeeCandidate => ImplSource::Pointee(ImplSourcePointeeData), - TraitAliasCandidate(alias_def_id) => { - let data = self.confirm_trait_alias_candidate(obligation, alias_def_id); + TraitAliasCandidate => { + let data = self.confirm_trait_alias_candidate(obligation); ImplSource::TraitAlias(data) } @@ -279,29 +279,19 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let predicate = obligation.predicate; let type_at = |i| predicate.map_bound(|p| p.trait_ref.substs.type_at(i)); - let bool_at = |i| { - predicate - .skip_binder() - .trait_ref - .substs - .const_at(i) - .try_eval_bool(self.tcx(), obligation.param_env) - .unwrap_or(true) - }; + let const_at = |i| predicate.skip_binder().trait_ref.substs.const_at(i); let src_and_dst = predicate.map_bound(|p| rustc_transmute::Types { - src: p.trait_ref.substs.type_at(1), dst: p.trait_ref.substs.type_at(0), + src: p.trait_ref.substs.type_at(1), }); let scope = type_at(2).skip_binder(); - let assume = rustc_transmute::Assume { - alignment: bool_at(3), - lifetimes: bool_at(4), - validity: bool_at(5), - visibility: bool_at(6), - }; + let Some(assume) = + rustc_transmute::Assume::from_const(self.infcx.tcx, obligation.param_env, const_at(3)) else { + return Err(Unimplemented); + }; let cause = obligation.cause.clone(); @@ -325,13 +315,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { fn confirm_auto_impl_candidate( &mut self, obligation: &TraitObligation<'tcx>, - trait_def_id: DefId, ) -> ImplSourceAutoImplData<PredicateObligation<'tcx>> { - debug!(?obligation, ?trait_def_id, "confirm_auto_impl_candidate"); + debug!(?obligation, "confirm_auto_impl_candidate"); let self_ty = self.infcx.shallow_resolve(obligation.predicate.self_ty()); let types = self.constituent_types_for_ty(self_ty); - self.vtable_auto_impl(obligation, trait_def_id, types) + self.vtable_auto_impl(obligation, obligation.predicate.def_id(), types) } /// See `confirm_auto_impl_candidate`. @@ -629,17 +618,47 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ) .map_bound(|(trait_ref, _)| trait_ref); - let nested = self.confirm_poly_trait_refs(obligation, trait_ref)?; + let mut nested = self.confirm_poly_trait_refs(obligation, trait_ref)?; + + // Confirm the `type Output: Sized;` bound that is present on `FnOnce` + let cause = obligation.derived_cause(BuiltinDerivedObligation); + // The binder on the Fn obligation is "less" important than the one on + // the signature, as evidenced by how we treat it during projection. + // The safe thing to do here is to liberate it, though, which should + // have no worse effect than skipping the binder here. + let liberated_fn_ty = + self.infcx.replace_bound_vars_with_placeholders(obligation.predicate.rebind(self_ty)); + let output_ty = self + .infcx + .replace_bound_vars_with_placeholders(liberated_fn_ty.fn_sig(self.tcx()).output()); + let output_ty = normalize_with_depth_to( + self, + obligation.param_env, + cause.clone(), + obligation.recursion_depth, + output_ty, + &mut nested, + ); + let tr = ty::Binder::dummy(ty::TraitRef::new( + self.tcx().require_lang_item(LangItem::Sized, None), + self.tcx().mk_substs_trait(output_ty, &[]), + )); + nested.push(Obligation::new( + cause, + obligation.param_env, + tr.to_poly_trait_predicate().to_predicate(self.tcx()), + )); + Ok(ImplSourceFnPointerData { fn_ty: self_ty, nested }) } fn confirm_trait_alias_candidate( &mut self, obligation: &TraitObligation<'tcx>, - alias_def_id: DefId, ) -> ImplSourceTraitAliasData<'tcx, PredicateObligation<'tcx>> { - debug!(?obligation, ?alias_def_id, "confirm_trait_alias_candidate"); + debug!(?obligation, "confirm_trait_alias_candidate"); + let alias_def_id = obligation.predicate.def_id(); let predicate = self.infcx().replace_bound_vars_with_placeholders(obligation.predicate); let trait_ref = predicate.trait_ref; let trait_def_id = trait_ref.def_id; @@ -794,7 +813,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let upcast_trait_ref; match (source.kind(), target.kind()) { // TraitA+Kx+'a -> TraitB+Ky+'b (trait upcasting coercion). - (&ty::Dynamic(ref data_a, r_a), &ty::Dynamic(ref data_b, r_b)) => { + (&ty::Dynamic(ref data_a, r_a, repr_a), &ty::Dynamic(ref data_b, r_b, repr_b)) + if repr_a == repr_b => + { // See `assemble_candidates_for_unsizing` for more info. // We already checked the compatibility of auto traits within `assemble_candidates_for_unsizing`. let principal_a = data_a.principal().unwrap(); @@ -820,7 +841,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { .map(ty::Binder::dummy), ); let existential_predicates = tcx.mk_poly_existential_predicates(iter); - let source_trait = tcx.mk_dynamic(existential_predicates, r_b); + let source_trait = tcx.mk_dynamic(existential_predicates, r_b, repr_b); // Require that the traits involved in this upcast are **equal**; // only the **lifetime bound** is changed. @@ -898,7 +919,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let mut nested = vec![]; match (source.kind(), target.kind()) { // Trait+Kx+'a -> Trait+Ky+'b (auto traits and lifetime subtyping). - (&ty::Dynamic(ref data_a, r_a), &ty::Dynamic(ref data_b, r_b)) => { + (&ty::Dynamic(ref data_a, r_a, ty::Dyn), &ty::Dynamic(ref data_b, r_b, ty::Dyn)) => { // See `assemble_candidates_for_unsizing` for more info. // We already checked the compatibility of auto traits within `assemble_candidates_for_unsizing`. let iter = data_a @@ -917,7 +938,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { .map(ty::Binder::dummy), ); let existential_predicates = tcx.mk_poly_existential_predicates(iter); - let source_trait = tcx.mk_dynamic(existential_predicates, r_b); + let source_trait = tcx.mk_dynamic(existential_predicates, r_b, ty::Dyn); // Require that the traits involved in this upcast are **equal**; // only the **lifetime bound** is changed. @@ -944,7 +965,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } // `T` -> `Trait` - (_, &ty::Dynamic(ref data, r)) => { + (_, &ty::Dynamic(ref data, r, ty::Dyn)) => { let mut object_dids = data.auto_traits().chain(data.principal_def_id()); if let Some(did) = object_dids.find(|did| !tcx.is_object_safe(*did)) { return Err(TraitNotObjectSafe(did)); @@ -1047,9 +1068,25 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { return Err(Unimplemented); } - // Extract `TailField<T>` and `TailField<U>` from `Struct<T>` and `Struct<U>`. - let source_tail = tail_field_ty.subst(tcx, substs_a); - let target_tail = tail_field_ty.subst(tcx, substs_b); + // Extract `TailField<T>` and `TailField<U>` from `Struct<T>` and `Struct<U>`, + // normalizing in the process, since `type_of` returns something directly from + // astconv (which means it's un-normalized). + let source_tail = normalize_with_depth_to( + self, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + tail_field_ty.subst(tcx, substs_a), + &mut nested, + ); + let target_tail = normalize_with_depth_to( + self, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + tail_field_ty.subst(tcx, substs_b), + &mut nested, + ); // Check that the source struct with the target's // unsizing parameters is equal to the target. @@ -1188,6 +1225,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::Never | ty::Foreign(_) => {} + // `ManuallyDrop` is trivially drop + ty::Adt(def, _) if Some(def.did()) == tcx.lang_items().manually_drop() => {} + // These types are built-in, so we can fast-track by registering // nested predicates for their constituent type(s) ty::Array(ty, _) | ty::Slice(ty) => { diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index c01ac1979..9ebff4892 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -20,7 +20,7 @@ use super::{ }; use crate::infer::{InferCtxt, InferOk, TypeFreshener}; -use crate::traits::error_reporting::InferCtxtExt; +use crate::traits::error_reporting::TypeErrCtxtExt; use crate::traits::project::ProjectAndUnifyResult; use crate::traits::project::ProjectionCacheKeyExt; use crate::traits::ProjectionCacheKey; @@ -35,9 +35,8 @@ use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::ty::abstract_const::NotConstEvaluatable; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; use rustc_middle::ty::fold::BottomUpFolder; -use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::relate::TypeRelation; -use rustc_middle::ty::subst::{Subst, SubstsRef}; +use rustc_middle::ty::SubstsRef; use rustc_middle::ty::{self, EarlyBinder, PolyProjectionPredicate, ToPolyTraitRef, ToPredicate}; use rustc_middle::ty::{Ty, TyCtxt, TypeFoldable, TypeVisitable}; use rustc_span::symbol::sym; @@ -94,7 +93,7 @@ impl IntercrateAmbiguityCause { } pub struct SelectionContext<'cx, 'tcx> { - infcx: &'cx InferCtxt<'cx, 'tcx>, + infcx: &'cx InferCtxt<'tcx>, /// Freshener used specifically for entries on the obligation /// stack. This ensures that all entries on the stack at one time @@ -215,7 +214,7 @@ enum BuiltinImplConditions<'tcx> { } impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { - pub fn new(infcx: &'cx InferCtxt<'cx, 'tcx>) -> SelectionContext<'cx, 'tcx> { + pub fn new(infcx: &'cx InferCtxt<'tcx>) -> SelectionContext<'cx, 'tcx> { SelectionContext { infcx, freshener: infcx.freshener_keep_static(), @@ -225,28 +224,16 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } - pub fn intercrate(infcx: &'cx InferCtxt<'cx, 'tcx>) -> SelectionContext<'cx, 'tcx> { - SelectionContext { - infcx, - freshener: infcx.freshener_keep_static(), - intercrate: true, - intercrate_ambiguity_causes: None, - query_mode: TraitQueryMode::Standard, - } + pub fn intercrate(infcx: &'cx InferCtxt<'tcx>) -> SelectionContext<'cx, 'tcx> { + SelectionContext { intercrate: true, ..SelectionContext::new(infcx) } } pub fn with_query_mode( - infcx: &'cx InferCtxt<'cx, 'tcx>, + infcx: &'cx InferCtxt<'tcx>, query_mode: TraitQueryMode, ) -> SelectionContext<'cx, 'tcx> { debug!(?query_mode, "with_query_mode"); - SelectionContext { - infcx, - freshener: infcx.freshener_keep_static(), - intercrate: false, - intercrate_ambiguity_causes: None, - query_mode, - } + SelectionContext { query_mode, ..SelectionContext::new(infcx) } } /// Enables tracking of intercrate ambiguity causes. See @@ -266,7 +253,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { self.intercrate_ambiguity_causes.take().unwrap_or_default() } - pub fn infcx(&self) -> &'cx InferCtxt<'cx, 'tcx> { + pub fn infcx(&self) -> &'cx InferCtxt<'tcx> { self.infcx } @@ -295,7 +282,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /// Attempts to satisfy the obligation. If successful, this will affect the surrounding /// type environment by performing unification. - #[instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip(self), ret)] pub fn select( &mut self, obligation: &TraitObligation<'tcx>, @@ -325,10 +312,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { Err(SelectionError::Overflow(OverflowError::Canonical)) } Err(e) => Err(e), - Ok(candidate) => { - debug!(?candidate, "confirmed"); - Ok(Some(candidate)) - } + Ok(candidate) => Ok(Some(candidate)), } } @@ -435,6 +419,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { level = "debug", skip(self, previous_stack), fields(previous_stack = ?previous_stack.head()) + ret, )] fn evaluate_predicate_recursively<'o>( &mut self, @@ -450,7 +435,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { None => self.check_recursion_limit(&obligation, &obligation)?, } - let result = ensure_sufficient_stack(|| { + ensure_sufficient_stack(|| { let bound_predicate = obligation.predicate.kind(); match bound_predicate.skip_binder() { ty::PredicateKind::Trait(t) => { @@ -464,15 +449,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let p = bound_predicate.rebind(p); // Does this code ever run? match self.infcx.subtype_predicate(&obligation.cause, obligation.param_env, p) { - Some(Ok(InferOk { mut obligations, .. })) => { + Ok(Ok(InferOk { mut obligations, .. })) => { self.add_depth(obligations.iter_mut(), obligation.recursion_depth); self.evaluate_predicates_recursively( previous_stack, obligations.into_iter(), ) } - Some(Err(_)) => Ok(EvaluatedToErr), - None => Ok(EvaluatedToAmbig), + Ok(Err(_)) => Ok(EvaluatedToErr), + Err(..) => Ok(EvaluatedToAmbig), } } @@ -480,15 +465,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let p = bound_predicate.rebind(p); // Does this code ever run? match self.infcx.coerce_predicate(&obligation.cause, obligation.param_env, p) { - Some(Ok(InferOk { mut obligations, .. })) => { + Ok(Ok(InferOk { mut obligations, .. })) => { self.add_depth(obligations.iter_mut(), obligation.recursion_depth); self.evaluate_predicates_recursively( previous_stack, obligations.into_iter(), ) } - Some(Err(_)) => Ok(EvaluatedToErr), - None => Ok(EvaluatedToAmbig), + Ok(Err(_)) => Ok(EvaluatedToErr), + Err(..) => Ok(EvaluatedToAmbig), } } @@ -691,23 +676,21 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } ty::PredicateKind::ConstEquate(c1, c2) => { + assert!( + self.tcx().features().generic_const_exprs, + "`ConstEquate` without a feature gate: {c1:?} {c2:?}", + ); debug!(?c1, ?c2, "evaluate_predicate_recursively: equating consts"); - if self.tcx().features().generic_const_exprs { - // FIXME: we probably should only try to unify abstract constants - // if the constants depend on generic parameters. - // - // Let's just see where this breaks :shrug: - if let (ty::ConstKind::Unevaluated(a), ty::ConstKind::Unevaluated(b)) = - (c1.kind(), c2.kind()) - { - if self.infcx.try_unify_abstract_consts( - a.shrink(), - b.shrink(), - obligation.param_env, - ) { - return Ok(EvaluatedToOk); - } + // FIXME: we probably should only try to unify abstract constants + // if the constants depend on generic parameters. + // + // Let's just see where this breaks :shrug: + if let (ty::ConstKind::Unevaluated(a), ty::ConstKind::Unevaluated(b)) = + (c1.kind(), c2.kind()) + { + if self.infcx.try_unify_abstract_consts(a, b, obligation.param_env) { + return Ok(EvaluatedToOk); } } @@ -747,7 +730,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ) } (Err(ErrorHandled::TooGeneric), _) | (_, Err(ErrorHandled::TooGeneric)) => { - if c1.has_infer_types_or_consts() || c2.has_infer_types_or_consts() { + if c1.has_non_region_infer() || c2.has_non_region_infer() { Ok(EvaluatedToAmbig) } else { // Two different constants using generic parameters ~> error. @@ -760,14 +743,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { bug!("TypeWellFormedFromEnv is only used for chalk") } } - }); - - debug!("finished: {:?} from {:?}", result, obligation); - - result + }) } - #[instrument(skip(self, previous_stack), level = "debug")] + #[instrument(skip(self, previous_stack), level = "debug", ret)] fn evaluate_trait_predicate_recursively<'o>( &mut self, previous_stack: TraitObligationStackList<'o, 'tcx>, @@ -798,12 +777,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // If a trait predicate is in the (local or global) evaluation cache, // then we know it holds without cycles. if let Some(result) = self.check_evaluation_cache(param_env, fresh_trait_pred) { - debug!(?result, "CACHE HIT"); + debug!("CACHE HIT"); return Ok(result); } if let Some(result) = stack.cache().get_provisional(fresh_trait_pred) { - debug!(?result, "PROVISIONAL CACHE HIT"); + debug!("PROVISIONAL CACHE HIT"); stack.update_reached_depth(result.reached_depth); return Ok(result.result); } @@ -826,11 +805,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let reached_depth = stack.reached_depth.get(); if reached_depth >= stack.depth { - debug!(?result, "CACHE MISS"); + debug!("CACHE MISS"); self.insert_evaluation_cache(param_env, fresh_trait_pred, dep_node, result); stack.cache().on_completion(stack.dfn); } else { - debug!(?result, "PROVISIONAL"); + debug!("PROVISIONAL"); debug!( "caching provisionally because {:?} \ is a cycle participant (at depth {}, reached depth {})", @@ -849,7 +828,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /// must be met of course). One obvious case this comes up is /// marker traits like `Send`. Think of a linked list: /// - /// struct List<T> { data: T, next: Option<Box<List<T>>> } + /// struct List<T> { data: T, next: Option<Box<List<T>>> } /// /// `Box<List<T>>` will be `Send` if `T` is `Send` and /// `Option<Box<List<T>>>` is `Send`, and in turn @@ -936,38 +915,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let unbound_input_types = stack.fresh_trait_pred.skip_binder().trait_ref.substs.types().any(|ty| ty.is_fresh()); - if stack.obligation.polarity() != ty::ImplPolarity::Negative { - // This check was an imperfect workaround for a bug in the old - // intercrate mode; it should be removed when that goes away. - if unbound_input_types && self.intercrate { - debug!("evaluate_stack --> unbound argument, intercrate --> ambiguous",); - // Heuristics: show the diagnostics when there are no candidates in crate. - if self.intercrate_ambiguity_causes.is_some() { - debug!("evaluate_stack: intercrate_ambiguity_causes is some"); - if let Ok(candidate_set) = self.assemble_candidates(stack) { - if !candidate_set.ambiguous && candidate_set.vec.is_empty() { - let trait_ref = stack.obligation.predicate.skip_binder().trait_ref; - let self_ty = trait_ref.self_ty(); - let cause = with_no_trimmed_paths!({ - IntercrateAmbiguityCause::DownstreamCrate { - trait_desc: trait_ref.print_only_trait_path().to_string(), - self_desc: if self_ty.has_concrete_skeleton() { - Some(self_ty.to_string()) - } else { - None - }, - } - }); - - debug!(?cause, "evaluate_stack: pushing cause"); - self.intercrate_ambiguity_causes.as_mut().unwrap().insert(cause); - } - } - } - return Ok(EvaluatedToAmbig); - } - } - if unbound_input_types && stack.iter().skip(1).any(|prev| { stack.obligation.param_env == prev.obligation.param_env @@ -1023,7 +970,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { #[instrument( level = "debug", skip(self, stack), - fields(depth = stack.obligation.recursion_depth) + fields(depth = stack.obligation.recursion_depth), + ret )] fn evaluate_candidate<'o>( &mut self, @@ -1056,7 +1004,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { result = result.max(EvaluatedToOkModuloRegions); } - debug!(?result); Ok(result) } @@ -1150,7 +1097,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ErrorGuaranteed::unchecked_claim_error_was_emitted(), )); } - self.infcx.report_overflow_error(error_obligation, true); + self.infcx.err_ctxt().report_overflow_error(error_obligation, true); } TraitQueryMode::Canonical => { return Err(OverflowError::Canonical); @@ -1202,8 +1149,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ImplCandidate(def_id) if tcx.constness(def_id) == hir::Constness::Const => {} // const param ParamCandidate(trait_pred) if trait_pred.is_const_if_const() => {} + // const projection + ProjectionCandidate(_, ty::BoundConstness::ConstIfConst) => {} // auto trait impl - AutoImplCandidate(..) => {} + AutoImplCandidate => {} // generator, this will raise error in other places // or ignore error with const_async_blocks feature GeneratorCandidate => {} @@ -1265,11 +1214,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { Ok(Some(candidate)) } - fn is_knowable<'o>(&mut self, stack: &TraitObligationStack<'o, 'tcx>) -> Option<Conflict> { + fn is_knowable<'o>(&mut self, stack: &TraitObligationStack<'o, 'tcx>) -> Result<(), Conflict> { debug!("is_knowable(intercrate={:?})", self.intercrate); if !self.intercrate || stack.obligation.polarity() == ty::ImplPolarity::Negative { - return None; + return Ok(()); } let obligation = &stack.obligation; @@ -1405,11 +1354,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /// a projection, look at the bounds of `T::Bar`, see if we can find a /// `Baz` bound. We return indexes into the list returned by /// `tcx.item_bounds` for any applicable bounds. - #[instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip(self), ret)] fn match_projection_obligation_against_definition_bounds( &mut self, obligation: &TraitObligation<'tcx>, - ) -> smallvec::SmallVec<[usize; 2]> { + ) -> smallvec::SmallVec<[(usize, ty::BoundConstness); 2]> { let poly_trait_predicate = self.infcx().resolve_vars_if_possible(obligation.predicate); let placeholder_trait_predicate = self.infcx().replace_bound_vars_with_placeholders(poly_trait_predicate); @@ -1435,7 +1384,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // unnecessary ambiguity. let mut distinct_normalized_bounds = FxHashSet::default(); - let matching_bounds = bounds + bounds .iter() .enumerate() .filter_map(|(idx, bound)| { @@ -1457,15 +1406,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { _ => false, } }) { - return Some(idx); + return Some((idx, pred.constness)); } } None }) - .collect(); - - debug!(?matching_bounds); - matching_bounds + .collect() } /// Equates the trait in `obligation` with trait bound. If the two traits @@ -1576,7 +1522,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { if !generics.params.is_empty() && obligation.predicate.substs[generics.parent_count..] .iter() - .any(|&p| p.has_infer_types_or_consts() && self.infcx.shallow_resolve(p) != p) + .any(|&p| p.has_non_region_infer() && self.infcx.shallow_resolve(p) != p) { ProjectionMatchesProjection::Ambiguous } else { @@ -1618,12 +1564,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { }; // (*) Prefer `BuiltinCandidate { has_nested: false }`, `PointeeCandidate`, - // `DiscriminantKindCandidate`, and `ConstDestructCandidate` to anything else. + // `DiscriminantKindCandidate`, `ConstDestructCandidate` + // to anything else. // // This is a fix for #53123 and prevents winnowing from accidentally extending the // lifetime of a variable. match (&other.candidate, &victim.candidate) { - (_, AutoImplCandidate(..)) | (AutoImplCandidate(..), _) => { + (_, AutoImplCandidate) | (AutoImplCandidate, _) => { bug!( "default implementations shouldn't be recorded \ when there are other valid candidates" @@ -1691,11 +1638,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | BuiltinUnsizeCandidate | TraitUpcastingUnsizeCandidate(_) | BuiltinCandidate { .. } - | TraitAliasCandidate(..) + | TraitAliasCandidate | ObjectCandidate(_) - | ProjectionCandidate(_), + | ProjectionCandidate(..), ) => !is_global(cand), - (ObjectCandidate(_) | ProjectionCandidate(_), ParamCandidate(ref cand)) => { + (ObjectCandidate(_) | ProjectionCandidate(..), ParamCandidate(ref cand)) => { // Prefer these to a global where-clause bound // (see issue #50825). is_global(cand) @@ -1709,7 +1656,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | BuiltinUnsizeCandidate | TraitUpcastingUnsizeCandidate(_) | BuiltinCandidate { has_nested: true } - | TraitAliasCandidate(..), + | TraitAliasCandidate, ParamCandidate(ref cand), ) => { // Prefer these to a global where-clause bound @@ -1717,20 +1664,20 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { is_global(cand) && other.evaluation.must_apply_modulo_regions() } - (ProjectionCandidate(i), ProjectionCandidate(j)) + (ProjectionCandidate(i, _), ProjectionCandidate(j, _)) | (ObjectCandidate(i), ObjectCandidate(j)) => { // Arbitrarily pick the lower numbered candidate for backwards // compatibility reasons. Don't let this affect inference. i < j && !needs_infer } - (ObjectCandidate(_), ProjectionCandidate(_)) - | (ProjectionCandidate(_), ObjectCandidate(_)) => { + (ObjectCandidate(_), ProjectionCandidate(..)) + | (ProjectionCandidate(..), ObjectCandidate(_)) => { bug!("Have both object and projection candidate") } // Arbitrarily give projection and object candidates priority. ( - ObjectCandidate(_) | ProjectionCandidate(_), + ObjectCandidate(_) | ProjectionCandidate(..), ImplCandidate(..) | ClosureCandidate | GeneratorCandidate @@ -1739,7 +1686,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | BuiltinUnsizeCandidate | TraitUpcastingUnsizeCandidate(_) | BuiltinCandidate { .. } - | TraitAliasCandidate(..), + | TraitAliasCandidate, ) => true, ( @@ -1751,18 +1698,18 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | BuiltinUnsizeCandidate | TraitUpcastingUnsizeCandidate(_) | BuiltinCandidate { .. } - | TraitAliasCandidate(..), - ObjectCandidate(_) | ProjectionCandidate(_), + | TraitAliasCandidate, + ObjectCandidate(_) | ProjectionCandidate(..), ) => false, (&ImplCandidate(other_def), &ImplCandidate(victim_def)) => { // See if we can toss out `victim` based on specialization. - // This requires us to know *for sure* that the `other` impl applies - // i.e., `EvaluatedToOk`. + // While this requires us to know *for sure* that the `other` impl applies + // we still use modulo regions here. // - // FIXME(@lcnr): Using `modulo_regions` here seems kind of scary - // to me but is required for `std` to compile, so I didn't change it - // for now. + // This is fine as specialization currently assumes that specializing + // impls have to be always applicable, meaning that the only allowed + // region constraints may be constraints also present on the default impl. let tcx = self.tcx(); if other.evaluation.must_apply_modulo_regions() { if tcx.specializes((other_def, victim_def)) { @@ -1832,7 +1779,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | BuiltinUnsizeCandidate | TraitUpcastingUnsizeCandidate(_) | BuiltinCandidate { has_nested: true } - | TraitAliasCandidate(..), + | TraitAliasCandidate, ImplCandidate(_) | ClosureCandidate | GeneratorCandidate @@ -1841,7 +1788,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | BuiltinUnsizeCandidate | TraitUpcastingUnsizeCandidate(_) | BuiltinCandidate { has_nested: true } - | TraitAliasCandidate(..), + | TraitAliasCandidate, ) => false, } } @@ -1871,6 +1818,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::Array(..) | ty::Closure(..) | ty::Never + | ty::Dynamic(_, _, ty::DynStar) | ty::Error(_) => { // safe for everything Where(ty::Binder::dummy(Vec::new())) @@ -1937,8 +1885,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ty::Dynamic(..) | ty::Str | ty::Slice(..) - | ty::Generator(..) - | ty::GeneratorWitness(..) + | ty::Generator(_, _, hir::Movability::Static) | ty::Foreign(..) | ty::Ref(_, _, hir::Mutability::Mut) => None, @@ -1947,6 +1894,43 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { Where(obligation.predicate.rebind(tys.iter().collect())) } + ty::Generator(_, substs, hir::Movability::Movable) => { + if self.tcx().features().generator_clone { + let resolved_upvars = + self.infcx.shallow_resolve(substs.as_generator().tupled_upvars_ty()); + let resolved_witness = + self.infcx.shallow_resolve(substs.as_generator().witness()); + if resolved_upvars.is_ty_var() || resolved_witness.is_ty_var() { + // Not yet resolved. + Ambiguous + } else { + let all = substs + .as_generator() + .upvar_tys() + .chain(iter::once(substs.as_generator().witness())) + .collect::<Vec<_>>(); + Where(obligation.predicate.rebind(all)) + } + } else { + None + } + } + + ty::GeneratorWitness(binder) => { + let witness_tys = binder.skip_binder(); + for witness_ty in witness_tys.iter() { + let resolved = self.infcx.shallow_resolve(witness_ty); + if resolved.is_ty_var() { + return Ambiguous; + } + } + // (*) binder moved here + let all_vars = self.tcx().mk_bound_variable_kinds( + obligation.predicate.bound_vars().iter().chain(binder.bound_vars().iter()), + ); + Where(ty::Binder::bind_with_vars(witness_tys.to_vec(), all_vars)) + } + ty::Closure(_, substs) => { // (*) binder moved here let ty = self.infcx.shallow_resolve(substs.as_closure().tupled_upvars_ty()); @@ -2153,7 +2137,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } - #[tracing::instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip(self), ret)] fn match_impl( &mut self, impl_def_id: DefId, @@ -2194,17 +2178,16 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { .at(&cause, obligation.param_env) .define_opaque_types(false) .eq(placeholder_obligation_trait_ref, impl_trait_ref) - .map_err(|e| debug!("match_impl: failed eq_trait_refs due to `{}`", e))?; + .map_err(|e| debug!("match_impl: failed eq_trait_refs due to `{e}`"))?; nested_obligations.extend(obligations); if !self.intercrate && self.tcx().impl_polarity(impl_def_id) == ty::ImplPolarity::Reservation { - debug!("match_impl: reservation impls only apply in intercrate mode"); + debug!("reservation impls only apply in intercrate mode"); return Err(()); } - debug!(?impl_substs, ?nested_obligations, "match_impl: success"); Ok(Normalized { value: impl_substs, obligations: nested_obligations }) } @@ -2335,7 +2318,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /// impl or trait. The obligations are substituted and fully /// normalized. This is used when confirming an impl or default /// impl. - #[tracing::instrument(level = "debug", skip(self, cause, param_env))] + #[instrument(level = "debug", skip(self, cause, param_env))] fn impl_or_trait_obligations( &mut self, cause: &ObligationCause<'tcx>, diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs index 6223c5ea3..c89165858 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs @@ -12,22 +12,21 @@ pub mod specialization_graph; use specialization_graph::GraphExt; +use crate::errors::NegativePositiveConflict; use crate::infer::{InferCtxt, InferOk, TyCtxtInferExt}; use crate::traits::select::IntercrateAmbiguityCause; -use crate::traits::{ - self, coherence, FutureCompatOverlapErrorKind, ObligationCause, TraitEngine, TraitEngineExt, -}; +use crate::traits::{self, coherence, FutureCompatOverlapErrorKind, ObligationCause}; use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; -use rustc_errors::{struct_span_err, EmissionGuarantee, LintDiagnosticBuilder}; +use rustc_errors::{struct_span_err, DiagnosticBuilder, EmissionGuarantee}; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_middle::ty::subst::{InternalSubsts, Subst, SubstsRef}; use rustc_middle::ty::{self, ImplSubject, TyCtxt}; +use rustc_middle::ty::{InternalSubsts, SubstsRef}; use rustc_session::lint::builtin::COHERENCE_LEAK_CHECK; use rustc_session::lint::builtin::ORDER_DEPENDENT_TRAIT_OBJECTS; use rustc_span::{Span, DUMMY_SP}; +use super::util; use super::SelectionContext; -use super::{util, FulfillmentContext}; /// Information pertinent to an overlapping impl error. #[derive(Debug)] @@ -74,8 +73,8 @@ pub struct OverlapError { /// through associated type projection. We deal with such cases by using /// *fulfillment* to relate the two impls, requiring that all projections are /// resolved. -pub fn translate_substs<'a, 'tcx>( - infcx: &InferCtxt<'a, 'tcx>, +pub fn translate_substs<'tcx>( + infcx: &InferCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, source_impl: DefId, source_substs: SubstsRef<'tcx>, @@ -150,14 +149,9 @@ pub(super) fn specializes(tcx: TyCtxt<'_>, (impl1_def_id, impl2_def_id): (DefId, let impl1_trait_ref = tcx.impl_trait_ref(impl1_def_id).unwrap(); // Create an infcx, taking the predicates of impl1 as assumptions: - tcx.infer_ctxt().enter(|infcx| { - let impl1_trait_ref = match traits::fully_normalize( - &infcx, - FulfillmentContext::new(), - ObligationCause::dummy(), - penv, - impl1_trait_ref, - ) { + let infcx = tcx.infer_ctxt().build(); + let impl1_trait_ref = + match traits::fully_normalize(&infcx, ObligationCause::dummy(), penv, impl1_trait_ref) { Ok(impl1_trait_ref) => impl1_trait_ref, Err(_errors) => { tcx.sess.delay_span_bug( @@ -168,9 +162,8 @@ pub(super) fn specializes(tcx: TyCtxt<'_>, (impl1_def_id, impl2_def_id): (DefId, } }; - // Attempt to prove that impl2 applies, given all of the above. - fulfill_implication(&infcx, penv, impl1_trait_ref, impl2_def_id).is_ok() - }) + // Attempt to prove that impl2 applies, given all of the above. + fulfill_implication(&infcx, penv, impl1_trait_ref, impl2_def_id).is_ok() } /// Attempt to fulfill all obligations of `target_impl` after unification with @@ -178,8 +171,8 @@ pub(super) fn specializes(tcx: TyCtxt<'_>, (impl1_def_id, impl2_def_id): (DefId, /// generics of `target_impl`, including both those needed to unify with /// `source_trait_ref` and those whose identity is determined via a where /// clause in the impl. -fn fulfill_implication<'a, 'tcx>( - infcx: &InferCtxt<'a, 'tcx>, +fn fulfill_implication<'tcx>( + infcx: &InferCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, source_trait_ref: ty::TraitRef<'tcx>, target_impl: DefId, @@ -211,11 +204,8 @@ fn fulfill_implication<'a, 'tcx>( // (which are packed up in penv) infcx.save_and_restore_in_snapshot_flag(|infcx| { - let mut fulfill_cx = <dyn TraitEngine<'tcx>>::new(infcx.tcx); - for oblig in obligations.chain(more_obligations) { - fulfill_cx.register_predicate_obligation(&infcx, oblig); - } - match fulfill_cx.select_all_or_error(infcx).as_slice() { + let errors = traits::fully_solve_obligations(&infcx, obligations.chain(more_obligations)); + match &errors[..] { [] => { debug!( "fulfill_implication: an impl for {:?} specializes {:?}", @@ -333,35 +323,13 @@ fn report_negative_positive_conflict( positive_impl_def_id: DefId, sg: &mut specialization_graph::Graph, ) { - let impl_span = tcx.def_span(local_impl_def_id); - - let mut err = struct_span_err!( - tcx.sess, - impl_span, - E0751, - "found both positive and negative implementation of trait `{}`{}:", - overlap.trait_desc, - overlap.self_desc.clone().map_or_else(String::new, |ty| format!(" for type `{}`", ty)) - ); - - match tcx.span_of_impl(negative_impl_def_id) { - Ok(span) => { - err.span_label(span, "negative implementation here"); - } - Err(cname) => { - err.note(&format!("negative implementation in crate `{}`", cname)); - } - } - - match tcx.span_of_impl(positive_impl_def_id) { - Ok(span) => { - err.span_label(span, "positive implementation here"); - } - Err(cname) => { - err.note(&format!("positive implementation in crate `{}`", cname)); - } - } - + let mut err = tcx.sess.create_err(NegativePositiveConflict { + impl_span: tcx.def_span(local_impl_def_id), + trait_desc: &overlap.trait_desc, + self_desc: &overlap.self_desc, + negative_impl_span: tcx.span_of_impl(negative_impl_def_id), + positive_impl_span: tcx.span_of_impl(positive_impl_def_id), + }); sg.has_errored = Some(err.emit()); } @@ -377,26 +345,12 @@ fn report_conflicting_impls( // Work to be done after we've built the DiagnosticBuilder. We have to define it // now because the struct_lint methods don't return back the DiagnosticBuilder // that's passed in. - fn decorate<G: EmissionGuarantee>( + fn decorate<'a, 'b, G: EmissionGuarantee>( tcx: TyCtxt<'_>, overlap: OverlapError, - used_to_be_allowed: Option<FutureCompatOverlapErrorKind>, impl_span: Span, - err: LintDiagnosticBuilder<'_, G>, - ) -> G { - let msg = format!( - "conflicting implementations of trait `{}`{}{}", - overlap.trait_desc, - overlap - .self_desc - .clone() - .map_or_else(String::new, |ty| { format!(" for type `{}`", ty) }), - match used_to_be_allowed { - Some(FutureCompatOverlapErrorKind::Issue33140) => ": (E0119)", - _ => "", - } - ); - let mut err = err.build(&msg); + err: &'b mut DiagnosticBuilder<'a, G>, + ) -> &'b mut DiagnosticBuilder<'a, G> { match tcx.span_of_impl(overlap.with_impl) { Ok(span) => { err.span_label(span, "first implementation here"); @@ -411,7 +365,9 @@ fn report_conflicting_impls( } Err(cname) => { let msg = match to_pretty_impl_header(tcx, overlap.with_impl) { - Some(s) => format!("conflicting implementation in crate `{}`:\n- {}", cname, s), + Some(s) => { + format!("conflicting implementation in crate `{}`:\n- {}", cname, s) + } None => format!("conflicting implementation in crate `{}`", cname), }; err.note(&msg); @@ -419,28 +375,33 @@ fn report_conflicting_impls( } for cause in &overlap.intercrate_ambiguity_causes { - cause.add_intercrate_ambiguity_hint(&mut err); + cause.add_intercrate_ambiguity_hint(err); } if overlap.involves_placeholder { - coherence::add_placeholder_note(&mut err); + coherence::add_placeholder_note(err); } - err.emit() + err } + let msg = format!( + "conflicting implementations of trait `{}`{}{}", + overlap.trait_desc, + overlap.self_desc.as_deref().map_or_else(String::new, |ty| format!(" for type `{ty}`")), + match used_to_be_allowed { + Some(FutureCompatOverlapErrorKind::Issue33140) => ": (E0119)", + _ => "", + } + ); + match used_to_be_allowed { None => { let reported = if overlap.with_impl.is_local() || tcx.orphan_check_impl(impl_def_id).is_ok() { - let err = struct_span_err!(tcx.sess, impl_span, E0119, ""); - Some(decorate( - tcx, - overlap, - used_to_be_allowed, - impl_span, - LintDiagnosticBuilder::new(err), - )) + let mut err = struct_span_err!(tcx.sess, impl_span, E0119, "{msg}",); + decorate(tcx, overlap, impl_span, &mut err); + Some(err.emit()) } else { Some(tcx.sess.delay_span_bug(impl_span, "impl should have failed the orphan check")) }; @@ -455,9 +416,8 @@ fn report_conflicting_impls( lint, tcx.hir().local_def_id_to_hir_id(impl_def_id), impl_span, - |ldb| { - decorate(tcx, overlap, used_to_be_allowed, impl_span, ldb); - }, + msg, + |err| decorate(tcx, overlap, impl_span, err), ); } }; diff --git a/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs b/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs index fcb73b43f..63f89a33e 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs @@ -137,9 +137,8 @@ impl ChildrenExt<'_> for Children { impl_def_id, traits::SkipLeakCheck::default(), overlap_mode, - |_| true, - || false, - ); + ) + .is_some(); let error = create_overlap_error(overlap); @@ -162,34 +161,29 @@ impl ChildrenExt<'_> for Children { impl_def_id, traits::SkipLeakCheck::Yes, overlap_mode, - |overlap| { - if let Some(overlap_kind) = - tcx.impls_are_allowed_to_overlap(impl_def_id, possible_sibling) - { - match overlap_kind { - ty::ImplOverlapKind::Permitted { marker: _ } => {} - ty::ImplOverlapKind::Issue33140 => { - *last_lint_mut = Some(FutureCompatOverlapError { - error: create_overlap_error(overlap), - kind: FutureCompatOverlapErrorKind::Issue33140, - }); - } + ) + .map_or(Ok((false, false)), |overlap| { + if let Some(overlap_kind) = + tcx.impls_are_allowed_to_overlap(impl_def_id, possible_sibling) + { + match overlap_kind { + ty::ImplOverlapKind::Permitted { marker: _ } => {} + ty::ImplOverlapKind::Issue33140 => { + *last_lint_mut = Some(FutureCompatOverlapError { + error: create_overlap_error(overlap), + kind: FutureCompatOverlapErrorKind::Issue33140, + }); } - - return Ok((false, false)); } - let le = tcx.specializes((impl_def_id, possible_sibling)); - let ge = tcx.specializes((possible_sibling, impl_def_id)); + return Ok((false, false)); + } - if le == ge { - report_overlap_error(overlap, last_lint_mut) - } else { - Ok((le, ge)) - } - }, - || Ok((false, false)), - )?; + let le = tcx.specializes((impl_def_id, possible_sibling)); + let ge = tcx.specializes((possible_sibling, impl_def_id)); + + if le == ge { report_overlap_error(overlap, last_lint_mut) } else { Ok((le, ge)) } + })?; if le && !ge { debug!( diff --git a/compiler/rustc_trait_selection/src/traits/structural_match.rs b/compiler/rustc_trait_selection/src/traits/structural_match.rs index 5829a0f92..932dbbb81 100644 --- a/compiler/rustc_trait_selection/src/traits/structural_match.rs +++ b/compiler/rustc_trait_selection/src/traits/structural_match.rs @@ -68,7 +68,7 @@ pub fn search_for_adt_const_param_violation<'tcx>( /// Note that this does *not* recursively check if the substructure of `adt_ty` /// implements the traits. fn type_marked_structural<'tcx>( - infcx: &InferCtxt<'_, 'tcx>, + infcx: &InferCtxt<'tcx>, adt_ty: Ty<'tcx>, cause: ObligationCause<'tcx>, ) -> bool { @@ -265,9 +265,8 @@ impl<'tcx> TypeVisitor<'tcx> for Search<'tcx> { pub fn provide(providers: &mut Providers) { providers.has_structural_eq_impls = |tcx, ty| { - tcx.infer_ctxt().enter(|infcx| { - let cause = ObligationCause::dummy(); - type_marked_structural(&infcx, ty, cause) - }) + let infcx = tcx.infer_ctxt().build(); + let cause = ObligationCause::dummy(); + type_marked_structural(&infcx, ty, cause) }; } diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs index d25006016..ed47d2f83 100644 --- a/compiler/rustc_trait_selection/src/traits/util.rs +++ b/compiler/rustc_trait_selection/src/traits/util.rs @@ -5,14 +5,12 @@ use smallvec::SmallVec; use rustc_data_structures::fx::FxHashSet; use rustc_hir::def_id::DefId; -use rustc_middle::ty::subst::{GenericArg, Subst, SubstsRef}; use rustc_middle::ty::{self, ImplSubject, ToPredicate, Ty, TyCtxt, TypeVisitable}; +use rustc_middle::ty::{GenericArg, SubstsRef}; use super::{Normalized, Obligation, ObligationCause, PredicateObligation, SelectionContext}; pub use rustc_infer::traits::{self, util::*}; -use std::iter; - /////////////////////////////////////////////////////////////////////////// // `TraitAliasExpander` iterator /////////////////////////////////////////////////////////////////////////// @@ -210,7 +208,7 @@ pub fn impl_subject_and_oblig<'a, 'tcx>( let Normalized { value: predicates, obligations: normalization_obligations2 } = super::normalize(selcx, param_env, ObligationCause::dummy(), predicates); let impl_obligations = - predicates_for_generics(ObligationCause::dummy(), 0, param_env, predicates); + super::predicates_for_generics(|_, _| ObligationCause::dummy(), param_env, predicates); let impl_obligations = impl_obligations .chain(normalization_obligations1.into_iter()) @@ -219,27 +217,6 @@ pub fn impl_subject_and_oblig<'a, 'tcx>( (subject, impl_obligations) } -pub fn predicates_for_generics<'tcx>( - cause: ObligationCause<'tcx>, - recursion_depth: usize, - param_env: ty::ParamEnv<'tcx>, - generic_bounds: ty::InstantiatedPredicates<'tcx>, -) -> impl Iterator<Item = PredicateObligation<'tcx>> { - debug!("predicates_for_generics(generic_bounds={:?})", generic_bounds); - - iter::zip(generic_bounds.predicates, generic_bounds.spans).map(move |(predicate, span)| { - let cause = match *cause.code() { - traits::ItemObligation(def_id) if !span.is_dummy() => traits::ObligationCause::new( - cause.span, - cause.body_id, - traits::BindingObligation(def_id, span), - ), - _ => cause.clone(), - }; - Obligation { cause, recursion_depth, param_env, predicate } - }) -} - pub fn predicate_for_trait_ref<'tcx>( tcx: TyCtxt<'tcx>, cause: ObligationCause<'tcx>, @@ -291,10 +268,7 @@ pub fn count_own_vtable_entries<'tcx>( tcx: TyCtxt<'tcx>, trait_ref: ty::PolyTraitRef<'tcx>, ) -> usize { - let existential_trait_ref = - trait_ref.map_bound(|trait_ref| ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref)); - let existential_trait_ref = tcx.erase_regions(existential_trait_ref); - tcx.own_existential_vtable_entries(existential_trait_ref).len() + tcx.own_existential_vtable_entries(trait_ref.def_id()).len() } /// Given an upcast trait object described by `object`, returns the @@ -305,15 +279,10 @@ pub fn get_vtable_index_of_object_method<'tcx, N>( object: &super::ImplSourceObjectData<'tcx, N>, method_def_id: DefId, ) -> Option<usize> { - let existential_trait_ref = object - .upcast_trait_ref - .map_bound(|trait_ref| ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref)); - let existential_trait_ref = tcx.erase_regions(existential_trait_ref); - // Count number of methods preceding the one we are selecting and // add them to the total offset. if let Some(index) = tcx - .own_existential_vtable_entries(existential_trait_ref) + .own_existential_vtable_entries(object.upcast_trait_ref.def_id()) .iter() .copied() .position(|def_id| def_id == method_def_id) diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 414857f0a..fc0a9f690 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -14,8 +14,8 @@ use std::iter; /// inference variable, returns `None`, because we are not able to /// make any progress at all. This is to prevent "livelock" where we /// say "$0 is WF if $0 is WF". -pub fn obligations<'a, 'tcx>( - infcx: &InferCtxt<'a, 'tcx>, +pub fn obligations<'tcx>( + infcx: &InferCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, body_id: hir::HirId, recursion_depth: usize, @@ -31,9 +31,9 @@ pub fn obligations<'a, 'tcx>( if resolved_ty == ty { // No progress, bail out to prevent "livelock". return None; + } else { + resolved_ty } - - resolved_ty } _ => ty, } @@ -41,16 +41,14 @@ pub fn obligations<'a, 'tcx>( } GenericArgKind::Const(ct) => { match ct.kind() { - ty::ConstKind::Infer(infer) => { - let resolved = infcx.shallow_resolve(infer); - if resolved == infer { + ty::ConstKind::Infer(_) => { + let resolved = infcx.shallow_resolve(ct); + if resolved == ct { // No progress. return None; + } else { + resolved } - - infcx - .tcx - .mk_const(ty::ConstS { kind: ty::ConstKind::Infer(resolved), ty: ct.ty() }) } _ => ct, } @@ -81,8 +79,8 @@ pub fn obligations<'a, 'tcx>( /// well-formed. For example, if there is a trait `Set` defined like /// `trait Set<K:Eq>`, then the trait reference `Foo: Set<Bar>` is WF /// if `Bar: Eq`. -pub fn trait_obligations<'a, 'tcx>( - infcx: &InferCtxt<'a, 'tcx>, +pub fn trait_obligations<'tcx>( + infcx: &InferCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, body_id: hir::HirId, trait_pred: &ty::TraitPredicate<'tcx>, @@ -103,8 +101,9 @@ pub fn trait_obligations<'a, 'tcx>( wf.normalize(infcx) } -pub fn predicate_obligations<'a, 'tcx>( - infcx: &InferCtxt<'a, 'tcx>, +#[instrument(skip(infcx), ret)] +pub fn predicate_obligations<'tcx>( + infcx: &InferCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, body_id: hir::HirId, predicate: ty::Predicate<'tcx>, @@ -131,9 +130,9 @@ pub fn predicate_obligations<'a, 'tcx>( } ty::PredicateKind::Projection(t) => { wf.compute_projection(t.projection_ty); - wf.compute(match t.term { - ty::Term::Ty(ty) => ty.into(), - ty::Term::Const(c) => c.into(), + wf.compute(match t.term.unpack() { + ty::TermKind::Ty(ty) => ty.into(), + ty::TermKind::Const(c) => c.into(), }) } ty::PredicateKind::WellFormed(arg) => { @@ -149,13 +148,8 @@ pub fn predicate_obligations<'a, 'tcx>( wf.compute(a.into()); wf.compute(b.into()); } - ty::PredicateKind::ConstEvaluatable(uv) => { - let obligations = wf.nominal_obligations(uv.def.did, uv.substs); - wf.out.extend(obligations); - - for arg in uv.substs.iter() { - wf.compute(arg); - } + ty::PredicateKind::ConstEvaluatable(ct) => { + wf.compute(ct.into()); } ty::PredicateKind::ConstEquate(c1, c2) => { wf.compute(c1.into()); @@ -220,12 +214,14 @@ fn extend_cause_with_original_assoc_item_obligation<'tcx>( trait_ref, item, cause, pred ); let (items, impl_def_id) = match item { - Some(hir::Item { kind: hir::ItemKind::Impl(impl_), def_id, .. }) => (impl_.items, *def_id), + Some(hir::Item { kind: hir::ItemKind::Impl(impl_), owner_id, .. }) => { + (impl_.items, *owner_id) + } _ => return, }; let fix_span = |impl_item_ref: &hir::ImplItemRef| match tcx.hir().impl_item(impl_item_ref.id).kind { - hir::ImplItemKind::Const(ty, _) | hir::ImplItemKind::TyAlias(ty) => ty.span, + hir::ImplItemKind::Const(ty, _) | hir::ImplItemKind::Type(ty) => ty.span, _ => impl_item_ref.span, }; @@ -242,7 +238,7 @@ fn extend_cause_with_original_assoc_item_obligation<'tcx>( tcx.impl_item_implementor_ids(impl_def_id).get(&projection_ty.item_def_id) && let Some(impl_item_span) = items .iter() - .find(|item| item.id.def_id.to_def_id() == impl_item_id) + .find(|item| item.id.owner_id.to_def_id() == impl_item_id) .map(fix_span) { cause.span = impl_item_span; @@ -257,7 +253,7 @@ fn extend_cause_with_original_assoc_item_obligation<'tcx>( tcx.impl_item_implementor_ids(impl_def_id).get(&item_def_id) && let Some(impl_item_span) = items .iter() - .find(|item| item.id.def_id.to_def_id() == impl_item_id) + .find(|item| item.id.owner_id.to_def_id() == impl_item_id) .map(fix_span) { cause.span = impl_item_span; @@ -276,7 +272,7 @@ impl<'tcx> WfPredicates<'tcx> { traits::ObligationCause::new(self.span, self.body_id, code) } - fn normalize(self, infcx: &InferCtxt<'_, 'tcx>) -> Vec<traits::PredicateObligation<'tcx>> { + fn normalize(self, infcx: &InferCtxt<'tcx>) -> Vec<traits::PredicateObligation<'tcx>> { let cause = self.cause(traits::WellFormed(None)); let param_env = self.param_env; let mut obligations = Vec::with_capacity(self.out.len()); @@ -393,7 +389,8 @@ impl<'tcx> WfPredicates<'tcx> { // `i32: Clone` // `i32: Copy` // ] - let obligations = self.nominal_obligations(data.item_def_id, data.substs); + // Projection types do not require const predicates. + let obligations = self.nominal_obligations_without_const(data.item_def_id, data.substs); self.out.extend(obligations); let tcx = self.tcx(); @@ -436,11 +433,13 @@ impl<'tcx> WfPredicates<'tcx> { } /// Pushes all the predicates needed to validate that `ty` is WF into `out`. + #[instrument(level = "debug", skip(self))] fn compute(&mut self, arg: GenericArg<'tcx>) { let mut walker = arg.walk(); let param_env = self.param_env; let depth = self.recursion_depth; while let Some(arg) = walker.next() { + debug!(?arg, ?self.out); let ty = match arg.unpack() { GenericArgKind::Type(ty) => ty, @@ -448,14 +447,14 @@ impl<'tcx> WfPredicates<'tcx> { // obligations are handled by the parent (e.g. `ty::Ref`). GenericArgKind::Lifetime(_) => continue, - GenericArgKind::Const(constant) => { - match constant.kind() { + GenericArgKind::Const(ct) => { + match ct.kind() { ty::ConstKind::Unevaluated(uv) => { let obligations = self.nominal_obligations(uv.def.did, uv.substs); self.out.extend(obligations); let predicate = - ty::Binder::dummy(ty::PredicateKind::ConstEvaluatable(uv.shrink())) + ty::Binder::dummy(ty::PredicateKind::ConstEvaluatable(ct)) .to_predicate(self.tcx()); let cause = self.cause(traits::WellFormed(None)); self.out.push(traits::Obligation::with_depth( @@ -472,7 +471,7 @@ impl<'tcx> WfPredicates<'tcx> { cause, self.recursion_depth, self.param_env, - ty::Binder::dummy(ty::PredicateKind::WellFormed(constant.into())) + ty::Binder::dummy(ty::PredicateKind::WellFormed(ct.into())) .to_predicate(self.tcx()), )); } @@ -490,6 +489,8 @@ impl<'tcx> WfPredicates<'tcx> { } }; + debug!("wf bounds for ty={:?} ty.kind={:#?}", ty, ty.kind()); + match *ty.kind() { ty::Bool | ty::Char @@ -546,7 +547,7 @@ impl<'tcx> WfPredicates<'tcx> { } ty::FnDef(did, substs) => { - let obligations = self.nominal_obligations(did, substs); + let obligations = self.nominal_obligations_without_const(did, substs); self.out.extend(obligations); } @@ -636,7 +637,7 @@ impl<'tcx> WfPredicates<'tcx> { } } - ty::Dynamic(data, r) => { + ty::Dynamic(data, r, _) => { // WfObject // // Here, we defer WF checking due to higher-ranked @@ -688,6 +689,8 @@ impl<'tcx> WfPredicates<'tcx> { )); } } + + debug!(?self.out); } } @@ -713,7 +716,7 @@ impl<'tcx> WfPredicates<'tcx> { iter::zip(iter::zip(predicates.predicates, predicates.spans), origins.into_iter().rev()) .map(|((mut pred, span), origin_def_id)| { let code = if span.is_dummy() { - traits::MiscObligation + traits::ItemObligation(origin_def_id) } else { traits::BindingObligation(origin_def_id, span) }; @@ -843,7 +846,7 @@ pub fn object_region_bounds<'tcx>( /// /// Requires that trait definitions have been processed so that we can /// elaborate predicates and walk supertraits. -#[instrument(skip(tcx, predicates), level = "debug")] +#[instrument(skip(tcx, predicates), level = "debug", ret)] pub(crate) fn required_region_bounds<'tcx>( tcx: TyCtxt<'tcx>, erased_self_ty: Ty<'tcx>, |