diff options
Diffstat (limited to 'compiler/rustc_trait_selection/src/traits')
30 files changed, 2825 insertions, 2839 deletions
diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs index ed34ab95a..8e04da4f9 100644 --- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs +++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs @@ -10,9 +10,9 @@ 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::{PolyTraitRef, Region, RegionVid}; +use rustc_middle::ty::{ImplPolarity, Region, RegionVid}; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; use std::collections::hash_map::Entry; use std::collections::VecDeque; @@ -27,8 +27,8 @@ pub enum RegionTarget<'tcx> { #[derive(Default, Debug, Clone)] pub struct RegionDeps<'tcx> { - larger: FxHashSet<RegionTarget<'tcx>>, - smaller: FxHashSet<RegionTarget<'tcx>>, + larger: FxIndexSet<RegionTarget<'tcx>>, + smaller: FxIndexSet<RegionTarget<'tcx>>, } pub enum AutoTraitResult<A> { @@ -86,18 +86,25 @@ impl<'tcx> AutoTraitFinder<'tcx> { ) -> AutoTraitResult<A> { let tcx = self.tcx; - let trait_ref = ty::TraitRef { def_id: trait_did, substs: tcx.mk_substs_trait(ty, &[]) }; - - let trait_pred = ty::Binder::dummy(trait_ref); + let trait_ref = tcx.mk_trait_ref(trait_did, [ty]); 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))); + for polarity in [true, false] { + let result = selcx.select(&Obligation::new( + tcx, + ObligationCause::dummy(), + orig_env, + ty::Binder::dummy(ty::TraitPredicate { + trait_ref, + constness: ty::BoundConstness::NotConst, + polarity: if polarity { + ImplPolarity::Positive + } else { + ImplPolarity::Negative + }, + }), + )); if let Ok(Some(ImplSource::UserDefined(_))) = result { debug!( "find_auto_trait_generics({:?}): \ @@ -256,17 +263,15 @@ impl<'tcx> AutoTraitFinder<'tcx> { let mut already_visited = FxHashSet::default(); let mut predicates = VecDeque::new(); predicates.push_back(ty::Binder::dummy(ty::TraitPredicate { - trait_ref: ty::TraitRef { - def_id: trait_did, - substs: infcx.tcx.mk_substs_trait(ty, &[]), - }, + trait_ref: infcx.tcx.mk_trait_ref(trait_did, [ty]), + constness: ty::BoundConstness::NotConst, // Auto traits are positive polarity: ty::ImplPolarity::Positive, })); let computed_preds = param_env.caller_bounds().iter(); - let mut user_computed_preds: FxHashSet<_> = user_env.caller_bounds().iter().collect(); + let mut user_computed_preds: FxIndexSet<_> = user_env.caller_bounds().iter().collect(); let mut new_env = param_env; let dummy_cause = ObligationCause::dummy(); @@ -280,8 +285,12 @@ impl<'tcx> AutoTraitFinder<'tcx> { // Call `infcx.resolve_vars_if_possible` to see if we can // get rid of any inference variables. - let obligation = - infcx.resolve_vars_if_possible(Obligation::new(dummy_cause.clone(), new_env, pred)); + let obligation = infcx.resolve_vars_if_possible(Obligation::new( + tcx, + dummy_cause.clone(), + new_env, + pred, + )); let result = select.select(&obligation); match result { @@ -389,13 +398,15 @@ impl<'tcx> AutoTraitFinder<'tcx> { /// not just one specific lifetime (e.g., `'static`). fn add_user_pred( &self, - user_computed_preds: &mut FxHashSet<ty::Predicate<'tcx>>, + user_computed_preds: &mut FxIndexSet<ty::Predicate<'tcx>>, new_pred: ty::Predicate<'tcx>, ) { let mut should_add_new = true; user_computed_preds.retain(|&old_pred| { - if let (ty::PredicateKind::Trait(new_trait), ty::PredicateKind::Trait(old_trait)) = - (new_pred.kind().skip_binder(), old_pred.kind().skip_binder()) + if let ( + ty::PredicateKind::Clause(ty::Clause::Trait(new_trait)), + ty::PredicateKind::Clause(ty::Clause::Trait(old_trait)), + ) = (new_pred.kind().skip_binder(), old_pred.kind().skip_binder()) { if new_trait.def_id() == old_trait.def_id() { let new_substs = new_trait.trait_ref.substs; @@ -585,20 +596,20 @@ impl<'tcx> AutoTraitFinder<'tcx> { &self, ty: Ty<'_>, nested: impl Iterator<Item = Obligation<'tcx, ty::Predicate<'tcx>>>, - computed_preds: &mut FxHashSet<ty::Predicate<'tcx>>, + computed_preds: &mut FxIndexSet<ty::Predicate<'tcx>>, fresh_preds: &mut FxHashSet<ty::Predicate<'tcx>>, predicates: &mut VecDeque<ty::PolyTraitPredicate<'tcx>>, - select: &mut SelectionContext<'_, 'tcx>, + selcx: &mut SelectionContext<'_, 'tcx>, only_projections: bool, ) -> bool { let dummy_cause = ObligationCause::dummy(); for obligation in nested { let is_new_pred = - fresh_preds.insert(self.clean_pred(select.infcx(), obligation.predicate)); + fresh_preds.insert(self.clean_pred(selcx.infcx, obligation.predicate)); // Resolve any inference variables that we can, to help selection succeed - let predicate = select.infcx().resolve_vars_if_possible(obligation.predicate); + let predicate = selcx.infcx.resolve_vars_if_possible(obligation.predicate); // We only add a predicate as a user-displayable bound if // it involves a generic parameter, and doesn't contain @@ -615,14 +626,14 @@ impl<'tcx> AutoTraitFinder<'tcx> { let bound_predicate = predicate.kind(); match bound_predicate.skip_binder() { - ty::PredicateKind::Trait(p) => { + ty::PredicateKind::Clause(ty::Clause::Trait(p)) => { // Add this to `predicates` so that we end up calling `select` // with it. If this predicate ends up being unimplemented, // then `evaluate_predicates` will handle adding it the `ParamEnv` // if possible. predicates.push_back(bound_predicate.rebind(p)); } - ty::PredicateKind::Projection(p) => { + ty::PredicateKind::Clause(ty::Clause::Projection(p)) => { let p = bound_predicate.rebind(p); debug!( "evaluate_nested_obligations: examining projection predicate {:?}", @@ -706,7 +717,8 @@ impl<'tcx> AutoTraitFinder<'tcx> { // and turn them into an explicit negative impl for our type. debug!("Projecting and unifying projection predicate {:?}", predicate); - match project::poly_project_and_unify_type(select, &obligation.with(p)) { + match project::poly_project_and_unify_type(selcx, &obligation.with(self.tcx, p)) + { ProjectAndUnifyResult::MismatchedProjectionTypes(e) => { debug!( "evaluate_nested_obligations: Unable to unify predicate \ @@ -731,7 +743,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { computed_preds, fresh_preds, predicates, - select, + selcx, only_projections, ) { return false; @@ -752,25 +764,25 @@ impl<'tcx> AutoTraitFinder<'tcx> { } } } - ty::PredicateKind::RegionOutlives(binder) => { + ty::PredicateKind::Clause(ty::Clause::RegionOutlives(binder)) => { let binder = bound_predicate.rebind(binder); - select.infcx().region_outlives_predicate(&dummy_cause, binder) + selcx.infcx.region_outlives_predicate(&dummy_cause, binder) } - ty::PredicateKind::TypeOutlives(binder) => { + ty::PredicateKind::Clause(ty::Clause::TypeOutlives(binder)) => { let binder = bound_predicate.rebind(binder); match ( binder.no_bound_vars(), binder.map_bound_ref(|pred| pred.0).no_bound_vars(), ) { (None, Some(t_a)) => { - select.infcx().register_region_obligation_with_cause( + selcx.infcx.register_region_obligation_with_cause( t_a, - select.infcx().tcx.lifetimes.re_static, + selcx.infcx.tcx.lifetimes.re_static, &dummy_cause, ); } (Some(ty::OutlivesPredicate(t_a, r_b)), _) => { - select.infcx().register_region_obligation_with_cause( + selcx.infcx.register_region_obligation_with_cause( t_a, r_b, &dummy_cause, @@ -782,14 +794,12 @@ impl<'tcx> AutoTraitFinder<'tcx> { ty::PredicateKind::ConstEquate(c1, c2) => { let evaluate = |c: ty::Const<'tcx>| { if let ty::ConstKind::Unevaluated(unevaluated) = c.kind() { - match select.infcx().const_eval_resolve( + match selcx.infcx.const_eval_resolve( obligation.param_env, unevaluated, Some(obligation.cause.span), ) { - Ok(Some(valtree)) => { - Ok(ty::Const::from_value(select.tcx(), valtree, c.ty())) - } + Ok(Some(valtree)) => Ok(selcx.tcx().mk_const(valtree, c.ty())), Ok(None) => { let tcx = self.tcx; let def_id = unevaluated.def.did; @@ -809,10 +819,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { match (evaluate(c1), evaluate(c2)) { (Ok(c1), Ok(c2)) => { - match select - .infcx() - .at(&obligation.cause, obligation.param_env) - .eq(c1, c2) + match selcx.infcx.at(&obligation.cause, obligation.param_env).eq(c1, c2) { Ok(_) => (), Err(_) => return false, @@ -832,6 +839,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { | ty::PredicateKind::ConstEvaluatable(..) | ty::PredicateKind::Coerce(..) | ty::PredicateKind::TypeWellFormedFromEnv(..) => {} + ty::PredicateKind::Ambiguous => return false, }; } true @@ -846,7 +854,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { } } -// Replaces all ReVars in a type with ty::Region's, using the provided map +/// Replaces all ReVars in a type with ty::Region's, using the provided map pub struct RegionReplacer<'a, 'tcx> { vid_to_region: &'a FxHashMap<ty::RegionVid, ty::Region<'tcx>>, tcx: TyCtxt<'tcx>, diff --git a/compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs b/compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs index 81e1d6449..e88950523 100644 --- a/compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs @@ -4,44 +4,43 @@ use crate::infer::canonical::OriginalQueryValues; use crate::infer::InferCtxt; use crate::traits::query::NoSolution; use crate::traits::{ - ChalkEnvironmentAndGoal, FulfillmentError, FulfillmentErrorCode, ObligationCause, - PredicateObligation, SelectionError, TraitEngine, + ChalkEnvironmentAndGoal, FulfillmentError, FulfillmentErrorCode, PredicateObligation, + SelectionError, TraitEngine, }; use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; -use rustc_middle::ty::{self, Ty, TypeVisitable}; +use rustc_middle::ty::{self, TypeVisitable}; pub struct FulfillmentContext<'tcx> { obligations: FxIndexSet<PredicateObligation<'tcx>>, relationships: FxHashMap<ty::TyVid, ty::FoundRelationships>, + + usable_in_snapshot: bool, } impl FulfillmentContext<'_> { - pub(crate) fn new() -> Self { + pub(super) fn new() -> Self { FulfillmentContext { obligations: FxIndexSet::default(), relationships: FxHashMap::default(), + usable_in_snapshot: false, } } -} -impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> { - fn normalize_projection_type( - &mut self, - infcx: &InferCtxt<'tcx>, - _param_env: ty::ParamEnv<'tcx>, - projection_ty: ty::ProjectionTy<'tcx>, - _cause: ObligationCause<'tcx>, - ) -> Ty<'tcx> { - infcx.tcx.mk_ty(ty::Projection(projection_ty)) + pub(crate) fn new_in_snapshot() -> Self { + FulfillmentContext { usable_in_snapshot: true, ..Self::new() } } +} +impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> { fn register_predicate_obligation( &mut self, infcx: &InferCtxt<'tcx>, obligation: PredicateObligation<'tcx>, ) { - assert!(!infcx.is_in_snapshot()); + if !self.usable_in_snapshot { + assert!(!infcx.is_in_snapshot()); + } let obligation = infcx.resolve_vars_if_possible(obligation); super::relationships::update(self, infcx, &obligation); @@ -72,7 +71,9 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> { } fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>> { - assert!(!infcx.is_in_snapshot()); + if !self.usable_in_snapshot { + assert!(!infcx.is_in_snapshot()); + } let mut errors = Vec::new(); let mut next_round = FxIndexSet::default(); diff --git a/compiler/rustc_trait_selection/src/traits/codegen.rs b/compiler/rustc_trait_selection/src/traits/codegen.rs deleted file mode 100644 index 8a62bf015..000000000 --- a/compiler/rustc_trait_selection/src/traits/codegen.rs +++ /dev/null @@ -1,89 +0,0 @@ -// This file contains various trait resolution methods used by codegen. -// They all assume regions can be erased and monomorphic types. It -// seems likely that they should eventually be merged into more -// 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}; - -/// Attempts to resolve an obligation to an `ImplSource`. The result is -/// a shallow `ImplSource` resolution, meaning that we do not -/// (necessarily) resolve all nested obligations on the impl. Note -/// that type check should guarantee to us that all nested -/// obligations *could be* resolved if we wanted to. -/// -/// This also expects that `trait_ref` is fully normalized. -pub fn codegen_select_candidate<'tcx>( - tcx: TyCtxt<'tcx>, - (param_env, trait_ref): (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>), -) -> Result<&'tcx ImplSource<'tcx, ()>, CodegenObligationError> { - // We expect the input to be fully normalized. - debug_assert_eq!(trait_ref, tcx.normalize_erasing_regions(param_env, trait_ref)); - - // Do the initial selection for the obligation. This yields the - // shallow result we are looking for -- that is, what specific impl. - 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 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); - - // 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() { - // `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); - - // 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(); - - 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 8aab75490..899e30275 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -11,14 +11,14 @@ use crate::traits::select::IntercrateAmbiguityCause; use crate::traits::util::impl_subject_and_oblig; use crate::traits::SkipLeakCheck; use crate::traits::{ - self, Normalized, Obligation, ObligationCause, ObligationCtxt, PredicateObligation, - PredicateObligations, SelectionContext, + self, Obligation, ObligationCause, ObligationCtxt, PredicateObligation, PredicateObligations, + SelectionContext, }; use rustc_data_structures::fx::FxIndexSet; use rustc_errors::Diagnostic; 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::infer::{DefiningAnchor, InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::util; use rustc_middle::traits::specialization_graph::OverlapMode; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; @@ -30,6 +30,8 @@ use std::fmt::Debug; use std::iter; use std::ops::ControlFlow; +use super::NormalizeExt; + /// Whether we do the orphan check relative to this crate or /// to some remote crate. #[derive(Copy, Clone, Debug)] @@ -64,13 +66,13 @@ pub fn add_placeholder_note(err: &mut Diagnostic) { /// with a suitably-freshened `ImplHeader` with those types /// substituted. Otherwise, returns `None`. #[instrument(skip(tcx, skip_leak_check), level = "debug")] -pub fn overlapping_impls( - tcx: TyCtxt<'_>, +pub fn overlapping_impls<'tcx>( + tcx: TyCtxt<'tcx>, impl1_def_id: DefId, impl2_def_id: DefId, skip_leak_check: SkipLeakCheck, overlap_mode: OverlapMode, -) -> Option<OverlapResult<'_>> { +) -> Option<OverlapResult<'tcx>> { // 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. @@ -94,8 +96,9 @@ pub fn overlapping_impls( return None; } - let infcx = tcx.infer_ctxt().build(); - let selcx = &mut SelectionContext::intercrate(&infcx); + let infcx = + tcx.infer_ctxt().with_opaque_type_inference(DefiningAnchor::Bubble).intercrate().build(); + let selcx = &mut SelectionContext::new(&infcx); let overlaps = overlap(selcx, skip_leak_check, impl1_def_id, impl2_def_id, overlap_mode).is_some(); if !overlaps { @@ -105,8 +108,9 @@ pub fn overlapping_impls( // 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.) - let infcx = tcx.infer_ctxt().build(); - let selcx = &mut SelectionContext::intercrate(&infcx); + let infcx = + tcx.infer_ctxt().with_opaque_type_inference(DefiningAnchor::Bubble).intercrate().build(); + let selcx = &mut SelectionContext::new(&infcx); selcx.enable_tracking_intercrate_ambiguity_causes(); Some(overlap(selcx, skip_leak_check, impl1_def_id, impl2_def_id, overlap_mode).unwrap()) } @@ -117,7 +121,7 @@ fn with_fresh_ty_vars<'cx, 'tcx>( impl_def_id: DefId, ) -> ty::ImplHeader<'tcx> { let tcx = selcx.tcx(); - let impl_substs = selcx.infcx().fresh_substs_for_item(DUMMY_SP, impl_def_id); + let impl_substs = selcx.infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id); let header = ty::ImplHeader { impl_def_id, @@ -126,8 +130,8 @@ fn with_fresh_ty_vars<'cx, 'tcx>( predicates: tcx.predicates_of(impl_def_id).instantiate(tcx, impl_substs).predicates, }; - let Normalized { value: mut header, obligations } = - traits::normalize(selcx, param_env, ObligationCause::dummy(), header); + let InferOk { value: mut header, obligations } = + selcx.infcx.at(&ObligationCause::dummy(), param_env).normalize(header); header.predicates.extend(obligations.into_iter().map(|o| o.predicate)); header @@ -147,7 +151,7 @@ fn overlap<'cx, 'tcx>( impl1_def_id, impl2_def_id, overlap_mode ); - selcx.infcx().probe_maybe_skip_leak_check(skip_leak_check.is_yes(), |snapshot| { + selcx.infcx.probe_maybe_skip_leak_check(skip_leak_check.is_yes(), |snapshot| { overlap_within_probe(selcx, impl1_def_id, impl2_def_id, overlap_mode, snapshot) }) } @@ -159,11 +163,11 @@ fn overlap_within_probe<'cx, 'tcx>( overlap_mode: OverlapMode, snapshot: &CombinedSnapshot<'tcx>, ) -> Option<OverlapResult<'tcx>> { - let infcx = selcx.infcx(); + let infcx = selcx.infcx; if overlap_mode.use_negative_impl() { - if negative_impl(selcx, impl1_def_id, impl2_def_id) - || negative_impl(selcx, impl2_def_id, impl1_def_id) + if negative_impl(infcx.tcx, impl1_def_id, impl2_def_id) + || negative_impl(infcx.tcx, impl2_def_id, impl1_def_id) { return None; } @@ -198,9 +202,9 @@ fn overlap_within_probe<'cx, 'tcx>( debug!("overlap: intercrate_ambiguity_causes={:#?}", intercrate_ambiguity_causes); let involves_placeholder = - matches!(selcx.infcx().region_constraints_added_in_snapshot(snapshot), Some(true)); + matches!(selcx.infcx.region_constraints_added_in_snapshot(snapshot), Some(true)); - let impl_header = selcx.infcx().resolve_vars_if_possible(impl1_header); + let impl_header = selcx.infcx.resolve_vars_if_possible(impl1_header); Some(OverlapResult { impl_header, intercrate_ambiguity_causes, involves_placeholder }) } @@ -212,7 +216,7 @@ fn equate_impl_headers<'cx, 'tcx>( // Do `a` and `b` unify? If not, no overlap. debug!("equate_impl_headers(impl1_header={:?}, impl2_header={:?}", impl1_header, impl2_header); selcx - .infcx() + .infcx .at(&ObligationCause::dummy(), ty::ParamEnv::empty()) .eq_impl_headers(impl1_header, impl2_header) .map(|infer_ok| infer_ok.obligations) @@ -253,7 +257,7 @@ fn implicit_negative<'cx, 'tcx>( "implicit_negative(impl1_header={:?}, impl2_header={:?}, obligations={:?})", impl1_header, impl2_header, obligations ); - let infcx = selcx.infcx(); + let infcx = selcx.infcx; let opt_failing_obligation = impl1_header .predicates .iter() @@ -279,13 +283,8 @@ fn implicit_negative<'cx, 'tcx>( /// Given impl1 and impl2 check if both impls are never satisfied by a common type (including /// where-clauses) If so, return true, they are disjoint and false otherwise. -fn negative_impl<'cx, 'tcx>( - selcx: &mut SelectionContext<'cx, 'tcx>, - impl1_def_id: DefId, - impl2_def_id: DefId, -) -> bool { +fn negative_impl<'tcx>(tcx: TyCtxt<'tcx>, impl1_def_id: DefId, impl2_def_id: DefId) -> bool { debug!("negative_impl(impl1_def_id={:?}, impl2_def_id={:?})", impl1_def_id, impl2_def_id); - let tcx = selcx.infcx().tcx; // Create an infcx, taking the predicates of impl1 as assumptions: let infcx = tcx.infer_ctxt().build(); @@ -332,11 +331,10 @@ fn equate<'tcx>( return true; }; - let selcx = &mut SelectionContext::new(&infcx); let opt_failing_obligation = obligations .into_iter() .chain(more_obligations) - .find(|o| negative_impl_exists(selcx, o, body_def_id)); + .find(|o| negative_impl_exists(infcx, o, body_def_id)); if let Some(failing_obligation) = opt_failing_obligation { debug!("overlap: obligation unsatisfiable {:?}", failing_obligation); @@ -347,19 +345,19 @@ fn equate<'tcx>( } /// Try to prove that a negative impl exist for the given obligation and its super predicates. -#[instrument(level = "debug", skip(selcx))] -fn negative_impl_exists<'cx, 'tcx>( - selcx: &SelectionContext<'cx, 'tcx>, +#[instrument(level = "debug", skip(infcx))] +fn negative_impl_exists<'tcx>( + infcx: &InferCtxt<'tcx>, o: &PredicateObligation<'tcx>, body_def_id: DefId, ) -> bool { - if resolve_negative_obligation(selcx.infcx().fork(), o, body_def_id) { + if resolve_negative_obligation(infcx.fork(), o, body_def_id) { return true; } // Try to prove a negative obligation exists for super predicates - for o in util::elaborate_predicates(selcx.tcx(), iter::once(o.predicate)) { - if resolve_negative_obligation(selcx.infcx().fork(), &o, body_def_id) { + for o in util::elaborate_predicates(infcx.tcx, iter::once(o.predicate)) { + if resolve_negative_obligation(infcx.fork(), &o, body_def_id) { return true; } } diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index 84038625f..7c9fde274 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -8,164 +8,30 @@ //! In this case we try to build an abstract representation of this constant using //! `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_span::Span; - -use std::iter; -use std::ops::ControlFlow; - -pub struct ConstUnifyCtxt<'tcx> { - pub tcx: TyCtxt<'tcx>, - pub param_env: ty::ParamEnv<'tcx>, -} - -impl<'tcx> ConstUnifyCtxt<'tcx> { - // Substitutes generics repeatedly to allow AbstractConsts to unify where a - // ConstKind::Unevaluated could be turned into an AbstractConst that would unify e.g. - // Param(N) should unify with Param(T), substs: [Unevaluated("T2", [Unevaluated("T3", [Param(N)])])] - #[inline] - #[instrument(skip(self), level = "debug")] - fn try_replace_substs_in_root( - &self, - mut abstr_const: AbstractConst<'tcx>, - ) -> Option<AbstractConst<'tcx>> { - while let Node::Leaf(ct) = abstr_const.root(self.tcx) { - match AbstractConst::from_const(self.tcx, ct) { - Ok(Some(act)) => abstr_const = act, - Ok(None) => break, - Err(_) => return None, - } - } - - Some(abstr_const) - } - /// Tries to unify two abstract constants using structural equality. - #[instrument(skip(self), level = "debug")] - pub fn try_unify(&self, a: AbstractConst<'tcx>, b: AbstractConst<'tcx>) -> bool { - let a = if let Some(a) = self.try_replace_substs_in_root(a) { - a - } else { - return true; - }; - - let b = if let Some(b) = self.try_replace_substs_in_root(b) { - b - } else { - return true; - }; - - let a_root = a.root(self.tcx); - let b_root = b.root(self.tcx); - debug!(?a_root, ?b_root); - - match (a_root, b_root) { - (Node::Leaf(a_ct), Node::Leaf(b_ct)) => { - let a_ct = a_ct.eval(self.tcx, self.param_env); - debug!("a_ct evaluated: {:?}", a_ct); - let b_ct = b_ct.eval(self.tcx, self.param_env); - debug!("b_ct evaluated: {:?}", b_ct); - - if a_ct.ty() != b_ct.ty() { - return false; - } +use rustc_middle::traits::ObligationCause; +use rustc_middle::ty::abstract_const::NotConstEvaluatable; +use rustc_middle::ty::{self, TyCtxt, TypeVisitable, TypeVisitor}; - match (a_ct.kind(), b_ct.kind()) { - // We can just unify errors with everything to reduce the amount of - // emitted errors here. - (ty::ConstKind::Error(_), _) | (_, ty::ConstKind::Error(_)) => true, - (ty::ConstKind::Param(a_param), ty::ConstKind::Param(b_param)) => { - a_param == b_param - } - (ty::ConstKind::Value(a_val), ty::ConstKind::Value(b_val)) => a_val == b_val, - // If we have `fn a<const N: usize>() -> [u8; N + 1]` and `fn b<const M: usize>() -> [u8; 1 + M]` - // we do not want to use `assert_eq!(a(), b())` to infer that `N` and `M` have to be `1`. This - // means that we only allow inference variables if they are equal. - (ty::ConstKind::Infer(a_val), ty::ConstKind::Infer(b_val)) => a_val == b_val, - // We expand generic anonymous constants at the start of this function, so this - // branch should only be taking when dealing with associated constants, at - // which point directly comparing them seems like the desired behavior. - // - // FIXME(generic_const_exprs): This isn't actually the case. - // We also take this branch for concrete anonymous constants and - // expand generic anonymous constants with concrete substs. - (ty::ConstKind::Unevaluated(a_uv), ty::ConstKind::Unevaluated(b_uv)) => { - 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 fully concrete or something like - // this, for now we just return false here. - _ => false, - } - } - (Node::Binop(a_op, al, ar), Node::Binop(b_op, bl, br)) if a_op == b_op => { - self.try_unify(a.subtree(al), b.subtree(bl)) - && self.try_unify(a.subtree(ar), b.subtree(br)) - } - (Node::UnaryOp(a_op, av), Node::UnaryOp(b_op, bv)) if a_op == b_op => { - self.try_unify(a.subtree(av), b.subtree(bv)) - } - (Node::FunctionCall(a_f, a_args), Node::FunctionCall(b_f, b_args)) - if a_args.len() == b_args.len() => - { - self.try_unify(a.subtree(a_f), b.subtree(b_f)) - && iter::zip(a_args, b_args) - .all(|(&an, &bn)| self.try_unify(a.subtree(an), b.subtree(bn))) - } - (Node::Cast(a_kind, a_operand, a_ty), Node::Cast(b_kind, b_operand, b_ty)) - if (a_ty == b_ty) && (a_kind == b_kind) => - { - self.try_unify(a.subtree(a_operand), b.subtree(b_operand)) - } - // use this over `_ => false` to make adding variants to `Node` less error prone - (Node::Cast(..), _) - | (Node::FunctionCall(..), _) - | (Node::UnaryOp(..), _) - | (Node::Binop(..), _) - | (Node::Leaf(..), _) => false, - } - } -} - -#[instrument(skip(tcx), level = "debug")] -pub fn try_unify_abstract_consts<'tcx>( - tcx: TyCtxt<'tcx>, - (a, b): (ty::UnevaluatedConst<'tcx>, ty::UnevaluatedConst<'tcx>), - param_env: ty::ParamEnv<'tcx>, -) -> bool { - (|| { - if let Some(a) = AbstractConst::new(tcx, a)? { - if let Some(b) = AbstractConst::new(tcx, b)? { - let const_unify_ctxt = ConstUnifyCtxt { tcx, param_env }; - return Ok(const_unify_ctxt.try_unify(a, b)); - } - } +use rustc_span::Span; +use std::ops::ControlFlow; - Ok(false) - })() - .unwrap_or_else(|_: ErrorGuaranteed| true) - // FIXME(generic_const_exprs): We should instead have this - // method return the resulting `ty::Const` and return `ConstKind::Error` - // on `ErrorGuaranteed`. -} +use crate::traits::ObligationCtxt; /// Check if a given constant can be evaluated. #[instrument(skip(infcx), level = "debug")] pub fn is_const_evaluatable<'tcx>( infcx: &InferCtxt<'tcx>, - ct: ty::Const<'tcx>, + unexpanded_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, + match tcx.expand_abstract_consts(unexpanded_ct).kind() { + ty::ConstKind::Unevaluated(_) | ty::ConstKind::Expr(_) => (), ty::ConstKind::Param(_) | ty::ConstKind::Bound(_, _) | ty::ConstKind::Placeholder(_) @@ -175,40 +41,59 @@ pub fn is_const_evaluatable<'tcx>( }; if tcx.features().generic_const_exprs { - if let Some(ct) = AbstractConst::new(tcx, uv)? { - if satisfied_from_param_env(tcx, ct, param_env)? { + let ct = tcx.expand_abstract_consts(unexpanded_ct); + + let is_anon_ct = if let ty::ConstKind::Unevaluated(uv) = ct.kind() { + tcx.def_kind(uv.def.did) == DefKind::AnonConst + } else { + false + }; + + if !is_anon_ct { + if satisfied_from_param_env(tcx, infcx, ct, param_env) { return Ok(()); } - match ct.unify_failure_kind(tcx) { - FailureKind::MentionsInfer => { - return Err(NotConstEvaluatable::MentionsInfer); - } - FailureKind::MentionsParam => { - return Err(NotConstEvaluatable::MentionsParam); - } - // returned below - FailureKind::Concrete => {} + if ct.has_non_region_infer() { + return Err(NotConstEvaluatable::MentionsInfer); + } else if ct.has_non_region_param() { + return Err(NotConstEvaluatable::MentionsParam); } } - 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( + + match unexpanded_ct.kind() { + ty::ConstKind::Expr(_) => { + // FIXME(generic_const_exprs): we have a `ConstKind::Expr` which is fully concrete, but + // currently it is not possible to evaluate `ConstKind::Expr` so we are unable to tell if it + // is evaluatable or not. For now we just ICE until this is implemented. + Err(NotConstEvaluatable::Error(tcx.sess.delay_span_bug( span, - format!("Missing value for constant, but no error reported?"), + "evaluating `ConstKind::Expr` is not currently supported", ))) } - Err(ErrorHandled::Linted) => { - let reported = infcx - .tcx - .sess - .delay_span_bug(span, "constant in type had error reported as lint"); - Err(NotConstEvaluatable::Error(reported)) + ty::ConstKind::Unevaluated(uv) => { + 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( + span, + "Missing value for constant, but no error reported?", + ))) + } + Err(ErrorHandled::Reported(e)) => Err(NotConstEvaluatable::Error(e)), + Ok(_) => Ok(()), + } } - Err(ErrorHandled::Reported(e)) => Err(NotConstEvaluatable::Error(e)), - Ok(_) => Ok(()), + _ => bug!("unexpected constkind in `is_const_evalautable: {unexpanded_ct:?}`"), } } else { + let uv = match unexpanded_ct.kind() { + ty::ConstKind::Unevaluated(uv) => uv, + ty::ConstKind::Expr(_) => { + bug!("`ConstKind::Expr` without `feature(generic_const_exprs)` enabled") + } + _ => bug!("unexpected constkind in `is_const_evalautable: {unexpanded_ct:?}`"), + }; + // FIXME: We should only try to evaluate a given constant here if it is fully concrete // as we don't want to allow things like `[u8; std::mem::size_of::<*mut T>()]`. // @@ -218,28 +103,33 @@ pub fn is_const_evaluatable<'tcx>( // // See #74595 for more details about this. 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 - // const exprs, AND it would've passed if that expression had been evaluated with - // generic const exprs, then suggest using generic const exprs. - Err(_) if tcx.sess.is_nightly_build() - && let Ok(Some(ct)) = AbstractConst::new(tcx, uv) - && satisfied_from_param_env(tcx, ct, param_env) == Ok(true) => { - tcx.sess - .struct_span_fatal( - // Slightly better span than just using `span` alone - if span == rustc_span::DUMMY_SP { tcx.def_span(uv.def.did) } else { span }, - "failed to evaluate generic const expression", - ) - .note("the crate this constant originates from uses `#![feature(generic_const_exprs)]`") - .span_suggestion_verbose( - rustc_span::DUMMY_SP, - "consider enabling this feature", - "#![feature(generic_const_exprs)]\n", - rustc_errors::Applicability::MaybeIncorrect, - ) - .emit() + // If we're evaluating a generic foreign constant, under a nightly compiler while + // the current crate does not enable `feature(generic_const_exprs)`, abort + // compilation with a useful error. + Err(_) + if tcx.sess.is_nightly_build() + && satisfied_from_param_env( + tcx, + infcx, + tcx.expand_abstract_consts(unexpanded_ct), + param_env, + ) => + { + tcx.sess + .struct_span_fatal( + // Slightly better span than just using `span` alone + if span == rustc_span::DUMMY_SP { tcx.def_span(uv.def.did) } else { span }, + "failed to evaluate generic const expression", + ) + .note("the crate this constant originates from uses `#![feature(generic_const_exprs)]`") + .span_suggestion_verbose( + rustc_span::DUMMY_SP, + "consider enabling this feature", + "#![feature(generic_const_exprs)]\n", + rustc_errors::Applicability::MaybeIncorrect, + ) + .emit() } Err(ErrorHandled::TooGeneric) => { @@ -248,16 +138,14 @@ pub fn is_const_evaluatable<'tcx>( } 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?")); + 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(_) => Ok(()), @@ -265,37 +153,69 @@ pub fn is_const_evaluatable<'tcx>( } } -#[instrument(skip(tcx), level = "debug")] +#[instrument(skip(infcx, tcx), level = "debug")] fn satisfied_from_param_env<'tcx>( tcx: TyCtxt<'tcx>, - ct: AbstractConst<'tcx>, + infcx: &InferCtxt<'tcx>, + ct: ty::Const<'tcx>, param_env: ty::ParamEnv<'tcx>, -) -> Result<bool, NotConstEvaluatable> { +) -> bool { + // Try to unify with each subtree in the AbstractConst to allow for + // `N + 1` being const evaluatable even if theres only a `ConstEvaluatable` + // predicate for `(N + 1) * 2` + struct Visitor<'a, 'tcx> { + ct: ty::Const<'tcx>, + param_env: ty::ParamEnv<'tcx>, + + infcx: &'a InferCtxt<'tcx>, + } + impl<'a, 'tcx> TypeVisitor<'tcx> for Visitor<'a, 'tcx> { + type BreakTy = (); + fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> { + debug!("is_const_evaluatable: candidate={:?}", c); + if let Ok(()) = self.infcx.commit_if_ok(|_| { + let ocx = ObligationCtxt::new_in_snapshot(self.infcx); + if let Ok(()) = ocx.eq(&ObligationCause::dummy(), self.param_env, c.ty(), self.ct.ty()) + && let Ok(()) = ocx.eq(&ObligationCause::dummy(), self.param_env, c, self.ct) + && ocx.select_all_or_error().is_empty() + { + Ok(()) + } else { + Err(()) + } + }) { + ControlFlow::BREAK + } else if let ty::ConstKind::Expr(e) = c.kind() { + e.visit_with(self) + } else { + // FIXME(generic_const_exprs): This doesn't recurse into `<T as Trait<U>>::ASSOC`'s substs. + // This is currently unobservable as `<T as Trait<{ U + 1 }>>::ASSOC` creates an anon const + // with its own `ConstEvaluatable` bound in the param env which we will visit separately. + // + // If we start allowing directly writing `ConstKind::Expr` without an intermediate anon const + // this will be incorrect. It might be worth investigating making `predicates_of` elaborate + // all of the `ConstEvaluatable` bounds rather than having a visitor here. + ControlFlow::CONTINUE + } + } + } + for pred in param_env.caller_bounds() { match pred.kind().skip_binder() { - ty::PredicateKind::ConstEvaluatable(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 - // `N + 1` being const evaluatable even if theres only a `ConstEvaluatable` - // predicate for `(N + 1) * 2` - let result = walk_abstract_const(tcx, b_ct, |b_ct| { - match const_unify_ctxt.try_unify(ct, b_ct) { - true => ControlFlow::BREAK, - false => ControlFlow::CONTINUE, - } - }); - - if let ControlFlow::Break(()) = result { - debug!("is_const_evaluatable: abstract_const ~~> ok"); - return Ok(true); - } + ty::PredicateKind::ConstEvaluatable(ce) => { + let b_ct = tcx.expand_abstract_consts(ce); + let mut v = Visitor { ct, infcx, param_env }; + let result = b_ct.visit_with(&mut v); + + if let ControlFlow::Break(()) = result { + debug!("is_const_evaluatable: yes"); + return true; } } _ => {} // don't care } } - Ok(false) + debug!("is_const_evaluatable: no"); + false } diff --git a/compiler/rustc_trait_selection/src/traits/engine.rs b/compiler/rustc_trait_selection/src/traits/engine.rs index e0c8deec9..c028e89e4 100644 --- a/compiler/rustc_trait_selection/src/traits/engine.rs +++ b/compiler/rustc_trait_selection/src/traits/engine.rs @@ -1,14 +1,21 @@ use std::cell::RefCell; +use std::fmt::Debug; use super::TraitEngine; use super::{ChalkFulfillmentContext, FulfillmentContext}; -use crate::infer::InferCtxtExt; -use rustc_data_structures::fx::FxHashSet; +use crate::traits::NormalizeExt; +use rustc_data_structures::fx::FxIndexSet; use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_infer::infer::at::ToTrace; +use rustc_infer::infer::canonical::{ + Canonical, CanonicalVarValues, CanonicalizedQueryResponse, QueryResponse, +}; use rustc_infer::infer::{InferCtxt, InferOk}; +use rustc_infer::traits::query::Fallible; use rustc_infer::traits::{ FulfillmentError, Obligation, ObligationCause, PredicateObligation, TraitEngineExt as _, }; +use rustc_middle::arena::ArenaAllocatable; use rustc_middle::ty::error::TypeError; use rustc_middle::ty::ToPredicate; use rustc_middle::ty::TypeFoldable; @@ -31,7 +38,7 @@ impl<'tcx> TraitEngineExt<'tcx> for dyn TraitEngine<'tcx> { fn new_in_snapshot(tcx: TyCtxt<'tcx>) -> Box<Self> { if tcx.sess.opts.unstable_opts.chalk { - Box::new(ChalkFulfillmentContext::new()) + Box::new(ChalkFulfillmentContext::new_in_snapshot()) } else { Box::new(FulfillmentContext::new_in_snapshot()) } @@ -86,7 +93,7 @@ impl<'a, 'tcx> ObligationCtxt<'a, 'tcx> { def_id: DefId, ) { let tcx = self.infcx.tcx; - let trait_ref = ty::TraitRef { def_id, substs: tcx.mk_substs_trait(ty, &[]) }; + let trait_ref = tcx.mk_trait_ref(def_id, [ty]); self.register_obligation(Obligation { cause, recursion_depth: 0, @@ -97,28 +104,75 @@ impl<'a, 'tcx> ObligationCtxt<'a, 'tcx> { pub fn normalize<T: TypeFoldable<'tcx>>( &self, - cause: ObligationCause<'tcx>, + cause: &ObligationCause<'tcx>, param_env: ty::ParamEnv<'tcx>, value: T, ) -> T { - let infer_ok = self.infcx.partially_normalize_associated_types_in(cause, param_env, value); + let infer_ok = self.infcx.at(&cause, param_env).normalize(value); self.register_infer_ok_obligations(infer_ok) } - pub fn equate_types( + /// Makes `expected <: actual`. + pub fn eq_exp<T>( + &self, + cause: &ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + a_is_expected: bool, + a: T, + b: T, + ) -> Result<(), TypeError<'tcx>> + where + T: ToTrace<'tcx>, + { + self.infcx + .at(cause, param_env) + .eq_exp(a_is_expected, a, b) + .map(|infer_ok| self.register_infer_ok_obligations(infer_ok)) + } + + pub fn eq<T: ToTrace<'tcx>>( &self, cause: &ObligationCause<'tcx>, param_env: ty::ParamEnv<'tcx>, - expected: Ty<'tcx>, - actual: Ty<'tcx>, + expected: T, + actual: T, ) -> Result<(), TypeError<'tcx>> { - match self.infcx.at(cause, param_env).eq(expected, actual) { - Ok(InferOk { obligations, value: () }) => { - self.register_obligations(obligations); - Ok(()) - } - Err(e) => Err(e), - } + self.infcx + .at(cause, param_env) + .eq(expected, actual) + .map(|infer_ok| self.register_infer_ok_obligations(infer_ok)) + } + + /// Checks whether `expected` is a subtype of `actual`: `expected <: actual`. + pub fn sub<T: ToTrace<'tcx>>( + &self, + cause: &ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + expected: T, + actual: T, + ) -> Result<(), TypeError<'tcx>> { + self.infcx + .at(cause, param_env) + .sup(expected, actual) + .map(|infer_ok| self.register_infer_ok_obligations(infer_ok)) + } + + /// Checks whether `expected` is a supertype of `actual`: `expected :> actual`. + pub fn sup<T: ToTrace<'tcx>>( + &self, + cause: &ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + expected: T, + actual: T, + ) -> Result<(), TypeError<'tcx>> { + self.infcx + .at(cause, param_env) + .sup(expected, actual) + .map(|infer_ok| self.register_infer_ok_obligations(infer_ok)) + } + + pub fn select_where_possible(&self) -> Vec<FulfillmentError<'tcx>> { + self.engine.borrow_mut().select_where_possible(self.infcx) } pub fn select_all_or_error(&self) -> Vec<FulfillmentError<'tcx>> { @@ -130,10 +184,10 @@ impl<'a, 'tcx> ObligationCtxt<'a, 'tcx> { param_env: ty::ParamEnv<'tcx>, span: Span, def_id: LocalDefId, - ) -> FxHashSet<Ty<'tcx>> { + ) -> FxIndexSet<Ty<'tcx>> { let tcx = self.infcx.tcx; let assumed_wf_types = tcx.assumed_wf_types(def_id); - let mut implied_bounds = FxHashSet::default(); + let mut implied_bounds = FxIndexSet::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 { @@ -149,9 +203,25 @@ impl<'a, 'tcx> ObligationCtxt<'a, 'tcx> { // sound and then uncomment this line again. // implied_bounds.insert(ty); - let normalized = self.normalize(cause.clone(), param_env, ty); + let normalized = self.normalize(&cause, param_env, ty); implied_bounds.insert(normalized); } implied_bounds } + + pub fn make_canonicalized_query_response<T>( + &self, + inference_vars: CanonicalVarValues<'tcx>, + answer: T, + ) -> Fallible<CanonicalizedQueryResponse<'tcx, T>> + where + T: Debug + TypeFoldable<'tcx>, + Canonical<'tcx, QueryResponse<'tcx, T>>: ArenaAllocatable<'tcx>, + { + self.infcx.make_canonicalized_query_response( + inference_vars, + answer, + &mut **self.engine.borrow_mut(), + ) + } } diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs new file mode 100644 index 000000000..752b53fbc --- /dev/null +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs @@ -0,0 +1,52 @@ +use rustc_hir::def_id::DefId; +use rustc_infer::infer::InferCtxt; +use rustc_infer::traits::{Obligation, ObligationCause, TraitObligation}; +use rustc_span::DUMMY_SP; + +use crate::traits::ObligationCtxt; + +pub fn recompute_applicable_impls<'tcx>( + infcx: &InferCtxt<'tcx>, + obligation: &TraitObligation<'tcx>, +) -> Vec<DefId> { + let tcx = infcx.tcx; + let param_env = obligation.param_env; + let dummy_cause = ObligationCause::dummy(); + let impl_may_apply = |impl_def_id| { + let ocx = ObligationCtxt::new_in_snapshot(infcx); + let placeholder_obligation = + infcx.replace_bound_vars_with_placeholders(obligation.predicate); + let obligation_trait_ref = + ocx.normalize(&dummy_cause, param_env, placeholder_obligation.trait_ref); + + let impl_substs = infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id); + let impl_trait_ref = tcx.bound_impl_trait_ref(impl_def_id).unwrap().subst(tcx, impl_substs); + let impl_trait_ref = ocx.normalize(&ObligationCause::dummy(), param_env, impl_trait_ref); + + if let Err(_) = ocx.eq(&dummy_cause, param_env, obligation_trait_ref, impl_trait_ref) { + return false; + } + + let impl_predicates = tcx.predicates_of(impl_def_id).instantiate(tcx, impl_substs); + ocx.register_obligations( + impl_predicates + .predicates + .iter() + .map(|&predicate| Obligation::new(tcx, dummy_cause.clone(), param_env, predicate)), + ); + + ocx.select_where_possible().is_empty() + }; + + let mut impls = Vec::new(); + tcx.for_each_relevant_impl( + obligation.predicate.def_id(), + obligation.predicate.skip_binder().trait_ref.self_ty(), + |impl_def_id| { + if infcx.probe(move |_snapshot| impl_may_apply(impl_def_id)) { + impls.push(impl_def_id) + } + }, + ); + impls +} 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 1217d264a..dda7b2b2f 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -1,34 +1,40 @@ +mod ambiguity; pub mod on_unimplemented; pub mod suggestions; use super::{ - FulfillmentContext, FulfillmentError, FulfillmentErrorCode, MismatchedProjectionTypes, - Obligation, ObligationCause, ObligationCauseCode, OnUnimplementedDirective, - OnUnimplementedNote, OutputTypeParameterMismatch, Overflow, PredicateObligation, - SelectionContext, SelectionError, TraitNotObjectSafe, + FulfillmentError, FulfillmentErrorCode, MismatchedProjectionTypes, Obligation, ObligationCause, + ObligationCauseCode, ObligationCtxt, OutputTypeParameterMismatch, Overflow, + PredicateObligation, SelectionContext, SelectionError, TraitNotObjectSafe, }; - use crate::infer::error_reporting::{TyCategory, TypeAnnotationNeeded as ErrorCode}; use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use crate::infer::{self, InferCtxt, TyCtxtInferExt}; -use rustc_data_structures::fx::FxHashMap; +use crate::infer::{self, InferCtxt}; +use crate::traits::query::evaluate_obligation::InferCtxtExt as _; +use crate::traits::query::normalize::QueryNormalizeExt as _; +use crate::traits::specialize::to_pretty_impl_header; +use crate::traits::NormalizeExt; +use on_unimplemented::OnUnimplementedNote; +use on_unimplemented::TypeErrCtxtExt as _; +use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_errors::{ pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan, Style, }; use rustc_hir as hir; +use rustc_hir::def::Namespace; use rustc_hir::def_id::DefId; 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_infer::infer::{InferOk, TypeTrace}; use rustc_middle::traits::select::OverflowError; use rustc_middle::ty::abstract_const::NotConstEvaluatable; use rustc_middle::ty::error::ExpectedFound; use rustc_middle::ty::fold::{TypeFolder, TypeSuperFoldable}; +use rustc_middle::ty::print::{FmtPrinter, Print}; use rustc_middle::ty::{ self, SubtypePredicate, ToPolyTraitRef, ToPredicate, TraitRef, Ty, TyCtxt, TypeFoldable, TypeVisitable, @@ -40,11 +46,6 @@ use rustc_span::{ExpnKind, Span, DUMMY_SP}; use std::fmt; use std::iter; 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::TypeErrCtxtExt as _; use suggestions::TypeErrCtxtExt as _; pub use rustc_infer::traits::error_reporting::*; @@ -70,7 +71,7 @@ pub trait InferCtxtExt<'tcx> { /// 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, 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 @@ -82,6 +83,7 @@ pub trait InferCtxtExt<'tcx> { expected_args: Vec<ArgKind>, found_args: Vec<ArgKind>, is_closure: bool, + closure_pipe_span: Option<Span>, ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>; /// Checks if the type implements one of `Fn`, `FnMut`, or `FnOnce` @@ -97,24 +99,36 @@ pub trait InferCtxtExt<'tcx> { } pub trait TypeErrCtxtExt<'tcx> { + fn report_overflow_error<T>( + &self, + predicate: &T, + span: Span, + suggest_increasing_limit: bool, + mutate: impl FnOnce(&mut Diagnostic), + ) -> ! + where + T: fmt::Display + + TypeFoldable<'tcx> + + Print<'tcx, FmtPrinter<'tcx, 'tcx>, Output = FmtPrinter<'tcx, 'tcx>>, + <T as Print<'tcx, FmtPrinter<'tcx, 'tcx>>>::Error: std::fmt::Debug; + fn report_fulfillment_errors( &self, errors: &[FulfillmentError<'tcx>], body_id: Option<hir::BodyId>, - fallback_has_occurred: bool, ) -> ErrorGuaranteed; - fn report_overflow_error<T>( + fn report_overflow_obligation<T>( &self, obligation: &Obligation<'tcx, T>, suggest_increasing_limit: bool, ) -> ! where - T: fmt::Display + TypeFoldable<'tcx>; + T: ToPredicate<'tcx> + Clone; fn suggest_new_overflow_limit(&self, err: &mut Diagnostic); - fn report_overflow_error_cycle(&self, cycle: &[PredicateObligation<'tcx>]) -> !; + fn report_overflow_obligation_cycle(&self, cycle: &[PredicateObligation<'tcx>]) -> !; /// The `root_obligation` parameter should be the `root_obligation` field /// from a `FulfillmentError`. If no `FulfillmentError` is available, @@ -124,7 +138,6 @@ pub trait TypeErrCtxtExt<'tcx> { obligation: PredicateObligation<'tcx>, root_obligation: &PredicateObligation<'tcx>, error: &SelectionError<'tcx>, - fallback_has_occurred: bool, ); } @@ -133,15 +146,16 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { /// 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, 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, .. }), + kind: hir::ExprKind::Closure(&hir::Closure { body, fn_decl_span, fn_arg_span, .. }), .. }) => ( fn_decl_span, + fn_arg_span, hir.body(body) .params .iter() @@ -172,6 +186,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { kind: hir::TraitItemKind::Fn(ref sig, _), .. }) => ( sig.span, + None, sig.decl .inputs .iter() @@ -186,7 +201,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { ), 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()]) + (span, None, vec![ArgKind::empty(); variant_data.fields().len()]) } _ => panic!("non-FnLike node found: {:?}", node), }) @@ -202,6 +217,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { expected_args: Vec<ArgKind>, found_args: Vec<ArgKind>, is_closure: bool, + closure_arg_span: Option<Span>, ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { let kind = if is_closure { "closure" } else { "function" }; @@ -239,24 +255,13 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { 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, + closure_arg_span.unwrap_or(found_span), &format!( "consider changing the closure to take and ignore the expected argument{}", pluralize!(expected_args.len()) @@ -344,22 +349,19 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { span: DUMMY_SP, kind: TypeVariableOriginKind::MiscVariable, }); - let substs = self.tcx.mk_substs_trait(ty.skip_binder(), &[var.into()]); + let trait_ref = self.tcx.mk_trait_ref(trait_def_id, [ty.skip_binder(), var]); let obligation = Obligation::new( + self.tcx, ObligationCause::dummy(), param_env, - ty.rebind(ty::TraitPredicate { - trait_ref: ty::TraitRef::new(trait_def_id, substs), - constness, - polarity, - }) - .to_predicate(self.tcx), + ty.rebind(ty::TraitPredicate { trait_ref, constness, polarity }), ); - 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() { + let ocx = ObligationCtxt::new_in_snapshot(self); + ocx.register_obligation(obligation); + if ocx.select_all_or_error().is_empty() { return Ok(( - ty::ClosureKind::from_def_id(self.tcx, trait_def_id) + self.tcx + .fn_trait_kind_from_def_id(trait_def_id) .expect("expected to map DefId to ClosureKind"), ty.rebind(self.resolve_vars_if_possible(var)), )); @@ -375,7 +377,6 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { &self, errors: &[FulfillmentError<'tcx>], body_id: Option<hir::BodyId>, - fallback_has_occurred: bool, ) -> ErrorGuaranteed { #[derive(Debug)] struct ErrorDescriptor<'tcx> { @@ -383,7 +384,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { index: Option<usize>, // None if this is an old error } - let mut error_map: FxHashMap<_, Vec<_>> = self + let mut error_map: FxIndexMap<_, Vec<_>> = self .reported_trait_errors .borrow() .iter() @@ -452,7 +453,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { for (error, suppressed) in iter::zip(errors, is_suppressed) { if !suppressed { - self.report_fulfillment_error(error, body_id, fallback_has_occurred); + self.report_fulfillment_error(error, body_id); } } @@ -467,39 +468,84 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { /// occurrences in any case. fn report_overflow_error<T>( &self, - obligation: &Obligation<'tcx, T>, + predicate: &T, + span: Span, suggest_increasing_limit: bool, + mutate: impl FnOnce(&mut Diagnostic), ) -> ! where - T: fmt::Display + TypeFoldable<'tcx>, + T: fmt::Display + + TypeFoldable<'tcx> + + Print<'tcx, FmtPrinter<'tcx, 'tcx>, Output = FmtPrinter<'tcx, 'tcx>>, + <T as Print<'tcx, FmtPrinter<'tcx, 'tcx>>>::Error: std::fmt::Debug, { - let predicate = self.resolve_vars_if_possible(obligation.predicate.clone()); + let predicate = self.resolve_vars_if_possible(predicate.clone()); + let mut pred_str = predicate.to_string(); + + if pred_str.len() > 50 { + // We don't need to save the type to a file, we will be talking about this type already + // in a separate note when we explain the obligation, so it will be available that way. + pred_str = predicate + .print(FmtPrinter::new_with_limit( + self.tcx, + Namespace::TypeNS, + rustc_session::Limit(6), + )) + .unwrap() + .into_buffer(); + } let mut err = struct_span_err!( self.tcx.sess, - obligation.cause.span, + span, E0275, "overflow evaluating the requirement `{}`", - predicate + pred_str, ); if suggest_increasing_limit { self.suggest_new_overflow_limit(&mut err); } - self.note_obligation_cause_code( - &mut err, - &obligation.predicate, - obligation.param_env, - obligation.cause.code(), - &mut vec![], - &mut Default::default(), - ); + mutate(&mut err); err.emit(); self.tcx.sess.abort_if_errors(); bug!(); } + /// Reports that an overflow has occurred and halts compilation. We + /// halt compilation unconditionally because it is important that + /// overflows never be masked -- they basically represent computations + /// whose result could not be truly determined and thus we can't say + /// if the program type checks or not -- and they are unusual + /// occurrences in any case. + fn report_overflow_obligation<T>( + &self, + obligation: &Obligation<'tcx, T>, + suggest_increasing_limit: bool, + ) -> ! + where + T: ToPredicate<'tcx> + Clone, + { + let predicate = obligation.predicate.clone().to_predicate(self.tcx); + let predicate = self.resolve_vars_if_possible(predicate); + self.report_overflow_error( + &predicate, + obligation.cause.span, + suggest_increasing_limit, + |err| { + self.note_obligation_cause_code( + err, + &predicate, + obligation.param_env, + obligation.cause.code(), + &mut vec![], + &mut Default::default(), + ); + }, + ); + } + fn suggest_new_overflow_limit(&self, err: &mut Diagnostic) { let suggested_limit = match self.tcx.recursion_limit() { Limit(0) => Limit(2), @@ -514,11 +560,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } /// Reports that a cycle was detected which led to overflow and halts - /// compilation. This is equivalent to `report_overflow_error` except + /// compilation. This is equivalent to `report_overflow_obligation` except /// that we can give a more helpful error message (and, in particular, /// we do not suggest increasing the overflow limit, which is not /// going to help). - fn report_overflow_error_cycle(&self, cycle: &[PredicateObligation<'tcx>]) -> ! { + fn report_overflow_obligation_cycle(&self, cycle: &[PredicateObligation<'tcx>]) -> ! { let cycle = self.resolve_vars_if_possible(cycle.to_owned()); assert!(!cycle.is_empty()); @@ -526,7 +572,10 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // The 'deepest' obligation is most likely to have a useful // cause 'backtrace' - self.report_overflow_error(cycle.iter().max_by_key(|p| p.recursion_depth).unwrap(), false); + self.report_overflow_obligation( + cycle.iter().max_by_key(|p| p.recursion_depth).unwrap(), + false, + ); } fn report_selection_error( @@ -534,22 +583,15 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { mut obligation: PredicateObligation<'tcx>, root_obligation: &PredicateObligation<'tcx>, error: &SelectionError<'tcx>, - fallback_has_occurred: bool, ) { - self.set_tainted_by_errors(); let tcx = self.tcx; let mut span = obligation.cause.span; + // FIXME: statically guarantee this by tainting after the diagnostic is emitted + self.set_tainted_by_errors( + tcx.sess.delay_span_bug(span, "`report_selection_error` did not emit an error"), + ); let mut err = match *error { - SelectionError::Ambiguous(ref impls) => { - let mut err = self.tcx.sess.struct_span_err( - obligation.cause.span, - &format!("multiple applicable `impl`s for `{}`", obligation.predicate), - ); - self.annotate_source_of_ambiguity(&mut err, impls, obligation.predicate); - err.emit(); - return; - } SelectionError::Unimplemented => { // If this obligation was generated as a result of well-formedness checking, see if we // can get a better error message by performing HIR-based well-formedness checking. @@ -582,7 +624,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let bound_predicate = obligation.predicate.kind(); match bound_predicate.skip_binder() { - ty::PredicateKind::Trait(trait_predicate) => { + ty::PredicateKind::Clause(ty::Clause::Trait(trait_predicate)) => { let trait_predicate = bound_predicate.rebind(trait_predicate); let mut trait_predicate = self.resolve_vars_if_possible(trait_predicate); @@ -663,40 +705,32 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { )) ); - if is_try_conversion { - let none_error = self - .tcx - .get_diagnostic_item(sym::none_error) - .map(|def_id| tcx.type_of(def_id)); - let should_convert_option_to_result = - Some(trait_ref.skip_binder().substs.type_at(1)) == none_error; - let should_convert_result_to_option = - Some(trait_ref.self_ty().skip_binder()) == none_error; - if should_convert_option_to_result { - err.span_suggestion_verbose( - span.shrink_to_lo(), - "consider converting the `Option<T>` into a `Result<T, _>` \ - using `Option::ok_or` or `Option::ok_or_else`", - ".ok_or_else(|| /* error value */)", - Applicability::HasPlaceholders, - ); - } else if should_convert_result_to_option { - err.span_suggestion_verbose( - span.shrink_to_lo(), - "consider converting the `Result<T, _>` into an `Option<T>` \ - using `Result::ok`", - ".ok()", - Applicability::MachineApplicable, - ); - } - if let Some(ret_span) = self.return_type_span(&obligation) { - err.span_label( - ret_span, - &format!( - "expected `{}` because of this", - trait_ref.skip_binder().self_ty() - ), - ); + if is_try_conversion && let Some(ret_span) = self.return_type_span(&obligation) { + err.span_label( + ret_span, + &format!( + "expected `{}` because of this", + trait_ref.skip_binder().self_ty() + ), + ); + } + + if Some(trait_ref.def_id()) == tcx.lang_items().tuple_trait() { + match obligation.cause.code().peel_derives() { + ObligationCauseCode::RustCall => { + err.set_primary_message("functions with the \"rust-call\" ABI must take a single non-self tuple argument"); + } + ObligationCauseCode::BindingObligation(def_id, _) + | ObligationCauseCode::ItemObligation(def_id) + if tcx.is_fn_trait(*def_id) => + { + err.code(rustc_errors::error_code!(E0059)); + err.set_primary_message(format!( + "type parameter to bare `{}` trait must be a tuple", + tcx.def_path_str(*def_id) + )); + } + _ => {} } } @@ -848,12 +882,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ); } - let is_fn_trait = [ - self.tcx.lang_items().fn_trait(), - self.tcx.lang_items().fn_mut_trait(), - self.tcx.lang_items().fn_once_trait(), - ] - .contains(&Some(trait_ref.def_id())); + let is_fn_trait = tcx.is_fn_trait(trait_ref.def_id()); let is_target_feature_fn = if let ty::FnDef(def_id, _) = *trait_ref.skip_binder().self_ty().kind() { @@ -883,7 +912,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // Note if the `FnMut` or `FnOnce` is less general than the trait we're trying // to implement. let selected_kind = - ty::ClosureKind::from_def_id(self.tcx, trait_ref.def_id()) + self.tcx.fn_trait_kind_from_def_id(trait_ref.def_id()) .expect("expected to map DefId to ClosureKind"); if !implemented_kind.extends(selected_kind) { err.note( @@ -974,7 +1003,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // useful for less general traits. if peeled && !self.tcx.trait_is_auto(def_id) - && !self.tcx.lang_items().items().contains(&Some(def_id)) + && !self.tcx.lang_items().iter().any(|(_, id)| id == def_id) { let trait_ref = trait_pred.to_poly_trait_ref(); let impl_candidates = @@ -1000,16 +1029,12 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // variable that used to fallback to `()` now falling back to `!`. Issue a // note informing about the change in behaviour. if trait_predicate.skip_binder().self_ty().is_never() - && fallback_has_occurred + && self.fallback_has_occurred { - let predicate = trait_predicate.map_bound(|mut trait_pred| { - trait_pred.trait_ref.substs = self.tcx.mk_substs_trait( - self.tcx.mk_unit(), - &trait_pred.trait_ref.substs[1..], - ); - trait_pred + let predicate = trait_predicate.map_bound(|trait_pred| { + trait_pred.with_self_type(self.tcx, self.tcx.mk_unit()) }); - let unit_obligation = obligation.with(predicate.to_predicate(tcx)); + let unit_obligation = obligation.with(tcx, predicate); if self.predicate_may_hold(&unit_obligation) { err.note( "this error might have been caused by changes to \ @@ -1062,9 +1087,9 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { span_bug!(span, "coerce requirement gave wrong error: `{:?}`", predicate) } - ty::PredicateKind::RegionOutlives(..) - | ty::PredicateKind::Projection(..) - | ty::PredicateKind::TypeOutlives(..) => { + ty::PredicateKind::Clause(ty::Clause::RegionOutlives(..)) + | ty::PredicateKind::Clause(ty::Clause::Projection(..)) + | ty::PredicateKind::Clause(ty::Clause::TypeOutlives(..)) => { let predicate = self.resolve_vars_if_possible(obligation.predicate); struct_span_err!( self.tcx.sess, @@ -1177,6 +1202,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ) } + ty::PredicateKind::Ambiguous => span_bug!(span, "ambiguous"), + ty::PredicateKind::TypeWellFormedFromEnv(..) => span_bug!( span, "TypeWellFormedFromEnv predicate should only exist in the environment" @@ -1260,13 +1287,14 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { obligation.cause.code(), ) } else { - let (closure_span, found) = found_did + let (closure_span, closure_arg_span, found) = found_did .and_then(|did| { let node = self.tcx.hir().get_if_local(did)?; - let (found_span, found) = self.get_fn_like_arguments(node)?; - Some((Some(found_span), found)) + let (found_span, closure_arg_span, found) = + self.get_fn_like_arguments(node)?; + Some((Some(found_span), closure_arg_span, found)) }) - .unwrap_or((found_span, found)); + .unwrap_or((found_span, None, found)); self.report_arg_count_mismatch( span, @@ -1274,6 +1302,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { expected, found, found_trait_ty.is_closure(), + closure_arg_span, ) } } @@ -1366,7 +1395,6 @@ trait InferCtxtPrivExt<'tcx> { &self, error: &FulfillmentError<'tcx>, body_id: Option<hir::BodyId>, - fallback_has_occurred: bool, ); fn report_projection_error( @@ -1483,9 +1511,10 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // FIXME: It should be possible to deal with `ForAll` in a cleaner way. let bound_error = error.kind(); let (cond, error) = match (cond.kind().skip_binder(), bound_error.skip_binder()) { - (ty::PredicateKind::Trait(..), ty::PredicateKind::Trait(error)) => { - (cond, bound_error.rebind(error)) - } + ( + ty::PredicateKind::Clause(ty::Clause::Trait(..)), + ty::PredicateKind::Clause(ty::Clause::Trait(error)), + ) => (cond, bound_error.rebind(error)), _ => { // FIXME: make this work in other cases too. return false; @@ -1494,7 +1523,9 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { for obligation in super::elaborate_predicates(self.tcx, std::iter::once(cond)) { let bound_predicate = obligation.predicate.kind(); - if let ty::PredicateKind::Trait(implication) = bound_predicate.skip_binder() { + if let ty::PredicateKind::Clause(ty::Clause::Trait(implication)) = + bound_predicate.skip_binder() + { let error = error.to_poly_trait_ref(); let implication = bound_predicate.rebind(implication.trait_ref); // FIXME: I'm just not taking associated types at all here. @@ -1516,7 +1547,6 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { &self, error: &FulfillmentError<'tcx>, body_id: Option<hir::BodyId>, - fallback_has_occurred: bool, ) { match error.code { FulfillmentErrorCode::CodeSelectionError(ref selection_error) => { @@ -1524,7 +1554,6 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { error.obligation.clone(), &error.root_obligation, selection_error, - fallback_has_occurred, ); } FulfillmentErrorCode::CodeProjectionError(ref e) => { @@ -1567,7 +1596,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { diag.emit(); } FulfillmentErrorCode::CodeCycle(ref cycle) => { - self.report_overflow_error_cycle(cycle); + self.report_overflow_obligation_cycle(cycle); } } } @@ -1585,29 +1614,26 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } self.probe(|_| { - let mut err = error.err; - let mut values = None; + let ocx = ObligationCtxt::new_in_snapshot(self); // try to find the mismatched types to report the error with. // // this can fail if the problem was higher-ranked, in which // cause I have no idea for a good error message. let bound_predicate = predicate.kind(); - if let ty::PredicateKind::Projection(data) = bound_predicate.skip_binder() { - let mut selcx = SelectionContext::new(self); + let (values, err) = if let ty::PredicateKind::Clause(ty::Clause::Projection(data)) = + bound_predicate.skip_binder() + { let data = self.replace_bound_vars_with_fresh_vars( obligation.cause.span, infer::LateBoundRegionConversionTime::HigherRankedType, bound_predicate.rebind(data), ); - let mut obligations = vec![]; - let normalized_ty = super::normalize_projection_type( - &mut selcx, + let normalized_ty = ocx.normalize( + &obligation.cause, obligation.param_env, - data.projection_ty, - obligation.cause.clone(), - 0, - &mut obligations, + self.tcx + .mk_projection(data.projection_ty.item_def_id, data.projection_ty.substs), ); debug!(?obligation.cause, ?obligation.param_env); @@ -1623,25 +1649,40 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { | ObligationCauseCode::ObjectCastObligation(..) | ObligationCauseCode::OpaqueType ); - if let Err(new_err) = self.at(&obligation.cause, obligation.param_env).eq_exp( + let expected_ty = data.term.ty().unwrap_or_else(|| self.tcx.ty_error()); + + // constrain inference variables a bit more to nested obligations from normalize so + // we can have more helpful errors. + ocx.select_where_possible(); + + if let Err(new_err) = ocx.eq_exp( + &obligation.cause, + obligation.param_env, is_normalized_ty_expected, normalized_ty, - data.term, + expected_ty, ) { - values = Some((data, is_normalized_ty_expected, normalized_ty, data.term)); - err = new_err; + (Some((data, is_normalized_ty_expected, normalized_ty, expected_ty)), new_err) + } else { + (None, error.err) } - } + } else { + (None, error.err) + }; let msg = values .and_then(|(predicate, _, normalized_ty, expected_ty)| { - self.maybe_detailed_projection_msg(predicate, normalized_ty, expected_ty) + self.maybe_detailed_projection_msg( + predicate, + normalized_ty.into(), + expected_ty.into(), + ) }) .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 + ty::PredicateKind::Clause(ty::Clause::Projection(proj)) => self .tcx .opt_associated_item(proj.projection_ty.item_def_id) .and_then(|trait_assoc_item| { @@ -1677,11 +1718,11 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { &mut diag, &obligation.cause, secondary_span, - values.map(|(_, is_normalized_ty_expected, normalized_ty, term)| { + values.map(|(_, is_normalized_ty_expected, normalized_ty, expected_ty)| { infer::ValuePairs::Terms(ExpectedFound::new( is_normalized_ty_expected, - normalized_ty, - term, + normalized_ty.into(), + expected_ty.into(), )) }), err, @@ -1733,7 +1774,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ty::Bool => Some(0), ty::Char => Some(1), ty::Str => Some(2), - ty::Adt(def, _) if tcx.is_diagnostic_item(sym::String, def.did()) => Some(2), + ty::Adt(def, _) if Some(def.did()) == tcx.lang_items().string() => Some(2), ty::Int(..) | ty::Uint(..) | ty::Float(..) @@ -1811,7 +1852,8 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { &self, trait_pred: ty::PolyTraitPredicate<'tcx>, ) -> Vec<ImplCandidate<'tcx>> { - self.tcx + let mut candidates: Vec<_> = self + .tcx .all_impls(trait_pred.def_id()) .filter_map(|def_id| { if self.tcx.impl_polarity(def_id) == ty::ImplPolarity::Negative @@ -1827,7 +1869,14 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { self.fuzzy_match_tys(trait_pred.skip_binder().self_ty(), imp.self_ty(), false) .map(|similarity| ImplCandidate { trait_ref: imp, similarity }) }) - .collect() + .collect(); + if candidates.iter().any(|c| matches!(c.similarity, CandidateSimilarity::Exact { .. })) { + // If any of the candidates is a perfect match, we don't want to show all of them. + // This is particularly relevant for the case of numeric types (as they all have the + // same cathegory). + candidates.retain(|c| matches!(c.similarity, CandidateSimilarity::Exact { .. })); + } + candidates } fn report_similar_impl_candidates( @@ -1898,7 +1947,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let def_id = trait_ref.def_id(); if impl_candidates.is_empty() { if self.tcx.trait_is_auto(def_id) - || self.tcx.lang_items().items().contains(&Some(def_id)) + || self.tcx.lang_items().iter().any(|(_, id)| id == def_id) || self.tcx.get_diagnostic_name(def_id).is_some() { // Mentioning implementers of `Copy`, `Debug` and friends is not useful. @@ -1935,14 +1984,6 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { return report(normalized_impl_candidates, err); } - let normalize = |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. // because the ordering of `impl_candidates` may not be deterministic: // https://github.com/rust-lang/rust/pull/57475#issuecomment-455519507 @@ -1952,7 +1993,11 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let mut normalized_impl_candidates_and_similarities = impl_candidates .into_iter() .map(|ImplCandidate { trait_ref, similarity }| { - let normalized = normalize(trait_ref); + // FIXME(compiler-errors): This should be using `NormalizeExt::normalize` + let normalized = self + .at(&ObligationCause::dummy(), ty::ParamEnv::empty()) + .query_normalize(trait_ref) + .map_or(trait_ref, |normalized| normalized.value); (similarity, normalized) }) .collect::<Vec<_>>(); @@ -2032,15 +2077,10 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { param_env: ty::ParamEnv<'tcx>, trait_ref_and_ty: ty::Binder<'tcx, (ty::TraitPredicate<'tcx>, Ty<'tcx>)>, ) -> PredicateObligation<'tcx> { - let trait_pred = trait_ref_and_ty.map_bound_ref(|(tr, new_self_ty)| ty::TraitPredicate { - trait_ref: ty::TraitRef { - substs: self.tcx.mk_substs_trait(*new_self_ty, &tr.trait_ref.substs[1..]), - ..tr.trait_ref - }, - ..*tr - }); + let trait_pred = trait_ref_and_ty + .map_bound(|(tr, new_self_ty)| tr.with_self_type(self.tcx, new_self_ty)); - Obligation::new(ObligationCause::dummy(), param_env, trait_pred.to_predicate(self.tcx)) + Obligation::new(self.tcx, ObligationCause::dummy(), param_env, trait_pred) } #[instrument(skip(self), level = "debug")] @@ -2064,7 +2104,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let bound_predicate = predicate.kind(); let mut err = match bound_predicate.skip_binder() { - ty::PredicateKind::Trait(data) => { + ty::PredicateKind::Clause(ty::Clause::Trait(data)) => { let trait_ref = bound_predicate.rebind(data.trait_ref); debug!(?trait_ref); @@ -2088,7 +2128,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // check upstream for type errors and don't add the obligations to // begin with in those cases. if self.tcx.lang_items().sized_trait() == Some(trait_ref.def_id()) { - if !self.is_tainted_by_errors() { + if let None = self.tainted_by_errors() { self.emit_inference_failure_err( body_id, span, @@ -2128,21 +2168,27 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ) }; - let obligation = Obligation::new( - obligation.cause.clone(), - obligation.param_env, - trait_ref.to_poly_trait_predicate(), - ); - let mut selcx = SelectionContext::with_query_mode( - &self, - crate::traits::TraitQueryMode::Standard, - ); + let obligation = obligation.with(self.tcx, trait_ref); + let mut selcx = SelectionContext::new(&self); match selcx.select_from_obligation(&obligation) { - Err(SelectionError::Ambiguous(impls)) if impls.len() > 1 => { - self.annotate_source_of_ambiguity(&mut err, &impls, predicate); + Ok(None) => { + let impls = ambiguity::recompute_applicable_impls(self.infcx, &obligation); + let has_non_region_infer = + trait_ref.skip_binder().substs.types().any(|t| !t.is_ty_infer()); + // It doesn't make sense to talk about applicable impls if there are more + // than a handful of them. + if impls.len() > 1 && impls.len() < 5 && has_non_region_infer { + self.annotate_source_of_ambiguity(&mut err, &impls, predicate); + } else { + if self.tainted_by_errors().is_some() { + err.cancel(); + return; + } + err.note(&format!("cannot satisfy `{}`", predicate)); + } } _ => { - if self.is_tainted_by_errors() { + if self.tainted_by_errors().is_some() { err.cancel(); return; } @@ -2160,7 +2206,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { if generics.params.iter().any(|p| p.name != kw::SelfUpper) && !snippet.ends_with('>') && !generics.has_impl_trait() - && !self.tcx.fn_trait_kind_from_lang_item(def_id).is_some() + && !self.tcx.is_fn_trait(def_id) { // FIXME: To avoid spurious suggestions in functions where type arguments // where already supplied, we check the snippet to make sure it doesn't @@ -2245,7 +2291,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ] = path.segments && data.trait_ref.def_id == *trait_id && self.tcx.trait_of_item(*item_id) == Some(*trait_id) - && !self.is_tainted_by_errors() + && let None = self.tainted_by_errors() { let (verb, noun) = match self.tcx.associated_item(item_id).kind { ty::AssocKind::Const => ("refer to the", "constant"), @@ -2314,7 +2360,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // with error messages. if arg.references_error() || self.tcx.sess.has_errors().is_some() - || self.is_tainted_by_errors() + || self.tainted_by_errors().is_some() { return; } @@ -2325,7 +2371,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ty::PredicateKind::Subtype(data) => { if data.references_error() || self.tcx.sess.has_errors().is_some() - || self.is_tainted_by_errors() + || self.tainted_by_errors().is_some() { // no need to overload user in such cases return; @@ -2335,8 +2381,8 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { assert!(a.is_ty_var() && b.is_ty_var()); self.emit_inference_failure_err(body_id, span, a.into(), ErrorCode::E0282, true) } - ty::PredicateKind::Projection(data) => { - if predicate.references_error() || self.is_tainted_by_errors() { + ty::PredicateKind::Clause(ty::Clause::Projection(data)) => { + if predicate.references_error() || self.tainted_by_errors().is_some() { return; } let subst = data @@ -2370,7 +2416,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } ty::PredicateKind::ConstEvaluatable(data) => { - if predicate.references_error() || self.is_tainted_by_errors() { + if predicate.references_error() || self.tainted_by_errors().is_some() { return; } let subst = data.walk().find(|g| g.is_non_region_infer()); @@ -2397,7 +2443,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } } _ => { - if self.tcx.sess.has_errors().is_some() || self.is_tainted_by_errors() { + if self.tcx.sess.has_errors().is_some() || self.tainted_by_errors().is_some() { return; } let mut err = struct_span_err!( @@ -2435,14 +2481,13 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } } } - let msg = format!("multiple `impl`s satisfying `{}` found", predicate); let mut crate_names: Vec<_> = crates.iter().map(|n| format!("`{}`", n)).collect(); crate_names.sort(); crate_names.dedup(); post.sort(); post.dedup(); - if self.is_tainted_by_errors() + if self.tainted_by_errors().is_some() && (crate_names.len() == 1 && spans.len() == 0 && ["`core`", "`alloc`", "`std`"].contains(&crate_names[0].as_str()) @@ -2456,13 +2501,9 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { err.downgrade_to_delayed_bug(); return; } - let post = if post.len() > 4 { - format!( - ":\n{}\nand {} more", - post.iter().map(|p| format!("- {}", p)).take(4).collect::<Vec<_>>().join("\n"), - post.len() - 4, - ) - } else if post.len() > 1 || (post.len() == 1 && post[0].contains('\n')) { + + let msg = format!("multiple `impl`s satisfying `{}` found", predicate); + let post = if post.len() > 1 || (post.len() == 1 && post[0].contains('\n')) { format!(":\n{}", post.iter().map(|p| format!("- {}", p)).collect::<Vec<_>>().join("\n"),) } else if post.len() == 1 { format!(": `{}`", post[0]) @@ -2541,24 +2582,14 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } self.probe(|_| { - let mut selcx = SelectionContext::new(self); - let cleaned_pred = pred.fold_with(&mut ParamToVarFolder { infcx: self, var_map: Default::default() }); - let cleaned_pred = super::project::normalize( - &mut selcx, - param_env, - ObligationCause::dummy(), - cleaned_pred, - ) - .value; - - let obligation = Obligation::new( - ObligationCause::dummy(), - param_env, - cleaned_pred.to_predicate(selcx.tcx()), - ); + let InferOk { value: cleaned_pred, .. } = + self.infcx.at(&ObligationCause::dummy(), param_env).normalize(cleaned_pred); + + let obligation = + Obligation::new(self.tcx, ObligationCause::dummy(), param_env, cleaned_pred); self.predicate_may_hold(&obligation) }) @@ -2586,7 +2617,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { err: &mut Diagnostic, obligation: &PredicateObligation<'tcx>, ) { - let ty::PredicateKind::Trait(pred) = obligation.predicate.kind().skip_binder() else { return; }; + let ty::PredicateKind::Clause(ty::Clause::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; }; @@ -2612,11 +2643,10 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let Some(param) = generics.params.iter().find(|param| param.span == span) else { return; }; - let param_def_id = self.tcx.hir().local_def_id(param.hir_id); // Check that none of the explicit trait bounds is `Sized`. Assume that an explicit // `Sized` bound is there intentionally and we don't need to suggest relaxing it. let explicitly_sized = generics - .bounds_for_param(param_def_id) + .bounds_for_param(param.def_id) .flat_map(|bp| bp.bounds) .any(|bound| bound.trait_ref().and_then(|tr| tr.trait_def_id()) == sized_trait); if explicitly_sized { @@ -2639,7 +2669,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { _ => {} }; // Didn't add an indirection suggestion, so add a general suggestion to relax `Sized`. - let (span, separator) = if let Some(s) = generics.bounds_span_for_suggestions(param_def_id) + let (span, separator) = if let Some(s) = generics.bounds_span_for_suggestions(param.def_id) { (s, " +") } else { 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 5eef54c63..9bfe52764 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,14 +1,22 @@ -use super::{ - ObligationCauseCode, OnUnimplementedDirective, OnUnimplementedNote, PredicateObligation, -}; +use super::{ObligationCauseCode, PredicateObligation}; use crate::infer::error_reporting::TypeErrCtxt; +use rustc_ast::{MetaItem, NestedMetaItem}; +use rustc_attr as attr; +use rustc_data_structures::fx::FxHashMap; +use rustc_errors::{struct_span_err, ErrorGuaranteed}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_middle::ty::SubstsRef; -use rustc_middle::ty::{self, GenericParamDefKind}; -use rustc_span::symbol::sym; +use rustc_middle::ty::{self, GenericParamDefKind, TyCtxt}; +use rustc_parse_format::{ParseMode, Parser, Piece, Position}; +use rustc_span::symbol::{kw, sym, Symbol}; +use rustc_span::{Span, DUMMY_SP}; use std::iter; +use crate::errors::{ + EmptyOnClauseInOnUnimplemented, InvalidOnClauseInOnUnimplemented, NoValueInOnUnimplemented, +}; + use super::InferCtxtPrivExt; pub trait TypeErrCtxtExt<'tcx> { @@ -276,3 +284,383 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } } } + +#[derive(Clone, Debug)] +pub struct OnUnimplementedFormatString(Symbol); + +#[derive(Debug)] +pub struct OnUnimplementedDirective { + pub condition: Option<MetaItem>, + pub subcommands: Vec<OnUnimplementedDirective>, + pub message: Option<OnUnimplementedFormatString>, + pub label: Option<OnUnimplementedFormatString>, + pub note: Option<OnUnimplementedFormatString>, + pub parent_label: Option<OnUnimplementedFormatString>, + pub append_const_msg: Option<Option<Symbol>>, +} + +/// For the `#[rustc_on_unimplemented]` attribute +#[derive(Default)] +pub struct OnUnimplementedNote { + pub message: Option<String>, + pub label: Option<String>, + pub note: 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 + /// default one.. + pub append_const_msg: Option<Option<Symbol>>, +} + +impl<'tcx> OnUnimplementedDirective { + fn parse( + tcx: TyCtxt<'tcx>, + item_def_id: DefId, + items: &[NestedMetaItem], + span: Span, + is_root: bool, + ) -> Result<Self, ErrorGuaranteed> { + let mut errored = None; + let mut item_iter = items.iter(); + + let parse_value = |value_str| { + OnUnimplementedFormatString::try_parse(tcx, item_def_id, value_str, span).map(Some) + }; + + let condition = if is_root { + None + } else { + let cond = item_iter + .next() + .ok_or_else(|| tcx.sess.emit_err(EmptyOnClauseInOnUnimplemented { span }))? + .meta_item() + .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); + } + true + }); + Some(cond.clone()) + }; + + let mut message = None; + let mut label = None; + let mut note = None; + let mut parent_label = None; + let mut subcommands = vec![]; + let mut append_const_msg = None; + + for item in item_iter { + if item.has_name(sym::message) && message.is_none() { + if let Some(message_) = item.value_str() { + message = parse_value(message_)?; + continue; + } + } else if item.has_name(sym::label) && label.is_none() { + if let Some(label_) = item.value_str() { + label = parse_value(label_)?; + continue; + } + } else if item.has_name(sym::note) && note.is_none() { + if let Some(note_) = item.value_str() { + note = parse_value(note_)?; + continue; + } + } 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) + && is_root + && message.is_none() + && label.is_none() + && note.is_none() + { + if let Some(items) = item.meta_item_list() { + match Self::parse(tcx, item_def_id, &items, item.span(), false) { + Ok(subcommand) => subcommands.push(subcommand), + Err(reported) => errored = Some(reported), + }; + continue; + } + } else if item.has_name(sym::append_const_msg) && append_const_msg.is_none() { + if let Some(msg) = item.value_str() { + append_const_msg = Some(Some(msg)); + continue; + } else if item.is_word() { + append_const_msg = Some(None); + continue; + } + } + + // nothing found + tcx.sess.emit_err(NoValueInOnUnimplemented { span: item.span() }); + } + + if let Some(reported) = errored { + Err(reported) + } else { + Ok(OnUnimplementedDirective { + condition, + subcommands, + message, + label, + note, + parent_label, + append_const_msg, + }) + } + } + + pub fn of_item(tcx: TyCtxt<'tcx>, item_def_id: DefId) -> Result<Option<Self>, ErrorGuaranteed> { + let Some(attr) = tcx.get_attr(item_def_id, sym::rustc_on_unimplemented) else { + return Ok(None); + }; + + let result = if let Some(items) = attr.meta_item_list() { + Self::parse(tcx, item_def_id, &items, attr.span, true).map(Some) + } else if let Some(value) = attr.value_str() { + Ok(Some(OnUnimplementedDirective { + condition: None, + message: None, + subcommands: vec![], + label: Some(OnUnimplementedFormatString::try_parse( + tcx, + item_def_id, + value, + attr.span, + )?), + note: None, + parent_label: None, + append_const_msg: None, + })) + } else { + let reported = + tcx.sess.delay_span_bug(DUMMY_SP, "of_item: neither meta_item_list nor value_str"); + return Err(reported); + }; + debug!("of_item({:?}) = {:?}", item_def_id, result); + result + } + + pub fn evaluate( + &self, + tcx: TyCtxt<'tcx>, + trait_ref: ty::TraitRef<'tcx>, + options: &[(Symbol, Option<String>)], + ) -> OnUnimplementedNote { + let mut message = None; + let mut label = None; + let mut note = None; + let mut parent_label = None; + let mut append_const_msg = None; + info!("evaluate({:?}, trait_ref={:?}, options={:?})", self, trait_ref, options); + + let options_map: FxHashMap<Symbol, String> = + options.iter().filter_map(|(k, v)| v.clone().map(|v| (*k, v))).collect(); + + for command in self.subcommands.iter().chain(Some(self)).rev() { + if let Some(ref condition) = command.condition && !attr::eval_condition( + condition, + &tcx.sess.parse_sess, + Some(tcx.features()), + &mut |cfg| { + let value = cfg.value.map(|v| { + OnUnimplementedFormatString(v).format(tcx, trait_ref, &options_map) + }); + + options.contains(&(cfg.name, value)) + }, + ) { + debug!("evaluate: skipping {:?} due to condition", command); + continue; + } + debug!("evaluate: {:?} succeeded", command); + if let Some(ref message_) = command.message { + message = Some(message_.clone()); + } + + if let Some(ref label_) = command.label { + label = Some(label_.clone()); + } + + if let Some(ref note_) = command.note { + note = Some(note_.clone()); + } + + if let Some(ref parent_label_) = command.parent_label { + parent_label = Some(parent_label_.clone()); + } + + append_const_msg = command.append_const_msg; + } + + OnUnimplementedNote { + 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)), + parent_label: parent_label.map(|e_s| e_s.format(tcx, trait_ref, &options_map)), + append_const_msg, + } + } +} + +impl<'tcx> OnUnimplementedFormatString { + fn try_parse( + tcx: TyCtxt<'tcx>, + item_def_id: DefId, + from: Symbol, + err_sp: Span, + ) -> Result<Self, ErrorGuaranteed> { + let result = OnUnimplementedFormatString(from); + result.verify(tcx, item_def_id, err_sp)?; + Ok(result) + } + + fn verify( + &self, + tcx: TyCtxt<'tcx>, + item_def_id: DefId, + span: Span, + ) -> Result<(), ErrorGuaranteed> { + let trait_def_id = if tcx.is_trait(item_def_id) { + item_def_id + } else { + tcx.trait_id_of_impl(item_def_id) + .expect("expected `on_unimplemented` to correspond to a trait") + }; + let trait_name = tcx.item_name(trait_def_id); + let generics = tcx.generics_of(item_def_id); + let s = self.0.as_str(); + let parser = Parser::new(s, None, None, false, ParseMode::Format); + let mut result = Ok(()); + for token in parser { + match token { + Piece::String(_) => (), // Normal string, no need to check it + Piece::NextArgument(a) => match a.position { + Position::ArgumentNamed(s) => { + match Symbol::intern(s) { + // `{Self}` is allowed + kw::SelfUpper => (), + // `{ThisTraitsName}` is allowed + s if s == trait_name => (), + // `{from_method}` is allowed + sym::from_method => (), + // `{from_desugaring}` is allowed + sym::from_desugaring => (), + // `{ItemContext}` is allowed + sym::ItemContext => (), + // `{integral}` and `{integer}` and `{float}` are allowed + sym::integral | sym::integer_ | sym::float => (), + // So is `{A}` if A is a type parameter + s => match generics.params.iter().find(|param| param.name == s) { + Some(_) => (), + None => { + let reported = struct_span_err!( + tcx.sess, + span, + E0230, + "there is no parameter `{}` on {}", + s, + if trait_def_id == item_def_id { + format!("trait `{}`", trait_name) + } else { + "impl".to_string() + } + ) + .emit(); + result = Err(reported); + } + }, + } + } + // `{:1}` and `{}` are not to be used + Position::ArgumentIs(..) | Position::ArgumentImplicitlyIs(_) => { + let reported = struct_span_err!( + tcx.sess, + span, + E0231, + "only named substitution parameters are allowed" + ) + .emit(); + result = Err(reported); + } + }, + } + } + + result + } + + pub fn format( + &self, + tcx: TyCtxt<'tcx>, + trait_ref: ty::TraitRef<'tcx>, + options: &FxHashMap<Symbol, String>, + ) -> String { + let name = tcx.item_name(trait_ref.def_id); + let trait_str = tcx.def_path_str(trait_ref.def_id); + let generics = tcx.generics_of(trait_ref.def_id); + let generic_map = generics + .params + .iter() + .filter_map(|param| { + let value = match param.kind { + GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => { + trait_ref.substs[param.index as usize].to_string() + } + GenericParamDefKind::Lifetime => return None, + }; + let name = param.name; + Some((name, value)) + }) + .collect::<FxHashMap<Symbol, String>>(); + let empty_string = String::new(); + + let s = self.0.as_str(); + let parser = Parser::new(s, None, None, false, ParseMode::Format); + let item_context = (options.get(&sym::ItemContext)).unwrap_or(&empty_string); + parser + .map(|p| match p { + Piece::String(s) => s, + Piece::NextArgument(a) => match a.position { + Position::ArgumentNamed(s) => { + let s = Symbol::intern(s); + match generic_map.get(&s) { + Some(val) => val, + None if s == name => &trait_str, + None => { + if let Some(val) = options.get(&s) { + val + } else if s == sym::from_desugaring || s == sym::from_method { + // don't break messages using these two arguments incorrectly + &empty_string + } else if s == sym::ItemContext { + &item_context + } else if s == sym::integral { + "{integral}" + } else if s == sym::integer_ { + "{integer}" + } else if s == sym::float { + "{float}" + } else { + bug!( + "broken on_unimplemented {:?} for {:?}: \ + no argument matching {:?}", + self.0, + trait_ref, + s + ) + } + } + } + } + _ => bug!("broken on_unimplemented {:?} - bad format arg", self.0), + }, + }) + .collect() + } +} 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 8c41d9d24..6ea54b625 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -1,11 +1,8 @@ -use super::{ - DefIdOrName, Obligation, ObligationCause, ObligationCauseCode, PredicateObligation, - SelectionContext, -}; +use super::{DefIdOrName, Obligation, ObligationCause, ObligationCauseCode, PredicateObligation}; use crate::autoderef::Autoderef; use crate::infer::InferCtxt; -use crate::traits::normalize_to; +use crate::traits::NormalizeExt; use hir::def::CtorOf; use hir::HirId; @@ -23,7 +20,7 @@ use rustc_hir::lang_items::LangItem; use rustc_hir::{AsyncGeneratorKind, GeneratorKind, Node}; use rustc_infer::infer::error_reporting::TypeErrCtxt; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use rustc_infer::infer::LateBoundRegionConversionTime; +use rustc_infer::infer::{InferOk, LateBoundRegionConversionTime}; use rustc_middle::hir::map; use rustc_middle::ty::{ self, suggest_arbitrary_trait_bound, suggest_constraining_type_param, AdtKind, DefIdTree, @@ -44,7 +41,7 @@ use rustc_middle::ty::print::with_no_trimmed_paths; #[derive(Debug)] pub enum GeneratorInteriorOrUpvar { // span of interior type - Interior(Span), + Interior(Span, Option<(Option<Span>, Span, Option<hir::HirId>, Option<Span>)>), // span of upvar Upvar(Span), } @@ -283,7 +280,6 @@ pub trait TypeErrCtxtExt<'tcx> { &self, err: &mut Diagnostic, interior_or_upvar_span: GeneratorInteriorOrUpvar, - interior_extra_info: Option<(Option<Span>, Span, Option<hir::HirId>, Option<Span>)>, is_async: bool, outer_generator: Option<DefId>, trait_pred: ty::TraitPredicate<'tcx>, @@ -302,7 +298,7 @@ pub trait TypeErrCtxtExt<'tcx> { obligated_types: &mut Vec<Ty<'tcx>>, seen_requirements: &mut FxHashSet<DefId>, ) where - T: fmt::Display; + T: fmt::Display + ToPredicate<'tcx>; /// Suggest to await before try: future? => future.await? fn suggest_await_before_try( @@ -335,7 +331,7 @@ pub trait TypeErrCtxtExt<'tcx> { ); } -fn predicate_constraint(generics: &hir::Generics<'_>, pred: String) -> (Span, String) { +fn predicate_constraint(generics: &hir::Generics<'_>, pred: ty::Predicate<'_>) -> (Span, String) { ( generics.tail_span_for_predicate_suggestion(), format!("{} {}", generics.add_where_or_trailing_comma(), pred), @@ -417,7 +413,7 @@ fn suggest_restriction<'tcx>( }, // `fn foo(t: impl Trait)` // ^ suggest `where <T as Trait>::A: Bound` - predicate_constraint(hir_generics, trait_pred.to_predicate(tcx).to_string()), + predicate_constraint(hir_generics, trait_pred.to_predicate(tcx)), ]; sugg.extend(ty_spans.into_iter().map(|s| (s, type_param_name.to_string()))); @@ -441,9 +437,7 @@ fn suggest_restriction<'tcx>( .find(|p| !matches!(p.kind, hir::GenericParamKind::Type { synthetic: true, .. })), super_traits, ) { - (_, None) => { - predicate_constraint(hir_generics, trait_pred.to_predicate(tcx).to_string()) - } + (_, None) => predicate_constraint(hir_generics, trait_pred.to_predicate(tcx)), (None, Some((ident, []))) => ( ident.span.shrink_to_hi(), format!(": {}", trait_pred.print_modifiers_and_trait_path()), @@ -714,7 +708,6 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { obligation.cause.body_id, span, base_ty, - span, ); if let Some(steps) = autoderef.find_map(|(ty, steps)| { // Re-add the `&` @@ -814,7 +807,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { err: &mut Diagnostic, trait_pred: ty::PolyTraitPredicate<'tcx>, ) -> bool { - if let ty::PredicateKind::Trait(trait_pred) = obligation.predicate.kind().skip_binder() + if let ty::PredicateKind::Clause(ty::Clause::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 @@ -843,7 +836,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } 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() + if let ty::PredicateKind::Clause(ty::Clause::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() @@ -877,7 +870,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } ty::Param(_) => { obligation.param_env.caller_bounds().iter().find_map(|pred| { - if let ty::PredicateKind::Projection(proj) = pred.kind().skip_binder() + if let ty::PredicateKind::Clause(ty::Clause::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] @@ -1019,7 +1012,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let mut never_suggest_borrow: Vec<_> = [LangItem::Copy, LangItem::Clone, LangItem::Unpin, LangItem::Sized] .iter() - .filter_map(|lang_item| self.tcx.lang_items().require(*lang_item).ok()) + .filter_map(|lang_item| self.tcx.lang_items().get(*lang_item)) .collect(); if let Some(def_id) = self.tcx.get_diagnostic_item(sym::Send) { @@ -1063,7 +1056,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { { ( mk_result(old_pred.map_bound(|trait_pred| (trait_pred, *ty))), - matches!(mutability, hir::Mutability::Mut), + mutability.is_mut(), ) } else { (false, false) @@ -1117,7 +1110,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { err.span_suggestions( span.shrink_to_lo(), "consider borrowing here", - ["&".to_string(), "&mut ".to_string()].into_iter(), + ["&".to_string(), "&mut ".to_string()], Applicability::MaybeIncorrect, ); } else { @@ -1164,7 +1157,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { for predicate in predicates.iter() { if !self.predicate_must_hold_modulo_regions( - &obligation.with(predicate.with_self_ty(self.tcx, self_ref_ty)), + &obligation.with(self.tcx, predicate.with_self_ty(self.tcx, self_ref_ty)), ) { return; } @@ -1260,7 +1253,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ); // FIXME: account for associated `async fn`s. if let hir::Expr { span, kind: hir::ExprKind::Call(base, _), .. } = expr { - if let ty::PredicateKind::Trait(pred) = + if let ty::PredicateKind::Clause(ty::Clause::Trait(pred)) = obligation.predicate.kind().skip_binder() { err.span_label( @@ -1353,7 +1346,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { .sess .source_map() .span_take_while(span, |c| c.is_whitespace() || *c == '&'); - if points_at_arg && mutability == hir::Mutability::Not && refs_number > 0 { + if points_at_arg && mutability.is_not() && refs_number > 0 { err.span_suggestion_verbose( sp, "consider changing this borrow's mutability", @@ -1525,7 +1518,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let self_ty_satisfies_dyn_predicates = |self_ty| { predicates.iter().all(|predicate| { let pred = predicate.with_self_ty(self.tcx, self_ty); - let obl = Obligation::new(cause.clone(), param_env, pred); + let obl = Obligation::new(self.tcx, cause.clone(), param_env, pred); self.predicate_may_hold(&obl) }) }; @@ -1688,9 +1681,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ) -> Ty<'tcx> { let inputs = trait_ref.skip_binder().substs.type_at(1); let sig = match inputs.kind() { - ty::Tuple(inputs) - if infcx.tcx.fn_trait_kind_from_lang_item(trait_ref.def_id()).is_some() => - { + ty::Tuple(inputs) if infcx.tcx.is_fn_trait(trait_ref.def_id()) => { infcx.tcx.mk_fn_sig( inputs.iter(), infcx.next_ty_var(TypeVariableOrigin { @@ -1760,8 +1751,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { 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 ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred)) = pred.kind().skip_binder() + && self.tcx.is_fn_trait(trait_pred.def_id()) { let expected_self = self.tcx.anonymize_late_bound_regions(pred.kind().rebind(trait_pred.self_ty())); @@ -1774,9 +1765,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { 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() + ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred)) + if self.tcx.is_fn_trait(trait_pred.def_id()) && other_idx != idx // Make sure that the self type matches // (i.e. constraining this closure) @@ -1890,13 +1880,9 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // // - `BuiltinDerivedObligation` with a generator witness (B) // - `BuiltinDerivedObligation` with a generator (B) - // - `BuiltinDerivedObligation` with `std::future::GenFuture` (B) - // - `BuiltinDerivedObligation` with `impl std::future::Future` (B) // - `BuiltinDerivedObligation` with `impl std::future::Future` (B) // - `BuiltinDerivedObligation` with a generator witness (A) // - `BuiltinDerivedObligation` with a generator (A) - // - `BuiltinDerivedObligation` with `std::future::GenFuture` (A) - // - `BuiltinDerivedObligation` with `impl std::future::Future` (A) // - `BuiltinDerivedObligation` with `impl std::future::Future` (A) // - `BindingObligation` with `impl_send (Send requirement) // @@ -1905,7 +1891,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // bound was introduced. At least one generator should be present for this diagnostic to be // modified. let (mut trait_ref, mut target_ty) = match obligation.predicate.kind().skip_binder() { - ty::PredicateKind::Trait(p) => (Some(p), Some(p.self_ty())), + ty::PredicateKind::Clause(ty::Clause::Trait(p)) => (Some(p), Some(p.self_ty())), _ => (None, None), }; let mut generator = None; @@ -2004,17 +1990,6 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { .as_local() .and_then(|def_id| hir.maybe_body_owned_by(def_id)) .map(|body_id| hir.body(body_id)); - let is_async = match generator_did.as_local() { - Some(_) => generator_body - .and_then(|body| body.generator_kind()) - .map(|generator_kind| matches!(generator_kind, hir::GeneratorKind::Async(..))) - .unwrap_or(false), - None => self - .tcx - .generator_kind(generator_did) - .map(|generator_kind| matches!(generator_kind, hir::GeneratorKind::Async(..))) - .unwrap_or(false), - }; let mut visitor = AwaitsVisitor::default(); if let Some(body) = generator_body { visitor.visit_body(body); @@ -2044,61 +2019,61 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { eq }; - let mut interior_or_upvar_span = None; - let mut interior_extra_info = None; - // Get the typeck results from the infcx if the generator is the function we are currently // 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 &self.typeck_results { - Some(t) if t.hir_owner.to_def_id() == generator_did_root => { - Some(GeneratorData::Local(&t)) - } + let generator_data = match &self.typeck_results { + Some(t) if t.hir_owner.to_def_id() == generator_did_root => GeneratorData::Local(&t), _ if generator_did.is_local() => { - Some(GeneratorData::Local(self.tcx.typeck(generator_did.expect_local()))) + GeneratorData::Local(self.tcx.typeck(generator_did.expect_local())) } - _ => self - .tcx - .generator_diagnostic_data(generator_did) - .as_ref() - .map(|generator_diag_data| GeneratorData::Foreign(generator_diag_data)), + _ if let Some(generator_diag_data) = self.tcx.generator_diagnostic_data(generator_did) => { + GeneratorData::Foreign(generator_diag_data) + } + _ => return false, }; - if let Some(generator_data) = generator_data.as_ref() { - interior_or_upvar_span = - generator_data.try_get_upvar_span(&self, generator_did, ty_matches); + let mut interior_or_upvar_span = None; - // The generator interior types share the same binders - if let Some(cause) = - generator_data.get_generator_interior_types().skip_binder().iter().find( - |ty::GeneratorInteriorTypeCause { ty, .. }| { - ty_matches(generator_data.get_generator_interior_types().rebind(*ty)) - }, - ) - { - let from_awaited_ty = generator_data.get_from_await_ty(visitor, hir, ty_matches); - let ty::GeneratorInteriorTypeCause { span, scope_span, yield_span, expr, .. } = - cause; + let from_awaited_ty = generator_data.get_from_await_ty(visitor, hir, ty_matches); + debug!(?from_awaited_ty); - interior_or_upvar_span = Some(GeneratorInteriorOrUpvar::Interior(*span)); - interior_extra_info = Some((*scope_span, *yield_span, *expr, from_awaited_ty)); - } + // The generator interior types share the same binders + if let Some(cause) = + generator_data.get_generator_interior_types().skip_binder().iter().find( + |ty::GeneratorInteriorTypeCause { ty, .. }| { + ty_matches(generator_data.get_generator_interior_types().rebind(*ty)) + }, + ) + { + let ty::GeneratorInteriorTypeCause { span, scope_span, yield_span, expr, .. } = cause; - if interior_or_upvar_span.is_none() && generator_data.is_foreign() { - interior_or_upvar_span = Some(GeneratorInteriorOrUpvar::Interior(span)); - } + interior_or_upvar_span = Some(GeneratorInteriorOrUpvar::Interior( + *span, + Some((*scope_span, *yield_span, *expr, from_awaited_ty)), + )); } + if interior_or_upvar_span.is_none() { + interior_or_upvar_span = + generator_data.try_get_upvar_span(&self, generator_did, ty_matches); + } + + if interior_or_upvar_span.is_none() && generator_data.is_foreign() { + interior_or_upvar_span = Some(GeneratorInteriorOrUpvar::Interior(span, None)); + } + + debug!(?interior_or_upvar_span); if let Some(interior_or_upvar_span) = interior_or_upvar_span { - let typeck_results = generator_data.and_then(|generator_data| match generator_data { + let is_async = self.tcx.generator_is_async(generator_did); + let typeck_results = match generator_data { GeneratorData::Local(typeck_results) => Some(typeck_results), GeneratorData::Foreign(_) => None, - }); + }; self.note_obligation_cause_for_async_await( err, interior_or_upvar_span, - interior_extra_info, is_async, outer_generator, trait_ref, @@ -2120,7 +2095,6 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { &self, err: &mut Diagnostic, interior_or_upvar_span: GeneratorInteriorOrUpvar, - interior_extra_info: Option<(Option<Span>, Span, Option<hir::HirId>, Option<Span>)>, is_async: bool, outer_generator: Option<DefId>, trait_pred: ty::TraitPredicate<'tcx>, @@ -2242,7 +2216,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } }; match interior_or_upvar_span { - GeneratorInteriorOrUpvar::Interior(interior_span) => { + GeneratorInteriorOrUpvar::Interior(interior_span, interior_extra_info) => { if let Some((scope_span, yield_span, expr, from_awaited_ty)) = interior_extra_info { if let Some(await_span) = from_awaited_ty { // The type causing this obligation is one being awaited at await_span. @@ -2379,7 +2353,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { obligated_types: &mut Vec<Ty<'tcx>>, seen_requirements: &mut FxHashSet<DefId>, ) where - T: fmt::Display, + T: fmt::Display + ToPredicate<'tcx>, { let tcx = self.tcx; match *cause_code { @@ -2407,7 +2381,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { | ObligationCauseCode::CheckAssociatedTypeBounds { .. } | ObligationCauseCode::LetElse | ObligationCauseCode::BinOp { .. } - | ObligationCauseCode::AscribeUserTypeProvePredicate(..) => {} + | ObligationCauseCode::AscribeUserTypeProvePredicate(..) + | ObligationCauseCode::RustCall => {} ObligationCauseCode::SliceOrArrayElem => { err.note("slice and array elements must have `Sized` type"); } @@ -2445,12 +2420,12 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { (Ok(l), Ok(r)) => l.line == r.line, _ => true, }; - if !ident.span.overlaps(span) && !same_line { + if !ident.span.is_dummy() && !ident.span.overlaps(span) && !same_line { multispan.push_span_label(ident.span, "required by a bound in this"); } } let descr = format!("required by a bound in `{}`", item_name); - if span != DUMMY_SP { + if !span.is_dummy() { let msg = format!("required by this bound in `{}`", item_name); multispan.push_span_label(span, msg); err.span_note(multispan, &descr); @@ -2636,30 +2611,24 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } }; - let from_generator = tcx.lang_items().from_generator_fn().unwrap(); + let identity_future = tcx.require_lang_item(LangItem::IdentityFuture, None); // Don't print the tuple of capture types 'print: { if !is_upvar_tys_infer_tuple { let msg = format!("required because it appears within the type `{}`", ty); match ty.kind() { - ty::Adt(def, _) => { - // `gen_future` is used in all async functions; it doesn't add any additional info. - if self.tcx.is_diagnostic_item(sym::gen_future, def.did()) { - break 'print; - } - match self.tcx.opt_item_ident(def.did()) { - Some(ident) => err.span_note(ident.span, &msg), - None => err.note(&msg), - } - } + ty::Adt(def, _) => match self.tcx.opt_item_ident(def.did()) { + Some(ident) => err.span_note(ident.span, &msg), + None => err.note(&msg), + }, ty::Opaque(def_id, _) => { - // Avoid printing the future from `core::future::from_generator`, it's not helpful - if tcx.parent(*def_id) == from_generator { + // Avoid printing the future from `core::future::identity_future`, it's not helpful + if tcx.parent(*def_id) == identity_future { break 'print; } - // If the previous type is `from_generator`, this is the future generated by the body of an async function. + // If the previous type is `identity_future`, this is the future generated by the body of an async function. // Avoid printing it twice (it was already printed in the `ty::Generator` arm below). let is_future = tcx.ty_is_opaque_future(ty); debug!( @@ -2669,8 +2638,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ); if is_future && obligated_types.last().map_or(false, |ty| match ty.kind() { - ty::Opaque(last_def_id, _) => { - tcx.parent(*last_def_id) == from_generator + ty::Generator(last_def_id, ..) => { + tcx.generator_is_async(*last_def_id) } _ => false, }) @@ -2696,7 +2665,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let sp = self.tcx.def_span(def_id); // Special-case this to say "async block" instead of `[static generator]`. - let kind = tcx.generator_kind(def_id).unwrap(); + let kind = tcx.generator_kind(def_id).unwrap().descr(); err.span_note( sp, &format!("required because it's used within this {}", kind), @@ -2713,7 +2682,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { obligated_types.push(ty); - let parent_predicate = parent_trait_ref.to_predicate(tcx); + let parent_predicate = parent_trait_ref; if !self.is_recursive_obligation(obligated_types, &data.parent_code) { // #74711: avoid a stack overflow ensure_sufficient_stack(|| { @@ -2744,9 +2713,10 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { self.resolve_vars_if_possible(data.derived.parent_trait_pred); parent_trait_pred.remap_constness_diag(param_env); let parent_def_id = parent_trait_pred.def_id(); + let (self_ty, file) = + self.tcx.short_ty_string(parent_trait_pred.skip_binder().self_ty()); let msg = format!( - "required for `{}` to implement `{}`", - parent_trait_pred.skip_binder().self_ty(), + "required for `{self_ty}` to implement `{}`", parent_trait_pred.print_modifiers_and_trait_path() ); let mut is_auto_trait = false; @@ -2775,7 +2745,13 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { _ => err.note(&msg), }; - let mut parent_predicate = parent_trait_pred.to_predicate(tcx); + if let Some(file) = file { + err.note(&format!( + "the full type name has been written to '{}'", + file.display(), + )); + } + let mut parent_predicate = parent_trait_pred; let mut data = &data.derived; let mut count = 0; seen_requirements.insert(parent_def_id); @@ -2815,11 +2791,18 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { count, pluralize!(count) )); + let (self_ty, file) = + self.tcx.short_ty_string(parent_trait_pred.skip_binder().self_ty()); err.note(&format!( - "required for `{}` to implement `{}`", - parent_trait_pred.skip_binder().self_ty(), + "required for `{self_ty}` to implement `{}`", parent_trait_pred.print_modifiers_and_trait_path() )); + if let Some(file) = file { + err.note(&format!( + "the full type name has been written to '{}'", + file.display(), + )); + } } // #74711: avoid a stack overflow ensure_sufficient_stack(|| { @@ -2835,7 +2818,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } ObligationCauseCode::DerivedObligation(ref data) => { let parent_trait_ref = self.resolve_vars_if_possible(data.parent_trait_pred); - let parent_predicate = parent_trait_ref.to_predicate(tcx); + let parent_predicate = parent_trait_ref; // #74711: avoid a stack overflow ensure_sufficient_stack(|| { self.note_obligation_cause_code( @@ -2968,8 +2951,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let self_ty = self.resolve_vars_if_possible(trait_pred.self_ty()); let impls_future = self.type_implements_trait( future_trait, - self.tcx.erase_late_bound_regions(self_ty), - ty::List::empty(), + [self.tcx.erase_late_bound_regions(self_ty)], obligation.param_env, ); if !impls_future.must_apply_modulo_regions() { @@ -2982,16 +2964,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { self.tcx.mk_projection( item_def_id, // Future::Output has no substs - self.tcx.mk_substs_trait(trait_pred.self_ty(), &[]), + self.tcx.mk_substs_trait(trait_pred.self_ty(), []), ) }); - let projection_ty = normalize_to( - &mut SelectionContext::new(self), - obligation.param_env, - obligation.cause.clone(), - projection_ty, - &mut vec![], - ); + let InferOk { value: projection_ty, .. } = + self.at(&obligation.cause, obligation.param_env).normalize(projection_ty); debug!( normalized_projection_type = ?self.resolve_vars_if_possible(projection_ty) @@ -3067,21 +3044,22 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let field_ty = field.ty(self.tcx, substs); let trait_substs = match diagnostic_name { sym::PartialEq | sym::PartialOrd => { - self.tcx.mk_substs_trait(field_ty, &[field_ty.into()]) + Some(field_ty) } - _ => self.tcx.mk_substs_trait(field_ty, &[]), + _ => None, }; let trait_pred = trait_pred.map_bound_ref(|tr| ty::TraitPredicate { - trait_ref: ty::TraitRef { - substs: trait_substs, - ..trait_pred.skip_binder().trait_ref - }, + trait_ref: self.tcx.mk_trait_ref( + trait_pred.def_id(), + [field_ty].into_iter().chain(trait_substs), + ), ..*tr }); let field_obl = Obligation::new( + self.tcx, obligation.cause.clone(), obligation.param_env, - trait_pred.to_predicate(self.tcx), + trait_pred, ); self.predicate_must_hold_modulo_regions(&field_obl) }) diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index a417e1440..76a755ed9 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -4,13 +4,12 @@ use rustc_data_structures::obligation_forest::ProcessResult; use rustc_data_structures::obligation_forest::{Error, ForestObligation, Outcome}; use rustc_data_structures::obligation_forest::{ObligationForest, ObligationProcessor}; use rustc_infer::traits::ProjectionCacheKey; -use rustc_infer::traits::{SelectionError, TraitEngine, TraitEngineExt as _, TraitObligation}; +use rustc_infer::traits::{SelectionError, TraitEngine, TraitObligation}; use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::ty::abstract_const::NotConstEvaluatable; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::subst::SubstsRef; -use rustc_middle::ty::ToPredicate; -use rustc_middle::ty::{self, Binder, Const, Ty, TypeVisitable}; +use rustc_middle::ty::{self, Binder, Const, TypeVisitable}; use std::marker::PhantomData; use super::const_evaluatable; @@ -21,9 +20,9 @@ use super::CodeAmbiguity; use super::CodeProjectionError; use super::CodeSelectionError; use super::EvaluationResult; +use super::PredicateObligation; use super::Unimplemented; use super::{FulfillmentError, FulfillmentErrorCode}; -use super::{ObligationCause, PredicateObligation}; use crate::traits::project::PolyProjectionObligation; use crate::traits::project::ProjectionCacheKeyExt as _; @@ -85,7 +84,7 @@ static_assert_size!(PendingPredicateObligation<'_>, 72); impl<'a, 'tcx> FulfillmentContext<'tcx> { /// Creates a new fulfillment context. - pub fn new() -> FulfillmentContext<'tcx> { + pub(super) fn new() -> FulfillmentContext<'tcx> { FulfillmentContext { predicates: ObligationForest::new(), relationships: FxHashMap::default(), @@ -93,7 +92,7 @@ impl<'a, 'tcx> FulfillmentContext<'tcx> { } } - pub fn new_in_snapshot() -> FulfillmentContext<'tcx> { + pub(super) fn new_in_snapshot() -> FulfillmentContext<'tcx> { FulfillmentContext { predicates: ObligationForest::new(), relationships: FxHashMap::default(), @@ -127,42 +126,6 @@ impl<'a, 'tcx> FulfillmentContext<'tcx> { } impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> { - /// "Normalize" a projection type `<SomeType as SomeTrait>::X` by - /// creating a fresh type variable `$0` as well as a projection - /// predicate `<SomeType as SomeTrait>::X == $0`. When the - /// inference engine runs, it will attempt to find an impl of - /// `SomeTrait` or a where-clause that lets us unify `$0` with - /// something concrete. If this fails, we'll unify `$0` with - /// `projection_ty` again. - #[instrument(level = "debug", skip(self, infcx, param_env, cause))] - fn normalize_projection_type( - &mut self, - infcx: &InferCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - projection_ty: ty::ProjectionTy<'tcx>, - cause: ObligationCause<'tcx>, - ) -> Ty<'tcx> { - debug_assert!(!projection_ty.has_escaping_bound_vars()); - - // FIXME(#20304) -- cache - - let mut selcx = SelectionContext::new(infcx); - let mut obligations = vec![]; - let normalized_ty = project::normalize_projection_type( - &mut selcx, - param_env, - projection_ty, - cause, - 0, - &mut obligations, - ); - self.register_predicate_obligations(infcx, obligations); - - debug!(?normalized_ty); - - normalized_ty.ty().unwrap() - } - fn register_predicate_obligation( &mut self, infcx: &InferCtxt<'tcx>, @@ -236,7 +199,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { // code is so hot. 1 and 0 dominate; 2+ is fairly rare. 1 => { let infer_var = pending_obligation.stalled_on[0]; - self.selcx.infcx().ty_or_const_infer_var_changed(infer_var) + self.selcx.infcx.ty_or_const_infer_var_changed(infer_var) } 0 => { // In this case we haven't changed, but wish to make a change. @@ -247,7 +210,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { // form was a perf win. See #64545 for details. (|| { for &infer_var in &pending_obligation.stalled_on { - if self.selcx.infcx().ty_or_const_infer_var_changed(infer_var) { + if self.selcx.infcx.ty_or_const_infer_var_changed(infer_var) { return true; } } @@ -277,13 +240,12 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { debug!(?obligation, "pre-resolve"); if obligation.predicate.has_non_region_infer() { - obligation.predicate = - self.selcx.infcx().resolve_vars_if_possible(obligation.predicate); + obligation.predicate = self.selcx.infcx.resolve_vars_if_possible(obligation.predicate); } let obligation = &pending_obligation.obligation; - let infcx = self.selcx.infcx(); + let infcx = self.selcx.infcx; if obligation.predicate.has_projections() { let mut obligations = Vec::new(); @@ -296,7 +258,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { &mut obligations, ); if predicate != obligation.predicate { - obligations.push(obligation.with(predicate)); + obligations.push(obligation.with(infcx.tcx, predicate)); return ProcessResult::Changed(mk_pending(obligations)); } } @@ -306,8 +268,8 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { // Evaluation will discard candidates using the leak check. // This means we need to pass it the bound version of our // predicate. - ty::PredicateKind::Trait(trait_ref) => { - let trait_obligation = obligation.with(binder.rebind(trait_ref)); + ty::PredicateKind::Clause(ty::Clause::Trait(trait_ref)) => { + let trait_obligation = obligation.with(infcx.tcx, binder.rebind(trait_ref)); self.process_trait_obligation( obligation, @@ -315,8 +277,8 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { &mut pending_obligation.stalled_on, ) } - ty::PredicateKind::Projection(data) => { - let project_obligation = obligation.with(binder.rebind(data)); + ty::PredicateKind::Clause(ty::Clause::Projection(data)) => { + let project_obligation = obligation.with(infcx.tcx, binder.rebind(data)); self.process_projection_obligation( obligation, @@ -324,8 +286,8 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { &mut pending_obligation.stalled_on, ) } - ty::PredicateKind::RegionOutlives(_) - | ty::PredicateKind::TypeOutlives(_) + ty::PredicateKind::Clause(ty::Clause::RegionOutlives(_)) + | ty::PredicateKind::Clause(ty::Clause::TypeOutlives(_)) | ty::PredicateKind::WellFormed(_) | ty::PredicateKind::ObjectSafe(_) | ty::PredicateKind::ClosureKind(..) @@ -335,17 +297,16 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { | ty::PredicateKind::ConstEquate(..) => { let pred = ty::Binder::dummy(infcx.replace_bound_vars_with_placeholders(binder)); - ProcessResult::Changed(mk_pending(vec![ - obligation.with(pred.to_predicate(self.selcx.tcx())), - ])) + ProcessResult::Changed(mk_pending(vec![obligation.with(infcx.tcx, pred)])) } + ty::PredicateKind::Ambiguous => ProcessResult::Unchanged, ty::PredicateKind::TypeWellFormedFromEnv(..) => { bug!("TypeWellFormedFromEnv is only used for Chalk") } }, Some(pred) => match pred { - ty::PredicateKind::Trait(data) => { - let trait_obligation = obligation.with(Binder::dummy(data)); + ty::PredicateKind::Clause(ty::Clause::Trait(data)) => { + let trait_obligation = obligation.with(infcx.tcx, Binder::dummy(data)); self.process_trait_obligation( obligation, @@ -354,7 +315,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { ) } - ty::PredicateKind::RegionOutlives(data) => { + ty::PredicateKind::Clause(ty::Clause::RegionOutlives(data)) => { if infcx.considering_regions { infcx.region_outlives_predicate(&obligation.cause, Binder::dummy(data)); } @@ -362,15 +323,18 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { ProcessResult::Changed(vec![]) } - ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(t_a, r_b)) => { + ty::PredicateKind::Clause(ty::Clause::TypeOutlives(ty::OutlivesPredicate( + t_a, + r_b, + ))) => { if infcx.considering_regions { infcx.register_region_obligation_with_cause(t_a, r_b, &obligation.cause); } ProcessResult::Changed(vec![]) } - ty::PredicateKind::Projection(ref data) => { - let project_obligation = obligation.with(Binder::dummy(*data)); + ty::PredicateKind::Clause(ty::Clause::Projection(ref data)) => { + let project_obligation = obligation.with(infcx.tcx, Binder::dummy(*data)); self.process_projection_obligation( obligation, @@ -388,7 +352,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { } ty::PredicateKind::ClosureKind(_, closure_substs, kind) => { - match self.selcx.infcx().closure_kind(closure_substs) { + match self.selcx.infcx.closure_kind(closure_substs) { Some(closure_kind) => { if closure_kind.extends(kind) { ProcessResult::Changed(vec![]) @@ -402,7 +366,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { ty::PredicateKind::WellFormed(arg) => { match wf::obligations( - self.selcx.infcx(), + self.selcx.infcx, obligation.param_env, obligation.cause.body_id, obligation.recursion_depth + 1, @@ -419,7 +383,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { } ty::PredicateKind::Subtype(subtype) => { - match self.selcx.infcx().subtype_predicate( + match self.selcx.infcx.subtype_predicate( &obligation.cause, obligation.param_env, Binder::dummy(subtype), @@ -443,7 +407,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { } ty::PredicateKind::Coerce(coerce) => { - match self.selcx.infcx().coerce_predicate( + match self.selcx.infcx.coerce_predicate( &obligation.cause, obligation.param_env, Binder::dummy(coerce), @@ -467,7 +431,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { ty::PredicateKind::ConstEvaluatable(uv) => { match const_evaluatable::is_const_evaluatable( - self.selcx.infcx(), + self.selcx.infcx, uv, obligation.param_env, obligation.cause.span, @@ -490,20 +454,47 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { } ty::PredicateKind::ConstEquate(c1, c2) => { + let tcx = self.selcx.tcx(); assert!( - self.selcx.tcx().features().generic_const_exprs, + tcx.features().generic_const_exprs, "`ConstEquate` without a feature gate: {c1:?} {c2:?}", ); - debug!(?c1, ?c2, "equating consts"); // 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![]); + let c1 = tcx.expand_abstract_consts(c1); + let c2 = tcx.expand_abstract_consts(c2); + debug!("equating consts:\nc1= {:?}\nc2= {:?}", c1, c2); + + use rustc_hir::def::DefKind; + use ty::ConstKind::Unevaluated; + match (c1.kind(), c2.kind()) { + (Unevaluated(a), Unevaluated(b)) + if a.def.did == b.def.did + && tcx.def_kind(a.def.did) == DefKind::AssocConst => + { + if let Ok(new_obligations) = infcx + .at(&obligation.cause, obligation.param_env) + .trace(c1, c2) + .eq(a.substs, b.substs) + { + return ProcessResult::Changed(mk_pending( + new_obligations.into_obligations(), + )); + } + } + (_, Unevaluated(_)) | (Unevaluated(_), _) => (), + (_, _) => { + if let Ok(new_obligations) = + infcx.at(&obligation.cause, obligation.param_env).eq(c1, c2) + { + return ProcessResult::Changed(mk_pending( + new_obligations.into_obligations(), + )); + } + } } } @@ -511,7 +502,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { let mut evaluate = |c: Const<'tcx>| { if let ty::ConstKind::Unevaluated(unevaluated) = c.kind() { - match self.selcx.infcx().try_const_eval_resolve( + match self.selcx.infcx.try_const_eval_resolve( obligation.param_env, unevaluated, c.ty(), @@ -539,11 +530,13 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { (Ok(c1), Ok(c2)) => { match self .selcx - .infcx() + .infcx .at(&obligation.cause, obligation.param_env) .eq(c1, c2) { - Ok(_) => ProcessResult::Changed(vec![]), + Ok(inf_ok) => { + ProcessResult::Changed(mk_pending(inf_ok.into_obligations())) + } Err(err) => ProcessResult::Error( FulfillmentErrorCode::CodeConstEquateError( ExpectedFound::new(true, c1, c2), @@ -558,12 +551,6 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { NotConstEvaluatable::Error(reported), )), ), - (Err(ErrorHandled::Linted), _) | (_, Err(ErrorHandled::Linted)) => { - span_bug!( - obligation.cause.span(), - "ConstEquate: const_eval_resolve returned an unexpected error" - ) - } (Err(ErrorHandled::TooGeneric), _) | (_, Err(ErrorHandled::TooGeneric)) => { if c1.has_non_region_infer() || c2.has_non_region_infer() { ProcessResult::Unchanged @@ -578,6 +565,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { } } } + ty::PredicateKind::Ambiguous => ProcessResult::Unchanged, ty::PredicateKind::TypeWellFormedFromEnv(..) => { bug!("TypeWellFormedFromEnv is only used for Chalk") } @@ -612,7 +600,7 @@ impl<'a, 'tcx> FulfillProcessor<'a, 'tcx> { trait_obligation: TraitObligation<'tcx>, stalled_on: &mut Vec<TyOrConstInferVar<'tcx>>, ) -> ProcessResult<PendingPredicateObligation<'tcx>, FulfillmentErrorCode<'tcx>> { - let infcx = self.selcx.infcx(); + let infcx = self.selcx.infcx; if obligation.predicate.is_global() { // no type variables present, can use evaluation for better caching. // FIXME: consider caching errors too. @@ -670,7 +658,7 @@ impl<'a, 'tcx> FulfillProcessor<'a, 'tcx> { if obligation.predicate.is_global() { // no type variables present, can use evaluation for better caching. // FIXME: consider caching errors too. - if self.selcx.infcx().predicate_must_hold_considering_regions(obligation) { + if self.selcx.infcx.predicate_must_hold_considering_regions(obligation) { if let Some(key) = ProjectionCacheKey::from_poly_projection_predicate( &mut self.selcx, project_obligation.predicate, @@ -679,7 +667,7 @@ impl<'a, 'tcx> FulfillProcessor<'a, 'tcx> { // evaluated all sub-obligations. We can therefore mark the 'root' // obligation as complete, and skip evaluating sub-obligations. self.selcx - .infcx() + .infcx .inner .borrow_mut() .projection_cache() @@ -703,7 +691,7 @@ impl<'a, 'tcx> FulfillProcessor<'a, 'tcx> { } // Let the caller handle the recursion ProjectAndUnifyResult::Recursive => ProcessResult::Changed(mk_pending(vec![ - project_obligation.with(project_obligation.predicate.to_predicate(tcx)), + project_obligation.with(tcx, project_obligation.predicate), ])), ProjectAndUnifyResult::MismatchedProjectionTypes(e) => { ProcessResult::Error(CodeProjectionError(e)) @@ -718,7 +706,7 @@ fn substs_infer_vars<'a, 'tcx>( substs: ty::Binder<'tcx, SubstsRef<'tcx>>, ) -> impl Iterator<Item = TyOrConstInferVar<'tcx>> { selcx - .infcx() + .infcx .resolve_vars_if_possible(substs) .skip_binder() // ok because this check doesn't care about regions .iter() diff --git a/compiler/rustc_trait_selection/src/traits/misc.rs b/compiler/rustc_trait_selection/src/traits/misc.rs index be603c609..b6ded4ce5 100644 --- a/compiler/rustc_trait_selection/src/traits/misc.rs +++ b/compiler/rustc_trait_selection/src/traits/misc.rs @@ -70,7 +70,7 @@ pub fn can_type_implement_copy<'tcx>( } } Err(errors) => { - infcx.err_ctxt().report_fulfillment_errors(&errors, None, false); + infcx.err_ctxt().report_fulfillment_errors(&errors, None); } }; } diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 0bf54c096..ea4bf42c5 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -4,7 +4,6 @@ pub mod auto_trait; mod chalk_fulfill; -pub mod codegen; mod coherence; pub mod const_evaluatable; mod engine; @@ -12,7 +11,6 @@ pub mod error_reporting; mod fulfill; pub mod misc; mod object_safety; -mod on_unimplemented; pub mod outlives_bounds; mod project; pub mod query; @@ -21,9 +19,9 @@ mod select; mod specialize; mod structural_match; mod util; +mod vtable; pub mod wf; -use crate::errors::DumpVTableEntries; use crate::infer::outlives::env::OutlivesEnvironment; use crate::infer::{InferCtxt, TyCtxtInferExt}; use crate::traits::error_reporting::TypeErrCtxtExt as _; @@ -31,16 +29,11 @@ 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::visit::TypeVisitable; -use rustc_middle::ty::{ - self, DefIdTree, GenericParamDefKind, ToPredicate, Ty, TyCtxt, TypeSuperVisitable, VtblEntry, -}; +use rustc_middle::ty::{self, DefIdTree, ToPredicate, Ty, TyCtxt, TypeSuperVisitable}; use rustc_middle::ty::{InternalSubsts, SubstsRef}; -use rustc_span::{sym, Span}; -use smallvec::SmallVec; +use rustc_span::Span; use std::fmt::Debug; use std::ops::ControlFlow; @@ -58,8 +51,7 @@ pub use self::object_safety::astconv_object_safety_violations; pub use self::object_safety::is_vtable_safe_method; pub use self::object_safety::MethodViolationCode; pub use self::object_safety::ObjectSafetyViolation; -pub use self::on_unimplemented::{OnUnimplementedDirective, OnUnimplementedNote}; -pub use self::project::{normalize, normalize_projection_type, normalize_to}; +pub use self::project::{normalize_projection_type, NormalizeExt}; pub use self::select::{EvaluationCache, SelectionCache, SelectionContext}; pub use self::select::{EvaluationResult, IntercrateAmbiguityCause, OverflowError}; pub use self::specialize::specialization_graph::FutureCompatOverlapError; @@ -117,14 +109,12 @@ pub enum TraitQueryMode { } /// Creates predicate obligations from the generic bounds. +#[instrument(level = "debug", skip(cause, param_env))] pub fn predicates_for_generics<'tcx>( cause: impl Fn(usize, Span) -> ObligationCause<'tcx>, param_env: ty::ParamEnv<'tcx>, generic_bounds: ty::InstantiatedPredicates<'tcx>, ) -> impl Iterator<Item = PredicateObligation<'tcx>> { - 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), @@ -147,64 +137,50 @@ pub fn type_known_to_meet_bound_modulo_regions<'tcx>( def_id: DefId, span: Span, ) -> bool { - debug!( - "type_known_to_meet_bound_modulo_regions(ty={:?}, bound={:?})", - ty, - infcx.tcx.def_path_str(def_id) - ); + let trait_ref = ty::Binder::dummy(infcx.tcx.mk_trait_ref(def_id, [ty])); + pred_known_to_hold_modulo_regions(infcx, param_env, trait_ref.without_const(), span) +} - let trait_ref = - ty::Binder::dummy(ty::TraitRef { def_id, substs: infcx.tcx.mk_substs_trait(ty, &[]) }); +#[instrument(level = "debug", skip(infcx, param_env, span, pred), ret)] +fn pred_known_to_hold_modulo_regions<'tcx>( + infcx: &InferCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + pred: impl ToPredicate<'tcx> + TypeVisitable<'tcx>, + span: Span, +) -> bool { + let has_non_region_infer = pred.has_non_region_infer(); let obligation = Obligation { param_env, + // 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). cause: ObligationCause::misc(span, hir::CRATE_HIR_ID), recursion_depth: 0, - predicate: trait_ref.without_const().to_predicate(infcx.tcx), + predicate: pred.to_predicate(infcx.tcx), }; let result = infcx.predicate_must_hold_modulo_regions(&obligation); - debug!( - "type_known_to_meet_ty={:?} bound={} => {:?}", - ty, - infcx.tcx.def_path_str(def_id), - result - ); + debug!(?result); - if result && ty.has_non_region_infer() { + if result && 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. - // 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); - + // FIXME(@lcnr): this function doesn't seem right. // 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); + let errors = fully_solve_obligation(infcx, obligation); // 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 &errors[..] { - [] => { - debug!( - "type_known_to_meet_bound_modulo_regions: ty={:?} bound={} success", - ty, - infcx.tcx.def_path_str(def_id) - ); - true - } + [] => true, errors => { - debug!( - ?ty, - bound = %infcx.tcx.def_path_str(def_id), - ?errors, - "type_known_to_meet_bound_modulo_regions" - ); + debug!(?errors); false } } @@ -238,7 +214,7 @@ fn do_normalize_predicates<'tcx>( 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); + let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None); return Err(reported); } }; @@ -336,7 +312,10 @@ pub fn normalize_param_env_or_error<'tcx>( // TypeOutlives predicates - these are normally used by regionck. let outlives_predicates: Vec<_> = predicates .drain_filter(|predicate| { - matches!(predicate.kind().skip_binder(), ty::PredicateKind::TypeOutlives(..)) + matches!( + predicate.kind().skip_binder(), + ty::PredicateKind::Clause(ty::Clause::TypeOutlives(..)) + ) }) .collect(); @@ -390,6 +369,7 @@ pub fn normalize_param_env_or_error<'tcx>( } /// Normalize a type and process all resulting obligations, returning any errors +#[instrument(skip_all)] pub fn fully_normalize<'tcx, T>( infcx: &InferCtxt<'tcx>, cause: ObligationCause<'tcx>, @@ -399,28 +379,18 @@ pub fn fully_normalize<'tcx, T>( where T: TypeFoldable<'tcx>, { - debug!("fully_normalize_with_fulfillcx(value={:?})", value); - let selcx = &mut SelectionContext::new(infcx); - let Normalized { value: normalized_value, obligations } = - project::normalize(selcx, param_env, cause, value); - debug!( - "fully_normalize: normalized_value={:?} obligations={:?}", - normalized_value, obligations - ); - - let mut fulfill_cx = FulfillmentContext::new(); - for obligation in obligations { - fulfill_cx.register_predicate_obligation(infcx, obligation); - } - - debug!("fully_normalize: select_all_or_error start"); - let errors = fulfill_cx.select_all_or_error(infcx); + let ocx = ObligationCtxt::new(infcx); + debug!(?value); + let normalized_value = ocx.normalize(&cause, param_env, value); + debug!(?normalized_value); + debug!("select_all_or_error start"); + let errors = ocx.select_all_or_error(); if !errors.is_empty() { return Err(errors); } - debug!("fully_normalize: select_all_or_error complete"); + debug!("select_all_or_error complete"); let resolved_value = infcx.resolve_vars_if_possible(normalized_value); - debug!("fully_normalize: resolved_value={:?}", resolved_value); + debug!(?resolved_value); Ok(resolved_value) } @@ -430,9 +400,7 @@ 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) + fully_solve_obligations(infcx, [obligation]) } /// Process a set of obligations (and any nested obligations that come from them) @@ -441,9 +409,9 @@ 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) + let ocx = ObligationCtxt::new(infcx); + ocx.register_obligations(obligations); + ocx.select_all_or_error() } /// Process a bound (and any nested obligations that come from it) to completion. @@ -456,9 +424,16 @@ pub fn fully_solve_bound<'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) + let tcx = infcx.tcx; + let trait_ref = ty::TraitRef { def_id: bound, substs: tcx.mk_substs_trait(ty, []) }; + let obligation = Obligation { + cause, + recursion_depth: 0, + param_env, + predicate: ty::Binder::dummy(trait_ref).without_const().to_predicate(tcx), + }; + + fully_solve_obligation(infcx, obligation) } /// Normalizes the predicates and checks whether they hold in an empty environment. If this @@ -473,9 +448,9 @@ pub fn impossible_predicates<'tcx>( 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); + let predicates = ocx.normalize(&ObligationCause::dummy(), param_env, predicates); for predicate in predicates { - let obligation = Obligation::new(ObligationCause::dummy(), param_env, predicate); + let obligation = Obligation::new(tcx, ObligationCause::dummy(), param_env, predicate); ocx.register_obligation(obligation); } let errors = ocx.select_all_or_error(); @@ -500,7 +475,7 @@ fn subst_and_check_impossible_predicates<'tcx>( // associated items. if let Some(trait_def_id) = tcx.trait_of_item(key.0) { let trait_ref = ty::TraitRef::from_method(tcx, trait_def_id, key.1); - predicates.push(ty::Binder::dummy(trait_ref).to_poly_trait_predicate().to_predicate(tcx)); + predicates.push(ty::Binder::dummy(trait_ref).to_predicate(tcx)); } predicates.retain(|predicate| !predicate.needs_subst()); @@ -565,6 +540,7 @@ fn is_impossible_method<'tcx>( let predicates_for_trait = predicates.predicates.iter().filter_map(|(pred, span)| { if pred.visit_with(&mut visitor).is_continue() { Some(Obligation::new( + tcx, ObligationCause::dummy_with_span(*span), param_env, ty::EarlyBinder(*pred).subst(tcx, impl_trait_ref.substs), @@ -586,390 +562,14 @@ fn is_impossible_method<'tcx>( false } -#[derive(Clone, Debug)] -enum VtblSegment<'tcx> { - MetadataDSA, - TraitOwnEntries { trait_ref: ty::PolyTraitRef<'tcx>, emit_vptr: bool }, -} - -/// Prepare the segments for a vtable -fn prepare_vtable_segments<'tcx, T>( - tcx: TyCtxt<'tcx>, - trait_ref: ty::PolyTraitRef<'tcx>, - mut segment_visitor: impl FnMut(VtblSegment<'tcx>) -> ControlFlow<T>, -) -> Option<T> { - // The following constraints holds for the final arrangement. - // 1. The whole virtual table of the first direct super trait is included as the - // the prefix. If this trait doesn't have any super traits, then this step - // consists of the dsa metadata. - // 2. Then comes the proper pointer metadata(vptr) and all own methods for all - // other super traits except those already included as part of the first - // direct super trait virtual table. - // 3. finally, the own methods of this trait. - - // This has the advantage that trait upcasting to the first direct super trait on each level - // is zero cost, and to another trait includes only replacing the pointer with one level indirection, - // while not using too much extra memory. - - // For a single inheritance relationship like this, - // D --> C --> B --> A - // The resulting vtable will consists of these segments: - // DSA, A, B, C, D - - // For a multiple inheritance relationship like this, - // D --> C --> A - // \-> B - // The resulting vtable will consists of these segments: - // DSA, A, B, B-vptr, C, D - - // For a diamond inheritance relationship like this, - // D --> B --> A - // \-> C -/ - // The resulting vtable will consists of these segments: - // DSA, A, B, C, C-vptr, D - - // For a more complex inheritance relationship like this: - // O --> G --> C --> A - // \ \ \-> B - // | |-> F --> D - // | \-> E - // |-> N --> J --> H - // \ \-> I - // |-> M --> K - // \-> L - // The resulting vtable will consists of these segments: - // DSA, A, B, B-vptr, C, D, D-vptr, E, E-vptr, F, F-vptr, G, - // H, H-vptr, I, I-vptr, J, J-vptr, K, K-vptr, L, L-vptr, M, M-vptr, - // N, N-vptr, O - - // emit dsa segment first. - if let ControlFlow::Break(v) = (segment_visitor)(VtblSegment::MetadataDSA) { - return Some(v); - } - - let mut emit_vptr_on_new_entry = false; - let mut visited = util::PredicateSet::new(tcx); - let predicate = trait_ref.without_const().to_predicate(tcx); - let mut stack: SmallVec<[(ty::PolyTraitRef<'tcx>, _, _); 5]> = - smallvec![(trait_ref, emit_vptr_on_new_entry, None)]; - visited.insert(predicate); - - // the main traversal loop: - // basically we want to cut the inheritance directed graph into a few non-overlapping slices of nodes - // that each node is emitted after all its descendents have been emitted. - // so we convert the directed graph into a tree by skipping all previously visited nodes using a visited set. - // this is done on the fly. - // Each loop run emits a slice - it starts by find a "childless" unvisited node, backtracking upwards, and it - // stops after it finds a node that has a next-sibling node. - // This next-sibling node will used as the starting point of next slice. - - // Example: - // For a diamond inheritance relationship like this, - // D#1 --> B#0 --> A#0 - // \-> C#1 -/ - - // Starting point 0 stack [D] - // Loop run #0: Stack after diving in is [D B A], A is "childless" - // after this point, all newly visited nodes won't have a vtable that equals to a prefix of this one. - // Loop run #0: Emitting the slice [B A] (in reverse order), B has a next-sibling node, so this slice stops here. - // Loop run #0: Stack after exiting out is [D C], C is the next starting point. - // Loop run #1: Stack after diving in is [D C], C is "childless", since its child A is skipped(already emitted). - // Loop run #1: Emitting the slice [D C] (in reverse order). No one has a next-sibling node. - // Loop run #1: Stack after exiting out is []. Now the function exits. - - loop { - // dive deeper into the stack, recording the path - 'diving_in: loop { - if let Some((inner_most_trait_ref, _, _)) = stack.last() { - let inner_most_trait_ref = *inner_most_trait_ref; - let mut direct_super_traits_iter = tcx - .super_predicates_of(inner_most_trait_ref.def_id()) - .predicates - .into_iter() - .filter_map(move |(pred, _)| { - pred.subst_supertrait(tcx, &inner_most_trait_ref).to_opt_poly_trait_pred() - }); - - 'diving_in_skip_visited_traits: loop { - if let Some(next_super_trait) = direct_super_traits_iter.next() { - if visited.insert(next_super_trait.to_predicate(tcx)) { - // We're throwing away potential constness of super traits here. - // FIXME: handle ~const super traits - let next_super_trait = next_super_trait.map_bound(|t| t.trait_ref); - stack.push(( - next_super_trait, - emit_vptr_on_new_entry, - Some(direct_super_traits_iter), - )); - break 'diving_in_skip_visited_traits; - } else { - continue 'diving_in_skip_visited_traits; - } - } else { - break 'diving_in; - } - } - } - } - - // Other than the left-most path, vptr should be emitted for each trait. - emit_vptr_on_new_entry = true; - - // emit innermost item, move to next sibling and stop there if possible, otherwise jump to outer level. - 'exiting_out: loop { - if let Some((inner_most_trait_ref, emit_vptr, siblings_opt)) = stack.last_mut() { - if let ControlFlow::Break(v) = (segment_visitor)(VtblSegment::TraitOwnEntries { - trait_ref: *inner_most_trait_ref, - emit_vptr: *emit_vptr, - }) { - return Some(v); - } - - 'exiting_out_skip_visited_traits: loop { - if let Some(siblings) = siblings_opt { - if let Some(next_inner_most_trait_ref) = siblings.next() { - if visited.insert(next_inner_most_trait_ref.to_predicate(tcx)) { - // We're throwing away potential constness of super traits here. - // FIXME: handle ~const super traits - let next_inner_most_trait_ref = - next_inner_most_trait_ref.map_bound(|t| t.trait_ref); - *inner_most_trait_ref = next_inner_most_trait_ref; - *emit_vptr = emit_vptr_on_new_entry; - break 'exiting_out; - } else { - continue 'exiting_out_skip_visited_traits; - } - } - } - stack.pop(); - continue 'exiting_out; - } - } - // all done - return None; - } - } -} - -fn dump_vtable_entries<'tcx>( - tcx: TyCtxt<'tcx>, - sp: Span, - trait_ref: ty::PolyTraitRef<'tcx>, - entries: &[VtblEntry<'tcx>], -) { - tcx.sess.emit_err(DumpVTableEntries { - span: sp, - trait_ref, - entries: format!("{:#?}", entries), - }); -} - -fn own_existential_vtable_entries<'tcx>(tcx: TyCtxt<'tcx>, trait_def_id: DefId) -> &'tcx [DefId] { - let trait_methods = tcx - .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). - let own_entries = trait_methods.filter_map(move |trait_method| { - debug!("own_existential_vtable_entry: trait_method={:?}", trait_method); - let def_id = trait_method.def_id; - - // Some methods cannot be called on an object; skip those. - if !is_vtable_safe_method(tcx, trait_def_id, &trait_method) { - debug!("own_existential_vtable_entry: not vtable safe"); - return None; - } - - Some(def_id) - }); - - tcx.arena.alloc_from_iter(own_entries.into_iter()) -} - -/// Given a trait `trait_ref`, iterates the vtable entries -/// that come from `trait_ref`, including its supertraits. -fn vtable_entries<'tcx>( - tcx: TyCtxt<'tcx>, - trait_ref: ty::PolyTraitRef<'tcx>, -) -> &'tcx [VtblEntry<'tcx>] { - debug!("vtable_entries({:?})", trait_ref); - - let mut entries = vec![]; - - let vtable_segment_callback = |segment| -> ControlFlow<()> { - match segment { - VtblSegment::MetadataDSA => { - entries.extend(TyCtxt::COMMON_VTABLE_ENTRIES); - } - VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => { - let existential_trait_ref = trait_ref - .map_bound(|trait_ref| ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref)); - - // Lookup the shape of vtable for the trait. - let own_existential_entries = - 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); - - // The method may have some early-bound lifetimes; add regions for those. - let substs = trait_ref.map_bound(|trait_ref| { - InternalSubsts::for_item(tcx, def_id, |param, _| match param.kind { - GenericParamDefKind::Lifetime => tcx.lifetimes.re_erased.into(), - GenericParamDefKind::Type { .. } - | GenericParamDefKind::Const { .. } => { - trait_ref.substs[param.index as usize] - } - }) - }); - - // The trait type may have higher-ranked lifetimes in it; - // erase them if they appear, so that we get the type - // at some particular call site. - let substs = tcx - .normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), substs); - - // It's possible that the method relies on where-clauses that - // do not hold for this particular set of type parameters. - // Note that this method could then never be called, so we - // do not want to try and codegen it, in that case (see #23435). - let predicates = tcx.predicates_of(def_id).instantiate_own(tcx, substs); - if impossible_predicates(tcx, predicates.predicates) { - debug!("vtable_entries: predicates do not hold"); - return VtblEntry::Vacant; - } - - let instance = ty::Instance::resolve_for_vtable( - tcx, - ty::ParamEnv::reveal_all(), - def_id, - substs, - ) - .expect("resolution failed during building vtable representation"); - VtblEntry::Method(instance) - }); - - entries.extend(own_entries); - - if emit_vptr { - entries.push(VtblEntry::TraitVPtr(trait_ref)); - } - } - } - - ControlFlow::Continue(()) - }; - - let _ = prepare_vtable_segments(tcx, trait_ref, vtable_segment_callback); - - if tcx.has_attr(trait_ref.def_id(), sym::rustc_dump_vtable) { - let sp = tcx.def_span(trait_ref.def_id()); - dump_vtable_entries(tcx, sp, trait_ref, &entries); - } - - tcx.arena.alloc_from_iter(entries.into_iter()) -} - -/// Find slot base for trait methods within vtable entries of another trait -fn vtable_trait_first_method_offset<'tcx>( - tcx: TyCtxt<'tcx>, - key: ( - ty::PolyTraitRef<'tcx>, // trait_to_be_found - ty::PolyTraitRef<'tcx>, // trait_owning_vtable - ), -) -> usize { - let (trait_to_be_found, trait_owning_vtable) = key; - - // #90177 - let trait_to_be_found_erased = tcx.erase_regions(trait_to_be_found); - - let vtable_segment_callback = { - let mut vtable_base = 0; - - move |segment| { - match segment { - VtblSegment::MetadataDSA => { - vtable_base += TyCtxt::COMMON_VTABLE_ENTRIES.len(); - } - VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => { - if tcx.erase_regions(trait_ref) == trait_to_be_found_erased { - return ControlFlow::Break(vtable_base); - } - vtable_base += util::count_own_vtable_entries(tcx, trait_ref); - if emit_vptr { - vtable_base += 1; - } - } - } - ControlFlow::Continue(()) - } - }; - - if let Some(vtable_base) = - prepare_vtable_segments(tcx, trait_owning_vtable, vtable_segment_callback) - { - vtable_base - } else { - bug!("Failed to find info for expected trait in vtable"); - } -} - -/// Find slot offset for trait vptr within vtable entries of another trait -pub fn vtable_trait_upcasting_coercion_new_vptr_slot<'tcx>( - tcx: TyCtxt<'tcx>, - key: ( - Ty<'tcx>, // trait object type whose trait owning vtable - Ty<'tcx>, // trait object for supertrait - ), -) -> Option<usize> { - let (source, target) = key; - assert!(matches!(&source.kind(), &ty::Dynamic(..)) && !source.needs_infer()); - assert!(matches!(&target.kind(), &ty::Dynamic(..)) && !target.needs_infer()); - - // this has been typecked-before, so diagnostics is not really needed. - let unsize_trait_did = tcx.require_lang_item(LangItem::Unsize, None); - - let trait_ref = ty::TraitRef { - def_id: unsize_trait_did, - substs: tcx.mk_substs_trait(source, &[target.into()]), - }; - let obligation = Obligation::new( - ObligationCause::dummy(), - ty::ParamEnv::reveal_all(), - ty::Binder::dummy(ty::TraitPredicate { - trait_ref, - constness: ty::BoundConstness::NotConst, - polarity: ty::ImplPolarity::Positive, - }), - ); - - 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!(); - }; - - implsrc_traitcasting.vtable_vptr_slot -} - pub fn provide(providers: &mut ty::query::Providers) { object_safety::provide(providers); - structural_match::provide(providers); + vtable::provide(providers); *providers = ty::query::Providers { specialization_graph_of: specialize::specialization_graph_provider, specializes: specialize::specializes, - 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) - }, ..*providers }; } diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs index 0bb25a74d..a45749fe4 100644 --- a/compiler/rustc_trait_selection/src/traits/object_safety.rs +++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs @@ -17,11 +17,10 @@ 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}; 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; @@ -288,11 +287,11 @@ fn predicate_references_self<'tcx>( let self_ty = tcx.types.self_param; let has_self_ty = |arg: &GenericArg<'tcx>| arg.walk().any(|arg| arg == self_ty.into()); match predicate.kind().skip_binder() { - ty::PredicateKind::Trait(ref data) => { + ty::PredicateKind::Clause(ty::Clause::Trait(ref data)) => { // In the case of a trait predicate, we can skip the "self" type. if data.trait_ref.substs[1..].iter().any(has_self_ty) { Some(sp) } else { None } } - ty::PredicateKind::Projection(ref data) => { + ty::PredicateKind::Clause(ty::Clause::Projection(ref data)) => { // And similarly for projections. This should be redundant with // the previous check because any projection should have a // matching `Trait` predicate with the same inputs, but we do @@ -312,13 +311,14 @@ fn predicate_references_self<'tcx>( } ty::PredicateKind::WellFormed(..) | ty::PredicateKind::ObjectSafe(..) - | ty::PredicateKind::TypeOutlives(..) - | ty::PredicateKind::RegionOutlives(..) + | ty::PredicateKind::Clause(ty::Clause::TypeOutlives(..)) + | ty::PredicateKind::Clause(ty::Clause::RegionOutlives(..)) | ty::PredicateKind::ClosureKind(..) | ty::PredicateKind::Subtype(..) | ty::PredicateKind::Coerce(..) | ty::PredicateKind::ConstEvaluatable(..) | ty::PredicateKind::ConstEquate(..) + | ty::PredicateKind::Ambiguous | ty::PredicateKind::TypeWellFormedFromEnv(..) => None, } } @@ -337,19 +337,20 @@ fn generics_require_sized_self(tcx: TyCtxt<'_>, def_id: DefId) -> bool { let predicates = predicates.instantiate_identity(tcx).predicates; elaborate_predicates(tcx, predicates.into_iter()).any(|obligation| { match obligation.predicate.kind().skip_binder() { - ty::PredicateKind::Trait(ref trait_pred) => { + ty::PredicateKind::Clause(ty::Clause::Trait(ref trait_pred)) => { trait_pred.def_id() == sized_def_id && trait_pred.self_ty().is_param(0) } - ty::PredicateKind::Projection(..) + ty::PredicateKind::Clause(ty::Clause::Projection(..)) | ty::PredicateKind::Subtype(..) | ty::PredicateKind::Coerce(..) - | ty::PredicateKind::RegionOutlives(..) + | ty::PredicateKind::Clause(ty::Clause::RegionOutlives(..)) | ty::PredicateKind::WellFormed(..) | ty::PredicateKind::ObjectSafe(..) | ty::PredicateKind::ClosureKind(..) - | ty::PredicateKind::TypeOutlives(..) + | ty::PredicateKind::Clause(ty::Clause::TypeOutlives(..)) | ty::PredicateKind::ConstEvaluatable(..) | ty::PredicateKind::ConstEquate(..) + | ty::PredicateKind::Ambiguous | ty::PredicateKind::TypeWellFormedFromEnv(..) => false, } }) @@ -375,6 +376,7 @@ fn object_safety_violation_for_method( let span = match (&v, node) { (MethodViolationCode::ReferencesSelfInput(Some(span)), _) => *span, (MethodViolationCode::UndispatchableReceiver(Some(span)), _) => *span, + (MethodViolationCode::ReferencesImplTraitInTrait(span), _) => *span, (MethodViolationCode::ReferencesSelfOutput, Some(node)) => { node.fn_decl().map_or(method.ident(tcx).span, |decl| decl.output.span()) } @@ -437,8 +439,8 @@ 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); + if let Some(code) = contains_illegal_impl_trait_in_trait(tcx, method.def_id, sig.output()) { + return Some(code); } // We can't monomorphize things like `fn foo<A>(...)`. @@ -684,10 +686,9 @@ fn receiver_is_dispatchable<'tcx>( let param_env = tcx.param_env(method.def_id); // Self: Unsize<U> - let unsize_predicate = ty::Binder::dummy(ty::TraitRef { - def_id: unsize_did, - substs: tcx.mk_substs_trait(tcx.types.self_param, &[unsized_self_ty.into()]), - }) + let unsize_predicate = ty::Binder::dummy( + tcx.mk_trait_ref(unsize_did, [tcx.types.self_param, unsized_self_ty]), + ) .without_const() .to_predicate(tcx); @@ -719,14 +720,11 @@ fn receiver_is_dispatchable<'tcx>( // Receiver: DispatchFromDyn<Receiver[Self => U]> let obligation = { - let predicate = ty::Binder::dummy(ty::TraitRef { - def_id: dispatch_from_dyn_did, - substs: tcx.mk_substs_trait(receiver_ty, &[unsized_receiver_ty.into()]), - }) - .without_const() - .to_predicate(tcx); + let predicate = ty::Binder::dummy( + tcx.mk_trait_ref(dispatch_from_dyn_did, [receiver_ty, unsized_receiver_ty]), + ); - Obligation::new(ObligationCause::dummy(), param_env, predicate) + Obligation::new(tcx, ObligationCause::dummy(), param_env, predicate) }; let infcx = tcx.infer_ctxt().build(); @@ -838,23 +836,9 @@ fn contains_illegal_self_type_reference<'tcx, T: TypeVisitable<'tcx>>( } fn visit_const(&mut self, ct: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> { - // Constants can only influence object safety if they reference `Self`. + // Constants can only influence object safety if they are generic and reference `Self`. // This is only possible for unevaluated constants, so we walk these here. - // - // If `AbstractConst::from_const` returned an error we already failed compilation - // so we don't have to emit an additional error here. - use rustc_middle::ty::abstract_const::Node; - 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), - Node::Binop(..) | Node::UnaryOp(..) | Node::FunctionCall(_, _) => { - ControlFlow::CONTINUE - } - }) - } else { - ct.super_visit_with(self) - } + self.tcx.expand_abstract_consts(ct).super_visit_with(self) } } @@ -865,16 +849,24 @@ fn contains_illegal_self_type_reference<'tcx, T: TypeVisitable<'tcx>>( pub fn contains_illegal_impl_trait_in_trait<'tcx>( tcx: TyCtxt<'tcx>, + fn_def_id: DefId, ty: ty::Binder<'tcx, Ty<'tcx>>, -) -> bool { +) -> Option<MethodViolationCode> { + // This would be caught below, but rendering the error as a separate + // `async-specific` message is better. + if tcx.asyncness(fn_def_id).is_async() { + return Some(MethodViolationCode::AsyncFn); + } + // FIXME(RPITIT): Perhaps we should use a visitor here? - ty.skip_binder().walk().any(|arg| { + ty.skip_binder().walk().find_map(|arg| { if let ty::GenericArgKind::Type(ty) = arg.unpack() && let ty::Projection(proj) = ty.kind() + && tcx.def_kind(proj.item_def_id) == DefKind::ImplTraitPlaceholder { - tcx.def_kind(proj.item_def_id) == DefKind::ImplTraitPlaceholder + Some(MethodViolationCode::ReferencesImplTraitInTrait(tcx.def_span(proj.item_def_id))) } else { - false + None } }) } diff --git a/compiler/rustc_trait_selection/src/traits/on_unimplemented.rs b/compiler/rustc_trait_selection/src/traits/on_unimplemented.rs deleted file mode 100644 index 4a4f34b76..000000000 --- a/compiler/rustc_trait_selection/src/traits/on_unimplemented.rs +++ /dev/null @@ -1,392 +0,0 @@ -use rustc_ast::{MetaItem, NestedMetaItem}; -use rustc_attr as attr; -use rustc_data_structures::fx::FxHashMap; -use rustc_errors::{struct_span_err, ErrorGuaranteed}; -use rustc_hir::def_id::DefId; -use rustc_middle::ty::{self, GenericParamDefKind, TyCtxt}; -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); - -#[derive(Debug)] -pub struct OnUnimplementedDirective { - pub condition: Option<MetaItem>, - pub subcommands: Vec<OnUnimplementedDirective>, - pub message: Option<OnUnimplementedFormatString>, - pub label: Option<OnUnimplementedFormatString>, - pub note: Option<OnUnimplementedFormatString>, - pub parent_label: Option<OnUnimplementedFormatString>, - pub append_const_msg: Option<Option<Symbol>>, -} - -#[derive(Default)] -pub struct OnUnimplementedNote { - pub message: Option<String>, - pub label: Option<String>, - pub note: 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 - /// default one.. - pub append_const_msg: Option<Option<Symbol>>, -} - -impl<'tcx> OnUnimplementedDirective { - fn parse( - tcx: TyCtxt<'tcx>, - item_def_id: DefId, - items: &[NestedMetaItem], - span: Span, - is_root: bool, - ) -> Result<Self, ErrorGuaranteed> { - let mut errored = None; - let mut item_iter = items.iter(); - - let parse_value = |value_str| { - OnUnimplementedFormatString::try_parse(tcx, item_def_id, value_str, span).map(Some) - }; - - let condition = if is_root { - None - } else { - let cond = item_iter - .next() - .ok_or_else(|| tcx.sess.emit_err(EmptyOnClauseInOnUnimplemented { span }))? - .meta_item() - .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); - } - true - }); - Some(cond.clone()) - }; - - let mut message = None; - let mut label = None; - let mut note = None; - let mut parent_label = None; - let mut subcommands = vec![]; - let mut append_const_msg = None; - - for item in item_iter { - if item.has_name(sym::message) && message.is_none() { - if let Some(message_) = item.value_str() { - message = parse_value(message_)?; - continue; - } - } else if item.has_name(sym::label) && label.is_none() { - if let Some(label_) = item.value_str() { - label = parse_value(label_)?; - continue; - } - } else if item.has_name(sym::note) && note.is_none() { - if let Some(note_) = item.value_str() { - note = parse_value(note_)?; - continue; - } - } 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) - && is_root - && message.is_none() - && label.is_none() - && note.is_none() - { - if let Some(items) = item.meta_item_list() { - match Self::parse(tcx, item_def_id, &items, item.span(), false) { - Ok(subcommand) => subcommands.push(subcommand), - Err(reported) => errored = Some(reported), - }; - continue; - } - } else if item.has_name(sym::append_const_msg) && append_const_msg.is_none() { - if let Some(msg) = item.value_str() { - append_const_msg = Some(Some(msg)); - continue; - } else if item.is_word() { - append_const_msg = Some(None); - continue; - } - } - - // nothing found - tcx.sess.emit_err(NoValueInOnUnimplemented { span: item.span() }); - } - - if let Some(reported) = errored { - Err(reported) - } else { - Ok(OnUnimplementedDirective { - condition, - subcommands, - message, - label, - note, - parent_label, - append_const_msg, - }) - } - } - - pub fn of_item(tcx: TyCtxt<'tcx>, item_def_id: DefId) -> Result<Option<Self>, ErrorGuaranteed> { - let Some(attr) = tcx.get_attr(item_def_id, sym::rustc_on_unimplemented) else { - return Ok(None); - }; - - let result = if let Some(items) = attr.meta_item_list() { - Self::parse(tcx, item_def_id, &items, attr.span, true).map(Some) - } else if let Some(value) = attr.value_str() { - Ok(Some(OnUnimplementedDirective { - condition: None, - message: None, - subcommands: vec![], - label: Some(OnUnimplementedFormatString::try_parse( - tcx, - item_def_id, - value, - attr.span, - )?), - note: None, - parent_label: None, - append_const_msg: None, - })) - } else { - let reported = - tcx.sess.delay_span_bug(DUMMY_SP, "of_item: neither meta_item_list nor value_str"); - return Err(reported); - }; - debug!("of_item({:?}) = {:?}", item_def_id, result); - result - } - - pub fn evaluate( - &self, - tcx: TyCtxt<'tcx>, - trait_ref: ty::TraitRef<'tcx>, - options: &[(Symbol, Option<String>)], - ) -> OnUnimplementedNote { - let mut message = None; - let mut label = None; - let mut note = None; - let mut parent_label = None; - let mut append_const_msg = None; - info!("evaluate({:?}, trait_ref={:?}, options={:?})", self, trait_ref, options); - - let options_map: FxHashMap<Symbol, String> = - options.iter().filter_map(|(k, v)| v.as_ref().map(|v| (*k, v.to_owned()))).collect(); - - for command in self.subcommands.iter().chain(Some(self)).rev() { - if let Some(ref condition) = command.condition && !attr::eval_condition( - condition, - &tcx.sess.parse_sess, - Some(tcx.features()), - &mut |cfg| { - let value = cfg.value.map(|v| { - OnUnimplementedFormatString(v).format(tcx, trait_ref, &options_map) - }); - - options.contains(&(cfg.name, value)) - }, - ) { - debug!("evaluate: skipping {:?} due to condition", command); - continue; - } - debug!("evaluate: {:?} succeeded", command); - if let Some(ref message_) = command.message { - message = Some(message_.clone()); - } - - if let Some(ref label_) = command.label { - label = Some(label_.clone()); - } - - if let Some(ref note_) = command.note { - note = Some(note_.clone()); - } - - if let Some(ref parent_label_) = command.parent_label { - parent_label = Some(parent_label_.clone()); - } - - append_const_msg = command.append_const_msg; - } - - OnUnimplementedNote { - 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)), - parent_label: parent_label.map(|e_s| e_s.format(tcx, trait_ref, &options_map)), - append_const_msg, - } - } -} - -impl<'tcx> OnUnimplementedFormatString { - fn try_parse( - tcx: TyCtxt<'tcx>, - item_def_id: DefId, - from: Symbol, - err_sp: Span, - ) -> Result<Self, ErrorGuaranteed> { - let result = OnUnimplementedFormatString(from); - result.verify(tcx, item_def_id, err_sp)?; - Ok(result) - } - - fn verify( - &self, - tcx: TyCtxt<'tcx>, - item_def_id: DefId, - span: Span, - ) -> Result<(), ErrorGuaranteed> { - let trait_def_id = if tcx.is_trait(item_def_id) { - item_def_id - } else { - tcx.trait_id_of_impl(item_def_id) - .expect("expected `on_unimplemented` to correspond to a trait") - }; - let trait_name = tcx.item_name(trait_def_id); - let generics = tcx.generics_of(item_def_id); - let s = self.0.as_str(); - let parser = Parser::new(s, None, None, false, ParseMode::Format); - let mut result = Ok(()); - for token in parser { - match token { - Piece::String(_) => (), // Normal string, no need to check it - Piece::NextArgument(a) => match a.position { - Position::ArgumentNamed(s) => { - match Symbol::intern(s) { - // `{Self}` is allowed - kw::SelfUpper => (), - // `{ThisTraitsName}` is allowed - s if s == trait_name => (), - // `{from_method}` is allowed - sym::from_method => (), - // `{from_desugaring}` is allowed - sym::from_desugaring => (), - // `{ItemContext}` is allowed - sym::ItemContext => (), - // `{integral}` and `{integer}` and `{float}` are allowed - sym::integral | sym::integer_ | sym::float => (), - // So is `{A}` if A is a type parameter - s => match generics.params.iter().find(|param| param.name == s) { - Some(_) => (), - None => { - let reported = struct_span_err!( - tcx.sess, - span, - E0230, - "there is no parameter `{}` on {}", - s, - if trait_def_id == item_def_id { - format!("trait `{}`", trait_name) - } else { - "impl".to_string() - } - ) - .emit(); - result = Err(reported); - } - }, - } - } - // `{:1}` and `{}` are not to be used - Position::ArgumentIs(..) | Position::ArgumentImplicitlyIs(_) => { - let reported = struct_span_err!( - tcx.sess, - span, - E0231, - "only named substitution parameters are allowed" - ) - .emit(); - result = Err(reported); - } - }, - } - } - - result - } - - pub fn format( - &self, - tcx: TyCtxt<'tcx>, - trait_ref: ty::TraitRef<'tcx>, - options: &FxHashMap<Symbol, String>, - ) -> String { - let name = tcx.item_name(trait_ref.def_id); - let trait_str = tcx.def_path_str(trait_ref.def_id); - let generics = tcx.generics_of(trait_ref.def_id); - let generic_map = generics - .params - .iter() - .filter_map(|param| { - let value = match param.kind { - GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => { - trait_ref.substs[param.index as usize].to_string() - } - GenericParamDefKind::Lifetime => return None, - }; - let name = param.name; - Some((name, value)) - }) - .collect::<FxHashMap<Symbol, String>>(); - let empty_string = String::new(); - - let s = self.0.as_str(); - let parser = Parser::new(s, None, None, false, ParseMode::Format); - let item_context = (options.get(&sym::ItemContext)).unwrap_or(&empty_string); - parser - .map(|p| match p { - Piece::String(s) => s, - Piece::NextArgument(a) => match a.position { - Position::ArgumentNamed(s) => { - let s = Symbol::intern(s); - match generic_map.get(&s) { - Some(val) => val, - None if s == name => &trait_str, - None => { - if let Some(val) = options.get(&s) { - val - } else if s == sym::from_desugaring || s == sym::from_method { - // don't break messages using these two arguments incorrectly - &empty_string - } else if s == sym::ItemContext { - &item_context - } else if s == sym::integral { - "{integral}" - } else if s == sym::integer_ { - "{integer}" - } else if s == sym::float { - "{float}" - } else { - bug!( - "broken on_unimplemented {:?} for {:?}: \ - no argument matching {:?}", - self.0, - trait_ref, - s - ) - } - } - } - } - _ => bug!("broken on_unimplemented {:?} - bad format arg", self.0), - }, - }) - .collect() - } -} diff --git a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs index 108dae092..e1092a788 100644 --- a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs @@ -1,8 +1,8 @@ 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 crate::traits::ObligationCause; +use rustc_data_structures::fx::FxIndexSet; use rustc_hir as hir; use rustc_hir::HirId; use rustc_middle::ty::{self, ParamEnv, Ty}; @@ -22,7 +22,7 @@ pub trait InferCtxtExt<'a, 'tcx> { &'a self, param_env: ty::ParamEnv<'tcx>, body_id: hir::HirId, - tys: FxHashSet<Ty<'tcx>>, + tys: FxIndexSet<Ty<'tcx>>, ) -> Bounds<'a, 'tcx>; } @@ -74,20 +74,20 @@ impl<'a, 'tcx: 'a> InferCtxtExt<'a, 'tcx> for InferCtxt<'tcx> { 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); - } + let errors = super::fully_solve_obligations( + self, + constraints.outlives.iter().map(|constraint| { + self.query_outlives_constraint_to_obligation( + *constraint, + cause.clone(), + param_env, + ) + }), + ); 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, @@ -103,7 +103,7 @@ impl<'a, 'tcx: 'a> InferCtxtExt<'a, 'tcx> for InferCtxt<'tcx> { &'a self, param_env: ParamEnv<'tcx>, body_id: HirId, - tys: FxHashSet<Ty<'tcx>>, + tys: FxIndexSet<Ty<'tcx>>, ) -> Bounds<'a, 'tcx> { tys.into_iter() .map(move |ty| { diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index e4284b9d3..5789754e4 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -11,8 +11,8 @@ use super::Selection; use super::SelectionContext; use super::SelectionError; use super::{ - ImplSourceClosureData, ImplSourceDiscriminantKindData, ImplSourceFnPointerData, - ImplSourceGeneratorData, ImplSourcePointeeData, ImplSourceUserDefinedData, + ImplSourceClosureData, ImplSourceFnPointerData, ImplSourceFutureData, ImplSourceGeneratorData, + ImplSourceUserDefinedData, }; use super::{Normalized, NormalizedTy, ProjectionCacheEntry, ProjectionCacheKey}; @@ -27,7 +27,9 @@ use rustc_errors::ErrorGuaranteed; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; +use rustc_infer::infer::at::At; use rustc_infer::infer::resolve::OpportunisticRegionResolver; +use rustc_infer::traits::ImplSourceBuiltinData; use rustc_middle::traits::select::OverflowError; use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; use rustc_middle::ty::visit::{MaxUniverse, TypeVisitable}; @@ -47,6 +49,23 @@ pub type ProjectionTyObligation<'tcx> = Obligation<'tcx, ty::ProjectionTy<'tcx>> pub(super) struct InProgress; +pub trait NormalizeExt<'tcx> { + /// Normalize a value using the `AssocTypeNormalizer`. + /// + /// This normalization should be used when the type contains inference variables or the + /// projection may be fallible. + fn normalize<T: TypeFoldable<'tcx>>(&self, t: T) -> InferOk<'tcx, T>; +} + +impl<'tcx> NormalizeExt<'tcx> for At<'_, 'tcx> { + fn normalize<T: TypeFoldable<'tcx>>(&self, value: T) -> InferOk<'tcx, T> { + let mut selcx = SelectionContext::new(self.infcx); + let Normalized { value, obligations } = + normalize_with_depth(&mut selcx, self.param_env, self.cause.clone(), 0, value); + InferOk { value, obligations } + } +} + /// When attempting to resolve `<T as TraitRef>::Name` ... #[derive(Debug)] pub enum ProjectionError<'tcx> { @@ -193,14 +212,14 @@ pub(super) fn poly_project_and_unify_type<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &PolyProjectionObligation<'tcx>, ) -> ProjectAndUnifyResult<'tcx> { - let infcx = selcx.infcx(); + let infcx = selcx.infcx; let r = infcx.commit_if_ok(|_snapshot| { let old_universe = infcx.universe(); let placeholder_predicate = infcx.replace_bound_vars_with_placeholders(obligation.predicate); let new_universe = infcx.universe(); - let placeholder_obligation = obligation.with(placeholder_predicate); + let placeholder_obligation = obligation.with(infcx.tcx, placeholder_predicate); match project_and_unify_type(selcx, &placeholder_obligation) { ProjectAndUnifyResult::MismatchedProjectionTypes(e) => Err(e), ProjectAndUnifyResult::Holds(obligations) @@ -249,7 +268,7 @@ fn project_and_unify_type<'cx, 'tcx>( ) -> ProjectAndUnifyResult<'tcx> { let mut obligations = vec![]; - let infcx = selcx.infcx(); + let infcx = selcx.infcx; let normalized = match opt_normalize_projection_type( selcx, obligation.param_env, @@ -268,7 +287,7 @@ fn project_and_unify_type<'cx, 'tcx>( // 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 } = - selcx.infcx().replace_opaque_types_with_inference_vars( + selcx.infcx.replace_opaque_types_with_inference_vars( actual, obligation.cause.body_id, obligation.cause.span, @@ -288,39 +307,8 @@ fn project_and_unify_type<'cx, 'tcx>( } } -/// Normalizes any associated type projections in `value`, replacing -/// them with a fully resolved type where possible. The return value -/// combines the normalized result and any additional obligations that -/// were incurred as result. -pub fn normalize<'a, 'b, 'tcx, T>( - selcx: &'a mut SelectionContext<'b, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - cause: ObligationCause<'tcx>, - value: T, -) -> Normalized<'tcx, T> -where - T: TypeFoldable<'tcx>, -{ - let mut obligations = Vec::new(); - let value = normalize_to(selcx, param_env, cause, value, &mut obligations); - Normalized { value, obligations } -} - -pub fn normalize_to<'a, 'b, 'tcx, T>( - selcx: &'a mut SelectionContext<'b, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - cause: ObligationCause<'tcx>, - value: T, - obligations: &mut Vec<PredicateObligation<'tcx>>, -) -> T -where - T: TypeFoldable<'tcx>, -{ - normalize_with_depth_to(selcx, param_env, cause, 0, value, obligations) -} - /// As `normalize`, but with a custom depth. -pub fn normalize_with_depth<'a, 'b, 'tcx, T>( +pub(crate) fn normalize_with_depth<'a, 'b, 'tcx, T>( selcx: &'a mut SelectionContext<'b, 'tcx>, param_env: ty::ParamEnv<'tcx>, cause: ObligationCause<'tcx>, @@ -336,7 +324,7 @@ where } #[instrument(level = "info", skip(selcx, param_env, cause, obligations))] -pub fn normalize_with_depth_to<'a, 'b, 'tcx, T>( +pub(crate) fn normalize_with_depth_to<'a, 'b, 'tcx, T>( selcx: &'a mut SelectionContext<'b, 'tcx>, param_env: ty::ParamEnv<'tcx>, cause: ObligationCause<'tcx>, @@ -356,7 +344,7 @@ where } #[instrument(level = "info", skip(selcx, param_env, cause, obligations))] -pub fn try_normalize_with_depth_to<'a, 'b, 'tcx, T>( +pub(crate) fn try_normalize_with_depth_to<'a, 'b, 'tcx, T>( selcx: &'a mut SelectionContext<'b, 'tcx>, param_env: ty::ParamEnv<'tcx>, cause: ObligationCause<'tcx>, @@ -444,7 +432,7 @@ impl<'a, 'b, 'tcx> AssocTypeNormalizer<'a, 'b, 'tcx> { } fn fold<T: TypeFoldable<'tcx>>(&mut self, value: T) -> T { - let value = self.selcx.infcx().resolve_vars_if_possible(value); + let value = self.selcx.infcx.resolve_vars_if_possible(value); debug!(?value); assert!( @@ -516,13 +504,12 @@ impl<'a, 'b, 'tcx> TypeFolder<'tcx> for AssocTypeNormalizer<'a, 'b, 'tcx> { Reveal::All => { let recursion_limit = self.tcx().recursion_limit(); if !recursion_limit.value_within_limit(self.depth) { - let obligation = Obligation::with_depth( - self.cause.clone(), - recursion_limit.0, - self.param_env, - ty, + self.selcx.infcx.err_ctxt().report_overflow_error( + &ty, + self.cause.span, + true, + |_| {}, ); - self.selcx.infcx().err_ctxt().report_overflow_error(&obligation, true); } let substs = substs.fold_with(self); @@ -588,7 +575,7 @@ impl<'a, 'b, 'tcx> TypeFolder<'tcx> for AssocTypeNormalizer<'a, 'b, 'tcx> { // want to figure out how to register obligations with escaping vars // or handle this some other way. - let infcx = self.selcx.infcx(); + let infcx = self.selcx.infcx; let (data, mapped_regions, mapped_types, mapped_consts) = BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, data); let data = data.fold_with(self); @@ -632,13 +619,13 @@ 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> { let tcx = self.selcx.tcx(); - if tcx.lazy_normalization() { + if tcx.lazy_normalization() || !needs_normalization(&constant, self.param_env.reveal()) { constant } else { let constant = constant.super_fold_with(self); debug!(?constant, ?self.param_env); with_replaced_escaping_bound_vars( - self.selcx.infcx(), + self.selcx.infcx, &mut self.universes, constant, |constant| constant.eval(tcx, self.param_env), @@ -816,9 +803,7 @@ impl<'tcx> TypeFolder<'tcx> for BoundVarReplacer<'_, 'tcx> { let universe = self.universe_for(debruijn); let p = ty::PlaceholderConst { universe, name: bound_const }; self.mapped_consts.insert(p, bound_const); - self.infcx - .tcx - .mk_const(ty::ConstS { kind: ty::ConstKind::Placeholder(p), ty: ct.ty() }) + self.infcx.tcx.mk_const(p, ct.ty()) } _ => ct.super_fold_with(self), } @@ -829,7 +814,7 @@ impl<'tcx> TypeFolder<'tcx> for BoundVarReplacer<'_, 'tcx> { } } -// The inverse of `BoundVarReplacer`: replaces placeholders with the bound vars from which they came. +/// The inverse of [`BoundVarReplacer`]: replaces placeholders with the bound vars from which they came. pub struct PlaceholderReplacer<'me, 'tcx> { infcx: &'me InferCtxt<'tcx>, mapped_regions: BTreeMap<ty::PlaceholderRegion, ty::BoundRegion>, @@ -953,10 +938,7 @@ impl<'tcx> TypeFolder<'tcx> for PlaceholderReplacer<'_, 'tcx> { let db = ty::DebruijnIndex::from_usize( self.universe_indices.len() - index + self.current_index.as_usize() - 1, ); - self.tcx().mk_const(ty::ConstS { - kind: ty::ConstKind::Bound(db, *replace_var), - ty: ct.ty(), - }) + self.tcx().mk_const(ty::ConstKind::Bound(db, *replace_var), ct.ty()) } None => ct, } @@ -995,10 +977,7 @@ pub fn normalize_projection_type<'a, 'b, 'tcx>( // and a deferred predicate to resolve this when more type // information is available. - selcx - .infcx() - .infer_projection(param_env, projection_ty, cause, depth + 1, obligations) - .into() + selcx.infcx.infer_projection(param_env, projection_ty, cause, depth + 1, obligations).into() }) } @@ -1021,7 +1000,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>( depth: usize, obligations: &mut Vec<PredicateObligation<'tcx>>, ) -> Result<Option<Term<'tcx>>, InProgress> { - let infcx = selcx.infcx(); + let infcx = selcx.infcx; // Don't use the projection cache in intercrate mode - // the `infcx` may be re-used between intercrate in non-intercrate // mode, which could lead to using incorrect cache results. @@ -1100,7 +1079,8 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>( } } - let obligation = Obligation::with_depth(cause.clone(), depth, param_env, projection_ty); + let obligation = + Obligation::with_depth(selcx.tcx(), cause.clone(), depth, param_env, projection_ty); match project(selcx, &obligation) { Ok(Projected::Progress(Progress { @@ -1112,7 +1092,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>( // an impl, where-clause etc) and hence we must // re-normalize it - let projected_term = selcx.infcx().resolve_vars_if_possible(projected_term); + let projected_term = selcx.infcx.resolve_vars_if_possible(projected_term); let mut result = if projected_term.has_projections() { let mut normalizer = AssocTypeNormalizer::new( @@ -1208,9 +1188,9 @@ fn normalize_to_error<'a, 'tcx>( param_env, predicate: trait_ref.without_const().to_predicate(selcx.tcx()), }; - let tcx = selcx.infcx().tcx; + let tcx = selcx.infcx.tcx; let def_id = projection_ty.item_def_id; - let new_value = selcx.infcx().next_ty_var(TypeVariableOrigin { + let new_value = selcx.infcx.next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::NormalizeProjectionType, span: tcx.def_span(def_id), }); @@ -1330,11 +1310,10 @@ fn assemble_candidate_for_impl_trait_in_trait<'cx, 'tcx>( 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(); + ty::Binder::dummy(ty::TraitRef { def_id: trait_def_id, substs: trait_substs }); - let _ = - selcx.infcx().commit_if_ok(|_| match selcx.select(&obligation.with(trait_predicate)) { + let _ = selcx.infcx.commit_if_ok(|_| { + match selcx.select(&obligation.with(tcx, trait_predicate)) { Ok(Some(super::ImplSource::UserDefined(data))) => { candidate_set.push_candidate(ProjectionCandidate::ImplTraitInTrait( ImplTraitInTraitCandidate::Impl(data), @@ -1354,7 +1333,8 @@ fn assemble_candidate_for_impl_trait_in_trait<'cx, 'tcx>( candidate_set.mark_error(e); return Err(()); } - }); + } + }); } } @@ -1437,7 +1417,7 @@ fn assemble_candidates_from_object_ty<'cx, 'tcx>( let tcx = selcx.tcx(); let self_ty = obligation.predicate.self_ty(); - let object_ty = selcx.infcx().shallow_resolve(self_ty); + let object_ty = selcx.infcx.shallow_resolve(self_ty); let data = match object_ty.kind() { ty::Dynamic(data, ..) => data, ty::Infer(ty::TyVar(_)) => { @@ -1475,10 +1455,12 @@ fn assemble_candidates_from_predicates<'cx, 'tcx>( env_predicates: impl Iterator<Item = ty::Predicate<'tcx>>, potentially_unnormalized_candidates: bool, ) { - let infcx = selcx.infcx(); + let infcx = selcx.infcx; for predicate in env_predicates { let bound_predicate = predicate.kind(); - if let ty::PredicateKind::Projection(data) = predicate.kind().skip_binder() { + if let ty::PredicateKind::Clause(ty::Clause::Projection(data)) = + predicate.kind().skip_binder() + { let data = bound_predicate.rebind(data); if data.projection_def_id() != obligation.predicate.item_def_id { continue; @@ -1528,8 +1510,8 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( // 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())); - let trait_obligation = obligation.with(poly_trait_ref.to_poly_trait_predicate()); - let _ = selcx.infcx().commit_if_ok(|_| { + let trait_obligation = obligation.with(selcx.tcx(), poly_trait_ref); + let _ = selcx.infcx.commit_if_ok(|_| { let impl_source = match selcx.select(&trait_obligation) { Ok(Some(impl_source)) => impl_source, Ok(None) => { @@ -1546,6 +1528,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( let eligible = match &impl_source { super::ImplSource::Closure(_) | super::ImplSource::Generator(_) + | super::ImplSource::Future(_) | super::ImplSource::FnPointer(_) | super::ImplSource::TraitAlias(_) => true, super::ImplSource::UserDefined(impl_data) => { @@ -1586,7 +1569,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( if obligation.param_env.reveal() == Reveal::All { // NOTE(eddyb) inference variables can resolve to parameters, so // assume `poly_trait_ref` isn't monomorphic, if it contains any. - let poly_trait_ref = selcx.infcx().resolve_vars_if_possible(poly_trait_ref); + let poly_trait_ref = selcx.infcx.resolve_vars_if_possible(poly_trait_ref); !poly_trait_ref.still_further_specializable() } else { debug!( @@ -1598,128 +1581,126 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( } } } - super::ImplSource::DiscriminantKind(..) => { - // While `DiscriminantKind` is automatically implemented for every type, - // the concrete discriminant may not be known yet. - // - // Any type with multiple potential discriminant types is therefore not eligible. - let self_ty = selcx.infcx().shallow_resolve(obligation.predicate.self_ty()); - - match self_ty.kind() { - 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::Tuple(..) - // Integers and floats always have `u8` as their discriminant. - | ty::Infer(ty::InferTy::IntVar(_) | ty::InferTy::FloatVar(..)) => true, - - ty::Projection(..) - | ty::Opaque(..) - | ty::Param(..) - | ty::Bound(..) - | ty::Placeholder(..) - | ty::Infer(..) - | ty::Error(_) => false, - } - } - super::ImplSource::Pointee(..) => { - // While `Pointee` is automatically implemented for every type, - // the concrete metadata type may not be known yet. - // - // Any type with multiple potential metadata types is therefore not eligible. - let self_ty = selcx.infcx().shallow_resolve(obligation.predicate.self_ty()); - - let tail = selcx.tcx().struct_tail_with_normalize( - self_ty, - |ty| { - // We throw away any obligations we get from this, since we normalize - // and confirm these obligations once again during confirmation - normalize_with_depth( - selcx, - obligation.param_env, - obligation.cause.clone(), - obligation.recursion_depth + 1, - ty, - ) - .value - }, - || {}, - ); - - match tail.kind() { - ty::Bool - | ty::Char - | ty::Int(_) - | ty::Uint(_) - | ty::Float(_) - | ty::Str - | ty::Array(..) - | ty::Slice(_) - | ty::RawPtr(..) - | ty::Ref(..) - | ty::FnDef(..) - | ty::FnPtr(..) - | ty::Dynamic(..) - | ty::Closure(..) - | ty::Generator(..) - | ty::GeneratorWitness(..) - | ty::Never - // Extern types have unit metadata, according to RFC 2850 - | ty::Foreign(_) - // If returned by `struct_tail_without_normalization` this is a unit struct - // without any fields, or not a struct, and therefore is Sized. - | ty::Adt(..) - // If returned by `struct_tail_without_normalization` this is the empty tuple. - | ty::Tuple(..) - // Integers and floats are always Sized, and so have unit type metadata. - | ty::Infer(ty::InferTy::IntVar(_) | ty::InferTy::FloatVar(..)) => true, - - // type parameters, opaques, and unnormalized projections have pointer - // metadata if they're known (e.g. by the param_env) to be sized - ty::Param(_) | ty::Projection(..) | ty::Opaque(..) - if selcx.infcx().predicate_must_hold_modulo_regions( - &obligation.with( - ty::Binder::dummy(ty::TraitRef::new( - selcx.tcx().require_lang_item(LangItem::Sized, None), - selcx.tcx().mk_substs_trait(self_ty, &[]), - )) - .without_const() - .to_predicate(selcx.tcx()), - ), - ) => - { - true + super::ImplSource::Builtin(..) => { + // While a builtin impl may be known to exist, the associated type may not yet + // be known. Any type with multiple potential associated types is therefore + // not eligible. + let self_ty = selcx.infcx.shallow_resolve(obligation.predicate.self_ty()); + + let lang_items = selcx.tcx().lang_items(); + if lang_items.discriminant_kind_trait() == Some(poly_trait_ref.def_id()) { + match self_ty.kind() { + 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::Tuple(..) + // Integers and floats always have `u8` as their discriminant. + | ty::Infer(ty::InferTy::IntVar(_) | ty::InferTy::FloatVar(..)) => true, + + // type parameters, opaques, and unnormalized projections have pointer + // metadata if they're known (e.g. by the param_env) to be sized + ty::Param(_) + | ty::Projection(..) + | ty::Opaque(..) + | ty::Bound(..) + | ty::Placeholder(..) + | ty::Infer(..) + | ty::Error(_) => false, } + } else if lang_items.pointee_trait() == Some(poly_trait_ref.def_id()) { + let tail = selcx.tcx().struct_tail_with_normalize( + self_ty, + |ty| { + // We throw away any obligations we get from this, since we normalize + // and confirm these obligations once again during confirmation + normalize_with_depth( + selcx, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + ty, + ) + .value + }, + || {}, + ); - // FIXME(compiler-errors): are Bound and Placeholder types ever known sized? - ty::Param(_) - | ty::Projection(..) - | ty::Opaque(..) - | ty::Bound(..) - | ty::Placeholder(..) - | ty::Infer(..) - | ty::Error(_) => { - if tail.has_infer_types() { - candidate_set.mark_ambiguous(); + match tail.kind() { + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Str + | ty::Array(..) + | ty::Slice(_) + | ty::RawPtr(..) + | ty::Ref(..) + | ty::FnDef(..) + | ty::FnPtr(..) + | ty::Dynamic(..) + | ty::Closure(..) + | ty::Generator(..) + | ty::GeneratorWitness(..) + | ty::Never + // Extern types have unit metadata, according to RFC 2850 + | ty::Foreign(_) + // If returned by `struct_tail_without_normalization` this is a unit struct + // without any fields, or not a struct, and therefore is Sized. + | ty::Adt(..) + // If returned by `struct_tail_without_normalization` this is the empty tuple. + | ty::Tuple(..) + // Integers and floats are always Sized, and so have unit type metadata. + | ty::Infer(ty::InferTy::IntVar(_) | ty::InferTy::FloatVar(..)) => true, + + // type parameters, opaques, and unnormalized projections have pointer + // metadata if they're known (e.g. by the param_env) to be sized + ty::Param(_) | ty::Projection(..) | ty::Opaque(..) + if selcx.infcx.predicate_must_hold_modulo_regions( + &obligation.with( + selcx.tcx(), + ty::Binder::dummy( + selcx.tcx().at(obligation.cause.span()).mk_trait_ref(LangItem::Sized, [self_ty]), + ) + .without_const(), + ), + ) => + { + true + } + + // FIXME(compiler-errors): are Bound and Placeholder types ever known sized? + ty::Param(_) + | ty::Projection(..) + | ty::Opaque(..) + | ty::Bound(..) + | ty::Placeholder(..) + | ty::Infer(..) + | ty::Error(_) => { + if tail.has_infer_types() { + candidate_set.mark_ambiguous(); + } + false } - false } + } else { + bug!("unexpected builtin trait with associated type: {poly_trait_ref:?}") } } super::ImplSource::Param(..) => { @@ -1757,7 +1738,6 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( false } super::ImplSource::AutoImpl(..) - | super::ImplSource::Builtin(..) | super::ImplSource::TraitUpcasting(_) | super::ImplSource::ConstDestruct(_) => { // These traits have no associated types. @@ -1820,8 +1800,7 @@ fn confirm_candidate<'cx, 'tcx>( // when possible for this to work. See `auto-trait-projection-recursion.rs` // for a case where this matters. if progress.term.has_infer_regions() { - progress.term = - progress.term.fold_with(&mut OpportunisticRegionResolver::new(selcx.infcx())); + progress.term = progress.term.fold_with(&mut OpportunisticRegionResolver::new(selcx.infcx)); } progress } @@ -1834,16 +1813,13 @@ fn confirm_select_candidate<'cx, 'tcx>( match impl_source { super::ImplSource::UserDefined(data) => confirm_impl_candidate(selcx, obligation, data), super::ImplSource::Generator(data) => confirm_generator_candidate(selcx, obligation, data), + super::ImplSource::Future(data) => confirm_future_candidate(selcx, obligation, data), super::ImplSource::Closure(data) => confirm_closure_candidate(selcx, obligation, data), super::ImplSource::FnPointer(data) => confirm_fn_pointer_candidate(selcx, obligation, data), - super::ImplSource::DiscriminantKind(data) => { - confirm_discriminant_kind_candidate(selcx, obligation, data) - } - super::ImplSource::Pointee(data) => confirm_pointee_candidate(selcx, obligation, data), + super::ImplSource::Builtin(data) => confirm_builtin_candidate(selcx, obligation, data), super::ImplSource::Object(_) | super::ImplSource::AutoImpl(..) | super::ImplSource::Param(..) - | super::ImplSource::Builtin(..) | super::ImplSource::TraitUpcasting(_) | super::ImplSource::TraitAlias(..) | super::ImplSource::ConstDestruct(_) => { @@ -1907,74 +1883,97 @@ fn confirm_generator_candidate<'cx, 'tcx>( .with_addl_obligations(obligations) } -fn confirm_discriminant_kind_candidate<'cx, 'tcx>( +fn confirm_future_candidate<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionTyObligation<'tcx>, - _: ImplSourceDiscriminantKindData, + impl_source: ImplSourceFutureData<'tcx, PredicateObligation<'tcx>>, ) -> Progress<'tcx> { - let tcx = selcx.tcx(); + let gen_sig = impl_source.substs.as_generator().poly_sig(); + let Normalized { value: gen_sig, obligations } = normalize_with_depth( + selcx, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + gen_sig, + ); - let self_ty = selcx.infcx().shallow_resolve(obligation.predicate.self_ty()); - // We get here from `poly_project_and_unify_type` which replaces bound vars - // with placeholders - debug_assert!(!self_ty.has_escaping_bound_vars()); - let substs = tcx.mk_substs([self_ty.into()].iter()); + debug!(?obligation, ?gen_sig, ?obligations, "confirm_future_candidate"); - let discriminant_def_id = tcx.require_lang_item(LangItem::Discriminant, None); + let tcx = selcx.tcx(); + let fut_def_id = tcx.require_lang_item(LangItem::Future, None); - let predicate = ty::ProjectionPredicate { - projection_ty: ty::ProjectionTy { substs, item_def_id: discriminant_def_id }, - term: self_ty.discriminant_ty(tcx).into(), - }; + let predicate = super::util::future_trait_ref_and_outputs( + tcx, + fut_def_id, + obligation.predicate.self_ty(), + gen_sig, + ) + .map_bound(|(trait_ref, return_ty)| { + debug_assert_eq!(tcx.associated_item(obligation.predicate.item_def_id).name, sym::Output); - // We get here from `poly_project_and_unify_type` which replaces bound vars - // with placeholders, so dummy is okay here. - confirm_param_env_candidate(selcx, obligation, ty::Binder::dummy(predicate), false) + ty::ProjectionPredicate { + projection_ty: ty::ProjectionTy { + substs: trait_ref.substs, + item_def_id: obligation.predicate.item_def_id, + }, + term: return_ty.into(), + } + }); + + confirm_param_env_candidate(selcx, obligation, predicate, false) + .with_addl_obligations(impl_source.nested) + .with_addl_obligations(obligations) } -fn confirm_pointee_candidate<'cx, 'tcx>( +fn confirm_builtin_candidate<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionTyObligation<'tcx>, - _: ImplSourcePointeeData, + data: ImplSourceBuiltinData<PredicateObligation<'tcx>>, ) -> Progress<'tcx> { let tcx = selcx.tcx(); - let self_ty = selcx.infcx().shallow_resolve(obligation.predicate.self_ty()); - - let mut obligations = vec![]; - let (metadata_ty, check_is_sized) = self_ty.ptr_metadata_ty(tcx, |ty| { - normalize_with_depth_to( - selcx, - obligation.param_env, - obligation.cause.clone(), - obligation.recursion_depth + 1, - ty, - &mut obligations, - ) - }); - if check_is_sized { - let sized_predicate = ty::Binder::dummy(ty::TraitRef::new( - tcx.require_lang_item(LangItem::Sized, None), - tcx.mk_substs_trait(self_ty, &[]), - )) - .without_const() - .to_predicate(tcx); - obligations.push(Obligation::new( - obligation.cause.clone(), - obligation.param_env, - sized_predicate, - )); - } - + let self_ty = obligation.predicate.self_ty(); let substs = tcx.mk_substs([self_ty.into()].iter()); - let metadata_def_id = tcx.require_lang_item(LangItem::Metadata, None); - - let predicate = ty::ProjectionPredicate { - projection_ty: ty::ProjectionTy { substs, item_def_id: metadata_def_id }, - term: metadata_ty.into(), + let lang_items = tcx.lang_items(); + let item_def_id = obligation.predicate.item_def_id; + let trait_def_id = tcx.trait_of_item(item_def_id).unwrap(); + let (term, obligations) = if lang_items.discriminant_kind_trait() == Some(trait_def_id) { + let discriminant_def_id = tcx.require_lang_item(LangItem::Discriminant, None); + assert_eq!(discriminant_def_id, item_def_id); + + (self_ty.discriminant_ty(tcx).into(), Vec::new()) + } else if lang_items.pointee_trait() == Some(trait_def_id) { + let metadata_def_id = tcx.require_lang_item(LangItem::Metadata, None); + assert_eq!(metadata_def_id, item_def_id); + + let mut obligations = Vec::new(); + let (metadata_ty, check_is_sized) = self_ty.ptr_metadata_ty(tcx, |ty| { + normalize_with_depth_to( + selcx, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + ty, + &mut obligations, + ) + }); + if check_is_sized { + let sized_predicate = ty::Binder::dummy( + tcx.at(obligation.cause.span()).mk_trait_ref(LangItem::Sized, [self_ty]), + ) + .without_const(); + obligations.push(obligation.with(tcx, sized_predicate)); + } + (metadata_ty.into(), obligations) + } else { + bug!("unexpected builtin trait with associated type: {:?}", obligation.predicate); }; + let predicate = + ty::ProjectionPredicate { projection_ty: ty::ProjectionTy { substs, item_def_id }, term }; + confirm_param_env_candidate(selcx, obligation, ty::Binder::dummy(predicate), false) .with_addl_obligations(obligations) + .with_addl_obligations(data.nested) } fn confirm_fn_pointer_candidate<'cx, 'tcx>( @@ -1982,7 +1981,7 @@ fn confirm_fn_pointer_candidate<'cx, 'tcx>( obligation: &ProjectionTyObligation<'tcx>, fn_pointer_impl_source: ImplSourceFnPointerData<'tcx, PredicateObligation<'tcx>>, ) -> Progress<'tcx> { - let fn_type = selcx.infcx().shallow_resolve(fn_pointer_impl_source.fn_ty); + let fn_type = selcx.infcx.shallow_resolve(fn_pointer_impl_source.fn_ty); let sig = fn_type.fn_sig(selcx.tcx()); let Normalized { value: sig, obligations } = normalize_with_depth( selcx, @@ -2055,7 +2054,7 @@ fn confirm_param_env_candidate<'cx, 'tcx>( poly_cache_entry: ty::PolyProjectionPredicate<'tcx>, potentially_unnormalized_candidate: bool, ) -> Progress<'tcx> { - let infcx = selcx.infcx(); + let infcx = selcx.infcx; let cause = &obligation.cause; let param_env = obligation.param_env; @@ -2150,7 +2149,7 @@ fn confirm_impl_candidate<'cx, 'tcx>( // * `substs` ends up as `[u32, S]` let substs = obligation.predicate.substs.rebase_onto(tcx, trait_def_id, substs); let substs = - translate_substs(selcx.infcx(), param_env, impl_def_id, substs, assoc_ty.defining_node); + translate_substs(selcx.infcx, param_env, impl_def_id, substs, assoc_ty.defining_node); let ty = tcx.bound_type_of(assoc_ty.item.def_id); let is_const = matches!(tcx.def_kind(assoc_ty.item.def_id), DefKind::AssocConst); let term: ty::EarlyBinder<ty::Term<'tcx>> = if is_const { @@ -2158,7 +2157,7 @@ fn confirm_impl_candidate<'cx, 'tcx>( 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::UnevaluatedConst::new(did, identity_substs)); - ty.map_bound(|ty| tcx.mk_const(ty::ConstS { ty, kind }).into()) + ty.map_bound(|ty| tcx.mk_const(kind, ty).into()) } else { ty.map_bound(|ty| ty.into()) }; @@ -2177,7 +2176,7 @@ 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, + assoc_item: &ty::AssocItem, substs: ty::SubstsRef<'tcx>, ) -> bool { fn check_substs_compatible_inner<'tcx>( @@ -2209,7 +2208,10 @@ fn check_substs_compatible<'tcx>( true } - check_substs_compatible_inner(tcx, tcx.generics_of(assoc_ty.def_id), substs.as_slice()) + let generics = tcx.generics_of(assoc_item.def_id); + // Chop off any additional substs (RPITIT) substs + let substs = &substs[0..generics.count().min(substs.len())]; + check_substs_compatible_inner(tcx, generics, substs) } fn confirm_impl_trait_in_trait_candidate<'tcx>( @@ -2238,11 +2240,27 @@ fn confirm_impl_trait_in_trait_candidate<'tcx>( }; } - 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 impl_fn_substs = translate_substs( + selcx.infcx, + obligation.param_env, + data.impl_def_id, + impl_fn_substs, + leaf_def.defining_node, + ); + + if !check_substs_compatible(tcx, &leaf_def.item, impl_fn_substs) { + let err = tcx.ty_error_with_message( + obligation.cause.span, + "impl method and trait method have different parameters", + ); + return Progress { term: err.into(), obligations }; + } + + let impl_fn_def_id = leaf_def.item.def_id; let cause = ObligationCause::new( obligation.cause.span, @@ -2260,6 +2278,7 @@ fn confirm_impl_trait_in_trait_candidate<'tcx>( obligations.extend(std::iter::zip(predicates.predicates, predicates.spans).map( |(pred, span)| { Obligation::with_depth( + tcx, ObligationCause::new( obligation.cause.span, obligation.cause.body_id, @@ -2276,10 +2295,11 @@ fn confirm_impl_trait_in_trait_candidate<'tcx>( }, )); - let ty = super::normalize_to( + let ty = normalize_with_depth_to( selcx, obligation.param_env, cause.clone(), + obligation.recursion_depth + 1, 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]) @@ -2299,11 +2319,10 @@ fn assoc_ty_own_obligations<'cx, 'tcx>( nested: &mut Vec<PredicateObligation<'tcx>>, ) { let tcx = selcx.tcx(); - for predicate in tcx + let own = tcx .predicates_of(obligation.predicate.item_def_id) - .instantiate_own(tcx, obligation.predicate.substs) - .predicates - { + .instantiate_own(tcx, obligation.predicate.substs); + for (predicate, span) in std::iter::zip(own.predicates, own.spans) { let normalized = normalize_with_depth_to( selcx, obligation.param_env, @@ -2312,8 +2331,30 @@ fn assoc_ty_own_obligations<'cx, 'tcx>( predicate, nested, ); + + let nested_cause = if matches!( + obligation.cause.code(), + super::CompareImplItemObligation { .. } + | super::CheckAssociatedTypeBounds { .. } + | super::AscribeUserTypeProvePredicate(..) + ) { + obligation.cause.clone() + } else if span.is_dummy() { + ObligationCause::new( + obligation.cause.span, + obligation.cause.body_id, + super::ItemObligation(obligation.predicate.item_def_id), + ) + } else { + ObligationCause::new( + obligation.cause.span, + obligation.cause.body_id, + super::BindingObligation(obligation.predicate.item_def_id, span), + ) + }; nested.push(Obligation::with_depth( - obligation.cause.clone(), + tcx, + nested_cause, obligation.recursion_depth + 1, obligation.param_env, normalized, @@ -2385,7 +2426,7 @@ impl<'cx, 'tcx> ProjectionCacheKeyExt<'cx, 'tcx> for ProjectionCacheKey<'tcx> { selcx: &mut SelectionContext<'cx, 'tcx>, predicate: ty::PolyProjectionPredicate<'tcx>, ) -> Option<Self> { - let infcx = selcx.infcx(); + let infcx = selcx.infcx; // We don't do cross-snapshot caching of obligations with escaping regions, // so there's no cache key to use predicate.no_bound_vars().map(|predicate| { 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 c84f128dd..09b58894d 100644 --- a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs +++ b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs @@ -2,9 +2,7 @@ use rustc_middle::ty; use crate::infer::canonical::OriginalQueryValues; use crate::infer::InferCtxt; -use crate::traits::{ - EvaluationResult, OverflowError, PredicateObligation, SelectionContext, TraitQueryMode, -}; +use crate::traits::{EvaluationResult, OverflowError, PredicateObligation, SelectionContext}; pub trait InferCtxtExt<'tcx> { fn predicate_may_hold(&self, obligation: &PredicateObligation<'tcx>) -> bool; @@ -68,7 +66,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { let mut _orig_values = OriginalQueryValues::default(); let param_env = match obligation.predicate.kind().skip_binder() { - ty::PredicateKind::Trait(pred) => { + ty::PredicateKind::Clause(ty::Clause::Trait(pred)) => { // we ignore the value set to it. let mut _constness = pred.constness; obligation @@ -97,7 +95,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { match self.evaluate_obligation(obligation) { Ok(result) => result, Err(OverflowError::Canonical) => { - let mut selcx = SelectionContext::with_query_mode(&self, TraitQueryMode::Standard); + let mut selcx = SelectionContext::new(&self); selcx.evaluate_root_obligation(obligation).unwrap_or_else(|r| match r { OverflowError::Canonical => { span_bug!( diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs index 58e4597b7..7ad532d8a 100644 --- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -7,7 +7,7 @@ use crate::infer::canonical::OriginalQueryValues; use crate::infer::{InferCtxt, InferOk}; use crate::traits::error_reporting::TypeErrCtxtExt; use crate::traits::project::{needs_normalization, BoundVarReplacer, PlaceholderReplacer}; -use crate::traits::{Obligation, ObligationCause, PredicateObligation, Reveal}; +use crate::traits::{ObligationCause, PredicateObligation, Reveal}; use rustc_data_structures::sso::SsoHashMap; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_infer::traits::Normalized; @@ -22,13 +22,20 @@ use super::NoSolution; pub use rustc_middle::traits::query::NormalizationResult; -pub trait AtExt<'tcx> { - fn normalize<T>(&self, value: T) -> Result<Normalized<'tcx, T>, NoSolution> +pub trait QueryNormalizeExt<'tcx> { + /// Normalize a value using the `QueryNormalizer`. + /// + /// This normalization should *only* be used when the projection does not + /// have possible ambiguity or may not be well-formed. + /// + /// After codegen, when lifetimes do not matter, it is preferable to instead + /// use [`TyCtxt::normalize_erasing_regions`], which wraps this procedure. + fn query_normalize<T>(&self, value: T) -> Result<Normalized<'tcx, T>, NoSolution> where T: TypeFoldable<'tcx>; } -impl<'cx, 'tcx> AtExt<'tcx> for At<'cx, 'tcx> { +impl<'cx, 'tcx> QueryNormalizeExt<'tcx> for At<'cx, 'tcx> { /// Normalize `value` in the context of the inference context, /// yielding a resulting type, or an error if `value` cannot be /// normalized. If you don't care about regions, you should prefer @@ -42,7 +49,7 @@ impl<'cx, 'tcx> AtExt<'tcx> for At<'cx, 'tcx> { /// normalizing, but for now should be used only when we actually /// know that normalization will succeed, since error reporting /// and other details are still "under development". - fn normalize<T>(&self, value: T) -> Result<Normalized<'tcx, T>, NoSolution> + fn query_normalize<T>(&self, value: T) -> Result<Normalized<'tcx, T>, NoSolution> where T: TypeFoldable<'tcx>, { @@ -207,13 +214,12 @@ impl<'cx, 'tcx> FallibleTypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> { let substs = substs.try_fold_with(self)?; let recursion_limit = self.tcx().recursion_limit(); if !recursion_limit.value_within_limit(self.anon_depth) { - let obligation = Obligation::with_depth( - self.cause.clone(), - recursion_limit.0, - self.param_env, - ty, + self.infcx.err_ctxt().report_overflow_error( + &ty, + self.cause.span, + true, + |_| {}, ); - self.infcx.err_ctxt().report_overflow_error(&obligation, true); } let generic_ty = self.tcx().bound_type_of(def_id); @@ -353,6 +359,10 @@ impl<'cx, 'tcx> FallibleTypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> { &mut self, constant: ty::Const<'tcx>, ) -> Result<ty::Const<'tcx>, Self::Error> { + if !needs_normalization(&constant, self.param_env.reveal()) { + return Ok(constant); + } + let constant = constant.try_super_fold_with(self)?; debug!(?constant, ?self.param_env); Ok(crate::traits::project::with_replaced_escaping_bound_vars( diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs index 081308ac7..68434c2b6 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs @@ -15,7 +15,9 @@ impl<'tcx> super::QueryTypeOp<'tcx> for ProvePredicate<'tcx> { // `&T`, accounts for about 60% percentage of the predicates // we have to prove. No need to canonicalize and all that for // such cases. - if let ty::PredicateKind::Trait(trait_ref) = key.value.predicate.kind().skip_binder() { + if let ty::PredicateKind::Clause(ty::Clause::Trait(trait_ref)) = + key.value.predicate.kind().skip_binder() + { if let Some(sized_def_id) = tcx.lang_items().sized_trait() { if trait_ref.def_id() == sized_def_id { if trait_ref.self_ty().is_trivially_sized(tcx) { @@ -33,7 +35,7 @@ impl<'tcx> super::QueryTypeOp<'tcx> for ProvePredicate<'tcx> { mut canonicalized: Canonicalized<'tcx, ParamEnvAnd<'tcx, Self>>, ) -> Fallible<CanonicalizedQueryResponse<'tcx, ()>> { match canonicalized.value.value.predicate.kind().skip_binder() { - ty::PredicateKind::Trait(pred) => { + ty::PredicateKind::Clause(ty::Clause::Trait(pred)) => { canonicalized.value.param_env.remap_constness_with(pred.constness); } _ => canonicalized.value.param_env = canonicalized.value.param_env.without_const(), diff --git a/compiler/rustc_trait_selection/src/traits/relationships.rs b/compiler/rustc_trait_selection/src/traits/relationships.rs index 8cf500a46..bfa318787 100644 --- a/compiler/rustc_trait_selection/src/traits/relationships.rs +++ b/compiler/rustc_trait_selection/src/traits/relationships.rs @@ -1,8 +1,8 @@ use crate::infer::InferCtxt; use crate::traits::query::evaluate_obligation::InferCtxtExt; -use crate::traits::{ObligationCause, PredicateObligation}; +use crate::traits::PredicateObligation; use rustc_infer::traits::TraitEngine; -use rustc_middle::ty::{self, ToPredicate}; +use rustc_middle::ty; pub(crate) fn update<'tcx, T>( engine: &mut T, @@ -12,34 +12,22 @@ pub(crate) fn update<'tcx, T>( T: TraitEngine<'tcx>, { // (*) binder skipped - if let ty::PredicateKind::Trait(tpred) = obligation.predicate.kind().skip_binder() + if let ty::PredicateKind::Clause(ty::Clause::Trait(tpred)) = obligation.predicate.kind().skip_binder() && let Some(ty) = infcx.shallow_resolve(tpred.self_ty()).ty_vid().map(|t| infcx.root_var(t)) && infcx.tcx.lang_items().sized_trait().map_or(false, |st| st != tpred.trait_ref.def_id) { let new_self_ty = infcx.tcx.types.unit; - let trait_ref = ty::TraitRef { - substs: infcx.tcx.mk_substs_trait(new_self_ty, &tpred.trait_ref.substs[1..]), - ..tpred.trait_ref - }; - // Then construct a new obligation with Self = () added // to the ParamEnv, and see if it holds. - let o = rustc_infer::traits::Obligation::new( - ObligationCause::dummy(), - obligation.param_env, + let o = obligation.with(infcx.tcx, obligation .predicate .kind() .rebind( // (*) binder moved here - ty::PredicateKind::Trait(ty::TraitPredicate { - trait_ref, - constness: tpred.constness, - polarity: tpred.polarity, - }) - ) - .to_predicate(infcx.tcx), + ty::PredicateKind::Clause(ty::Clause::Trait(tpred.with_self_type(infcx.tcx, new_self_ty))) + ), ); // Don't report overflow errors. Otherwise equivalent to may_hold. if let Ok(result) = infcx.probe(|_| infcx.evaluate_obligation(&o)) && result.may_apply() { @@ -47,7 +35,9 @@ pub(crate) fn update<'tcx, T>( } } - if let ty::PredicateKind::Projection(predicate) = obligation.predicate.kind().skip_binder() { + if let ty::PredicateKind::Clause(ty::Clause::Projection(predicate)) = + obligation.predicate.kind().skip_binder() + { // If the projection predicate (Foo::Bar == X) has X as a non-TyVid, // we need to make it into one. if let Some(vid) = predicate.term.ty().and_then(|ty| ty.ty_vid()) { 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 4c5bc3339..e4b70f0d2 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -6,239 +6,21 @@ //! //! [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::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; -use rustc_middle::ty::{self, ToPredicate, Ty, TypeVisitable}; +use rustc_middle::ty::{self, Ty, TypeVisitable}; use rustc_target::spec::abi::Abi; use crate::traits; -use crate::traits::coherence::Conflict; use crate::traits::query::evaluate_obligation::InferCtxtExt; -use crate::traits::{util, SelectionResult}; -use crate::traits::{Ambiguous, ErrorReporting, Overflow, Unimplemented}; +use crate::traits::util; use super::BuiltinImplConditions; -use super::IntercrateAmbiguityCause; -use super::OverflowError; -use super::SelectionCandidate::{self, *}; -use super::{EvaluatedCandidate, SelectionCandidateSet, SelectionContext, TraitObligationStack}; +use super::SelectionCandidate::*; +use super::{SelectionCandidateSet, SelectionContext, TraitObligationStack}; impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { - #[instrument(level = "debug", skip(self), ret)] - pub(super) fn candidate_from_obligation<'o>( - &mut self, - stack: &TraitObligationStack<'o, 'tcx>, - ) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> { - // Watch out for overflow. This intentionally bypasses (and does - // not update) the cache. - self.check_recursion_limit(&stack.obligation, &stack.obligation)?; - - // Check the cache. Note that we freshen the trait-ref - // separately rather than using `stack.fresh_trait_ref` -- - // this is because we want the unbound variables to be - // replaced with fresh types starting from index 0. - let cache_fresh_trait_pred = self.infcx.freshen(stack.obligation.predicate); - debug!(?cache_fresh_trait_pred); - debug_assert!(!stack.obligation.predicate.has_escaping_bound_vars()); - - if let Some(c) = - self.check_candidate_cache(stack.obligation.param_env, cache_fresh_trait_pred) - { - debug!("CACHE HIT"); - return c; - } - - // If no match, compute result and insert into cache. - // - // FIXME(nikomatsakis) -- this cache is not taking into - // account cycles that may have occurred in forming the - // candidate. I don't know of any specific problems that - // result but it seems awfully suspicious. - let (candidate, dep_node) = - self.in_task(|this| this.candidate_from_obligation_no_cache(stack)); - - debug!("CACHE MISS"); - self.insert_candidate_cache( - stack.obligation.param_env, - cache_fresh_trait_pred, - dep_node, - candidate.clone(), - ); - candidate - } - - fn candidate_from_obligation_no_cache<'o>( - &mut self, - stack: &TraitObligationStack<'o, 'tcx>, - ) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> { - 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"); - // Heuristics: show the diagnostics when there are no candidates in crate. - if let Ok(candidate_set) = self.assemble_candidates(stack) { - let mut no_candidates_apply = true; - - for c in candidate_set.vec.iter() { - if self.evaluate_candidate(stack, &c)?.may_apply() { - no_candidates_apply = false; - break; - } - } - - if !candidate_set.ambiguous && no_candidates_apply { - let trait_ref = stack.obligation.predicate.skip_binder().trait_ref; - let self_ty = trait_ref.self_ty(); - let (trait_desc, self_desc) = with_no_trimmed_paths!({ - let trait_desc = trait_ref.print_only_trait_path().to_string(); - let self_desc = if self_ty.has_concrete_skeleton() { - Some(self_ty.to_string()) - } else { - None - }; - (trait_desc, self_desc) - }); - let cause = if let Conflict::Upstream = conflict { - IntercrateAmbiguityCause::UpstreamCrateUpdate { trait_desc, self_desc } - } else { - IntercrateAmbiguityCause::DownstreamCrate { trait_desc, self_desc } - }; - debug!(?cause, "evaluate_stack: pushing cause"); - self.intercrate_ambiguity_causes.as_mut().unwrap().insert(cause); - } - } - } - return Ok(None); - } - - let candidate_set = self.assemble_candidates(stack)?; - - if candidate_set.ambiguous { - debug!("candidate set contains ambig"); - return Ok(None); - } - - let candidates = candidate_set.vec; - - debug!(?stack, ?candidates, "assembled {} candidates", candidates.len()); - - // At this point, we know that each of the entries in the - // candidate set is *individually* applicable. Now we have to - // figure out if they contain mutual incompatibilities. This - // frequently arises if we have an unconstrained input type -- - // for example, we are looking for `$0: Eq` where `$0` is some - // unconstrained type variable. In that case, we'll get a - // candidate which assumes $0 == int, one that assumes `$0 == - // usize`, etc. This spells an ambiguity. - - let mut candidates = self.filter_impls(candidates, stack.obligation); - - // If there is more than one candidate, first winnow them down - // by considering extra conditions (nested obligations and so - // forth). We don't winnow if there is exactly one - // candidate. This is a relatively minor distinction but it - // can lead to better inference and error-reporting. An - // example would be if there was an impl: - // - // impl<T:Clone> Vec<T> { fn push_clone(...) { ... } } - // - // and we were to see some code `foo.push_clone()` where `boo` - // is a `Vec<Bar>` and `Bar` does not implement `Clone`. If - // we were to winnow, we'd wind up with zero candidates. - // Instead, we select the right impl now but report "`Bar` does - // not implement `Clone`". - if candidates.len() == 1 { - return self.filter_reservation_impls(candidates.pop().unwrap(), stack.obligation); - } - - // Winnow, but record the exact outcome of evaluation, which - // is needed for specialization. Propagate overflow if it occurs. - let mut candidates = candidates - .into_iter() - .map(|c| match self.evaluate_candidate(stack, &c) { - Ok(eval) if eval.may_apply() => { - Ok(Some(EvaluatedCandidate { candidate: c, evaluation: eval })) - } - Ok(_) => Ok(None), - Err(OverflowError::Canonical) => Err(Overflow(OverflowError::Canonical)), - Err(OverflowError::ErrorReporting) => Err(ErrorReporting), - Err(OverflowError::Error(e)) => Err(Overflow(OverflowError::Error(e))), - }) - .flat_map(Result::transpose) - .collect::<Result<Vec<_>, _>>()?; - - debug!(?stack, ?candidates, "winnowed to {} candidates", candidates.len()); - - 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 - // resolving specializations. - if candidates.len() > 1 { - let mut i = 0; - while i < candidates.len() { - let is_dup = (0..candidates.len()).filter(|&j| i != j).any(|j| { - self.candidate_should_be_dropped_in_favor_of( - &candidates[i], - &candidates[j], - needs_infer, - ) - }); - if is_dup { - debug!(candidate = ?candidates[i], "Dropping candidate #{}/{}", i, candidates.len()); - candidates.swap_remove(i); - } else { - debug!(candidate = ?candidates[i], "Retaining candidate #{}/{}", i, candidates.len()); - i += 1; - - // If there are *STILL* multiple candidates, give up - // and report ambiguity. - if i > 1 { - debug!("multiple matches, ambig"); - return Err(Ambiguous( - candidates - .into_iter() - .filter_map(|c| match c.candidate { - SelectionCandidate::ImplCandidate(def_id) => Some(def_id), - _ => None, - }) - .collect(), - )); - } - } - } - } - - // If there are *NO* candidates, then there are no impls -- - // that we know of, anyway. Note that in the case where there - // are unbound type variables within the obligation, it might - // be the case that you could still satisfy the obligation - // from another crate by instantiating the type variables with - // a type from another crate that does have an impl. This case - // is checked for in `evaluate_stack` (and hence users - // who might care about this case, like coherence, should use - // that function). - if candidates.is_empty() { - // If there's an error type, 'downgrade' our result from - // `Err(Unimplemented)` to `Ok(None)`. This helps us avoid - // emitting additional spurious errors, since we're guaranteed - // to have emitted at least one. - if stack.obligation.predicate.references_error() { - debug!(?stack.obligation.predicate, "found error type in predicate, treating as ambiguous"); - return Ok(None); - } - return Err(Unimplemented); - } - - // Just one candidate left. - self.filter_reservation_impls(candidates.pop().unwrap().candidate, stack.obligation) - } - #[instrument(skip(self, stack), level = "debug")] pub(super) fn assemble_candidates<'o>( &mut self, @@ -249,7 +31,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { param_env: obligation.param_env, cause: obligation.cause.clone(), recursion_depth: obligation.recursion_depth, - predicate: self.infcx().resolve_vars_if_possible(obligation.predicate), + predicate: self.infcx.resolve_vars_if_possible(obligation.predicate), }; if obligation.predicate.skip_binder().self_ty().is_ty_var() { @@ -293,10 +75,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { self.assemble_builtin_bound_candidates(copy_conditions, &mut candidates); } else if lang_items.discriminant_kind_trait() == Some(def_id) { // `DiscriminantKind` is automatically implemented for every type. - candidates.vec.push(DiscriminantKindCandidate); + candidates.vec.push(BuiltinCandidate { has_nested: false }); } else if lang_items.pointee_trait() == Some(def_id) { // `Pointee` is automatically implemented for every type. - candidates.vec.push(PointeeCandidate); + candidates.vec.push(BuiltinCandidate { has_nested: false }); } else if lang_items.sized_trait() == Some(def_id) { // Sized is never implementable by end-users, it is // always automatically computed. @@ -312,6 +94,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { 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.pointer_sized() == Some(def_id) { + self.assemble_candidate_for_ptr_sized(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 @@ -321,7 +105,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { self.assemble_builtin_bound_candidates(clone_conditions, &mut candidates); } - self.assemble_generator_candidates(obligation, &mut candidates); + if lang_items.gen_trait() == Some(def_id) { + self.assemble_generator_candidates(obligation, &mut candidates); + } else if lang_items.future_trait() == Some(def_id) { + self.assemble_future_candidates(obligation, &mut candidates); + } + self.assemble_closure_candidates(obligation, &mut candidates); self.assemble_fn_pointer_candidates(obligation, &mut candidates); self.assemble_candidates_from_impls(obligation, &mut candidates); @@ -409,16 +198,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation: &TraitObligation<'tcx>, candidates: &mut SelectionCandidateSet<'tcx>, ) { - if self.tcx().lang_items().gen_trait() != Some(obligation.predicate.def_id()) { - return; - } - // Okay to skip binder because the substs on generator types never // touch bound regions, they just capture the in-scope // type/region parameters. let self_ty = obligation.self_ty().skip_binder(); match self_ty.kind() { - ty::Generator(..) => { + // async constructs get lowered to a special kind of generator that + // should *not* `impl Generator`. + ty::Generator(did, ..) if !self.tcx().generator_is_async(*did) => { debug!(?self_ty, ?obligation, "assemble_generator_candidates",); candidates.vec.push(GeneratorCandidate); @@ -431,6 +218,23 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } + fn assemble_future_candidates( + &mut self, + obligation: &TraitObligation<'tcx>, + candidates: &mut SelectionCandidateSet<'tcx>, + ) { + let self_ty = obligation.self_ty().skip_binder(); + if let ty::Generator(did, ..) = self_ty.kind() { + // async constructs get lowered to a special kind of generator that + // should directly `impl Future`. + if self.tcx().generator_is_async(*did) { + debug!(?self_ty, ?obligation, "assemble_future_candidates",); + + candidates.vec.push(FutureCandidate); + } + } + } + /// Checks for the artificial impl that the compiler will create for an obligation like `X : /// FnMut<..>` where `X` is a closure type. /// @@ -442,7 +246,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation: &TraitObligation<'tcx>, candidates: &mut SelectionCandidateSet<'tcx>, ) { - let Some(kind) = self.tcx().fn_trait_kind_from_lang_item(obligation.predicate.def_id()) else { + let Some(kind) = self.tcx().fn_trait_kind_from_def_id(obligation.predicate.def_id()) else { return; }; @@ -480,7 +284,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { candidates: &mut SelectionCandidateSet<'tcx>, ) { // We provide impl of all fn traits for fn pointers. - if self.tcx().fn_trait_kind_from_lang_item(obligation.predicate.def_id()).is_none() { + if !self.tcx().is_fn_trait(obligation.predicate.def_id()) { return; } @@ -680,9 +484,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { debug!(?poly_trait_ref, "assemble_candidates_from_object_ty"); - let poly_trait_predicate = self.infcx().resolve_vars_if_possible(obligation.predicate); + 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); + self.infcx.replace_bound_vars_with_placeholders(poly_trait_predicate); // Count only those upcast versions that match the trait-ref // we are looking for. Specifically, do not only check for the @@ -713,48 +517,40 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ty: Ty<'tcx>, param_env: ty::ParamEnv<'tcx>, cause: &ObligationCause<'tcx>, - ) -> Option<(Ty<'tcx>, DefId)> { + ) -> Option<ty::PolyExistentialTraitRef<'tcx>> { let tcx = self.tcx(); if tcx.features().trait_upcasting { return None; } // <ty as Deref> - let trait_ref = ty::TraitRef { - def_id: tcx.lang_items().deref_trait()?, - substs: tcx.mk_substs_trait(ty, &[]), - }; + let trait_ref = tcx.mk_trait_ref(tcx.lang_items().deref_trait()?, [ty]); - let obligation = traits::Obligation::new( - cause.clone(), - param_env, - ty::Binder::dummy(trait_ref).without_const().to_predicate(tcx), - ); + let obligation = + traits::Obligation::new(tcx, cause.clone(), param_env, ty::Binder::dummy(trait_ref)); if !self.infcx.predicate_may_hold(&obligation) { return None; } - 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(), - 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 - } + self.infcx.probe(|_| { + 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(), + 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() { data.principal() } else { None } + }) } /// Searches for unsizing that might apply to `obligation`. @@ -787,7 +583,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { match (source.kind(), target.kind()) { // Trait+Kx+'a -> Trait+Ky+'b (upcasts). - (&ty::Dynamic(ref data_a, ..), &ty::Dynamic(ref data_b, ..)) => { + (&ty::Dynamic(ref data_a, _, ty::Dyn), &ty::Dynamic(ref data_b, _, ty::Dyn)) => { // Upcast coercions permit several things: // // 1. Dropping auto traits, e.g., `Foo + Send` to `Foo` @@ -814,24 +610,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let principal_a = data_a.principal().unwrap(); let target_trait_did = principal_def_id_b.unwrap(); let source_trait_ref = principal_a.with_self_ty(self.tcx(), source); - if let Some((deref_output_ty, deref_output_trait_did)) = self - .need_migrate_deref_output_trait_object( - source, - obligation.param_env, - &obligation.cause, - ) - { - if deref_output_trait_did == target_trait_did { - self.tcx().struct_span_lint_hir( - DEREF_INTO_DYN_SUPERTRAIT, - obligation.cause.body_id, - obligation.cause.span, - DelayDm(|| format!( - "`{}` implements `Deref` with supertrait `{}` as output", - source, deref_output_ty - )), - |lint| lint, - ); + if let Some(deref_trait_ref) = self.need_migrate_deref_output_trait_object( + source, + obligation.param_env, + &obligation.cause, + ) { + if deref_trait_ref.def_id() == target_trait_did { return; } } @@ -848,7 +632,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } // `T` -> `Trait` - (_, &ty::Dynamic(..)) => { + (_, &ty::Dynamic(_, _, ty::Dyn)) => { candidates.vec.push(BuiltinUnsizeCandidate); } @@ -951,7 +735,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { return; } - let self_ty = self.infcx().shallow_resolve(obligation.self_ty()); + let self_ty = self.infcx.shallow_resolve(obligation.self_ty()); match self_ty.skip_binder().kind() { ty::Opaque(..) | ty::Dynamic(..) @@ -1018,7 +802,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation: &TraitObligation<'tcx>, candidates: &mut SelectionCandidateSet<'tcx>, ) { - let self_ty = self.infcx().shallow_resolve(obligation.self_ty().skip_binder()); + 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 }); @@ -1054,4 +838,30 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | ty::Placeholder(_) => {} } } + + fn assemble_candidate_for_ptr_sized( + &mut self, + obligation: &TraitObligation<'tcx>, + candidates: &mut SelectionCandidateSet<'tcx>, + ) { + // The regions of a type don't affect the size of the type + let self_ty = self + .tcx() + .erase_regions(self.tcx().erase_late_bound_regions(obligation.predicate.self_ty())); + + // But if there are inference variables, we have to wait until it's resolved. + if self_ty.has_non_region_infer() { + candidates.ambiguous = true; + return; + } + + let usize_layout = + self.tcx().layout_of(ty::ParamEnv::empty().and(self.tcx().types.usize)).unwrap().layout; + if let Ok(layout) = self.tcx().layout_of(obligation.param_env.and(self_ty)) + && layout.layout.size() == usize_layout.size() + && layout.layout.align().abi == usize_layout.align().abi + { + candidates.vec.push(BuiltinCandidate { has_nested: false }); + } + } } diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index ed22058c6..fda415155 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -19,14 +19,18 @@ use rustc_span::def_id::DefId; use crate::traits::project::{normalize_with_depth, normalize_with_depth_to}; use crate::traits::util::{self, closure_trait_ref_and_return_type, predicate_for_trait_def}; +use crate::traits::vtable::{ + count_own_vtable_entries, prepare_vtable_segments, vtable_trait_first_method_offset, + VtblSegment, +}; use crate::traits::{ BuiltinDerivedObligation, ImplDerivedObligation, ImplDerivedObligationCause, ImplSource, ImplSourceAutoImplData, ImplSourceBuiltinData, ImplSourceClosureData, - ImplSourceConstDestructData, ImplSourceDiscriminantKindData, ImplSourceFnPointerData, - ImplSourceGeneratorData, ImplSourceObjectData, ImplSourcePointeeData, ImplSourceTraitAliasData, + ImplSourceConstDestructData, ImplSourceFnPointerData, ImplSourceFutureData, + ImplSourceGeneratorData, ImplSourceObjectData, ImplSourceTraitAliasData, ImplSourceTraitUpcastingData, ImplSourceUserDefinedData, Normalized, ObjectCastObligation, Obligation, ObligationCause, OutputTypeParameterMismatch, PredicateObligation, Selection, - SelectionError, TraitNotObjectSafe, TraitObligation, Unimplemented, VtblSegment, + SelectionError, TraitNotObjectSafe, TraitObligation, Unimplemented, }; use super::BuiltinImplConditions; @@ -89,17 +93,16 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ImplSource::Generator(vtable_generator) } + FutureCandidate => { + let vtable_future = self.confirm_future_candidate(obligation)?; + ImplSource::Future(vtable_future) + } + FnPointerCandidate { .. } => { let data = self.confirm_fn_pointer_candidate(obligation)?; ImplSource::FnPointer(data) } - DiscriminantKindCandidate => { - ImplSource::DiscriminantKind(ImplSourceDiscriminantKindData) - } - - PointeeCandidate => ImplSource::Pointee(ImplSourcePointeeData), - TraitAliasCandidate => { let data = self.confirm_trait_alias_candidate(obligation); ImplSource::TraitAlias(data) @@ -148,7 +151,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let trait_predicate = self.infcx.shallow_resolve(obligation.predicate); let placeholder_trait_predicate = - self.infcx().replace_bound_vars_with_placeholders(trait_predicate).trait_ref; + self.infcx.replace_bound_vars_with_placeholders(trait_predicate).trait_ref; let placeholder_self_ty = placeholder_trait_predicate.self_ty(); let placeholder_trait_predicate = ty::Binder::dummy(placeholder_trait_predicate); let (def_id, substs) = match *placeholder_self_ty.kind() { @@ -194,6 +197,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut obligations, ); obligations.push(Obligation::with_depth( + self.tcx(), obligation.cause.clone(), obligation.recursion_depth + 1, obligation.param_env, @@ -482,11 +486,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { super_trait, &mut nested, ); - nested.push(Obligation::new( - obligation.cause.clone(), - obligation.param_env, - normalized_super_trait, - )); + nested.push(obligation.with(tcx, normalized_super_trait)); } let assoc_types: Vec<_> = tcx @@ -555,13 +555,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { GenericParamDefKind::Const { .. } => { let bound_var = ty::BoundVariableKind::Const; bound_vars.push(bound_var); - tcx.mk_const(ty::ConstS { - ty: tcx.type_of(param.def_id), - kind: ty::ConstKind::Bound( + tcx.mk_const( + ty::ConstKind::Bound( ty::INNERMOST, ty::BoundVar::from_usize(bound_vars.len() - 1), ), - }) + tcx.type_of(param.def_id), + ) .into() } }); @@ -581,17 +581,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { subst_bound, &mut nested, ); - nested.push(Obligation::new( - obligation.cause.clone(), - obligation.param_env, - normalized_bound, - )); + nested.push(obligation.with(tcx, normalized_bound)); } } debug!(?nested, "object nested obligations"); - let vtable_base = super::super::vtable_trait_first_method_offset( + let vtable_base = vtable_trait_first_method_offset( tcx, (unnormalized_upcast_trait_ref, ty::Binder::dummy(object_trait_ref)), ); @@ -606,8 +602,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { { debug!(?obligation, "confirm_fn_pointer_candidate"); - // Okay to skip binder; it is reintroduced below. - let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder()); + let self_ty = self + .infcx + .shallow_resolve(obligation.self_ty().no_bound_vars()) + .expect("fn pointer should not capture bound vars from predicate"); let sig = self_ty.fn_sig(self.tcx()); let trait_ref = closure_trait_ref_and_return_type( self.tcx(), @@ -622,15 +620,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // 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 = self.infcx.replace_bound_vars_with_placeholders(sig.output()); let output_ty = normalize_with_depth_to( self, obligation.param_env, @@ -639,15 +629,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { 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()), - )); + let tr = + ty::Binder::dummy(self.tcx().at(cause.span).mk_trait_ref(LangItem::Sized, [output_ty])); + nested.push(Obligation::new(self.infcx.tcx, cause, obligation.param_env, tr)); Ok(ImplSourceFnPointerData { fn_ty: self_ty, nested }) } @@ -659,7 +643,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { 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 predicate = self.infcx.replace_bound_vars_with_placeholders(obligation.predicate); let trait_ref = predicate.trait_ref; let trait_def_id = trait_ref.def_id; let substs = trait_ref.substs; @@ -693,7 +677,24 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { debug!(?obligation, ?generator_def_id, ?substs, "confirm_generator_candidate"); - let trait_ref = self.generator_trait_ref_unnormalized(obligation, substs); + let gen_sig = substs.as_generator().poly_sig(); + + // NOTE: The self-type is a generator type and hence is + // in fact unparameterized (or at least does not reference any + // regions bound in the obligation). + let self_ty = obligation + .predicate + .self_ty() + .no_bound_vars() + .expect("unboxed closure type should not capture bound vars from the predicate"); + + let trait_ref = super::util::generator_trait_ref_and_outputs( + self.tcx(), + obligation.predicate.def_id(), + self_ty, + gen_sig, + ) + .map_bound(|(trait_ref, ..)| trait_ref); let nested = self.confirm_poly_trait_refs(obligation, trait_ref)?; debug!(?trait_ref, ?nested, "generator candidate obligations"); @@ -701,6 +702,36 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { Ok(ImplSourceGeneratorData { generator_def_id, substs, nested }) } + fn confirm_future_candidate( + &mut self, + obligation: &TraitObligation<'tcx>, + ) -> Result<ImplSourceFutureData<'tcx, PredicateObligation<'tcx>>, SelectionError<'tcx>> { + // Okay to skip binder because the substs on generator types never + // touch bound regions, they just capture the in-scope + // type/region parameters. + let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder()); + let ty::Generator(generator_def_id, substs, _) = *self_ty.kind() else { + bug!("closure candidate for non-closure {:?}", obligation); + }; + + debug!(?obligation, ?generator_def_id, ?substs, "confirm_future_candidate"); + + let gen_sig = substs.as_generator().poly_sig(); + + let trait_ref = super::util::future_trait_ref_and_outputs( + self.tcx(), + obligation.predicate.def_id(), + obligation.predicate.no_bound_vars().expect("future has no bound vars").self_ty(), + gen_sig, + ) + .map_bound(|(trait_ref, ..)| trait_ref); + + let nested = self.confirm_poly_trait_refs(obligation, trait_ref)?; + debug!(?trait_ref, ?nested, "future candidate obligations"); + + Ok(ImplSourceFutureData { generator_def_id, substs, nested }) + } + #[instrument(skip(self), level = "debug")] fn confirm_closure_candidate( &mut self, @@ -708,7 +739,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ) -> Result<ImplSourceClosureData<'tcx, PredicateObligation<'tcx>>, SelectionError<'tcx>> { let kind = self .tcx() - .fn_trait_kind_from_lang_item(obligation.predicate.def_id()) + .fn_trait_kind_from_def_id(obligation.predicate.def_id()) .unwrap_or_else(|| bug!("closure candidate for non-fn trait {:?}", obligation)); // Okay to skip binder because the substs on closure types never @@ -727,11 +758,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // FIXME: Chalk if !self.tcx().sess.opts.unstable_opts.chalk { - nested.push(Obligation::new( - obligation.cause.clone(), - obligation.param_env, - ty::Binder::dummy(ty::PredicateKind::ClosureKind(closure_def_id, substs, kind)) - .to_predicate(self.tcx()), + nested.push(obligation.with( + self.tcx(), + ty::Binder::dummy(ty::PredicateKind::ClosureKind(closure_def_id, substs, kind)), )); } @@ -813,9 +842,10 @@ 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, repr_a), &ty::Dynamic(ref data_b, r_b, repr_b)) - if repr_a == repr_b => - { + ( + &ty::Dynamic(ref data_a, r_a, repr_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 principal_a = data_a.principal().unwrap(); @@ -841,7 +871,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, repr_b); + let source_trait = tcx.mk_dynamic(existential_predicates, r_b, repr_a); // Require that the traits involved in this upcast are **equal**; // only the **lifetime bound** is changed. @@ -860,10 +890,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ); let outlives = ty::OutlivesPredicate(r_a, r_b); nested.push(Obligation::with_depth( + tcx, cause, obligation.recursion_depth + 1, obligation.param_env, - obligation.predicate.rebind(outlives).to_predicate(tcx), + obligation.predicate.rebind(outlives), )); } _ => bug!(), @@ -877,7 +908,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { vptr_offset += TyCtxt::COMMON_VTABLE_ENTRIES.len(); } VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => { - vptr_offset += util::count_own_vtable_entries(tcx, trait_ref); + vptr_offset += count_own_vtable_entries(tcx, trait_ref); if trait_ref == upcast_trait_ref { if emit_vptr { return ControlFlow::Break(Some(vptr_offset)); @@ -896,8 +927,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { }; let vtable_vptr_slot = - super::super::prepare_vtable_segments(tcx, source_trait_ref, vtable_segment_callback) - .unwrap(); + prepare_vtable_segments(tcx, source_trait_ref, vtable_segment_callback).unwrap(); Ok(ImplSourceTraitUpcastingData { upcast_trait_ref, vtable_vptr_slot, nested }) } @@ -919,7 +949,9 @@ 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::Dyn), &ty::Dynamic(ref data_b, r_b, ty::Dyn)) => { + (&ty::Dynamic(ref data_a, r_a, dyn_a), &ty::Dynamic(ref data_b, r_b, dyn_b)) + if dyn_a == dyn_b => + { // 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 @@ -938,7 +970,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, ty::Dyn); + let source_trait = tcx.mk_dynamic(existential_predicates, r_b, dyn_a); // Require that the traits involved in this upcast are **equal**; // only the **lifetime bound** is changed. @@ -957,10 +989,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ); let outlives = ty::OutlivesPredicate(r_a, r_b); nested.push(Obligation::with_depth( + tcx, cause, obligation.recursion_depth + 1, obligation.param_env, - obligation.predicate.rebind(outlives).to_predicate(tcx), + obligation.predicate.rebind(outlives), )); } @@ -979,6 +1012,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let predicate_to_obligation = |predicate| { Obligation::with_depth( + tcx, cause.clone(), obligation.recursion_depth + 1, obligation.param_env, @@ -999,10 +1033,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ); // We can only make objects from sized types. - let tr = ty::Binder::dummy(ty::TraitRef::new( - tcx.require_lang_item(LangItem::Sized, None), - tcx.mk_substs_trait(source, &[]), - )); + let tr = + ty::Binder::dummy(tcx.at(cause.span).mk_trait_ref(LangItem::Sized, [source])); nested.push(predicate_to_obligation(tr.without_const().to_predicate(tcx))); // If the type is `Foo + 'a`, ensure that the type @@ -1108,8 +1140,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation.cause.clone(), obligation.predicate.def_id(), obligation.recursion_depth + 1, - source_tail, - &[target_tail.into()], + [source_tail, target_tail], )); } @@ -1139,13 +1170,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation.cause.clone(), obligation.predicate.def_id(), obligation.recursion_depth + 1, - a_last, - &[b_last.into()], + [a_last, b_last], ) })); } - _ => bug!(), + _ => bug!("source: {source}, target: {target}"), }; Ok(ImplSourceBuiltinData { nested }) @@ -1255,20 +1285,19 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation.param_env, cause.clone(), obligation.recursion_depth + 1, - self_ty - .rebind(ty::TraitPredicate { - trait_ref: ty::TraitRef { - def_id: self.tcx().require_lang_item(LangItem::Destruct, None), - substs: self.tcx().mk_substs_trait(nested_ty, &[]), - }, - constness: ty::BoundConstness::ConstIfConst, - polarity: ty::ImplPolarity::Positive, - }) - .to_predicate(tcx), + self_ty.rebind(ty::TraitPredicate { + trait_ref: self + .tcx() + .at(cause.span) + .mk_trait_ref(LangItem::Destruct, [nested_ty]), + constness: ty::BoundConstness::ConstIfConst, + polarity: ty::ImplPolarity::Positive, + }), &mut nested, ); nested.push(Obligation::with_depth( + tcx, cause.clone(), obligation.recursion_depth + 1, obligation.param_env, @@ -1280,18 +1309,17 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // since it's either not `const Drop` (and we raise an error during selection), // or it's an ADT (and we need to check for a custom impl during selection) _ => { - let predicate = self_ty - .rebind(ty::TraitPredicate { - trait_ref: ty::TraitRef { - def_id: self.tcx().require_lang_item(LangItem::Destruct, None), - substs: self.tcx().mk_substs_trait(nested_ty, &[]), - }, - constness: ty::BoundConstness::ConstIfConst, - polarity: ty::ImplPolarity::Positive, - }) - .to_predicate(tcx); + let predicate = self_ty.rebind(ty::TraitPredicate { + trait_ref: self + .tcx() + .at(cause.span) + .mk_trait_ref(LangItem::Destruct, [nested_ty]), + constness: ty::BoundConstness::ConstIfConst, + polarity: ty::ImplPolarity::Positive, + }); nested.push(Obligation::with_depth( + tcx, cause.clone(), obligation.recursion_depth + 1, obligation.param_env, diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 9ebff4892..035deb616 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -2,6 +2,12 @@ //! //! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/resolution.html#selection +// FIXME: The `map` field in ProvisionalEvaluationCache should be changed to +// a `FxIndexMap` to avoid query instability, but right now it causes a perf regression. This would be +// fixed or at least lightened by the addition of the `drain_filter` method to `FxIndexMap` +// Relevant: https://github.com/rust-lang/rust/pull/103723 and https://github.com/bluss/indexmap/issues/242 +#![allow(rustc::potential_query_instability)] + use self::EvaluationResult::*; use self::SelectionCandidate::*; @@ -24,9 +30,11 @@ use crate::traits::error_reporting::TypeErrCtxtExt; use crate::traits::project::ProjectAndUnifyResult; use crate::traits::project::ProjectionCacheKeyExt; use crate::traits::ProjectionCacheKey; -use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; +use crate::traits::Unimplemented; +use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; use rustc_data_structures::stack::ensure_sufficient_stack; -use rustc_errors::{Diagnostic, ErrorGuaranteed}; +use rustc_errors::Diagnostic; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_infer::infer::LateBoundRegionConversionTime; @@ -47,6 +55,7 @@ use std::fmt::{self, Display}; use std::iter; pub use rustc_middle::traits::select::*; +use rustc_middle::ty::print::with_no_trimmed_paths; mod candidate_assembly; mod confirmation; @@ -93,7 +102,7 @@ impl IntercrateAmbiguityCause { } pub struct SelectionContext<'cx, 'tcx> { - infcx: &'cx InferCtxt<'tcx>, + pub infcx: &'cx InferCtxt<'tcx>, /// Freshener used specifically for entries on the obligation /// stack. This ensures that all entries on the stack at one time @@ -102,25 +111,6 @@ pub struct SelectionContext<'cx, 'tcx> { /// require themselves. freshener: TypeFreshener<'cx, 'tcx>, - /// During coherence we have to assume that other crates may add - /// additional impls which we currently don't know about. - /// - /// To deal with this evaluation should be conservative - /// and consider the possibility of impls from outside this crate. - /// This comes up primarily when resolving ambiguity. Imagine - /// there is some trait reference `$0: Bar` where `$0` is an - /// inference variable. If `intercrate` is true, then we can never - /// say for sure that this reference is not implemented, even if - /// there are *no impls at all for `Bar`*, because `$0` could be - /// bound to some type that in a downstream crate that implements - /// `Bar`. - /// - /// Outside of coherence we set this to false because we are only - /// interested in types that the user could actually have written. - /// In other words, we consider `$0: Bar` to be unimplemented if - /// there is no type that the user could *actually name* that - /// would satisfy it. This avoids crippling inference, basically. - intercrate: bool, /// If `intercrate` is set, we remember predicates which were /// considered ambiguous because of impls potentially added in other crates. /// This is used in coherence to give improved diagnostics. @@ -218,16 +208,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { SelectionContext { infcx, freshener: infcx.freshener_keep_static(), - intercrate: false, 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<'tcx>, query_mode: TraitQueryMode, @@ -239,7 +224,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /// Enables tracking of intercrate ambiguity causes. See /// the documentation of [`Self::intercrate_ambiguity_causes`] for more. pub fn enable_tracking_intercrate_ambiguity_causes(&mut self) { - assert!(self.intercrate); + assert!(self.is_intercrate()); assert!(self.intercrate_ambiguity_causes.is_none()); self.intercrate_ambiguity_causes = Some(FxIndexSet::default()); debug!("selcx: enable_tracking_intercrate_ambiguity_causes"); @@ -249,20 +234,16 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /// was enabled and disables tracking at the same time. If /// tracking is not enabled, just returns an empty vector. pub fn take_intercrate_ambiguity_causes(&mut self) -> FxIndexSet<IntercrateAmbiguityCause> { - assert!(self.intercrate); + assert!(self.is_intercrate()); self.intercrate_ambiguity_causes.take().unwrap_or_default() } - pub fn infcx(&self) -> &'cx InferCtxt<'tcx> { - self.infcx - } - pub fn tcx(&self) -> TyCtxt<'tcx> { self.infcx.tcx } pub fn is_intercrate(&self) -> bool { - self.intercrate + self.infcx.intercrate } /////////////////////////////////////////////////////////////////////////// @@ -294,9 +275,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { assert!(self.query_mode == TraitQueryMode::Canonical); return Err(SelectionError::Overflow(OverflowError::Canonical)); } - Err(SelectionError::Ambiguous(_)) => { - return Ok(None); - } Err(e) => { return Err(e); } @@ -328,6 +306,213 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { self.candidate_from_obligation(&stack) } + #[instrument(level = "debug", skip(self), ret)] + fn candidate_from_obligation<'o>( + &mut self, + stack: &TraitObligationStack<'o, 'tcx>, + ) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> { + // Watch out for overflow. This intentionally bypasses (and does + // not update) the cache. + self.check_recursion_limit(&stack.obligation, &stack.obligation)?; + + // Check the cache. Note that we freshen the trait-ref + // separately rather than using `stack.fresh_trait_ref` -- + // this is because we want the unbound variables to be + // replaced with fresh types starting from index 0. + let cache_fresh_trait_pred = self.infcx.freshen(stack.obligation.predicate); + debug!(?cache_fresh_trait_pred); + debug_assert!(!stack.obligation.predicate.has_escaping_bound_vars()); + + if let Some(c) = + self.check_candidate_cache(stack.obligation.param_env, cache_fresh_trait_pred) + { + debug!("CACHE HIT"); + return c; + } + + // If no match, compute result and insert into cache. + // + // FIXME(nikomatsakis) -- this cache is not taking into + // account cycles that may have occurred in forming the + // candidate. I don't know of any specific problems that + // result but it seems awfully suspicious. + let (candidate, dep_node) = + self.in_task(|this| this.candidate_from_obligation_no_cache(stack)); + + debug!("CACHE MISS"); + self.insert_candidate_cache( + stack.obligation.param_env, + cache_fresh_trait_pred, + dep_node, + candidate.clone(), + ); + candidate + } + + fn candidate_from_obligation_no_cache<'o>( + &mut self, + stack: &TraitObligationStack<'o, 'tcx>, + ) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> { + 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"); + // Heuristics: show the diagnostics when there are no candidates in crate. + if let Ok(candidate_set) = self.assemble_candidates(stack) { + let mut no_candidates_apply = true; + + for c in candidate_set.vec.iter() { + if self.evaluate_candidate(stack, &c)?.may_apply() { + no_candidates_apply = false; + break; + } + } + + if !candidate_set.ambiguous && no_candidates_apply { + let trait_ref = stack.obligation.predicate.skip_binder().trait_ref; + if !trait_ref.references_error() { + let self_ty = trait_ref.self_ty(); + let (trait_desc, self_desc) = with_no_trimmed_paths!({ + let trait_desc = trait_ref.print_only_trait_path().to_string(); + let self_desc = if self_ty.has_concrete_skeleton() { + Some(self_ty.to_string()) + } else { + None + }; + (trait_desc, self_desc) + }); + let cause = if let Conflict::Upstream = conflict { + IntercrateAmbiguityCause::UpstreamCrateUpdate { + trait_desc, + self_desc, + } + } else { + IntercrateAmbiguityCause::DownstreamCrate { trait_desc, self_desc } + }; + debug!(?cause, "evaluate_stack: pushing cause"); + self.intercrate_ambiguity_causes.as_mut().unwrap().insert(cause); + } + } + } + } + return Ok(None); + } + + let candidate_set = self.assemble_candidates(stack)?; + + if candidate_set.ambiguous { + debug!("candidate set contains ambig"); + return Ok(None); + } + + let candidates = candidate_set.vec; + + debug!(?stack, ?candidates, "assembled {} candidates", candidates.len()); + + // At this point, we know that each of the entries in the + // candidate set is *individually* applicable. Now we have to + // figure out if they contain mutual incompatibilities. This + // frequently arises if we have an unconstrained input type -- + // for example, we are looking for `$0: Eq` where `$0` is some + // unconstrained type variable. In that case, we'll get a + // candidate which assumes $0 == int, one that assumes `$0 == + // usize`, etc. This spells an ambiguity. + + let mut candidates = self.filter_impls(candidates, stack.obligation); + + // If there is more than one candidate, first winnow them down + // by considering extra conditions (nested obligations and so + // forth). We don't winnow if there is exactly one + // candidate. This is a relatively minor distinction but it + // can lead to better inference and error-reporting. An + // example would be if there was an impl: + // + // impl<T:Clone> Vec<T> { fn push_clone(...) { ... } } + // + // and we were to see some code `foo.push_clone()` where `boo` + // is a `Vec<Bar>` and `Bar` does not implement `Clone`. If + // we were to winnow, we'd wind up with zero candidates. + // Instead, we select the right impl now but report "`Bar` does + // not implement `Clone`". + if candidates.len() == 1 { + return self.filter_reservation_impls(candidates.pop().unwrap(), stack.obligation); + } + + // Winnow, but record the exact outcome of evaluation, which + // is needed for specialization. Propagate overflow if it occurs. + let mut candidates = candidates + .into_iter() + .map(|c| match self.evaluate_candidate(stack, &c) { + Ok(eval) if eval.may_apply() => { + Ok(Some(EvaluatedCandidate { candidate: c, evaluation: eval })) + } + Ok(_) => Ok(None), + Err(OverflowError::Canonical) => Err(Overflow(OverflowError::Canonical)), + Err(OverflowError::ErrorReporting) => Err(ErrorReporting), + Err(OverflowError::Error(e)) => Err(Overflow(OverflowError::Error(e))), + }) + .flat_map(Result::transpose) + .collect::<Result<Vec<_>, _>>()?; + + debug!(?stack, ?candidates, "winnowed to {} candidates", candidates.len()); + + 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 + // resolving specializations. + if candidates.len() > 1 { + let mut i = 0; + while i < candidates.len() { + let is_dup = (0..candidates.len()).filter(|&j| i != j).any(|j| { + self.candidate_should_be_dropped_in_favor_of( + &candidates[i], + &candidates[j], + needs_infer, + ) + }); + if is_dup { + debug!(candidate = ?candidates[i], "Dropping candidate #{}/{}", i, candidates.len()); + candidates.swap_remove(i); + } else { + debug!(candidate = ?candidates[i], "Retaining candidate #{}/{}", i, candidates.len()); + i += 1; + + // If there are *STILL* multiple candidates, give up + // and report ambiguity. + if i > 1 { + debug!("multiple matches, ambig"); + return Ok(None); + } + } + } + } + + // If there are *NO* candidates, then there are no impls -- + // that we know of, anyway. Note that in the case where there + // are unbound type variables within the obligation, it might + // be the case that you could still satisfy the obligation + // from another crate by instantiating the type variables with + // a type from another crate that does have an impl. This case + // is checked for in `evaluate_stack` (and hence users + // who might care about this case, like coherence, should use + // that function). + if candidates.is_empty() { + // If there's an error type, 'downgrade' our result from + // `Err(Unimplemented)` to `Ok(None)`. This helps us avoid + // emitting additional spurious errors, since we're guaranteed + // to have emitted at least one. + if stack.obligation.predicate.references_error() { + debug!(?stack.obligation.predicate, "found error type in predicate, treating as ambiguous"); + return Ok(None); + } + return Err(Unimplemented); + } + + // Just one candidate left. + self.filter_reservation_impls(candidates.pop().unwrap().candidate, stack.obligation) + } + /////////////////////////////////////////////////////////////////////////// // EVALUATION // @@ -438,10 +623,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ensure_sufficient_stack(|| { let bound_predicate = obligation.predicate.kind(); match bound_predicate.skip_binder() { - ty::PredicateKind::Trait(t) => { + ty::PredicateKind::Clause(ty::Clause::Trait(t)) => { let t = bound_predicate.rebind(t); debug_assert!(!t.has_escaping_bound_vars()); - let obligation = obligation.with(t); + let obligation = obligation.with(self.tcx(), t); self.evaluate_trait_predicate_recursively(previous_stack, obligation) } @@ -565,7 +750,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } - ty::PredicateKind::TypeOutlives(pred) => { + ty::PredicateKind::Clause(ty::Clause::TypeOutlives(pred)) => { // A global type with no late-bound regions can only // contain the "'static" lifetime (any other lifetime // would either be late-bound or local), so it is guaranteed @@ -577,7 +762,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } - ty::PredicateKind::RegionOutlives(..) => { + ty::PredicateKind::Clause(ty::Clause::RegionOutlives(..)) => { // We do not consider region relationships when evaluating trait matches. Ok(EvaluatedToOkModuloRegions) } @@ -590,9 +775,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } - ty::PredicateKind::Projection(data) => { + ty::PredicateKind::Clause(ty::Clause::Projection(data)) => { let data = bound_predicate.rebind(data); - let project_obligation = obligation.with(data); + let project_obligation = obligation.with(self.tcx(), data); match project::poly_project_and_unify_type(self, &project_obligation) { ProjectAndUnifyResult::Holds(mut subobligations) => { 'compute_res: { @@ -676,21 +861,62 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } ty::PredicateKind::ConstEquate(c1, c2) => { + let tcx = self.tcx(); assert!( - self.tcx().features().generic_const_exprs, + tcx.features().generic_const_exprs, "`ConstEquate` without a feature gate: {c1:?} {c2:?}", ); - debug!(?c1, ?c2, "evaluate_predicate_recursively: equating consts"); - // 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); + let c1 = tcx.expand_abstract_consts(c1); + let c2 = tcx.expand_abstract_consts(c2); + debug!( + "evalaute_predicate_recursively: equating consts:\nc1= {:?}\nc2= {:?}", + c1, c2 + ); + + use rustc_hir::def::DefKind; + use ty::ConstKind::Unevaluated; + match (c1.kind(), c2.kind()) { + (Unevaluated(a), Unevaluated(b)) + if a.def.did == b.def.did + && tcx.def_kind(a.def.did) == DefKind::AssocConst => + { + if let Ok(new_obligations) = self + .infcx + .at(&obligation.cause, obligation.param_env) + .trace(c1, c2) + .eq(a.substs, b.substs) + { + let mut obligations = new_obligations.obligations; + self.add_depth( + obligations.iter_mut(), + obligation.recursion_depth, + ); + return self.evaluate_predicates_recursively( + previous_stack, + obligations.into_iter(), + ); + } + } + (_, Unevaluated(_)) | (Unevaluated(_), _) => (), + (_, _) => { + if let Ok(new_obligations) = self + .infcx + .at(&obligation.cause, obligation.param_env) + .eq(c1, c2) + { + let mut obligations = new_obligations.obligations; + self.add_depth( + obligations.iter_mut(), + obligation.recursion_depth, + ); + return self.evaluate_predicates_recursively( + previous_stack, + obligations.into_iter(), + ); + } + } } } @@ -712,23 +938,17 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { match (evaluate(c1), evaluate(c2)) { (Ok(c1), Ok(c2)) => { - match self - .infcx() - .at(&obligation.cause, obligation.param_env) - .eq(c1, c2) + match self.infcx.at(&obligation.cause, obligation.param_env).eq(c1, c2) { - Ok(_) => Ok(EvaluatedToOk), + Ok(inf_ok) => self.evaluate_predicates_recursively( + previous_stack, + inf_ok.into_obligations(), + ), Err(_) => Ok(EvaluatedToErr), } } (Err(ErrorHandled::Reported(_)), _) | (_, Err(ErrorHandled::Reported(_))) => Ok(EvaluatedToErr), - (Err(ErrorHandled::Linted), _) | (_, Err(ErrorHandled::Linted)) => { - span_bug!( - obligation.cause.span(), - "ConstEquate: const_eval_resolve returned an unexpected error" - ) - } (Err(ErrorHandled::TooGeneric), _) | (_, Err(ErrorHandled::TooGeneric)) => { if c1.has_non_region_infer() || c2.has_non_region_infer() { Ok(EvaluatedToAmbig) @@ -742,6 +962,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ty::PredicateKind::TypeWellFormedFromEnv(..) => { bug!("TypeWellFormedFromEnv is only used for chalk") } + ty::PredicateKind::Ambiguous => Ok(EvaluatedToAmbig), } }) } @@ -752,7 +973,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { previous_stack: TraitObligationStackList<'o, 'tcx>, mut obligation: TraitObligation<'tcx>, ) -> Result<EvaluationResult, OverflowError> { - if !self.intercrate + if !self.is_intercrate() && obligation.is_global() && obligation.param_env.caller_bounds().iter().all(|bound| bound.needs_subst()) { @@ -931,7 +1152,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { match self.candidate_from_obligation(stack) { Ok(Some(c)) => self.evaluate_candidate(stack, &c), - Err(SelectionError::Ambiguous(_)) => Ok(EvaluatedToAmbig), Ok(None) => Ok(EvaluatedToAmbig), Err(Overflow(OverflowError::Canonical)) => Err(OverflowError::Canonical), Err(ErrorReporting) => Err(OverflowError::ErrorReporting), @@ -956,7 +1176,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { fn coinductive_predicate(&self, predicate: ty::Predicate<'tcx>) -> bool { let result = match predicate.kind().skip_binder() { - ty::PredicateKind::Trait(ref data) => self.tcx().trait_is_auto(data.def_id()), + ty::PredicateKind::Clause(ty::Clause::Trait(ref data)) => { + self.tcx().trait_is_coinductive(data.def_id()) + } ty::PredicateKind::WellFormed(_) => true, _ => false, }; @@ -1016,7 +1238,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // mode, so don't do any caching. In particular, we might // re-use the same `InferCtxt` with both an intercrate // and non-intercrate `SelectionContext` - if self.intercrate { + if self.is_intercrate() { return None; } @@ -1046,7 +1268,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // mode, so don't do any caching. In particular, we might // re-use the same `InferCtxt` with both an intercrate // and non-intercrate `SelectionContext` - if self.intercrate { + if self.is_intercrate() { return; } @@ -1084,20 +1306,21 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { it.for_each(|o| o.recursion_depth = cmp::max(min_depth, o.recursion_depth) + 1); } - fn check_recursion_depth<T: Display + TypeFoldable<'tcx>>( + fn check_recursion_depth<T>( &self, depth: usize, error_obligation: &Obligation<'tcx, T>, - ) -> Result<(), OverflowError> { + ) -> Result<(), OverflowError> + where + T: ToPredicate<'tcx> + Clone, + { if !self.infcx.tcx.recursion_limit().value_within_limit(depth) { match self.query_mode { TraitQueryMode::Standard => { - if self.infcx.is_tainted_by_errors() { - return Err(OverflowError::Error( - ErrorGuaranteed::unchecked_claim_error_was_emitted(), - )); + if let Some(e) = self.infcx.tainted_by_errors() { + return Err(OverflowError::Error(e)); } - self.infcx.err_ctxt().report_overflow_error(error_obligation, true); + self.infcx.err_ctxt().report_overflow_obligation(error_obligation, true); } TraitQueryMode::Canonical => { return Err(OverflowError::Canonical); @@ -1112,11 +1335,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /// The weird return type of this function allows it to be used with the `try` (`?`) /// operator within certain functions. #[inline(always)] - fn check_recursion_limit<T: Display + TypeFoldable<'tcx>, V: Display + TypeFoldable<'tcx>>( + fn check_recursion_limit<T: Display + TypeFoldable<'tcx>, V>( &self, obligation: &Obligation<'tcx, T>, error_obligation: &Obligation<'tcx, V>, - ) -> Result<(), OverflowError> { + ) -> Result<(), OverflowError> + where + V: ToPredicate<'tcx> + Clone, + { self.check_recursion_depth(obligation.recursion_depth, error_obligation) } @@ -1132,12 +1358,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /// filter_impls filters constant trait obligations and candidates that have a positive impl /// for a negative goal and a negative impl for a positive goal - #[instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip(self, candidates))] fn filter_impls( &mut self, candidates: Vec<SelectionCandidate<'tcx>>, obligation: &TraitObligation<'tcx>, ) -> Vec<SelectionCandidate<'tcx>> { + trace!("{candidates:#?}"); let tcx = self.tcx(); let mut result = Vec::with_capacity(candidates.len()); @@ -1153,9 +1380,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ProjectionCandidate(_, ty::BoundConstness::ConstIfConst) => {} // auto trait impl AutoImplCandidate => {} - // generator, this will raise error in other places + // generator / future, this will raise error in other places // or ignore error with const_async_blocks feature GeneratorCandidate => {} + FutureCandidate => {} // FnDef where the function is const FnPointerCandidate { is_const: true } => {} ConstDestructCandidate(_) => {} @@ -1177,6 +1405,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } + trace!("{result:#?}"); result } @@ -1215,14 +1444,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } fn is_knowable<'o>(&mut self, stack: &TraitObligationStack<'o, 'tcx>) -> Result<(), Conflict> { - debug!("is_knowable(intercrate={:?})", self.intercrate); + debug!("is_knowable(intercrate={:?})", self.is_intercrate()); - if !self.intercrate || stack.obligation.polarity() == ty::ImplPolarity::Negative { + if !self.is_intercrate() || stack.obligation.polarity() == ty::ImplPolarity::Negative { return Ok(()); } let obligation = &stack.obligation; - let predicate = self.infcx().resolve_vars_if_possible(obligation.predicate); + let predicate = self.infcx.resolve_vars_if_possible(obligation.predicate); // Okay to skip binder because of the nature of the // trait-ref-is-knowable check, which does not care about @@ -1248,7 +1477,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // the master cache. Since coherence executes pretty quickly, // it's not worth going to more trouble to increase the // hit-rate, I don't think. - if self.intercrate { + if self.is_intercrate() { return false; } @@ -1265,7 +1494,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // mode, so don't do any caching. In particular, we might // re-use the same `InferCtxt` with both an intercrate // and non-intercrate `SelectionContext` - if self.intercrate { + if self.is_intercrate() { return None; } let tcx = self.tcx(); @@ -1304,7 +1533,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // mode, so don't do any caching. In particular, we might // re-use the same `InferCtxt` with both an intercrate // and non-intercrate `SelectionContext` - if self.intercrate { + if self.is_intercrate() { return false; } match result { @@ -1359,9 +1588,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut self, obligation: &TraitObligation<'tcx>, ) -> smallvec::SmallVec<[(usize, ty::BoundConstness); 2]> { - let poly_trait_predicate = self.infcx().resolve_vars_if_possible(obligation.predicate); + 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); + self.infcx.replace_bound_vars_with_placeholders(poly_trait_predicate); debug!(?placeholder_trait_predicate); let tcx = self.infcx.tcx; @@ -1389,7 +1618,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { .enumerate() .filter_map(|(idx, bound)| { let bound_predicate = bound.kind(); - if let ty::PredicateKind::Trait(pred) = bound_predicate.skip_binder() { + if let ty::PredicateKind::Clause(ty::Clause::Trait(pred)) = + bound_predicate.skip_binder() + { let bound = bound_predicate.rebind(pred.trait_ref); if self.infcx.probe(|_| { match self.match_normalize_trait_ref( @@ -1581,20 +1812,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { (TransmutabilityCandidate, _) | (_, TransmutabilityCandidate) => false, // (*) - ( - BuiltinCandidate { has_nested: false } - | DiscriminantKindCandidate - | PointeeCandidate - | ConstDestructCandidate(_), - _, - ) => true, - ( - _, - BuiltinCandidate { has_nested: false } - | DiscriminantKindCandidate - | PointeeCandidate - | ConstDestructCandidate(_), - ) => false, + (BuiltinCandidate { has_nested: false } | ConstDestructCandidate(_), _) => true, + (_, BuiltinCandidate { has_nested: false } | ConstDestructCandidate(_)) => false, (ParamCandidate(other), ParamCandidate(victim)) => { let same_except_bound_vars = other.skip_binder().trait_ref @@ -1633,6 +1852,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ImplCandidate(..) | ClosureCandidate | GeneratorCandidate + | FutureCandidate | FnPointerCandidate { .. } | BuiltinObjectCandidate | BuiltinUnsizeCandidate @@ -1651,6 +1871,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ImplCandidate(_) | ClosureCandidate | GeneratorCandidate + | FutureCandidate | FnPointerCandidate { .. } | BuiltinObjectCandidate | BuiltinUnsizeCandidate @@ -1681,6 +1902,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ImplCandidate(..) | ClosureCandidate | GeneratorCandidate + | FutureCandidate | FnPointerCandidate { .. } | BuiltinObjectCandidate | BuiltinUnsizeCandidate @@ -1693,6 +1915,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ImplCandidate(..) | ClosureCandidate | GeneratorCandidate + | FutureCandidate | FnPointerCandidate { .. } | BuiltinObjectCandidate | BuiltinUnsizeCandidate @@ -1774,6 +1997,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ImplCandidate(_) | ClosureCandidate | GeneratorCandidate + | FutureCandidate | FnPointerCandidate { .. } | BuiltinObjectCandidate | BuiltinUnsizeCandidate @@ -1783,6 +2007,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ImplCandidate(_) | ClosureCandidate | GeneratorCandidate + | FutureCandidate | FnPointerCandidate { .. } | BuiltinObjectCandidate | BuiltinUnsizeCandidate @@ -1973,6 +2198,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /// Bar<i32> where struct Bar<T> { x: T, y: u32 } -> [i32, u32] /// Zed<i32> where enum Zed { A(T), B(u32) } -> [i32, u32] /// ``` + #[instrument(level = "debug", skip(self), ret)] fn constituent_types_for_ty( &self, t: ty::Binder<'tcx, Ty<'tcx>>, @@ -2089,8 +2315,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { cause.clone(), trait_def_id, recursion_depth, - normalized_ty, - &[], + [normalized_ty], ); obligations.push(placeholder_obligation); obligations @@ -2117,6 +2342,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { match self.match_impl(impl_def_id, impl_trait_ref, obligation) { Ok(substs) => substs, Err(()) => { + // FIXME: A rematch may fail when a candidate cache hit occurs + // on thefreshened form of the trait predicate, but the match + // fails for some reason that is not captured in the freshened + // cache key. For example, equating an impl trait ref against + // the placeholder trait ref may fail due the Generalizer relation + // raising a CyclicalTy error due to a sub_root_var relation + // for a variable being generalized... self.infcx.tcx.sess.delay_span_bug( obligation.cause.span, &format!( @@ -2145,7 +2377,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation: &TraitObligation<'tcx>, ) -> Result<Normalized<'tcx, SubstsRef<'tcx>>, ()> { let placeholder_obligation = - self.infcx().replace_bound_vars_with_placeholders(obligation.predicate); + self.infcx.replace_bound_vars_with_placeholders(obligation.predicate); let placeholder_obligation_trait_ref = placeholder_obligation.trait_ref; let impl_substs = self.infcx.fresh_substs_for_item(obligation.cause.span, impl_def_id); @@ -2181,7 +2413,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { .map_err(|e| debug!("match_impl: failed eq_trait_refs due to `{e}`"))?; nested_obligations.extend(obligations); - if !self.intercrate + if !self.is_intercrate() && self.tcx().impl_polarity(impl_def_id) == ty::ImplPolarity::Reservation { debug!("reservation impls only apply in intercrate mode"); @@ -2277,43 +2509,25 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { debug!(?closure_sig); - // (1) Feels icky to skip the binder here, but OTOH we know - // that the self-type is an unboxed closure type and hence is + // NOTE: The self-type is an unboxed closure type and hence is // in fact unparameterized (or at least does not reference any - // regions bound in the obligation). Still probably some - // refactoring could make this nicer. + // regions bound in the obligation). + let self_ty = obligation + .predicate + .self_ty() + .no_bound_vars() + .expect("unboxed closure type should not capture bound vars from the predicate"); + closure_trait_ref_and_return_type( self.tcx(), obligation.predicate.def_id(), - obligation.predicate.skip_binder().self_ty(), // (1) + self_ty, closure_sig, util::TupleArgumentsFlag::No, ) .map_bound(|(trait_ref, _)| trait_ref) } - fn generator_trait_ref_unnormalized( - &mut self, - obligation: &TraitObligation<'tcx>, - substs: SubstsRef<'tcx>, - ) -> ty::PolyTraitRef<'tcx> { - let gen_sig = substs.as_generator().poly_sig(); - - // (1) Feels icky to skip the binder here, but OTOH we know - // that the self-type is an generator type and hence is - // in fact unparameterized (or at least does not reference any - // regions bound in the obligation). Still probably some - // refactoring could make this nicer. - - super::util::generator_trait_ref_and_outputs( - self.tcx(), - obligation.predicate.def_id(), - obligation.predicate.skip_binder().self_ty(), // (1) - gen_sig, - ) - .map_bound(|(trait_ref, ..)| trait_ref) - } - /// Returns the obligations that are implied by instantiating an /// impl or trait. The obligations are substituted and fully /// normalized. This is used when confirming an impl or default diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs index c89165858..a251a508b 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs @@ -15,11 +15,13 @@ 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}; -use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; -use rustc_errors::{struct_span_err, DiagnosticBuilder, EmissionGuarantee}; +use crate::traits::{ + self, coherence, FutureCompatOverlapErrorKind, ObligationCause, ObligationCtxt, +}; +use rustc_data_structures::fx::FxIndexSet; +use rustc_errors::{error_code, DelayDm, Diagnostic}; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_middle::ty::{self, ImplSubject, TyCtxt}; +use rustc_middle::ty::{self, ImplSubject, Ty, TyCtxt}; use rustc_middle::ty::{InternalSubsts, SubstsRef}; use rustc_session::lint::builtin::COHERENCE_LEAK_CHECK; use rustc_session::lint::builtin::ORDER_DEPENDENT_TRAIT_OBJECTS; @@ -30,10 +32,10 @@ use super::SelectionContext; /// Information pertinent to an overlapping impl error. #[derive(Debug)] -pub struct OverlapError { +pub struct OverlapError<'tcx> { pub with_impl: DefId, - pub trait_desc: String, - pub self_desc: Option<String>, + pub trait_ref: ty::TraitRef<'tcx>, + pub self_ty: Option<Ty<'tcx>>, pub intercrate_ambiguity_causes: FxIndexSet<IntercrateAmbiguityCause>, pub involves_placeholder: bool, } @@ -200,39 +202,35 @@ fn fulfill_implication<'tcx>( return Err(()); }; + // Needs to be `in_snapshot` because this function is used to rebase + // substitutions, which may happen inside of a select within a probe. + let ocx = ObligationCtxt::new_in_snapshot(infcx); // attempt to prove all of the predicates for impl2 given those for impl1 // (which are packed up in penv) + ocx.register_obligations(obligations.chain(more_obligations)); - infcx.save_and_restore_in_snapshot_flag(|infcx| { - let errors = traits::fully_solve_obligations(&infcx, obligations.chain(more_obligations)); - match &errors[..] { - [] => { - debug!( - "fulfill_implication: an impl for {:?} specializes {:?}", - source_trait, target_trait - ); + let errors = ocx.select_all_or_error(); + if !errors.is_empty() { + // no dice! + debug!( + "fulfill_implication: for impls on {:?} and {:?}, \ + could not fulfill: {:?} given {:?}", + source_trait, + target_trait, + errors, + param_env.caller_bounds() + ); + return Err(()); + } - // Now resolve the *substitution* we built for the target earlier, replacing - // the inference variables inside with whatever we got from fulfillment. - Ok(infcx.resolve_vars_if_possible(target_substs)) - } - errors => { - // no dice! - debug!( - "fulfill_implication: for impls on {:?} and {:?}, \ - could not fulfill: {:?} given {:?}", - source_trait, - target_trait, - errors, - param_env.caller_bounds() - ); - Err(()) - } - } - }) + debug!("fulfill_implication: an impl for {:?} specializes {:?}", source_trait, target_trait); + + // Now resolve the *substitution* we built for the target earlier, replacing + // the inference variables inside with whatever we got from fulfillment. + Ok(infcx.resolve_vars_if_possible(target_substs)) } -// Query provider for `specialization_graph_of`. +/// Query provider for `specialization_graph_of`. pub(super) fn specialization_graph_provider( tcx: TyCtxt<'_>, trait_id: DefId, @@ -277,9 +275,9 @@ pub(super) fn specialization_graph_provider( // it negatively impacts perf. #[cold] #[inline(never)] -fn report_overlap_conflict( - tcx: TyCtxt<'_>, - overlap: OverlapError, +fn report_overlap_conflict<'tcx>( + tcx: TyCtxt<'tcx>, + overlap: OverlapError<'tcx>, impl_def_id: LocalDefId, used_to_be_allowed: Option<FutureCompatOverlapErrorKind>, sg: &mut specialization_graph::Graph, @@ -315,9 +313,9 @@ fn report_overlap_conflict( } } -fn report_negative_positive_conflict( - tcx: TyCtxt<'_>, - overlap: &OverlapError, +fn report_negative_positive_conflict<'tcx>( + tcx: TyCtxt<'tcx>, + overlap: &OverlapError<'tcx>, local_impl_def_id: LocalDefId, negative_impl_def_id: DefId, positive_impl_def_id: DefId, @@ -325,17 +323,17 @@ fn report_negative_positive_conflict( ) { 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, + trait_desc: overlap.trait_ref, + self_ty: overlap.self_ty, 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()); } -fn report_conflicting_impls( - tcx: TyCtxt<'_>, - overlap: OverlapError, +fn report_conflicting_impls<'tcx>( + tcx: TyCtxt<'tcx>, + overlap: OverlapError<'tcx>, impl_def_id: LocalDefId, used_to_be_allowed: Option<FutureCompatOverlapErrorKind>, sg: &mut specialization_graph::Graph, @@ -345,12 +343,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<'a, 'b, G: EmissionGuarantee>( - tcx: TyCtxt<'_>, - overlap: OverlapError, + fn decorate<'tcx>( + tcx: TyCtxt<'tcx>, + overlap: &OverlapError<'tcx>, impl_span: Span, - err: &'b mut DiagnosticBuilder<'a, G>, - ) -> &'b mut DiagnosticBuilder<'a, G> { + err: &mut Diagnostic, + ) { match tcx.span_of_impl(overlap.with_impl) { Ok(span) => { err.span_label(span, "first implementation here"); @@ -359,7 +357,7 @@ fn report_conflicting_impls( impl_span, format!( "conflicting implementation{}", - overlap.self_desc.map_or_else(String::new, |ty| format!(" for `{}`", ty)) + overlap.self_ty.map_or_else(String::new, |ty| format!(" for `{}`", ty)) ), ); } @@ -381,26 +379,28 @@ fn report_conflicting_impls( if overlap.involves_placeholder { coherence::add_placeholder_note(err); } - 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)", - _ => "", - } - ); + let msg = DelayDm(|| { + format!( + "conflicting implementations of trait `{}`{}{}", + overlap.trait_ref.print_only_trait_path(), + overlap.self_ty.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 mut err = struct_span_err!(tcx.sess, impl_span, E0119, "{msg}",); - decorate(tcx, overlap, impl_span, &mut err); + let mut err = tcx.sess.struct_span_err(impl_span, msg); + err.code(error_code!(E0119)); + 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")) @@ -417,7 +417,10 @@ fn report_conflicting_impls( tcx.hir().local_def_id_to_hir_id(impl_def_id), impl_span, msg, - |err| decorate(tcx, overlap, impl_span, err), + |err| { + decorate(tcx, &overlap, impl_span, err); + err + }, ); } }; @@ -435,7 +438,7 @@ pub(crate) fn to_pretty_impl_header(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Opti // FIXME: Currently only handles ?Sized. // Needs to support ?Move and ?DynSized when they are implemented. - let mut types_without_default_bounds = FxHashSet::default(); + let mut types_without_default_bounds = FxIndexSet::default(); let sized_trait = tcx.lang_items().sized_trait(); if !substs.is_empty() { @@ -473,7 +476,9 @@ pub(crate) fn to_pretty_impl_header(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Opti trait_pred }); - p = tcx.mk_predicate(new_trait_pred.map_bound(ty::PredicateKind::Trait)) + p = tcx.mk_predicate( + new_trait_pred.map_bound(|p| ty::PredicateKind::Clause(ty::Clause::Trait(p))), + ) } } pretty_predicates.push(p.to_string()); 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 63f89a33e..4546c9533 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs @@ -3,7 +3,6 @@ use super::OverlapError; use crate::traits; use rustc_hir::def_id::DefId; use rustc_middle::ty::fast_reject::{self, SimplifiedType, TreatParams}; -use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, TyCtxt, TypeVisitable}; pub use rustc_middle::traits::specialization_graph::*; @@ -15,15 +14,15 @@ pub enum FutureCompatOverlapErrorKind { } #[derive(Debug)] -pub struct FutureCompatOverlapError { - pub error: OverlapError, +pub struct FutureCompatOverlapError<'tcx> { + pub error: OverlapError<'tcx>, pub kind: FutureCompatOverlapErrorKind, } /// The result of attempting to insert an impl into a group of children. -enum Inserted { +enum Inserted<'tcx> { /// The impl was inserted as a new child in this group of children. - BecameNewSibling(Option<FutureCompatOverlapError>), + BecameNewSibling(Option<FutureCompatOverlapError<'tcx>>), /// The impl should replace existing impls [X1, ..], because the impl specializes X1, X2, etc. ReplaceChildren(Vec<DefId>), @@ -42,12 +41,12 @@ trait ChildrenExt<'tcx> { impl_def_id: DefId, simplified_self: Option<SimplifiedType>, overlap_mode: OverlapMode, - ) -> Result<Inserted, OverlapError>; + ) -> Result<Inserted<'tcx>, OverlapError<'tcx>>; } -impl ChildrenExt<'_> for Children { +impl<'tcx> ChildrenExt<'tcx> for Children { /// Insert an impl into this set of children without comparing to any existing impls. - fn insert_blindly(&mut self, tcx: TyCtxt<'_>, impl_def_id: DefId) { + fn insert_blindly(&mut self, tcx: TyCtxt<'tcx>, impl_def_id: DefId) { let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); if let Some(st) = fast_reject::simplify_type(tcx, trait_ref.self_ty(), TreatParams::AsInfer) { @@ -62,7 +61,7 @@ impl ChildrenExt<'_> for Children { /// Removes an impl from this set of children. Used when replacing /// an impl with a parent. The impl must be present in the list of /// children already. - fn remove_existing(&mut self, tcx: TyCtxt<'_>, impl_def_id: DefId) { + fn remove_existing(&mut self, tcx: TyCtxt<'tcx>, impl_def_id: DefId) { let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); let vec: &mut Vec<DefId>; if let Some(st) = fast_reject::simplify_type(tcx, trait_ref.self_ty(), TreatParams::AsInfer) @@ -82,11 +81,11 @@ impl ChildrenExt<'_> for Children { /// specialization relationships. fn insert( &mut self, - tcx: TyCtxt<'_>, + tcx: TyCtxt<'tcx>, impl_def_id: DefId, simplified_self: Option<SimplifiedType>, overlap_mode: OverlapMode, - ) -> Result<Inserted, OverlapError> { + ) -> Result<Inserted<'tcx>, OverlapError<'tcx>> { let mut last_lint = None; let mut replace_children = Vec::new(); @@ -103,30 +102,23 @@ impl ChildrenExt<'_> for Children { impl_def_id, simplified_self, possible_sibling, ); - let create_overlap_error = |overlap: traits::coherence::OverlapResult<'_>| { + let create_overlap_error = |overlap: traits::coherence::OverlapResult<'tcx>| { let trait_ref = overlap.impl_header.trait_ref.unwrap(); let self_ty = trait_ref.self_ty(); - // FIXME: should postpone string formatting until we decide to actually emit. - with_no_trimmed_paths!({ - OverlapError { - with_impl: possible_sibling, - trait_desc: trait_ref.print_only_trait_path().to_string(), - // Only report the `Self` type if it has at least - // some outer concrete shell; otherwise, it's - // not adding much information. - self_desc: if self_ty.has_concrete_skeleton() { - Some(self_ty.to_string()) - } else { - None - }, - intercrate_ambiguity_causes: overlap.intercrate_ambiguity_causes, - involves_placeholder: overlap.involves_placeholder, - } - }) + OverlapError { + with_impl: possible_sibling, + trait_ref, + // Only report the `Self` type if it has at least + // some outer concrete shell; otherwise, it's + // not adding much information. + self_ty: if self_ty.has_concrete_skeleton() { Some(self_ty) } else { None }, + intercrate_ambiguity_causes: overlap.intercrate_ambiguity_causes, + involves_placeholder: overlap.involves_placeholder, + } }; - let report_overlap_error = |overlap: traits::coherence::OverlapResult<'_>, + let report_overlap_error = |overlap: traits::coherence::OverlapResult<'tcx>, last_lint: &mut _| { // Found overlap, but no specialization; error out or report future-compat warning. @@ -255,31 +247,31 @@ where } } -pub trait GraphExt { +pub trait GraphExt<'tcx> { /// Insert a local impl into the specialization graph. If an existing impl /// conflicts with it (has overlap, but neither specializes the other), /// information about the area of overlap is returned in the `Err`. fn insert( &mut self, - tcx: TyCtxt<'_>, + tcx: TyCtxt<'tcx>, impl_def_id: DefId, overlap_mode: OverlapMode, - ) -> Result<Option<FutureCompatOverlapError>, OverlapError>; + ) -> Result<Option<FutureCompatOverlapError<'tcx>>, OverlapError<'tcx>>; /// Insert cached metadata mapping from a child impl back to its parent. - fn record_impl_from_cstore(&mut self, tcx: TyCtxt<'_>, parent: DefId, child: DefId); + fn record_impl_from_cstore(&mut self, tcx: TyCtxt<'tcx>, parent: DefId, child: DefId); } -impl GraphExt for Graph { +impl<'tcx> GraphExt<'tcx> for Graph { /// Insert a local impl into the specialization graph. If an existing impl /// conflicts with it (has overlap, but neither specializes the other), /// information about the area of overlap is returned in the `Err`. fn insert( &mut self, - tcx: TyCtxt<'_>, + tcx: TyCtxt<'tcx>, impl_def_id: DefId, overlap_mode: OverlapMode, - ) -> Result<Option<FutureCompatOverlapError>, OverlapError> { + ) -> Result<Option<FutureCompatOverlapError<'tcx>>, OverlapError<'tcx>> { assert!(impl_def_id.is_local()); let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); @@ -376,7 +368,7 @@ impl GraphExt for Graph { } /// Insert cached metadata mapping from a child impl back to its parent. - fn record_impl_from_cstore(&mut self, tcx: TyCtxt<'_>, parent: DefId, child: DefId) { + fn record_impl_from_cstore(&mut self, tcx: TyCtxt<'tcx>, parent: DefId, child: DefId) { if self.parent.insert(child, parent).is_some() { bug!( "When recording an impl from the crate store, information about its parent \ diff --git a/compiler/rustc_trait_selection/src/traits/structural_match.rs b/compiler/rustc_trait_selection/src/traits/structural_match.rs index 932dbbb81..4dc08e0f9 100644 --- a/compiler/rustc_trait_selection/src/traits/structural_match.rs +++ b/compiler/rustc_trait_selection/src/traits/structural_match.rs @@ -1,11 +1,5 @@ -use crate::infer::{InferCtxt, TyCtxtInferExt}; -use crate::traits::ObligationCause; -use crate::traits::{TraitEngine, TraitEngineExt}; - use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; -use rustc_hir::lang_items::LangItem; -use rustc_middle::ty::query::Providers; use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor}; use rustc_span::Span; use std::ops::ControlFlow; @@ -60,53 +54,6 @@ pub fn search_for_adt_const_param_violation<'tcx>( .break_value() } -/// This method returns true if and only if `adt_ty` itself has been marked as -/// eligible for structural-match: namely, if it implements both -/// `StructuralPartialEq` and `StructuralEq` (which are respectively injected by -/// `#[derive(PartialEq)]` and `#[derive(Eq)]`). -/// -/// Note that this does *not* recursively check if the substructure of `adt_ty` -/// implements the traits. -fn type_marked_structural<'tcx>( - infcx: &InferCtxt<'tcx>, - adt_ty: Ty<'tcx>, - cause: ObligationCause<'tcx>, -) -> bool { - let mut fulfillment_cx = <dyn TraitEngine<'tcx>>::new(infcx.tcx); - // require `#[derive(PartialEq)]` - let structural_peq_def_id = - infcx.tcx.require_lang_item(LangItem::StructuralPeq, Some(cause.span)); - fulfillment_cx.register_bound( - infcx, - ty::ParamEnv::empty(), - adt_ty, - structural_peq_def_id, - cause.clone(), - ); - // for now, require `#[derive(Eq)]`. (Doing so is a hack to work around - // the type `for<'a> fn(&'a ())` failing to implement `Eq` itself.) - let structural_teq_def_id = - infcx.tcx.require_lang_item(LangItem::StructuralTeq, Some(cause.span)); - fulfillment_cx.register_bound( - infcx, - ty::ParamEnv::empty(), - adt_ty, - structural_teq_def_id, - cause, - ); - - // We deliberately skip *reporting* fulfillment errors (via - // `report_fulfillment_errors`), for two reasons: - // - // 1. The error messages would mention `std::marker::StructuralPartialEq` - // (a trait which is solely meant as an implementation detail - // for now), and - // - // 2. We are sometimes doing future-incompatibility lints for - // now, so we do not want unconditional errors here. - fulfillment_cx.select_all_or_error(infcx).is_empty() -} - /// This implements the traversal over the structure of a given type to try to /// find instances of ADTs (specifically structs or enums) that do not implement /// the structural-match traits (`StructuralPartialEq` and `StructuralEq`). @@ -262,11 +209,3 @@ impl<'tcx> TypeVisitor<'tcx> for Search<'tcx> { }) } } - -pub fn provide(providers: &mut Providers) { - providers.has_structural_eq_impls = |tcx, ty| { - 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 ed47d2f83..f3ca6a6c7 100644 --- a/compiler/rustc_trait_selection/src/traits/util.rs +++ b/compiler/rustc_trait_selection/src/traits/util.rs @@ -8,7 +8,9 @@ use rustc_hir::def_id::DefId; use rustc_middle::ty::{self, ImplSubject, ToPredicate, Ty, TyCtxt, TypeVisitable}; use rustc_middle::ty::{GenericArg, SubstsRef}; -use super::{Normalized, Obligation, ObligationCause, PredicateObligation, SelectionContext}; +use super::NormalizeExt; +use super::{Obligation, ObligationCause, PredicateObligation, SelectionContext}; +use rustc_infer::infer::InferOk; pub use rustc_infer::traits::{self, util::*}; /////////////////////////////////////////////////////////////////////////// @@ -200,13 +202,13 @@ pub fn impl_subject_and_oblig<'a, 'tcx>( ) -> (ImplSubject<'tcx>, impl Iterator<Item = PredicateObligation<'tcx>>) { let subject = selcx.tcx().bound_impl_subject(impl_def_id); let subject = subject.subst(selcx.tcx(), impl_substs); - let Normalized { value: subject, obligations: normalization_obligations1 } = - super::normalize(selcx, param_env, ObligationCause::dummy(), subject); + let InferOk { value: subject, obligations: normalization_obligations1 } = + selcx.infcx.at(&ObligationCause::dummy(), param_env).normalize(subject); let predicates = selcx.tcx().predicates_of(impl_def_id); let predicates = predicates.instantiate(selcx.tcx(), impl_substs); - let Normalized { value: predicates, obligations: normalization_obligations2 } = - super::normalize(selcx, param_env, ObligationCause::dummy(), predicates); + let InferOk { value: predicates, obligations: normalization_obligations2 } = + selcx.infcx.at(&ObligationCause::dummy(), param_env).normalize(predicates); let impl_obligations = super::predicates_for_generics(|_, _| ObligationCause::dummy(), param_env, predicates); @@ -238,11 +240,9 @@ pub fn predicate_for_trait_def<'tcx>( cause: ObligationCause<'tcx>, trait_def_id: DefId, recursion_depth: usize, - self_ty: Ty<'tcx>, - params: &[GenericArg<'tcx>], + params: impl IntoIterator<Item = impl Into<GenericArg<'tcx>>>, ) -> PredicateObligation<'tcx> { - let trait_ref = - ty::TraitRef { def_id: trait_def_id, substs: tcx.mk_substs_trait(self_ty, params) }; + let trait_ref = tcx.mk_trait_ref(trait_def_id, params); predicate_for_trait_ref(tcx, cause, param_env, trait_ref, recursion_depth) } @@ -261,16 +261,6 @@ pub fn upcast_choices<'tcx>( supertraits(tcx, source_trait_ref).filter(|r| r.def_id() == target_trait_def_id).collect() } -/// Given a trait `trait_ref`, returns the number of vtable entries -/// that come from `trait_ref`, excluding its supertraits. Used in -/// computing the vtable base for an upcast trait of a trait object. -pub fn count_own_vtable_entries<'tcx>( - tcx: TyCtxt<'tcx>, - trait_ref: ty::PolyTraitRef<'tcx>, -) -> usize { - tcx.own_existential_vtable_entries(trait_ref.def_id()).len() -} - /// Given an upcast trait object described by `object`, returns the /// index of the method `method_def_id` (which should be part of /// `object.upcast_trait_ref`) within the vtable for `object`. @@ -300,15 +290,12 @@ pub fn closure_trait_ref_and_return_type<'tcx>( sig: ty::PolyFnSig<'tcx>, tuple_arguments: TupleArgumentsFlag, ) -> ty::Binder<'tcx, (ty::TraitRef<'tcx>, Ty<'tcx>)> { + assert!(!self_ty.has_escaping_bound_vars()); let arguments_tuple = match tuple_arguments { TupleArgumentsFlag::No => sig.skip_binder().inputs()[0], TupleArgumentsFlag::Yes => tcx.intern_tup(sig.skip_binder().inputs()), }; - debug_assert!(!self_ty.has_escaping_bound_vars()); - let trait_ref = ty::TraitRef { - def_id: fn_trait_def_id, - substs: tcx.mk_substs_trait(self_ty, &[arguments_tuple.into()]), - }; + let trait_ref = tcx.mk_trait_ref(fn_trait_def_id, [self_ty, arguments_tuple]); sig.map_bound(|sig| (trait_ref, sig.output())) } @@ -318,14 +305,22 @@ pub fn generator_trait_ref_and_outputs<'tcx>( self_ty: Ty<'tcx>, sig: ty::PolyGenSig<'tcx>, ) -> ty::Binder<'tcx, (ty::TraitRef<'tcx>, Ty<'tcx>, Ty<'tcx>)> { - debug_assert!(!self_ty.has_escaping_bound_vars()); - let trait_ref = ty::TraitRef { - def_id: fn_trait_def_id, - substs: tcx.mk_substs_trait(self_ty, &[sig.skip_binder().resume_ty.into()]), - }; + assert!(!self_ty.has_escaping_bound_vars()); + let trait_ref = tcx.mk_trait_ref(fn_trait_def_id, [self_ty, sig.skip_binder().resume_ty]); sig.map_bound(|sig| (trait_ref, sig.yield_ty, sig.return_ty)) } +pub fn future_trait_ref_and_outputs<'tcx>( + tcx: TyCtxt<'tcx>, + fn_trait_def_id: DefId, + self_ty: Ty<'tcx>, + sig: ty::PolyGenSig<'tcx>, +) -> ty::Binder<'tcx, (ty::TraitRef<'tcx>, Ty<'tcx>)> { + assert!(!self_ty.has_escaping_bound_vars()); + let trait_ref = tcx.mk_trait_ref(fn_trait_def_id, [self_ty]); + sig.map_bound(|sig| (trait_ref, sig.return_ty)) +} + pub fn impl_item_is_final(tcx: TyCtxt<'_>, assoc_item: &ty::AssocItem) -> bool { assoc_item.defaultness(tcx).is_final() && tcx.impl_defaultness(assoc_item.container_id(tcx)).is_final() diff --git a/compiler/rustc_trait_selection/src/traits/vtable.rs b/compiler/rustc_trait_selection/src/traits/vtable.rs new file mode 100644 index 000000000..41ce6cdf7 --- /dev/null +++ b/compiler/rustc_trait_selection/src/traits/vtable.rs @@ -0,0 +1,386 @@ +use crate::errors::DumpVTableEntries; +use crate::traits::{impossible_predicates, is_vtable_safe_method}; +use rustc_hir::def_id::DefId; +use rustc_hir::lang_items::LangItem; +use rustc_infer::traits::util::PredicateSet; +use rustc_infer::traits::ImplSource; +use rustc_middle::ty::visit::TypeVisitable; +use rustc_middle::ty::InternalSubsts; +use rustc_middle::ty::{self, GenericParamDefKind, ToPredicate, Ty, TyCtxt, VtblEntry}; +use rustc_span::{sym, Span}; +use smallvec::SmallVec; + +use std::fmt::Debug; +use std::ops::ControlFlow; + +#[derive(Clone, Debug)] +pub(super) enum VtblSegment<'tcx> { + MetadataDSA, + TraitOwnEntries { trait_ref: ty::PolyTraitRef<'tcx>, emit_vptr: bool }, +} + +/// Prepare the segments for a vtable +pub(super) fn prepare_vtable_segments<'tcx, T>( + tcx: TyCtxt<'tcx>, + trait_ref: ty::PolyTraitRef<'tcx>, + mut segment_visitor: impl FnMut(VtblSegment<'tcx>) -> ControlFlow<T>, +) -> Option<T> { + // The following constraints holds for the final arrangement. + // 1. The whole virtual table of the first direct super trait is included as the + // the prefix. If this trait doesn't have any super traits, then this step + // consists of the dsa metadata. + // 2. Then comes the proper pointer metadata(vptr) and all own methods for all + // other super traits except those already included as part of the first + // direct super trait virtual table. + // 3. finally, the own methods of this trait. + + // This has the advantage that trait upcasting to the first direct super trait on each level + // is zero cost, and to another trait includes only replacing the pointer with one level indirection, + // while not using too much extra memory. + + // For a single inheritance relationship like this, + // D --> C --> B --> A + // The resulting vtable will consists of these segments: + // DSA, A, B, C, D + + // For a multiple inheritance relationship like this, + // D --> C --> A + // \-> B + // The resulting vtable will consists of these segments: + // DSA, A, B, B-vptr, C, D + + // For a diamond inheritance relationship like this, + // D --> B --> A + // \-> C -/ + // The resulting vtable will consists of these segments: + // DSA, A, B, C, C-vptr, D + + // For a more complex inheritance relationship like this: + // O --> G --> C --> A + // \ \ \-> B + // | |-> F --> D + // | \-> E + // |-> N --> J --> H + // \ \-> I + // |-> M --> K + // \-> L + // The resulting vtable will consists of these segments: + // DSA, A, B, B-vptr, C, D, D-vptr, E, E-vptr, F, F-vptr, G, + // H, H-vptr, I, I-vptr, J, J-vptr, K, K-vptr, L, L-vptr, M, M-vptr, + // N, N-vptr, O + + // emit dsa segment first. + if let ControlFlow::Break(v) = (segment_visitor)(VtblSegment::MetadataDSA) { + return Some(v); + } + + let mut emit_vptr_on_new_entry = false; + let mut visited = PredicateSet::new(tcx); + let predicate = trait_ref.without_const().to_predicate(tcx); + let mut stack: SmallVec<[(ty::PolyTraitRef<'tcx>, _, _); 5]> = + smallvec![(trait_ref, emit_vptr_on_new_entry, None)]; + visited.insert(predicate); + + // the main traversal loop: + // basically we want to cut the inheritance directed graph into a few non-overlapping slices of nodes + // that each node is emitted after all its descendents have been emitted. + // so we convert the directed graph into a tree by skipping all previously visited nodes using a visited set. + // this is done on the fly. + // Each loop run emits a slice - it starts by find a "childless" unvisited node, backtracking upwards, and it + // stops after it finds a node that has a next-sibling node. + // This next-sibling node will used as the starting point of next slice. + + // Example: + // For a diamond inheritance relationship like this, + // D#1 --> B#0 --> A#0 + // \-> C#1 -/ + + // Starting point 0 stack [D] + // Loop run #0: Stack after diving in is [D B A], A is "childless" + // after this point, all newly visited nodes won't have a vtable that equals to a prefix of this one. + // Loop run #0: Emitting the slice [B A] (in reverse order), B has a next-sibling node, so this slice stops here. + // Loop run #0: Stack after exiting out is [D C], C is the next starting point. + // Loop run #1: Stack after diving in is [D C], C is "childless", since its child A is skipped(already emitted). + // Loop run #1: Emitting the slice [D C] (in reverse order). No one has a next-sibling node. + // Loop run #1: Stack after exiting out is []. Now the function exits. + + loop { + // dive deeper into the stack, recording the path + 'diving_in: loop { + if let Some((inner_most_trait_ref, _, _)) = stack.last() { + let inner_most_trait_ref = *inner_most_trait_ref; + let mut direct_super_traits_iter = tcx + .super_predicates_of(inner_most_trait_ref.def_id()) + .predicates + .into_iter() + .filter_map(move |(pred, _)| { + pred.subst_supertrait(tcx, &inner_most_trait_ref).to_opt_poly_trait_pred() + }); + + 'diving_in_skip_visited_traits: loop { + if let Some(next_super_trait) = direct_super_traits_iter.next() { + if visited.insert(next_super_trait.to_predicate(tcx)) { + // We're throwing away potential constness of super traits here. + // FIXME: handle ~const super traits + let next_super_trait = next_super_trait.map_bound(|t| t.trait_ref); + stack.push(( + next_super_trait, + emit_vptr_on_new_entry, + Some(direct_super_traits_iter), + )); + break 'diving_in_skip_visited_traits; + } else { + continue 'diving_in_skip_visited_traits; + } + } else { + break 'diving_in; + } + } + } + } + + // Other than the left-most path, vptr should be emitted for each trait. + emit_vptr_on_new_entry = true; + + // emit innermost item, move to next sibling and stop there if possible, otherwise jump to outer level. + 'exiting_out: loop { + if let Some((inner_most_trait_ref, emit_vptr, siblings_opt)) = stack.last_mut() { + if let ControlFlow::Break(v) = (segment_visitor)(VtblSegment::TraitOwnEntries { + trait_ref: *inner_most_trait_ref, + emit_vptr: *emit_vptr, + }) { + return Some(v); + } + + 'exiting_out_skip_visited_traits: loop { + if let Some(siblings) = siblings_opt { + if let Some(next_inner_most_trait_ref) = siblings.next() { + if visited.insert(next_inner_most_trait_ref.to_predicate(tcx)) { + // We're throwing away potential constness of super traits here. + // FIXME: handle ~const super traits + let next_inner_most_trait_ref = + next_inner_most_trait_ref.map_bound(|t| t.trait_ref); + *inner_most_trait_ref = next_inner_most_trait_ref; + *emit_vptr = emit_vptr_on_new_entry; + break 'exiting_out; + } else { + continue 'exiting_out_skip_visited_traits; + } + } + } + stack.pop(); + continue 'exiting_out; + } + } + // all done + return None; + } + } +} + +fn dump_vtable_entries<'tcx>( + tcx: TyCtxt<'tcx>, + sp: Span, + trait_ref: ty::PolyTraitRef<'tcx>, + entries: &[VtblEntry<'tcx>], +) { + tcx.sess.emit_err(DumpVTableEntries { + span: sp, + trait_ref, + entries: format!("{:#?}", entries), + }); +} + +fn own_existential_vtable_entries<'tcx>(tcx: TyCtxt<'tcx>, trait_def_id: DefId) -> &'tcx [DefId] { + let trait_methods = tcx + .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). + let own_entries = trait_methods.filter_map(move |trait_method| { + debug!("own_existential_vtable_entry: trait_method={:?}", trait_method); + let def_id = trait_method.def_id; + + // Some methods cannot be called on an object; skip those. + if !is_vtable_safe_method(tcx, trait_def_id, &trait_method) { + debug!("own_existential_vtable_entry: not vtable safe"); + return None; + } + + Some(def_id) + }); + + tcx.arena.alloc_from_iter(own_entries.into_iter()) +} + +/// Given a trait `trait_ref`, iterates the vtable entries +/// that come from `trait_ref`, including its supertraits. +fn vtable_entries<'tcx>( + tcx: TyCtxt<'tcx>, + trait_ref: ty::PolyTraitRef<'tcx>, +) -> &'tcx [VtblEntry<'tcx>] { + debug!("vtable_entries({:?})", trait_ref); + + let mut entries = vec![]; + + let vtable_segment_callback = |segment| -> ControlFlow<()> { + match segment { + VtblSegment::MetadataDSA => { + entries.extend(TyCtxt::COMMON_VTABLE_ENTRIES); + } + VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => { + let existential_trait_ref = trait_ref + .map_bound(|trait_ref| ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref)); + + // Lookup the shape of vtable for the trait. + let own_existential_entries = + 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); + + // The method may have some early-bound lifetimes; add regions for those. + let substs = trait_ref.map_bound(|trait_ref| { + InternalSubsts::for_item(tcx, def_id, |param, _| match param.kind { + GenericParamDefKind::Lifetime => tcx.lifetimes.re_erased.into(), + GenericParamDefKind::Type { .. } + | GenericParamDefKind::Const { .. } => { + trait_ref.substs[param.index as usize] + } + }) + }); + + // The trait type may have higher-ranked lifetimes in it; + // erase them if they appear, so that we get the type + // at some particular call site. + let substs = tcx + .normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), substs); + + // It's possible that the method relies on where-clauses that + // do not hold for this particular set of type parameters. + // Note that this method could then never be called, so we + // do not want to try and codegen it, in that case (see #23435). + let predicates = tcx.predicates_of(def_id).instantiate_own(tcx, substs); + if impossible_predicates(tcx, predicates.predicates) { + debug!("vtable_entries: predicates do not hold"); + return VtblEntry::Vacant; + } + + let instance = ty::Instance::resolve_for_vtable( + tcx, + ty::ParamEnv::reveal_all(), + def_id, + substs, + ) + .expect("resolution failed during building vtable representation"); + VtblEntry::Method(instance) + }); + + entries.extend(own_entries); + + if emit_vptr { + entries.push(VtblEntry::TraitVPtr(trait_ref)); + } + } + } + + ControlFlow::Continue(()) + }; + + let _ = prepare_vtable_segments(tcx, trait_ref, vtable_segment_callback); + + if tcx.has_attr(trait_ref.def_id(), sym::rustc_dump_vtable) { + let sp = tcx.def_span(trait_ref.def_id()); + dump_vtable_entries(tcx, sp, trait_ref, &entries); + } + + tcx.arena.alloc_from_iter(entries.into_iter()) +} + +/// Find slot base for trait methods within vtable entries of another trait +pub(super) fn vtable_trait_first_method_offset<'tcx>( + tcx: TyCtxt<'tcx>, + key: ( + ty::PolyTraitRef<'tcx>, // trait_to_be_found + ty::PolyTraitRef<'tcx>, // trait_owning_vtable + ), +) -> usize { + let (trait_to_be_found, trait_owning_vtable) = key; + + // #90177 + let trait_to_be_found_erased = tcx.erase_regions(trait_to_be_found); + + let vtable_segment_callback = { + let mut vtable_base = 0; + + move |segment| { + match segment { + VtblSegment::MetadataDSA => { + vtable_base += TyCtxt::COMMON_VTABLE_ENTRIES.len(); + } + VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => { + if tcx.erase_regions(trait_ref) == trait_to_be_found_erased { + return ControlFlow::Break(vtable_base); + } + vtable_base += count_own_vtable_entries(tcx, trait_ref); + if emit_vptr { + vtable_base += 1; + } + } + } + ControlFlow::Continue(()) + } + }; + + if let Some(vtable_base) = + prepare_vtable_segments(tcx, trait_owning_vtable, vtable_segment_callback) + { + vtable_base + } else { + bug!("Failed to find info for expected trait in vtable"); + } +} + +/// Find slot offset for trait vptr within vtable entries of another trait +pub(crate) fn vtable_trait_upcasting_coercion_new_vptr_slot<'tcx>( + tcx: TyCtxt<'tcx>, + key: ( + Ty<'tcx>, // trait object type whose trait owning vtable + Ty<'tcx>, // trait object for supertrait + ), +) -> Option<usize> { + let (source, target) = key; + assert!(matches!(&source.kind(), &ty::Dynamic(..)) && !source.needs_infer()); + assert!(matches!(&target.kind(), &ty::Dynamic(..)) && !target.needs_infer()); + + // this has been typecked-before, so diagnostics is not really needed. + let unsize_trait_did = tcx.require_lang_item(LangItem::Unsize, None); + + let trait_ref = tcx.mk_trait_ref(unsize_trait_did, [source, target]); + + match tcx.codegen_select_candidate((ty::ParamEnv::reveal_all(), ty::Binder::dummy(trait_ref))) { + Ok(ImplSource::TraitUpcasting(implsrc_traitcasting)) => { + implsrc_traitcasting.vtable_vptr_slot + } + otherwise => bug!("expected TraitUpcasting candidate, got {otherwise:?}"), + } +} + +/// Given a trait `trait_ref`, returns the number of vtable entries +/// that come from `trait_ref`, excluding its supertraits. Used in +/// computing the vtable base for an upcast trait of a trait object. +pub(crate) fn count_own_vtable_entries<'tcx>( + tcx: TyCtxt<'tcx>, + trait_ref: ty::PolyTraitRef<'tcx>, +) -> usize { + tcx.own_existential_vtable_entries(trait_ref.def_id()).len() +} + +pub(super) fn provide(providers: &mut ty::query::Providers) { + *providers = ty::query::Providers { + own_existential_vtable_entries, + vtable_entries, + vtable_trait_upcasting_coercion_new_vptr_slot, + ..*providers + }; +} diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index fc0a9f690..681fb753f 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -4,7 +4,7 @@ use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef}; -use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt, TypeVisitable}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitable}; use rustc_span::Span; use std::iter; @@ -121,14 +121,14 @@ pub fn predicate_obligations<'tcx>( // It's ok to skip the binder here because wf code is prepared for it match predicate.kind().skip_binder() { - ty::PredicateKind::Trait(t) => { + ty::PredicateKind::Clause(ty::Clause::Trait(t)) => { wf.compute_trait_pred(&t, Elaborate::None); } - ty::PredicateKind::RegionOutlives(..) => {} - ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ty, _reg)) => { + ty::PredicateKind::Clause(ty::Clause::RegionOutlives(..)) => {} + ty::PredicateKind::Clause(ty::Clause::TypeOutlives(ty::OutlivesPredicate(ty, _reg))) => { wf.compute(ty.into()); } - ty::PredicateKind::Projection(t) => { + ty::PredicateKind::Clause(ty::Clause::Projection(t)) => { wf.compute_projection(t.projection_ty); wf.compute(match t.term.unpack() { ty::TermKind::Ty(ty) => ty.into(), @@ -155,6 +155,7 @@ pub fn predicate_obligations<'tcx>( wf.compute(c1.into()); wf.compute(c2.into()); } + ty::PredicateKind::Ambiguous => {} ty::PredicateKind::TypeWellFormedFromEnv(..) => { bug!("TypeWellFormedFromEnv is only used for Chalk") } @@ -227,7 +228,7 @@ fn extend_cause_with_original_assoc_item_obligation<'tcx>( // It is fine to skip the binder as we don't care about regions here. match pred.kind().skip_binder() { - ty::PredicateKind::Projection(proj) => { + ty::PredicateKind::Clause(ty::Clause::Projection(proj)) => { // The obligation comes not from the current `impl` nor the `trait` being implemented, // but rather from a "second order" obligation, where an associated type has a // projection coming from another associated type. See @@ -244,7 +245,7 @@ fn extend_cause_with_original_assoc_item_obligation<'tcx>( cause.span = impl_item_span; } } - ty::PredicateKind::Trait(pred) => { + ty::PredicateKind::Clause(ty::Clause::Trait(pred)) => { // An associated item obligation born out of the `trait` failed to be met. An example // can be seen in `ui/associated-types/point-at-type-on-obligation-failure-2.rs`. debug!("extended_cause_with_original_assoc_item_obligation trait proj {:?}", pred); @@ -324,7 +325,7 @@ impl<'tcx> WfPredicates<'tcx> { extend_cause_with_original_assoc_item_obligation( tcx, trait_ref, item, &mut cause, predicate, ); - traits::Obligation::with_depth(cause, depth, param_env, predicate) + traits::Obligation::with_depth(tcx, cause, depth, param_env, predicate) }; if let Elaborate::All = elaborate { @@ -356,10 +357,11 @@ impl<'tcx> WfPredicates<'tcx> { } } traits::Obligation::with_depth( + tcx, cause, depth, param_env, - ty::Binder::dummy(ty::PredicateKind::WellFormed(arg)).to_predicate(tcx), + ty::Binder::dummy(ty::PredicateKind::WellFormed(arg)), ) }), ); @@ -407,10 +409,11 @@ impl<'tcx> WfPredicates<'tcx> { .filter(|arg| !arg.has_escaping_bound_vars()) .map(|arg| { traits::Obligation::with_depth( + tcx, cause.clone(), depth, param_env, - ty::Binder::dummy(ty::PredicateKind::WellFormed(arg)).to_predicate(tcx), + ty::Binder::dummy(ty::PredicateKind::WellFormed(arg)), ) }), ); @@ -419,15 +422,13 @@ impl<'tcx> WfPredicates<'tcx> { fn require_sized(&mut self, subty: Ty<'tcx>, cause: traits::ObligationCauseCode<'tcx>) { if !subty.has_escaping_bound_vars() { let cause = self.cause(cause); - let trait_ref = ty::TraitRef { - def_id: self.tcx.require_lang_item(LangItem::Sized, None), - substs: self.tcx.mk_substs_trait(subty, &[]), - }; + let trait_ref = self.tcx.at(cause.span).mk_trait_ref(LangItem::Sized, [subty]); self.out.push(traits::Obligation::with_depth( + self.tcx, cause, self.recursion_depth, self.param_env, - ty::Binder::dummy(trait_ref).without_const().to_predicate(self.tcx), + ty::Binder::dummy(trait_ref).without_const(), )); } } @@ -454,10 +455,10 @@ impl<'tcx> WfPredicates<'tcx> { self.out.extend(obligations); let predicate = - ty::Binder::dummy(ty::PredicateKind::ConstEvaluatable(ct)) - .to_predicate(self.tcx()); + ty::Binder::dummy(ty::PredicateKind::ConstEvaluatable(ct)); let cause = self.cause(traits::WellFormed(None)); self.out.push(traits::Obligation::with_depth( + self.tcx(), cause, self.recursion_depth, self.param_env, @@ -468,13 +469,33 @@ impl<'tcx> WfPredicates<'tcx> { let cause = self.cause(traits::WellFormed(None)); self.out.push(traits::Obligation::with_depth( + self.tcx(), cause, self.recursion_depth, self.param_env, - ty::Binder::dummy(ty::PredicateKind::WellFormed(ct.into())) - .to_predicate(self.tcx()), + ty::Binder::dummy(ty::PredicateKind::WellFormed(ct.into())), )); } + ty::ConstKind::Expr(_) => { + // FIXME(generic_const_exprs): this doesnt verify that given `Expr(N + 1)` the + // trait bound `typeof(N): Add<typeof(1)>` holds. This is currently unnecessary + // as `ConstKind::Expr` is only produced via normalization of `ConstKind::Unevaluated` + // which means that the `DefId` would have been typeck'd elsewhere. However in + // the future we may allow directly lowering to `ConstKind::Expr` in which case + // we would not be proving bounds we should. + + let predicate = + ty::Binder::dummy(ty::PredicateKind::ConstEvaluatable(ct)); + let cause = self.cause(traits::WellFormed(None)); + self.out.push(traits::Obligation::with_depth( + self.tcx(), + cause, + self.recursion_depth, + self.param_env, + predicate, + )); + } + ty::ConstKind::Error(_) | ty::ConstKind::Param(_) | ty::ConstKind::Bound(..) @@ -556,13 +577,13 @@ impl<'tcx> WfPredicates<'tcx> { if !r.has_escaping_bound_vars() && !rty.has_escaping_bound_vars() { let cause = self.cause(traits::ReferenceOutlivesReferent(ty)); self.out.push(traits::Obligation::with_depth( + self.tcx(), cause, depth, param_env, - ty::Binder::dummy(ty::PredicateKind::TypeOutlives( + ty::Binder::dummy(ty::PredicateKind::Clause(ty::Clause::TypeOutlives( ty::OutlivesPredicate(rty, r), - )) - .to_predicate(self.tcx()), + ))), )); } } @@ -631,7 +652,7 @@ impl<'tcx> WfPredicates<'tcx> { // All of the requirements on type parameters // have already been checked for `impl Trait` in // return position. We do need to check type-alias-impl-trait though. - if ty::is_impl_trait_defn(self.tcx, did).is_none() { + if self.tcx.is_type_alias_impl_trait(did) { let obligations = self.nominal_obligations(did, substs); self.out.extend(obligations); } @@ -656,11 +677,11 @@ impl<'tcx> WfPredicates<'tcx> { let tcx = self.tcx(); self.out.extend(component_traits.map(|did| { traits::Obligation::with_depth( + tcx, cause.clone(), depth, param_env, - ty::Binder::dummy(ty::PredicateKind::ObjectSafe(did)) - .to_predicate(tcx), + ty::Binder::dummy(ty::PredicateKind::ObjectSafe(did)), ) })); } @@ -681,11 +702,11 @@ impl<'tcx> WfPredicates<'tcx> { ty::Infer(_) => { let cause = self.cause(traits::WellFormed(None)); self.out.push(traits::Obligation::with_depth( + self.tcx(), cause, self.recursion_depth, param_env, - ty::Binder::dummy(ty::PredicateKind::WellFormed(ty.into())) - .to_predicate(self.tcx()), + ty::Binder::dummy(ty::PredicateKind::WellFormed(ty.into())), )); } } @@ -724,7 +745,13 @@ impl<'tcx> WfPredicates<'tcx> { if remap_constness { pred = pred.without_const(self.tcx); } - traits::Obligation::with_depth(cause, self.recursion_depth, self.param_env, pred) + traits::Obligation::with_depth( + self.tcx, + cause, + self.recursion_depth, + self.param_env, + pred, + ) }) .filter(|pred| !pred.has_escaping_bound_vars()) .collect() @@ -749,7 +776,7 @@ impl<'tcx> WfPredicates<'tcx> { fn from_object_ty( &mut self, ty: Ty<'tcx>, - data: &'tcx ty::List<ty::Binder<'tcx, ty::ExistentialPredicate<'tcx>>>, + data: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>, region: ty::Region<'tcx>, ) { // Imagine a type like this: @@ -794,10 +821,11 @@ impl<'tcx> WfPredicates<'tcx> { let outlives = ty::Binder::dummy(ty::OutlivesPredicate(explicit_bound, implicit_bound)); self.out.push(traits::Obligation::with_depth( + self.tcx, cause, self.recursion_depth, self.param_env, - outlives.to_predicate(self.tcx), + outlives, )); } } @@ -812,7 +840,7 @@ impl<'tcx> WfPredicates<'tcx> { /// `infer::required_region_bounds`, see that for more information. pub fn object_region_bounds<'tcx>( tcx: TyCtxt<'tcx>, - existential_predicates: &'tcx ty::List<ty::Binder<'tcx, ty::ExistentialPredicate<'tcx>>>, + existential_predicates: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>, ) -> Vec<ty::Region<'tcx>> { // Since we don't actually *know* the self type for an object, // this "open(err)" serves as a kind of dummy standin -- basically @@ -858,18 +886,22 @@ pub(crate) fn required_region_bounds<'tcx>( .filter_map(|obligation| { debug!(?obligation); match obligation.predicate.kind().skip_binder() { - ty::PredicateKind::Projection(..) - | ty::PredicateKind::Trait(..) + ty::PredicateKind::Clause(ty::Clause::Projection(..)) + | ty::PredicateKind::Clause(ty::Clause::Trait(..)) | ty::PredicateKind::Subtype(..) | ty::PredicateKind::Coerce(..) | ty::PredicateKind::WellFormed(..) | ty::PredicateKind::ObjectSafe(..) | ty::PredicateKind::ClosureKind(..) - | ty::PredicateKind::RegionOutlives(..) + | ty::PredicateKind::Clause(ty::Clause::RegionOutlives(..)) | ty::PredicateKind::ConstEvaluatable(..) | ty::PredicateKind::ConstEquate(..) + | ty::PredicateKind::Ambiguous | ty::PredicateKind::TypeWellFormedFromEnv(..) => None, - ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ref t, ref r)) => { + ty::PredicateKind::Clause(ty::Clause::TypeOutlives(ty::OutlivesPredicate( + ref t, + ref r, + ))) => { // Search for a bound of the form `erased_self_ty // : 'a`, but be wary of something like `for<'a> // erased_self_ty : 'a` (we interpret a |