diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-30 03:57:19 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-30 03:57:19 +0000 |
commit | a0b8f38ab54ac451646aa00cd5e91b6c76f22a84 (patch) | |
tree | fc451898ccaf445814e26b46664d78702178101d /compiler/rustc_trait_selection | |
parent | Adding debian version 1.71.1+dfsg1-2. (diff) | |
download | rustc-a0b8f38ab54ac451646aa00cd5e91b6c76f22a84.tar.xz rustc-a0b8f38ab54ac451646aa00cd5e91b6c76f22a84.zip |
Merging upstream version 1.72.1+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'compiler/rustc_trait_selection')
52 files changed, 3727 insertions, 1877 deletions
diff --git a/compiler/rustc_trait_selection/src/infer.rs b/compiler/rustc_trait_selection/src/infer.rs index 312bd3817..6efc1e730 100644 --- a/compiler/rustc_trait_selection/src/infer.rs +++ b/compiler/rustc_trait_selection/src/infer.rs @@ -1,5 +1,5 @@ use crate::traits::query::evaluate_obligation::InferCtxtExt as _; -use crate::traits::{self, ObligationCtxt}; +use crate::traits::{self, DefiningAnchor, ObligationCtxt}; use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; @@ -80,7 +80,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { pub trait InferCtxtBuilderExt<'tcx> { fn enter_canonical_trait_query<K, R>( - &mut self, + self, canonical_key: &Canonical<'tcx, K>, operation: impl FnOnce(&ObligationCtxt<'_, 'tcx>, K) -> Result<R, NoSolution>, ) -> Result<CanonicalQueryResponse<'tcx, R>, NoSolution> @@ -108,7 +108,7 @@ impl<'tcx> InferCtxtBuilderExt<'tcx> for InferCtxtBuilder<'tcx> { /// have `'tcx` be free on this function so that we can talk about /// `K: TypeFoldable<TyCtxt<'tcx>>`.) fn enter_canonical_trait_query<K, R>( - &mut self, + self, canonical_key: &Canonical<'tcx, K>, operation: impl FnOnce(&ObligationCtxt<'_, 'tcx>, K) -> Result<R, NoSolution>, ) -> Result<CanonicalQueryResponse<'tcx, R>, NoSolution> @@ -117,8 +117,9 @@ impl<'tcx> InferCtxtBuilderExt<'tcx> for InferCtxtBuilder<'tcx> { R: Debug + TypeFoldable<TyCtxt<'tcx>>, Canonical<'tcx, QueryResponse<'tcx, R>>: ArenaAllocatable<'tcx>, { - let (infcx, key, canonical_inference_vars) = - self.build_with_canonical(DUMMY_SP, canonical_key); + let (infcx, key, canonical_inference_vars) = self + .with_opaque_type_inference(DefiningAnchor::Bubble) + .build_with_canonical(DUMMY_SP, canonical_key); let ocx = ObligationCtxt::new(&infcx); let value = operation(&ocx, key)?; ocx.make_canonicalized_query_response(canonical_inference_vars, value) diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs index ed3994be9..56d37d58d 100644 --- a/compiler/rustc_trait_selection/src/lib.rs +++ b/compiler/rustc_trait_selection/src/lib.rs @@ -14,8 +14,7 @@ #![feature(associated_type_bounds)] #![feature(box_patterns)] #![feature(control_flow_enum)] -#![feature(drain_filter)] -#![feature(hash_drain_filter)] +#![feature(extract_if)] #![feature(let_chains)] #![feature(if_let_guard)] #![feature(never_type)] diff --git a/compiler/rustc_trait_selection/src/solve/alias_relate.rs b/compiler/rustc_trait_selection/src/solve/alias_relate.rs new file mode 100644 index 000000000..422a6ee34 --- /dev/null +++ b/compiler/rustc_trait_selection/src/solve/alias_relate.rs @@ -0,0 +1,195 @@ +use super::{EvalCtxt, SolverMode}; +use rustc_infer::traits::query::NoSolution; +use rustc_middle::traits::solve::{Certainty, Goal, QueryResult}; +use rustc_middle::ty; + +/// We may need to invert the alias relation direction if dealing an alias on the RHS. +#[derive(Debug)] +enum Invert { + No, + Yes, +} + +impl<'tcx> EvalCtxt<'_, 'tcx> { + #[instrument(level = "debug", skip(self), ret)] + pub(super) fn compute_alias_relate_goal( + &mut self, + goal: Goal<'tcx, (ty::Term<'tcx>, ty::Term<'tcx>, ty::AliasRelationDirection)>, + ) -> QueryResult<'tcx> { + let tcx = self.tcx(); + let Goal { param_env, predicate: (lhs, rhs, direction) } = goal; + if lhs.is_infer() || rhs.is_infer() { + bug!( + "`AliasRelate` goal with an infer var on lhs or rhs which should have been instantiated" + ); + } + + match (lhs.to_alias_ty(tcx), rhs.to_alias_ty(tcx)) { + (None, None) => bug!("`AliasRelate` goal without an alias on either lhs or rhs"), + + // RHS is not a projection, only way this is true is if LHS normalizes-to RHS + (Some(alias_lhs), None) => self.assemble_normalizes_to_candidate( + param_env, + alias_lhs, + rhs, + direction, + Invert::No, + ), + + // LHS is not a projection, only way this is true is if RHS normalizes-to LHS + (None, Some(alias_rhs)) => self.assemble_normalizes_to_candidate( + param_env, + alias_rhs, + lhs, + direction, + Invert::Yes, + ), + + (Some(alias_lhs), Some(alias_rhs)) => { + debug!("both sides are aliases"); + + let mut candidates = Vec::new(); + // LHS normalizes-to RHS + candidates.extend(self.assemble_normalizes_to_candidate( + param_env, + alias_lhs, + rhs, + direction, + Invert::No, + )); + // RHS normalizes-to RHS + candidates.extend(self.assemble_normalizes_to_candidate( + param_env, + alias_rhs, + lhs, + direction, + Invert::Yes, + )); + // Relate via substs + let subst_relate_response = self + .assemble_subst_relate_candidate(param_env, alias_lhs, alias_rhs, direction); + candidates.extend(subst_relate_response); + debug!(?candidates); + + if let Some(merged) = self.try_merge_responses(&candidates) { + Ok(merged) + } else { + // When relating two aliases and we have ambiguity, we prefer + // relating the generic arguments of the aliases over normalizing + // them. This is necessary for inference during typeck. + // + // As this is incomplete, we must not do so during coherence. + match self.solver_mode() { + SolverMode::Normal => { + if let Ok(subst_relate_response) = subst_relate_response { + Ok(subst_relate_response) + } else if let Ok(bidirectional_normalizes_to_response) = self + .assemble_bidirectional_normalizes_to_candidate( + param_env, lhs, rhs, direction, + ) + { + Ok(bidirectional_normalizes_to_response) + } else { + self.flounder(&candidates) + } + } + SolverMode::Coherence => self.flounder(&candidates), + } + } + } + } + } + + #[instrument(level = "debug", skip(self), ret)] + fn assemble_normalizes_to_candidate( + &mut self, + param_env: ty::ParamEnv<'tcx>, + alias: ty::AliasTy<'tcx>, + other: ty::Term<'tcx>, + direction: ty::AliasRelationDirection, + invert: Invert, + ) -> QueryResult<'tcx> { + self.probe_candidate("normalizes-to").enter(|ecx| { + ecx.normalizes_to_inner(param_env, alias, other, direction, invert)?; + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }) + } + + fn normalizes_to_inner( + &mut self, + param_env: ty::ParamEnv<'tcx>, + alias: ty::AliasTy<'tcx>, + other: ty::Term<'tcx>, + direction: ty::AliasRelationDirection, + invert: Invert, + ) -> Result<(), NoSolution> { + let other = match direction { + // This is purely an optimization. + ty::AliasRelationDirection::Equate => other, + + ty::AliasRelationDirection::Subtype => { + let fresh = self.next_term_infer_of_kind(other); + let (sub, sup) = match invert { + Invert::No => (fresh, other), + Invert::Yes => (other, fresh), + }; + self.sub(param_env, sub, sup)?; + fresh + } + }; + self.add_goal(Goal::new( + self.tcx(), + param_env, + ty::Binder::dummy(ty::ProjectionPredicate { projection_ty: alias, term: other }), + )); + + Ok(()) + } + + fn assemble_subst_relate_candidate( + &mut self, + param_env: ty::ParamEnv<'tcx>, + alias_lhs: ty::AliasTy<'tcx>, + alias_rhs: ty::AliasTy<'tcx>, + direction: ty::AliasRelationDirection, + ) -> QueryResult<'tcx> { + self.probe_candidate("substs relate").enter(|ecx| { + match direction { + ty::AliasRelationDirection::Equate => { + ecx.eq(param_env, alias_lhs, alias_rhs)?; + } + ty::AliasRelationDirection::Subtype => { + ecx.sub(param_env, alias_lhs, alias_rhs)?; + } + } + + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }) + } + + fn assemble_bidirectional_normalizes_to_candidate( + &mut self, + param_env: ty::ParamEnv<'tcx>, + lhs: ty::Term<'tcx>, + rhs: ty::Term<'tcx>, + direction: ty::AliasRelationDirection, + ) -> QueryResult<'tcx> { + self.probe_candidate("bidir normalizes-to").enter(|ecx| { + ecx.normalizes_to_inner( + param_env, + lhs.to_alias_ty(ecx.tcx()).unwrap(), + rhs, + direction, + Invert::No, + )?; + ecx.normalizes_to_inner( + param_env, + rhs.to_alias_ty(ecx.tcx()).unwrap(), + lhs, + direction, + Invert::Yes, + )?; + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }) + } +} diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs index f32ff0442..28138054a 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs @@ -8,6 +8,7 @@ use rustc_hir::def_id::DefId; use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::util::elaborate; use rustc_infer::traits::Reveal; +use rustc_middle::traits::solve::inspect::CandidateKind; use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, MaybeCause, QueryResult}; use rustc_middle::ty::fast_reject::TreatProjections; use rustc_middle::ty::TypeFoldable; @@ -48,7 +49,7 @@ pub(super) enum CandidateSource { /// Notable examples are auto traits, `Sized`, and `DiscriminantKind`. /// For a list of all traits with builtin impls, check out the /// [`EvalCtxt::assemble_builtin_impl_candidates`] method. Not - BuiltinImpl, + BuiltinImpl(BuiltinImplSource), /// An assumption from the environment. /// /// More precisely we've used the `n-th` assumption in the `param_env`. @@ -86,6 +87,16 @@ pub(super) enum CandidateSource { AliasBound, } +/// Records additional information about what kind of built-in impl this is. +/// This should only be used by selection. +#[derive(Debug, Clone, Copy)] +pub(super) enum BuiltinImplSource { + TraitUpcasting, + Object, + Misc, + Ambiguity, +} + /// Methods used to assemble candidates for either trait or projection goals. pub(super) trait GoalKind<'tcx>: TypeFoldable<TyCtxt<'tcx>> + Copy + Eq + std::fmt::Display @@ -105,7 +116,7 @@ pub(super) trait GoalKind<'tcx>: fn probe_and_match_goal_against_assumption( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - assumption: ty::Predicate<'tcx>, + assumption: ty::Clause<'tcx>, then: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> QueryResult<'tcx>, ) -> QueryResult<'tcx>; @@ -115,7 +126,7 @@ pub(super) trait GoalKind<'tcx>: fn consider_implied_clause( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - assumption: ty::Predicate<'tcx>, + assumption: ty::Clause<'tcx>, requirements: impl IntoIterator<Item = Goal<'tcx, ty::Predicate<'tcx>>>, ) -> QueryResult<'tcx> { Self::probe_and_match_goal_against_assumption(ecx, goal, assumption, |ecx| { @@ -131,7 +142,7 @@ pub(super) trait GoalKind<'tcx>: fn consider_alias_bound_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - assumption: ty::Predicate<'tcx>, + assumption: ty::Clause<'tcx>, ) -> QueryResult<'tcx> { Self::probe_and_match_goal_against_assumption(ecx, goal, assumption, |ecx| { ecx.validate_alias_bound_self_from_param_env(goal) @@ -144,7 +155,7 @@ pub(super) trait GoalKind<'tcx>: fn consider_object_bound_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - assumption: ty::Predicate<'tcx>, + assumption: ty::Clause<'tcx>, ) -> QueryResult<'tcx> { Self::probe_and_match_goal_against_assumption(ecx, goal, assumption, |ecx| { let tcx = ecx.tcx(); @@ -294,7 +305,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { // least structurally resolve the type one layer. if goal.predicate.self_ty().is_ty_var() { return vec![Candidate { - source: CandidateSource::BuiltinImpl, + source: CandidateSource::BuiltinImpl(BuiltinImplSource::Ambiguity), result: self .evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) .unwrap(), @@ -320,11 +331,20 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { candidates } - /// If the self type of a goal is a projection, computing the relevant candidates is difficult. + /// If the self type of a goal is an alias we first try to normalize the self type + /// and compute the candidates for the normalized self type in case that succeeds. + /// + /// These candidates are used in addition to the ones with the alias as a self type. + /// We do this to simplify both builtin candidates and for better performance. + /// + /// We generate the builtin candidates on the fly by looking at the self type, e.g. + /// add `FnPtr` candidates if the self type is a function pointer. Handling builtin + /// candidates while the self type is still an alias seems difficult. This is similar + /// to `try_structurally_resolve_type` during hir typeck (FIXME once implemented). /// - /// To deal with this, we first try to normalize the self type and add the candidates for the normalized - /// self type to the list of candidates in case that succeeds. We also have to consider candidates with the - /// projection as a self type as well + /// Looking at all impls for some trait goal is prohibitively expensive. We therefore + /// only look at implementations with a matching self type. Because of this function, + /// we can avoid looking at all existing impls if the self type is an alias. #[instrument(level = "debug", skip_all)] fn assemble_candidates_after_normalizing_self_ty<G: GoalKind<'tcx>>( &mut self, @@ -336,37 +356,41 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { return }; - let normalized_self_candidates: Result<_, NoSolution> = self.probe(|ecx| { - ecx.with_incremented_depth( - |ecx| { - let result = ecx.evaluate_added_goals_and_make_canonical_response( - Certainty::Maybe(MaybeCause::Overflow), - )?; - Ok(vec![Candidate { source: CandidateSource::BuiltinImpl, result }]) - }, - |ecx| { - let normalized_ty = ecx.next_ty_infer(); - let normalizes_to_goal = goal.with( - tcx, - ty::Binder::dummy(ty::ProjectionPredicate { - projection_ty, - term: normalized_ty.into(), - }), - ); - ecx.add_goal(normalizes_to_goal); - let _ = ecx.try_evaluate_added_goals().inspect_err(|_| { - debug!("self type normalization failed"); - })?; - let normalized_ty = ecx.resolve_vars_if_possible(normalized_ty); - debug!(?normalized_ty, "self type normalized"); - // NOTE: Alternatively we could call `evaluate_goal` here and only - // have a `Normalized` candidate. This doesn't work as long as we - // use `CandidateSource` in winnowing. - let goal = goal.with(tcx, goal.predicate.with_self_ty(tcx, normalized_ty)); - Ok(ecx.assemble_and_evaluate_candidates(goal)) - }, - ) - }); + let normalized_self_candidates: Result<_, NoSolution> = + self.probe(|_| CandidateKind::NormalizedSelfTyAssembly).enter(|ecx| { + ecx.with_incremented_depth( + |ecx| { + let result = ecx.evaluate_added_goals_and_make_canonical_response( + Certainty::Maybe(MaybeCause::Overflow), + )?; + Ok(vec![Candidate { + source: CandidateSource::BuiltinImpl(BuiltinImplSource::Ambiguity), + result, + }]) + }, + |ecx| { + let normalized_ty = ecx.next_ty_infer(); + let normalizes_to_goal = goal.with( + tcx, + ty::Binder::dummy(ty::ProjectionPredicate { + projection_ty, + term: normalized_ty.into(), + }), + ); + ecx.add_goal(normalizes_to_goal); + let _ = ecx.try_evaluate_added_goals().inspect_err(|_| { + debug!("self type normalization failed"); + })?; + let normalized_ty = ecx.resolve_vars_if_possible(normalized_ty); + debug!(?normalized_ty, "self type normalized"); + // NOTE: Alternatively we could call `evaluate_goal` here and only + // have a `Normalized` candidate. This doesn't work as long as we + // use `CandidateSource` in winnowing. + let goal = goal.with(tcx, goal.predicate.with_self_ty(tcx, normalized_ty)); + Ok(ecx.assemble_and_evaluate_candidates(goal)) + }, + ) + }); if let Ok(normalized_self_candidates) = normalized_self_candidates { candidates.extend(normalized_self_candidates); @@ -445,9 +469,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { }; match result { - Ok(result) => { - candidates.push(Candidate { source: CandidateSource::BuiltinImpl, result }) - } + Ok(result) => candidates.push(Candidate { + source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), + result, + }), Err(NoSolution) => (), } @@ -455,7 +480,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { // `trait Foo: Bar<A> + Bar<B>` and `dyn Foo: Unsize<dyn Bar<_>>` if lang_items.unsize_trait() == Some(trait_def_id) { for result in G::consider_builtin_dyn_upcast_candidates(self, goal) { - candidates.push(Candidate { source: CandidateSource::BuiltinImpl, result }); + candidates.push(Candidate { + source: CandidateSource::BuiltinImpl(BuiltinImplSource::TraitUpcasting), + result, + }); } } } @@ -508,10 +536,11 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { | ty::Placeholder(..) | ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) | ty::Alias(ty::Inherent, _) + | ty::Alias(ty::Weak, _) | ty::Error(_) => return, ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) | ty::Bound(..) => bug!("unexpected self type for `{goal:?}`"), - // Excluding IATs here as they don't have meaningful item bounds. + // Excluding IATs and type aliases here as they don't have meaningful item bounds. ty::Alias(ty::Projection | ty::Opaque, alias_ty) => alias_ty, }; @@ -618,9 +647,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { }; match result { - Ok(result) => { - candidates.push(Candidate { source: CandidateSource::BuiltinImpl, result }) - } + Ok(result) => candidates.push(Candidate { + source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), + result, + }), Err(NoSolution) => (), } } @@ -631,6 +661,11 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { goal: Goal<'tcx, G>, candidates: &mut Vec<Candidate<'tcx>>, ) { + let tcx = self.tcx(); + if !tcx.trait_def(goal.predicate.trait_def_id(tcx)).implement_via_object { + return; + } + let self_ty = goal.predicate.self_ty(); let bounds = match *self_ty.kind() { ty::Bool @@ -663,7 +698,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { ty::Dynamic(bounds, ..) => bounds, }; - let tcx = self.tcx(); let own_bounds: FxIndexSet<_> = bounds.iter().map(|bound| bound.with_self_ty(tcx, self_ty)).collect(); for assumption in elaborate(tcx, own_bounds.iter().copied()) @@ -675,17 +709,16 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { // projection predicates that we reach by elaborating the principal trait ref, // since that'll cause ambiguity. // - // We can remove this when we have implemented intersections in responses. - if assumption.to_opt_poly_projection_pred().is_some() - && !own_bounds.contains(&assumption) - { + // We can remove this when we have implemented lifetime intersections in responses. + if assumption.as_projection_clause().is_some() && !own_bounds.contains(&assumption) { continue; } match G::consider_object_bound_candidate(self, goal, assumption) { - Ok(result) => { - candidates.push(Candidate { source: CandidateSource::BuiltinImpl, result }) - } + Ok(result) => candidates.push(Candidate { + source: CandidateSource::BuiltinImpl(BuiltinImplSource::Object), + result, + }), Err(NoSolution) => (), } } @@ -706,8 +739,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { Err(_) => match self .evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) { - Ok(result) => candidates - .push(Candidate { source: CandidateSource::BuiltinImpl, result }), + Ok(result) => candidates.push(Candidate { + source: CandidateSource::BuiltinImpl(BuiltinImplSource::Ambiguity), + result, + }), // FIXME: This will be reachable at some point if we're in // `assemble_candidates_after_normalizing_self_ty` and we get a // universe error. We'll deal with it at this point. diff --git a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs index 0ede32c75..3bb8cad15 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs @@ -28,12 +28,12 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_auto_trait<'tcx>( | ty::Char => Ok(vec![]), // Treat `str` like it's defined as `struct str([u8]);` - ty::Str => Ok(vec![tcx.mk_slice(tcx.types.u8)]), + ty::Str => Ok(vec![Ty::new_slice(tcx, tcx.types.u8)]), ty::Dynamic(..) | ty::Param(..) | ty::Foreign(..) - | ty::Alias(ty::Projection | ty::Inherent, ..) + | ty::Alias(ty::Projection | ty::Inherent | ty::Weak, ..) | ty::Placeholder(..) | ty::Bound(..) | ty::Infer(_) => { @@ -96,7 +96,7 @@ pub(in crate::solve) fn replace_erased_lifetimes_with_bound_vars<'tcx>( let br = ty::BoundRegion { var: ty::BoundVar::from_u32(counter), kind: ty::BrAnon(None) }; counter += 1; - tcx.mk_re_late_bound(current_depth, br) + ty::Region::new_late_bound(tcx, current_depth, br) } // All free regions should be erased here. r => bug!("unexpected region: {r:?}"), @@ -148,11 +148,7 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_sized_trait<'tcx>( ty::Adt(def, substs) => { let sized_crit = def.sized_constraint(ecx.tcx()); - Ok(sized_crit - .0 - .iter() - .map(|ty| sized_crit.rebind(*ty).subst(ecx.tcx(), substs)) - .collect()) + Ok(sized_crit.subst_iter_copied(ecx.tcx(), substs).collect()) } } } @@ -237,7 +233,7 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<'tcx>( { Ok(Some( sig.subst(tcx, substs) - .map_bound(|sig| (tcx.mk_tup(sig.inputs()), sig.output())), + .map_bound(|sig| (Ty::new_tup(tcx, sig.inputs()), sig.output())), )) } else { Err(NoSolution) @@ -246,7 +242,7 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<'tcx>( // keep this in sync with assemble_fn_pointer_candidates until the old solver is removed. ty::FnPtr(sig) => { if sig.is_fn_trait_compatible() { - Ok(Some(sig.map_bound(|sig| (tcx.mk_tup(sig.inputs()), sig.output())))) + Ok(Some(sig.map_bound(|sig| (Ty::new_tup(tcx, sig.inputs()), sig.output())))) } else { Err(NoSolution) } @@ -347,7 +343,7 @@ pub(in crate::solve) fn predicates_for_object_candidate<'tcx>( param_env: ty::ParamEnv<'tcx>, trait_ref: ty::TraitRef<'tcx>, object_bound: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>, -) -> Vec<ty::Predicate<'tcx>> { +) -> Vec<ty::Clause<'tcx>> { let tcx = ecx.tcx(); let mut requirements = vec![]; requirements.extend( @@ -357,7 +353,7 @@ pub(in crate::solve) fn predicates_for_object_candidate<'tcx>( // FIXME(associated_const_equality): Also add associated consts to // the requirements here. if item.kind == ty::AssocKind::Type { - requirements.extend(tcx.item_bounds(item.def_id).subst(tcx, trait_ref.substs)); + requirements.extend(tcx.item_bounds(item.def_id).subst_iter(tcx, trait_ref.substs)); } } diff --git a/compiler/rustc_trait_selection/src/solve/canonicalize.rs b/compiler/rustc_trait_selection/src/solve/canonicalize.rs index ff4bff10c..255620489 100644 --- a/compiler/rustc_trait_selection/src/solve/canonicalize.rs +++ b/compiler/rustc_trait_selection/src/solve/canonicalize.rs @@ -208,8 +208,25 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'_, 'tcx> { t } - fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { - let r = self.infcx.shallow_resolve(r); + fn fold_region(&mut self, mut r: ty::Region<'tcx>) -> ty::Region<'tcx> { + match self.canonicalize_mode { + CanonicalizeMode::Input => { + // Don't resolve infer vars in input, since it affects + // caching and may cause trait selection bugs which rely + // on regions to be equal. + } + CanonicalizeMode::Response { .. } => { + if let ty::ReVar(vid) = *r { + r = self + .infcx + .inner + .borrow_mut() + .unwrap_region_constraints() + .opportunistic_resolve_var(self.infcx.tcx, vid); + } + } + } + let kind = match *r { ty::ReLateBound(..) => return r, @@ -255,7 +272,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'_, 'tcx> { }), ); let br = ty::BoundRegion { var, kind: BrAnon(None) }; - self.interner().mk_re_late_bound(self.binder_index, br) + ty::Region::new_late_bound(self.interner(), self.binder_index, br) } fn fold_ty(&mut self, mut t: Ty<'tcx>) -> Ty<'tcx> { @@ -266,7 +283,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'_, 'tcx> { // any equated inference vars correctly! let root_vid = self.infcx.root_var(vid); if root_vid != vid { - t = self.infcx.tcx.mk_ty_var(root_vid); + t = Ty::new_var(self.infcx.tcx, root_vid); vid = root_vid; } @@ -349,7 +366,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'_, 'tcx> { }), ); let bt = ty::BoundTy { var, kind: BoundTyKind::Anon }; - self.interner().mk_bound(self.binder_index, bt) + Ty::new_bound(self.infcx.tcx, self.binder_index, bt) } fn fold_const(&mut self, mut c: ty::Const<'tcx>) -> ty::Const<'tcx> { @@ -360,7 +377,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'_, 'tcx> { // any equated inference vars correctly! let root_vid = self.infcx.root_const_var(vid); if root_vid != vid { - c = self.infcx.tcx.mk_const(ty::InferConst::Var(root_vid), c.ty()); + c = ty::Const::new_var(self.infcx.tcx, root_vid, c.ty()); vid = root_vid; } @@ -409,6 +426,6 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'_, 'tcx> { var }), ); - self.interner().mk_const(ty::ConstKind::Bound(self.binder_index, var), c.ty()) + ty::Const::new_bound(self.infcx.tcx, self.binder_index, var, c.ty()) } } diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs index f91c67277..74dfbdddb 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs @@ -9,25 +9,32 @@ use rustc_infer::infer::{ use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::ObligationCause; use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; +use rustc_middle::traits::solve::inspect; use rustc_middle::traits::solve::{ - CanonicalInput, CanonicalResponse, Certainty, MaybeCause, PredefinedOpaques, - PredefinedOpaquesData, QueryResult, + CanonicalInput, CanonicalResponse, Certainty, IsNormalizesToHack, MaybeCause, + PredefinedOpaques, PredefinedOpaquesData, QueryResult, }; use rustc_middle::traits::DefiningAnchor; use rustc_middle::ty::{ - self, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, - TypeVisitor, + self, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable, + TypeVisitableExt, TypeVisitor, }; +use rustc_session::config::DumpSolverProofTree; use rustc_span::DUMMY_SP; +use std::io::Write; use std::ops::ControlFlow; use crate::traits::specialization_graph; +use super::inspect::ProofTreeBuilder; use super::search_graph::{self, OverflowHandler}; use super::SolverMode; use super::{search_graph::SearchGraph, Goal}; +pub use select::InferCtxtSelectExt; mod canonical; +mod probe; +mod select; pub struct EvalCtxt<'a, 'tcx> { /// The inference context that backs (mostly) inference and placeholder terms @@ -73,12 +80,8 @@ pub struct EvalCtxt<'a, 'tcx> { // ambiguous goals. Instead, a probe needs to be introduced somewhere in the // evaluation code. tainted: Result<(), NoSolution>, -} -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub(super) enum IsNormalizesToHack { - Yes, - No, + inspect: ProofTreeBuilder<'tcx>, } #[derive(Debug, Clone)] @@ -110,6 +113,26 @@ impl NestedGoals<'_> { } } +#[derive(PartialEq, Eq, Debug, Hash, HashStable, Clone, Copy)] +pub enum GenerateProofTree { + Yes(UseGlobalCache), + No, +} + +#[derive(PartialEq, Eq, Debug, Hash, HashStable, Clone, Copy)] +pub enum UseGlobalCache { + Yes, + No, +} +impl UseGlobalCache { + pub fn from_bool(use_cache: bool) -> Self { + match use_cache { + true => UseGlobalCache::Yes, + false => UseGlobalCache::No, + } + } +} + pub trait InferCtxtEvalExt<'tcx> { /// Evaluates a goal from **outside** of the trait solver. /// @@ -118,7 +141,11 @@ pub trait InferCtxtEvalExt<'tcx> { fn evaluate_root_goal( &self, goal: Goal<'tcx, ty::Predicate<'tcx>>, - ) -> Result<(bool, Certainty, Vec<Goal<'tcx, ty::Predicate<'tcx>>>), NoSolution>; + generate_proof_tree: GenerateProofTree, + ) -> ( + Result<(bool, Certainty, Vec<Goal<'tcx, ty::Predicate<'tcx>>>), NoSolution>, + Option<inspect::GoalEvaluation<'tcx>>, + ); } impl<'tcx> InferCtxtEvalExt<'tcx> for InferCtxt<'tcx> { @@ -126,16 +153,39 @@ impl<'tcx> InferCtxtEvalExt<'tcx> for InferCtxt<'tcx> { fn evaluate_root_goal( &self, goal: Goal<'tcx, ty::Predicate<'tcx>>, - ) -> Result<(bool, Certainty, Vec<Goal<'tcx, ty::Predicate<'tcx>>>), NoSolution> { - let mode = if self.intercrate { SolverMode::Coherence } else { SolverMode::Normal }; - let mut search_graph = search_graph::SearchGraph::new(self.tcx, mode); + generate_proof_tree: GenerateProofTree, + ) -> ( + Result<(bool, Certainty, Vec<Goal<'tcx, ty::Predicate<'tcx>>>), NoSolution>, + Option<inspect::GoalEvaluation<'tcx>>, + ) { + EvalCtxt::enter_root(self, generate_proof_tree, |ecx| { + ecx.evaluate_goal(IsNormalizesToHack::No, goal) + }) + } +} + +impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { + pub(super) fn solver_mode(&self) -> SolverMode { + self.search_graph.solver_mode() + } + + /// Creates a root evaluation context and search graph. This should only be + /// used from outside of any evaluation, and other methods should be preferred + /// over using this manually (such as [`InferCtxtEvalExt::evaluate_root_goal`]). + fn enter_root<R>( + infcx: &InferCtxt<'tcx>, + generate_proof_tree: GenerateProofTree, + f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> R, + ) -> (R, Option<inspect::GoalEvaluation<'tcx>>) { + let mode = if infcx.intercrate { SolverMode::Coherence } else { SolverMode::Normal }; + let mut search_graph = search_graph::SearchGraph::new(infcx.tcx, mode); let mut ecx = EvalCtxt { search_graph: &mut search_graph, - infcx: self, + infcx: infcx, // Only relevant when canonicalizing the response, // which we don't do within this evaluation context. - predefined_opaques_in_body: self + predefined_opaques_in_body: infcx .tcx .mk_predefined_opaques_in_body(PredefinedOpaquesData::default()), // Only relevant when canonicalizing the response. @@ -143,8 +193,18 @@ impl<'tcx> InferCtxtEvalExt<'tcx> for InferCtxt<'tcx> { var_values: CanonicalVarValues::dummy(), nested_goals: NestedGoals::new(), tainted: Ok(()), + inspect: ProofTreeBuilder::new_maybe_root(infcx.tcx, generate_proof_tree), }; - let result = ecx.evaluate_goal(IsNormalizesToHack::No, goal); + let result = f(&mut ecx); + + let tree = ecx.inspect.finalize(); + if let (Some(tree), DumpSolverProofTree::Always) = + (&tree, infcx.tcx.sess.opts.unstable_opts.dump_solver_proof_tree) + { + let mut lock = std::io::stdout().lock(); + let _ = lock.write_fmt(format_args!("{tree:?}")); + let _ = lock.flush(); + } assert!( ecx.nested_goals.is_empty(), @@ -152,13 +212,68 @@ impl<'tcx> InferCtxtEvalExt<'tcx> for InferCtxt<'tcx> { ); assert!(search_graph.is_empty()); - result + (result, tree) } -} -impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { - pub(super) fn solver_mode(&self) -> SolverMode { - self.search_graph.solver_mode() + /// Creates a nested evaluation context that shares the same search graph as the + /// one passed in. This is suitable for evaluation, granted that the search graph + /// has had the nested goal recorded on its stack ([`SearchGraph::with_new_goal`]), + /// but it's preferable to use other methods that call this one rather than this + /// method directly. + /// + /// This function takes care of setting up the inference context, setting the anchor, + /// and registering opaques from the canonicalized input. + fn enter_canonical<R>( + tcx: TyCtxt<'tcx>, + search_graph: &'a mut search_graph::SearchGraph<'tcx>, + canonical_input: CanonicalInput<'tcx>, + goal_evaluation: &mut ProofTreeBuilder<'tcx>, + f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>, Goal<'tcx, ty::Predicate<'tcx>>) -> R, + ) -> R { + let intercrate = match search_graph.solver_mode() { + SolverMode::Normal => false, + SolverMode::Coherence => true, + }; + let (ref infcx, input, var_values) = tcx + .infer_ctxt() + .intercrate(intercrate) + .with_next_trait_solver(true) + .with_opaque_type_inference(canonical_input.value.anchor) + .build_with_canonical(DUMMY_SP, &canonical_input); + + let mut ecx = EvalCtxt { + infcx, + var_values, + predefined_opaques_in_body: input.predefined_opaques_in_body, + max_input_universe: canonical_input.max_universe, + search_graph, + nested_goals: NestedGoals::new(), + tainted: Ok(()), + inspect: goal_evaluation.new_goal_evaluation_step(input), + }; + + for &(key, ty) in &input.predefined_opaques_in_body.opaque_types { + ecx.insert_hidden_type(key, input.goal.param_env, ty) + .expect("failed to prepopulate opaque types"); + } + + if !ecx.nested_goals.is_empty() { + panic!("prepopulating opaque types shouldn't add goals: {:?}", ecx.nested_goals); + } + + let result = f(&mut ecx, input.goal); + + goal_evaluation.goal_evaluation_step(ecx.inspect); + + // When creating a query response we clone the opaque type constraints + // instead of taking them. This would cause an ICE here, since we have + // assertions against dropping an `InferCtxt` without taking opaques. + // FIXME: Once we remove support for the old impl we can remove this. + if input.anchor != DefiningAnchor::Error { + let _ = infcx.take_opaque_types(); + } + + result } /// The entry point of the solver. @@ -170,58 +285,36 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { /// Instead of calling this function directly, use either [EvalCtxt::evaluate_goal] /// if you're inside of the solver or [InferCtxtEvalExt::evaluate_root_goal] if you're /// outside of it. - #[instrument(level = "debug", skip(tcx, search_graph), ret)] + #[instrument(level = "debug", skip(tcx, search_graph, goal_evaluation), ret)] fn evaluate_canonical_goal( tcx: TyCtxt<'tcx>, search_graph: &'a mut search_graph::SearchGraph<'tcx>, canonical_input: CanonicalInput<'tcx>, + mut goal_evaluation: &mut ProofTreeBuilder<'tcx>, ) -> QueryResult<'tcx> { + goal_evaluation.canonicalized_goal(canonical_input); + // Deal with overflow, caching, and coinduction. // // The actual solver logic happens in `ecx.compute_goal`. - search_graph.with_new_goal(tcx, canonical_input, |search_graph| { - let intercrate = match search_graph.solver_mode() { - SolverMode::Normal => false, - SolverMode::Coherence => true, - }; - let (ref infcx, input, var_values) = tcx - .infer_ctxt() - .intercrate(intercrate) - .with_opaque_type_inference(canonical_input.value.anchor) - .build_with_canonical(DUMMY_SP, &canonical_input); - - for &(a, b) in &input.predefined_opaques_in_body.opaque_types { - let InferOk { value: (), obligations } = infcx - .register_hidden_type_in_new_solver(a, input.goal.param_env, b) - .expect("expected opaque type instantiation to succeed"); - // We're only registering opaques already defined by the caller, - // so we're not responsible for proving that they satisfy their - // item bounds, unless we use them in a normalizes-to goal, - // which is handled in `EvalCtxt::unify_existing_opaque_tys`. - let _ = obligations; - } - let mut ecx = EvalCtxt { - infcx, - var_values, - predefined_opaques_in_body: input.predefined_opaques_in_body, - max_input_universe: canonical_input.max_universe, - search_graph, - nested_goals: NestedGoals::new(), - tainted: Ok(()), - }; - - let result = ecx.compute_goal(input.goal); - - // When creating a query response we clone the opaque type constraints - // instead of taking them. This would cause an ICE here, since we have - // assertions against dropping an `InferCtxt` without taking opaques. - // FIXME: Once we remove support for the old impl we can remove this. - if input.anchor != DefiningAnchor::Error { - let _ = infcx.take_opaque_types(); - } - - result - }) + search_graph.with_new_goal( + tcx, + canonical_input, + goal_evaluation, + |search_graph, goal_evaluation| { + EvalCtxt::enter_canonical( + tcx, + search_graph, + canonical_input, + goal_evaluation, + |ecx, goal| { + let result = ecx.compute_goal(goal); + ecx.inspect.query_result(result); + result + }, + ) + }, + ) } /// Recursively evaluates `goal`, returning whether any inference vars have @@ -232,16 +325,37 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { goal: Goal<'tcx, ty::Predicate<'tcx>>, ) -> Result<(bool, Certainty, Vec<Goal<'tcx, ty::Predicate<'tcx>>>), NoSolution> { let (orig_values, canonical_goal) = self.canonicalize_goal(goal); - let canonical_response = - EvalCtxt::evaluate_canonical_goal(self.tcx(), self.search_graph, canonical_goal)?; + let mut goal_evaluation = self.inspect.new_goal_evaluation(goal, is_normalizes_to_hack); + let canonical_response = EvalCtxt::evaluate_canonical_goal( + self.tcx(), + self.search_graph, + canonical_goal, + &mut goal_evaluation, + ); + goal_evaluation.query_result(canonical_response); + let canonical_response = match canonical_response { + Err(e) => { + self.inspect.goal_evaluation(goal_evaluation); + return Err(e); + } + Ok(response) => response, + }; let has_changed = !canonical_response.value.var_values.is_identity() || !canonical_response.value.external_constraints.opaque_types.is_empty(); - let (certainty, nested_goals) = self.instantiate_and_apply_query_response( + let (certainty, nested_goals) = match self.instantiate_and_apply_query_response( goal.param_env, orig_values, canonical_response, - )?; + ) { + Err(e) => { + self.inspect.goal_evaluation(goal_evaluation); + return Err(e); + } + Ok(response) => response, + }; + goal_evaluation.returned_goals(&nested_goals); + self.inspect.goal_evaluation(goal_evaluation); if !has_changed && !nested_goals.is_empty() { bug!("an unchanged goal shouldn't have any side-effects on instantiation"); @@ -261,9 +375,17 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { { debug!("rerunning goal to check result is stable"); let (_orig_values, canonical_goal) = self.canonicalize_goal(goal); - let new_canonical_response = - EvalCtxt::evaluate_canonical_goal(self.tcx(), self.search_graph, canonical_goal)?; - if !new_canonical_response.value.var_values.is_identity() { + let new_canonical_response = EvalCtxt::evaluate_canonical_goal( + self.tcx(), + self.search_graph, + canonical_goal, + // FIXME(-Ztrait-solver=next): we do not track what happens in `evaluate_canonical_goal` + &mut ProofTreeBuilder::new_noop(), + )?; + // We only check for modulo regions as we convert all regions in + // the input to new existentials, even if they're expected to be + // `'static` or a placeholder region. + if !new_canonical_response.value.var_values.is_identity_modulo_regions() { bug!( "unstable result: re-canonicalized goal={canonical_goal:#?} \ first_response={canonical_response:#?} \ @@ -287,19 +409,19 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { let kind = predicate.kind(); if let Some(kind) = kind.no_bound_vars() { match kind { - ty::PredicateKind::Clause(ty::Clause::Trait(predicate)) => { + ty::PredicateKind::Clause(ty::ClauseKind::Trait(predicate)) => { self.compute_trait_goal(Goal { param_env, predicate }) } - ty::PredicateKind::Clause(ty::Clause::Projection(predicate)) => { + ty::PredicateKind::Clause(ty::ClauseKind::Projection(predicate)) => { self.compute_projection_goal(Goal { param_env, predicate }) } - ty::PredicateKind::Clause(ty::Clause::TypeOutlives(predicate)) => { + ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(predicate)) => { self.compute_type_outlives_goal(Goal { param_env, predicate }) } - ty::PredicateKind::Clause(ty::Clause::RegionOutlives(predicate)) => { + ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(predicate)) => { self.compute_region_outlives_goal(Goal { param_env, predicate }) } - ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(ct, ty)) => { + ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, ty)) => { self.compute_const_arg_has_type_goal(Goal { param_env, predicate: (ct, ty) }) } ty::PredicateKind::Subtype(predicate) => { @@ -316,24 +438,23 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { ty::PredicateKind::ObjectSafe(trait_def_id) => { self.compute_object_safe_goal(trait_def_id) } - ty::PredicateKind::WellFormed(arg) => { + ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => { self.compute_well_formed_goal(Goal { param_env, predicate: arg }) } - ty::PredicateKind::Ambiguous => { - self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) - } - // FIXME: implement these predicates :) - ty::PredicateKind::ConstEvaluatable(_) | ty::PredicateKind::ConstEquate(_, _) => { - self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(ct)) => { + self.compute_const_evaluatable_goal(Goal { param_env, predicate: ct }) } - ty::PredicateKind::TypeWellFormedFromEnv(..) => { - bug!("TypeWellFormedFromEnv is only used for Chalk") + ty::PredicateKind::ConstEquate(_, _) => { + bug!("ConstEquate should not be emitted when `-Ztrait-solver=next` is active") } ty::PredicateKind::AliasRelate(lhs, rhs, direction) => self .compute_alias_relate_goal(Goal { param_env, predicate: (lhs, rhs, direction), }), + ty::PredicateKind::Ambiguous => { + self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) + } } } else { let kind = self.infcx.instantiate_binder_with_placeholders(kind); @@ -347,12 +468,17 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { // the certainty of all the goals. #[instrument(level = "debug", skip(self))] pub(super) fn try_evaluate_added_goals(&mut self) -> Result<Certainty, NoSolution> { + let inspect = self.inspect.new_evaluate_added_goals(); + let inspect = core::mem::replace(&mut self.inspect, inspect); + let mut goals = core::mem::replace(&mut self.nested_goals, NestedGoals::new()); let mut new_goals = NestedGoals::new(); let response = self.repeat_while_none( |_| Ok(Certainty::Maybe(MaybeCause::Overflow)), |this| { + this.inspect.evaluate_added_goals_loop_start(); + let mut has_changed = Err(Certainty::Yes); if let Some(goal) = goals.normalizes_to_hack_goal.take() { @@ -441,29 +567,21 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { }, ); + self.inspect.eval_added_goals_result(response); + if response.is_err() { self.tainted = Err(NoSolution); } + let goal_evaluations = std::mem::replace(&mut self.inspect, inspect); + self.inspect.added_goals_evaluation(goal_evaluations); + self.nested_goals = goals; response } } impl<'tcx> EvalCtxt<'_, 'tcx> { - pub(super) fn probe<T>(&mut self, f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> T) -> T { - let mut ecx = EvalCtxt { - infcx: self.infcx, - var_values: self.var_values, - predefined_opaques_in_body: self.predefined_opaques_in_body, - max_input_universe: self.max_input_universe, - search_graph: self.search_graph, - nested_goals: self.nested_goals.clone(), - tainted: self.tainted, - }; - self.infcx.probe(|_| f(&mut ecx)) - } - pub(super) fn tcx(&self) -> TyCtxt<'tcx> { self.infcx.tcx } @@ -706,6 +824,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { scope: Ty<'tcx>, assume: rustc_transmute::Assume, ) -> Result<Certainty, NoSolution> { + use rustc_transmute::Answer; // FIXME(transmutability): This really should be returning nested goals for `Answer::If*` match rustc_transmute::TransmuteTypeEnv::new(self.infcx).is_transmutable( ObligationCause::dummy(), @@ -713,30 +832,53 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { scope, assume, ) { - rustc_transmute::Answer::Yes => Ok(Certainty::Yes), - rustc_transmute::Answer::No(_) - | rustc_transmute::Answer::IfTransmutable { .. } - | rustc_transmute::Answer::IfAll(_) - | rustc_transmute::Answer::IfAny(_) => Err(NoSolution), + Answer::Yes => Ok(Certainty::Yes), + Answer::No(_) | Answer::If(_) => Err(NoSolution), } } - pub(super) fn can_define_opaque_ty(&mut self, def_id: LocalDefId) -> bool { + pub(super) fn can_define_opaque_ty(&self, def_id: LocalDefId) -> bool { self.infcx.opaque_type_origin(def_id).is_some() } - pub(super) fn register_opaque_ty( + pub(super) fn insert_hidden_type( &mut self, - a: ty::OpaqueTypeKey<'tcx>, - b: Ty<'tcx>, + opaque_type_key: OpaqueTypeKey<'tcx>, param_env: ty::ParamEnv<'tcx>, + hidden_ty: Ty<'tcx>, ) -> Result<(), NoSolution> { - let InferOk { value: (), obligations } = - self.infcx.register_hidden_type_in_new_solver(a, param_env, b)?; - self.add_goals(obligations.into_iter().map(|obligation| obligation.into())); + let mut obligations = Vec::new(); + self.infcx.insert_hidden_type( + opaque_type_key, + &ObligationCause::dummy(), + param_env, + hidden_ty, + true, + &mut obligations, + )?; + self.add_goals(obligations.into_iter().map(|o| o.into())); Ok(()) } + pub(super) fn add_item_bounds_for_hidden_type( + &mut self, + opaque_def_id: DefId, + opaque_substs: ty::SubstsRef<'tcx>, + param_env: ty::ParamEnv<'tcx>, + hidden_ty: Ty<'tcx>, + ) { + let mut obligations = Vec::new(); + self.infcx.add_item_bounds_for_hidden_type( + opaque_def_id, + opaque_substs, + ObligationCause::dummy(), + param_env, + hidden_ty, + &mut obligations, + ); + self.add_goals(obligations.into_iter().map(|o| o.into())); + } + // Do something for each opaque/hidden pair defined with `def_id` in the // current inference context. pub(super) fn unify_existing_opaque_tys( @@ -753,23 +895,37 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { if candidate_key.def_id != key.def_id { continue; } - values.extend(self.probe(|ecx| { + values.extend(self.probe_candidate("opaque type storage").enter(|ecx| { for (a, b) in std::iter::zip(candidate_key.substs, key.substs) { ecx.eq(param_env, a, b)?; } ecx.eq(param_env, candidate_ty, ty)?; - let mut obl = vec![]; - ecx.infcx.add_item_bounds_for_hidden_type( - candidate_key, - ObligationCause::dummy(), + ecx.add_item_bounds_for_hidden_type( + candidate_key.def_id.to_def_id(), + candidate_key.substs, param_env, candidate_ty, - &mut obl, ); - ecx.add_goals(obl.into_iter().map(Into::into)); ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) })); } values } + + // Try to evaluate a const, or return `None` if the const is too generic. + // This doesn't mean the const isn't evaluatable, though, and should be treated + // as an ambiguity rather than no-solution. + pub(super) fn try_const_eval_resolve( + &self, + param_env: ty::ParamEnv<'tcx>, + unevaluated: ty::UnevaluatedConst<'tcx>, + ty: Ty<'tcx>, + ) -> Option<ty::Const<'tcx>> { + use rustc_middle::mir::interpret::ErrorHandled; + match self.infcx.try_const_eval_resolve(param_env, unevaluated, ty, None) { + Ok(ct) => Some(ct), + Err(ErrorHandled::Reported(e)) => Some(ty::Const::new_error(self.tcx(), e.into(), ty)), + Err(ErrorHandled::TooGeneric) => None, + } + } } diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs index fdb209fbf..637d45888 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs @@ -11,16 +11,16 @@ use super::{CanonicalInput, Certainty, EvalCtxt, Goal}; use crate::solve::canonicalize::{CanonicalizeMode, Canonicalizer}; use crate::solve::{CanonicalResponse, QueryResult, Response}; +use rustc_data_structures::fx::FxHashSet; use rustc_index::IndexVec; use rustc_infer::infer::canonical::query_response::make_query_region_constraints; use rustc_infer::infer::canonical::CanonicalVarValues; use rustc_infer::infer::canonical::{CanonicalExt, QueryRegionConstraints}; -use rustc_infer::infer::InferOk; use rustc_middle::traits::query::NoSolution; use rustc_middle::traits::solve::{ ExternalConstraints, ExternalConstraintsData, MaybeCause, PredefinedOpaquesData, QueryInput, }; -use rustc_middle::ty::{self, BoundVar, GenericArgKind, Ty}; +use rustc_middle::ty::{self, BoundVar, GenericArgKind, Ty, TyCtxt, TypeFoldable}; use rustc_span::DUMMY_SP; use std::iter; use std::ops::Deref; @@ -28,10 +28,10 @@ use std::ops::Deref; impl<'tcx> EvalCtxt<'_, 'tcx> { /// Canonicalizes the goal remembering the original values /// for each bound variable. - pub(super) fn canonicalize_goal( + pub(super) fn canonicalize_goal<T: TypeFoldable<TyCtxt<'tcx>>>( &self, - goal: Goal<'tcx, ty::Predicate<'tcx>>, - ) -> (Vec<ty::GenericArg<'tcx>>, CanonicalInput<'tcx>) { + goal: Goal<'tcx, T>, + ) -> (Vec<ty::GenericArg<'tcx>>, CanonicalInput<'tcx, T>) { let mut orig_values = Default::default(); let canonical_goal = Canonicalizer::canonicalize( self.infcx, @@ -137,10 +137,17 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { #[instrument(level = "debug", skip(self), ret)] fn compute_external_query_constraints(&self) -> Result<ExternalConstraints<'tcx>, NoSolution> { + // We only check for leaks from universes which were entered inside + // of the query. + self.infcx.leak_check(self.max_input_universe, None).map_err(|e| { + debug!(?e, "failed the leak check"); + NoSolution + })?; + // Cannot use `take_registered_region_obligations` as we may compute the response // inside of a `probe` whenever we have multiple choices inside of the solver. let region_obligations = self.infcx.inner.borrow().region_obligations().to_owned(); - let region_constraints = self.infcx.with_region_constraints(|region_constraints| { + let mut region_constraints = self.infcx.with_region_constraints(|region_constraints| { make_query_region_constraints( self.tcx(), region_obligations @@ -150,6 +157,9 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { ) }); + let mut seen = FxHashSet::default(); + region_constraints.outlives.retain(|outlives| seen.insert(*outlives)); + let mut opaque_types = self.infcx.clone_opaque_types_for_query_response(); // Only return opaque type keys for newly-defined opaques opaque_types.retain(|(a, _)| { @@ -310,12 +320,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { param_env: ty::ParamEnv<'tcx>, opaque_types: &[(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)], ) -> Result<(), NoSolution> { - for &(a, b) in opaque_types { - let InferOk { value: (), obligations } = - self.infcx.register_hidden_type_in_new_solver(a, param_env, b)?; - // It's sound to drop these obligations, since the normalizes-to goal - // is responsible for proving these obligations. - let _ = obligations; + for &(key, ty) in opaque_types { + self.insert_hidden_type(key, param_env, ty)?; } Ok(()) } diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs new file mode 100644 index 000000000..4477ea7d5 --- /dev/null +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs @@ -0,0 +1,67 @@ +use super::EvalCtxt; +use rustc_middle::traits::solve::{inspect, QueryResult}; +use std::marker::PhantomData; + +pub(in crate::solve) struct ProbeCtxt<'me, 'a, 'tcx, F, T> { + ecx: &'me mut EvalCtxt<'a, 'tcx>, + probe_kind: F, + _result: PhantomData<T>, +} + +impl<'tcx, F, T> ProbeCtxt<'_, '_, 'tcx, F, T> +where + F: FnOnce(&T) -> inspect::CandidateKind<'tcx>, +{ + pub(in crate::solve) fn enter(self, f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> T) -> T { + let ProbeCtxt { ecx: outer_ecx, probe_kind, _result } = self; + + let mut nested_ecx = EvalCtxt { + infcx: outer_ecx.infcx, + var_values: outer_ecx.var_values, + predefined_opaques_in_body: outer_ecx.predefined_opaques_in_body, + max_input_universe: outer_ecx.max_input_universe, + search_graph: outer_ecx.search_graph, + nested_goals: outer_ecx.nested_goals.clone(), + tainted: outer_ecx.tainted, + inspect: outer_ecx.inspect.new_goal_candidate(), + }; + let r = nested_ecx.infcx.probe(|_| f(&mut nested_ecx)); + if !outer_ecx.inspect.is_noop() { + let cand_kind = probe_kind(&r); + nested_ecx.inspect.candidate_kind(cand_kind); + outer_ecx.inspect.goal_candidate(nested_ecx.inspect); + } + r + } +} + +impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { + /// `probe_kind` is only called when proof tree building is enabled so it can be + /// as expensive as necessary to output the desired information. + pub(in crate::solve) fn probe<F, T>(&mut self, probe_kind: F) -> ProbeCtxt<'_, 'a, 'tcx, F, T> + where + F: FnOnce(&T) -> inspect::CandidateKind<'tcx>, + { + ProbeCtxt { ecx: self, probe_kind, _result: PhantomData } + } + + pub(in crate::solve) fn probe_candidate( + &mut self, + name: &'static str, + ) -> ProbeCtxt< + '_, + 'a, + 'tcx, + impl FnOnce(&QueryResult<'tcx>) -> inspect::CandidateKind<'tcx>, + QueryResult<'tcx>, + > { + ProbeCtxt { + ecx: self, + probe_kind: move |result: &QueryResult<'tcx>| inspect::CandidateKind::Candidate { + name: name.to_string(), + result: *result, + }, + _result: PhantomData, + } + } +} diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs new file mode 100644 index 000000000..bf6cbef8c --- /dev/null +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs @@ -0,0 +1,307 @@ +use std::ops::ControlFlow; + +use rustc_hir::def_id::DefId; +use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk}; +use rustc_infer::traits::util::supertraits; +use rustc_infer::traits::{ + Obligation, PolyTraitObligation, PredicateObligation, Selection, SelectionResult, +}; +use rustc_middle::traits::solve::{CanonicalInput, Certainty, Goal}; +use rustc_middle::traits::{ + ImplSource, ImplSourceObjectData, ImplSourceTraitUpcastingData, ImplSourceUserDefinedData, + ObligationCause, SelectionError, +}; +use rustc_middle::ty::{self, TyCtxt}; +use rustc_span::DUMMY_SP; + +use crate::solve::assembly::{BuiltinImplSource, Candidate, CandidateSource}; +use crate::solve::eval_ctxt::{EvalCtxt, GenerateProofTree}; +use crate::solve::inspect::ProofTreeBuilder; +use crate::solve::search_graph::OverflowHandler; +use crate::traits::vtable::{count_own_vtable_entries, prepare_vtable_segments, VtblSegment}; + +pub trait InferCtxtSelectExt<'tcx> { + fn select_in_new_trait_solver( + &self, + obligation: &PolyTraitObligation<'tcx>, + ) -> SelectionResult<'tcx, Selection<'tcx>>; +} + +impl<'tcx> InferCtxtSelectExt<'tcx> for InferCtxt<'tcx> { + fn select_in_new_trait_solver( + &self, + obligation: &PolyTraitObligation<'tcx>, + ) -> SelectionResult<'tcx, Selection<'tcx>> { + assert!(self.next_trait_solver()); + + let trait_goal = Goal::new( + self.tcx, + obligation.param_env, + self.instantiate_binder_with_placeholders(obligation.predicate), + ); + + let (result, _) = EvalCtxt::enter_root(self, GenerateProofTree::No, |ecx| { + let goal = Goal::new(ecx.tcx(), trait_goal.param_env, trait_goal.predicate); + let (orig_values, canonical_goal) = ecx.canonicalize_goal(goal); + let mut candidates = ecx.compute_canonical_trait_candidates(canonical_goal); + + // pseudo-winnow + if candidates.len() == 0 { + return Err(SelectionError::Unimplemented); + } else if candidates.len() > 1 { + let mut i = 0; + while i < candidates.len() { + let should_drop_i = (0..candidates.len()).filter(|&j| i != j).any(|j| { + candidate_should_be_dropped_in_favor_of( + ecx.tcx(), + &candidates[i], + &candidates[j], + ) + }); + if should_drop_i { + candidates.swap_remove(i); + } else { + i += 1; + if i > 1 { + return Ok(None); + } + } + } + } + + let candidate = candidates.pop().unwrap(); + let (certainty, nested_goals) = ecx + .instantiate_and_apply_query_response( + trait_goal.param_env, + orig_values, + candidate.result, + ) + .map_err(|_| SelectionError::Unimplemented)?; + + Ok(Some((candidate, certainty, nested_goals))) + }); + + let (candidate, certainty, nested_goals) = match result { + Ok(Some((candidate, certainty, nested_goals))) => (candidate, certainty, nested_goals), + Ok(None) => return Ok(None), + Err(e) => return Err(e), + }; + + let nested_obligations: Vec<_> = nested_goals + .into_iter() + .map(|goal| { + Obligation::new(self.tcx, ObligationCause::dummy(), goal.param_env, goal.predicate) + }) + .collect(); + + let goal = self.resolve_vars_if_possible(trait_goal); + match (certainty, candidate.source) { + // Rematching the implementation will instantiate the same nested goals that + // would have caused the ambiguity, so we can still make progress here regardless. + (_, CandidateSource::Impl(def_id)) => { + rematch_impl(self, goal, def_id, nested_obligations) + } + + // Rematching the dyn upcast or object goal will instantiate the same nested + // goals that would have caused the ambiguity, so we can still make progress here + // regardless. + // FIXME: This doesn't actually check the object bounds hold here. + ( + _, + CandidateSource::BuiltinImpl( + BuiltinImplSource::Object | BuiltinImplSource::TraitUpcasting, + ), + ) => rematch_object(self, goal, nested_obligations), + + // Technically some builtin impls have nested obligations, but if + // `Certainty::Yes`, then they should've all been verified and don't + // need re-checking. + (Certainty::Yes, CandidateSource::BuiltinImpl(BuiltinImplSource::Misc)) => { + Ok(Some(ImplSource::Builtin(nested_obligations))) + } + + // It's fine not to do anything to rematch these, since there are no + // nested obligations. + (Certainty::Yes, CandidateSource::ParamEnv(_) | CandidateSource::AliasBound) => { + Ok(Some(ImplSource::Param(nested_obligations, ty::BoundConstness::NotConst))) + } + + (_, CandidateSource::BuiltinImpl(BuiltinImplSource::Ambiguity)) + | (Certainty::Maybe(_), _) => Ok(None), + } + } +} + +impl<'tcx> EvalCtxt<'_, 'tcx> { + fn compute_canonical_trait_candidates( + &mut self, + canonical_input: CanonicalInput<'tcx>, + ) -> Vec<Candidate<'tcx>> { + // This doesn't record the canonical goal on the stack during the + // candidate assembly step, but that's fine. Selection is conceptually + // outside of the solver, and if there were any cycles, we'd encounter + // the cycle anyways one step later. + EvalCtxt::enter_canonical( + self.tcx(), + self.search_graph(), + canonical_input, + // FIXME: This is wrong, idk if we even want to track stuff here. + &mut ProofTreeBuilder::new_noop(), + |ecx, goal| { + let trait_goal = Goal { + param_env: goal.param_env, + predicate: goal + .predicate + .to_opt_poly_trait_pred() + .expect("we canonicalized a trait goal") + .no_bound_vars() + .expect("we instantiated all bound vars"), + }; + ecx.assemble_and_evaluate_candidates(trait_goal) + }, + ) + } +} + +fn candidate_should_be_dropped_in_favor_of<'tcx>( + tcx: TyCtxt<'tcx>, + victim: &Candidate<'tcx>, + other: &Candidate<'tcx>, +) -> bool { + match (victim.source, other.source) { + (CandidateSource::ParamEnv(victim_idx), CandidateSource::ParamEnv(other_idx)) => { + victim_idx >= other_idx + } + (_, CandidateSource::ParamEnv(_)) => true, + + ( + CandidateSource::BuiltinImpl(BuiltinImplSource::Object), + CandidateSource::BuiltinImpl(BuiltinImplSource::Object), + ) => false, + (_, CandidateSource::BuiltinImpl(BuiltinImplSource::Object)) => true, + + (CandidateSource::Impl(victim_def_id), CandidateSource::Impl(other_def_id)) => { + tcx.specializes((other_def_id, victim_def_id)) + && other.result.value.certainty == Certainty::Yes + } + + _ => false, + } +} + +fn rematch_impl<'tcx>( + infcx: &InferCtxt<'tcx>, + goal: Goal<'tcx, ty::TraitPredicate<'tcx>>, + impl_def_id: DefId, + mut nested: Vec<PredicateObligation<'tcx>>, +) -> SelectionResult<'tcx, Selection<'tcx>> { + let substs = infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id); + let impl_trait_ref = infcx.tcx.impl_trait_ref(impl_def_id).unwrap().subst(infcx.tcx, substs); + + nested.extend( + infcx + .at(&ObligationCause::dummy(), goal.param_env) + .eq(DefineOpaqueTypes::No, goal.predicate.trait_ref, impl_trait_ref) + .map_err(|_| SelectionError::Unimplemented)? + .into_obligations(), + ); + + nested.extend( + infcx.tcx.predicates_of(impl_def_id).instantiate(infcx.tcx, substs).into_iter().map( + |(pred, _)| Obligation::new(infcx.tcx, ObligationCause::dummy(), goal.param_env, pred), + ), + ); + + Ok(Some(ImplSource::UserDefined(ImplSourceUserDefinedData { impl_def_id, substs, nested }))) +} + +fn rematch_object<'tcx>( + infcx: &InferCtxt<'tcx>, + goal: Goal<'tcx, ty::TraitPredicate<'tcx>>, + mut nested: Vec<PredicateObligation<'tcx>>, +) -> SelectionResult<'tcx, Selection<'tcx>> { + let self_ty = goal.predicate.self_ty(); + let ty::Dynamic(data, _, source_kind) = *self_ty.kind() + else { + bug!() + }; + let source_trait_ref = data.principal().unwrap().with_self_ty(infcx.tcx, self_ty); + + let (is_upcasting, target_trait_ref_unnormalized) = if Some(goal.predicate.def_id()) + == infcx.tcx.lang_items().unsize_trait() + { + assert_eq!(source_kind, ty::Dyn, "cannot upcast dyn*"); + if let ty::Dynamic(data, _, ty::Dyn) = goal.predicate.trait_ref.substs.type_at(1).kind() { + (true, data.principal().unwrap().with_self_ty(infcx.tcx, self_ty)) + } else { + bug!() + } + } else { + (false, ty::Binder::dummy(goal.predicate.trait_ref)) + }; + + let mut target_trait_ref = None; + for candidate_trait_ref in supertraits(infcx.tcx, source_trait_ref) { + let result = infcx.commit_if_ok(|_| { + infcx.at(&ObligationCause::dummy(), goal.param_env).eq( + DefineOpaqueTypes::No, + target_trait_ref_unnormalized, + candidate_trait_ref, + ) + + // FIXME: We probably should at least shallowly verify these... + }); + + match result { + Ok(InferOk { value: (), obligations }) => { + target_trait_ref = Some(candidate_trait_ref); + nested.extend(obligations); + break; + } + Err(_) => continue, + } + } + + let target_trait_ref = target_trait_ref.unwrap(); + + let mut offset = 0; + let Some((vtable_base, vtable_vptr_slot)) = + prepare_vtable_segments(infcx.tcx, source_trait_ref, |segment| { + match segment { + VtblSegment::MetadataDSA => { + offset += TyCtxt::COMMON_VTABLE_ENTRIES.len(); + } + VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => { + let own_vtable_entries = count_own_vtable_entries(infcx.tcx, trait_ref); + + if trait_ref == target_trait_ref { + if emit_vptr { + return ControlFlow::Break(( + offset, + Some(offset + count_own_vtable_entries(infcx.tcx, trait_ref)), + )); + } else { + return ControlFlow::Break((offset, None)); + } + } + + offset += own_vtable_entries; + if emit_vptr { + offset += 1; + } + } + } + ControlFlow::Continue(()) + }) + else { + bug!(); + }; + + // If we're upcasting, get the offset of the vtable pointer, otherwise get + // the base of the vtable. + Ok(Some(if is_upcasting { + ImplSource::TraitUpcasting(ImplSourceTraitUpcastingData { vtable_vptr_slot, nested }) + } else { + ImplSource::Object(ImplSourceObjectData { vtable_base, nested }) + })) +} diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index 4a403196c..88ee14c4d 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -10,6 +10,7 @@ use rustc_infer::traits::{ use rustc_middle::ty; use rustc_middle::ty::error::{ExpectedFound, TypeError}; +use super::eval_ctxt::GenerateProofTree; use super::{Certainty, InferCtxtEvalExt}; /// A trait engine using the new trait solver. @@ -25,20 +26,28 @@ use super::{Certainty, InferCtxtEvalExt}; /// here as this will have to deal with far more root goals than `evaluate_all`. pub struct FulfillmentCtxt<'tcx> { obligations: Vec<PredicateObligation<'tcx>>, + + /// The snapshot in which this context was created. Using the context + /// outside of this snapshot leads to subtle bugs if the snapshot + /// gets rolled back. Because of this we explicitly check that we only + /// use the context in exactly this snapshot. + usable_in_snapshot: usize, } impl<'tcx> FulfillmentCtxt<'tcx> { - pub fn new() -> FulfillmentCtxt<'tcx> { - FulfillmentCtxt { obligations: Vec::new() } + pub fn new(infcx: &InferCtxt<'tcx>) -> FulfillmentCtxt<'tcx> { + FulfillmentCtxt { obligations: Vec::new(), usable_in_snapshot: infcx.num_open_snapshots() } } } impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> { + #[instrument(level = "debug", skip(self, infcx))] fn register_predicate_obligation( &mut self, - _infcx: &InferCtxt<'tcx>, + infcx: &InferCtxt<'tcx>, obligation: PredicateObligation<'tcx>, ) { + assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots()); self.obligations.push(obligation); } @@ -46,8 +55,11 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> { self.obligations .drain(..) .map(|obligation| { - let code = - infcx.probe(|_| match infcx.evaluate_root_goal(obligation.clone().into()) { + let code = infcx.probe(|_| { + match infcx + .evaluate_root_goal(obligation.clone().into(), GenerateProofTree::No) + .0 + { Ok((_, Certainty::Maybe(MaybeCause::Ambiguity), _)) => { FulfillmentErrorCode::CodeAmbiguity { overflow: false } } @@ -60,7 +72,8 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> { Err(_) => { bug!("did not expect selection error when collecting ambiguity errors") } - }); + } + }); FulfillmentError { obligation: obligation.clone(), @@ -72,6 +85,7 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> { } fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>> { + assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots()); let mut errors = Vec::new(); for i in 0.. { if !infcx.tcx.recursion_limit().value_within_limit(i) { @@ -81,72 +95,61 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> { let mut has_changed = false; for obligation in mem::take(&mut self.obligations) { let goal = obligation.clone().into(); - let (changed, certainty, nested_goals) = match infcx.evaluate_root_goal(goal) { - Ok(result) => result, - Err(NoSolution) => { - errors.push(FulfillmentError { - obligation: obligation.clone(), - code: match goal.predicate.kind().skip_binder() { - ty::PredicateKind::Clause(ty::Clause::Projection(_)) => { - FulfillmentErrorCode::CodeProjectionError( - // FIXME: This could be a `Sorts` if the term is a type - MismatchedProjectionTypes { err: TypeError::Mismatch }, - ) - } - ty::PredicateKind::AliasRelate(_, _, _) => { - FulfillmentErrorCode::CodeProjectionError( - MismatchedProjectionTypes { err: TypeError::Mismatch }, - ) - } - ty::PredicateKind::Subtype(pred) => { - let (a, b) = infcx.instantiate_binder_with_placeholders( - goal.predicate.kind().rebind((pred.a, pred.b)), - ); - let expected_found = ExpectedFound::new(true, a, b); - FulfillmentErrorCode::CodeSubtypeError( - expected_found, - TypeError::Sorts(expected_found), - ) - } - ty::PredicateKind::Coerce(pred) => { - let (a, b) = infcx.instantiate_binder_with_placeholders( - goal.predicate.kind().rebind((pred.a, pred.b)), - ); - let expected_found = ExpectedFound::new(false, a, b); - FulfillmentErrorCode::CodeSubtypeError( - expected_found, - TypeError::Sorts(expected_found), - ) - } - ty::PredicateKind::ConstEquate(a, b) => { - let (a, b) = infcx.instantiate_binder_with_placeholders( - goal.predicate.kind().rebind((a, b)), - ); - let expected_found = ExpectedFound::new(true, a, b); - FulfillmentErrorCode::CodeConstEquateError( - expected_found, - TypeError::ConstMismatch(expected_found), - ) - } - ty::PredicateKind::Clause(_) - | ty::PredicateKind::WellFormed(_) - | ty::PredicateKind::ObjectSafe(_) - | ty::PredicateKind::ClosureKind(_, _, _) - | ty::PredicateKind::ConstEvaluatable(_) - | ty::PredicateKind::Ambiguous => { - FulfillmentErrorCode::CodeSelectionError( - SelectionError::Unimplemented, - ) - } - ty::PredicateKind::TypeWellFormedFromEnv(_) => { - bug!("unexpected goal: {goal:?}") - } - }, - root_obligation: obligation, - }); - continue; - } - }; + let (changed, certainty, nested_goals) = + match infcx.evaluate_root_goal(goal, GenerateProofTree::No).0 { + Ok(result) => result, + Err(NoSolution) => { + errors.push(FulfillmentError { + obligation: obligation.clone(), + code: match goal.predicate.kind().skip_binder() { + ty::PredicateKind::Clause(ty::ClauseKind::Projection(_)) => { + FulfillmentErrorCode::CodeProjectionError( + // FIXME: This could be a `Sorts` if the term is a type + MismatchedProjectionTypes { err: TypeError::Mismatch }, + ) + } + ty::PredicateKind::AliasRelate(_, _, _) => { + FulfillmentErrorCode::CodeProjectionError( + MismatchedProjectionTypes { err: TypeError::Mismatch }, + ) + } + ty::PredicateKind::Subtype(pred) => { + let (a, b) = infcx.instantiate_binder_with_placeholders( + goal.predicate.kind().rebind((pred.a, pred.b)), + ); + let expected_found = ExpectedFound::new(true, a, b); + FulfillmentErrorCode::CodeSubtypeError( + expected_found, + TypeError::Sorts(expected_found), + ) + } + ty::PredicateKind::Coerce(pred) => { + let (a, b) = infcx.instantiate_binder_with_placeholders( + goal.predicate.kind().rebind((pred.a, pred.b)), + ); + let expected_found = ExpectedFound::new(false, a, b); + FulfillmentErrorCode::CodeSubtypeError( + expected_found, + TypeError::Sorts(expected_found), + ) + } + ty::PredicateKind::Clause(_) + | ty::PredicateKind::ObjectSafe(_) + | ty::PredicateKind::ClosureKind(_, _, _) + | ty::PredicateKind::Ambiguous => { + FulfillmentErrorCode::CodeSelectionError( + SelectionError::Unimplemented, + ) + } + ty::PredicateKind::ConstEquate(..) => { + bug!("unexpected goal: {goal:?}") + } + }, + root_obligation: obligation, + }); + continue; + } + }; // Push any nested goals that we get from unifying our canonical response // with our obligation onto the fulfillment context. self.obligations.extend(nested_goals.into_iter().map(|goal| { diff --git a/compiler/rustc_trait_selection/src/solve/inspect.rs b/compiler/rustc_trait_selection/src/solve/inspect.rs new file mode 100644 index 000000000..2d6717fda --- /dev/null +++ b/compiler/rustc_trait_selection/src/solve/inspect.rs @@ -0,0 +1,435 @@ +use rustc_middle::traits::query::NoSolution; +use rustc_middle::traits::solve::inspect::{self, CacheHit, CandidateKind}; +use rustc_middle::traits::solve::{ + CanonicalInput, Certainty, Goal, IsNormalizesToHack, QueryInput, QueryResult, +}; +use rustc_middle::ty::{self, TyCtxt}; +use rustc_session::config::DumpSolverProofTree; + +use super::eval_ctxt::UseGlobalCache; +use super::GenerateProofTree; + +#[derive(Eq, PartialEq, Debug, Hash, HashStable)] +pub struct WipGoalEvaluation<'tcx> { + pub uncanonicalized_goal: Goal<'tcx, ty::Predicate<'tcx>>, + pub canonicalized_goal: Option<CanonicalInput<'tcx>>, + + pub evaluation_steps: Vec<WipGoalEvaluationStep<'tcx>>, + + pub cache_hit: Option<CacheHit>, + pub is_normalizes_to_hack: IsNormalizesToHack, + pub returned_goals: Vec<Goal<'tcx, ty::Predicate<'tcx>>>, + + pub result: Option<QueryResult<'tcx>>, +} + +impl<'tcx> WipGoalEvaluation<'tcx> { + pub fn finalize(self) -> inspect::GoalEvaluation<'tcx> { + inspect::GoalEvaluation { + uncanonicalized_goal: self.uncanonicalized_goal, + canonicalized_goal: self.canonicalized_goal.unwrap(), + kind: match self.cache_hit { + Some(hit) => inspect::GoalEvaluationKind::CacheHit(hit), + None => inspect::GoalEvaluationKind::Uncached { + revisions: self + .evaluation_steps + .into_iter() + .map(WipGoalEvaluationStep::finalize) + .collect(), + }, + }, + is_normalizes_to_hack: self.is_normalizes_to_hack, + returned_goals: self.returned_goals, + result: self.result.unwrap(), + } + } +} + +#[derive(Eq, PartialEq, Debug, Hash, HashStable)] +pub struct WipAddedGoalsEvaluation<'tcx> { + pub evaluations: Vec<Vec<WipGoalEvaluation<'tcx>>>, + pub result: Option<Result<Certainty, NoSolution>>, +} + +impl<'tcx> WipAddedGoalsEvaluation<'tcx> { + pub fn finalize(self) -> inspect::AddedGoalsEvaluation<'tcx> { + inspect::AddedGoalsEvaluation { + evaluations: self + .evaluations + .into_iter() + .map(|evaluations| { + evaluations.into_iter().map(WipGoalEvaluation::finalize).collect() + }) + .collect(), + result: self.result.unwrap(), + } + } +} + +#[derive(Eq, PartialEq, Debug, Hash, HashStable)] +pub struct WipGoalEvaluationStep<'tcx> { + pub instantiated_goal: QueryInput<'tcx, ty::Predicate<'tcx>>, + + pub nested_goal_evaluations: Vec<WipAddedGoalsEvaluation<'tcx>>, + pub candidates: Vec<WipGoalCandidate<'tcx>>, + + pub result: Option<QueryResult<'tcx>>, +} + +impl<'tcx> WipGoalEvaluationStep<'tcx> { + pub fn finalize(self) -> inspect::GoalEvaluationStep<'tcx> { + inspect::GoalEvaluationStep { + instantiated_goal: self.instantiated_goal, + nested_goal_evaluations: self + .nested_goal_evaluations + .into_iter() + .map(WipAddedGoalsEvaluation::finalize) + .collect(), + candidates: self.candidates.into_iter().map(WipGoalCandidate::finalize).collect(), + result: self.result.unwrap(), + } + } +} + +#[derive(Eq, PartialEq, Debug, Hash, HashStable)] +pub struct WipGoalCandidate<'tcx> { + pub nested_goal_evaluations: Vec<WipAddedGoalsEvaluation<'tcx>>, + pub candidates: Vec<WipGoalCandidate<'tcx>>, + pub kind: Option<CandidateKind<'tcx>>, +} + +impl<'tcx> WipGoalCandidate<'tcx> { + pub fn finalize(self) -> inspect::GoalCandidate<'tcx> { + inspect::GoalCandidate { + nested_goal_evaluations: self + .nested_goal_evaluations + .into_iter() + .map(WipAddedGoalsEvaluation::finalize) + .collect(), + candidates: self.candidates.into_iter().map(WipGoalCandidate::finalize).collect(), + kind: self.kind.unwrap(), + } + } +} + +#[derive(Debug)] +pub enum DebugSolver<'tcx> { + Root, + GoalEvaluation(WipGoalEvaluation<'tcx>), + AddedGoalsEvaluation(WipAddedGoalsEvaluation<'tcx>), + GoalEvaluationStep(WipGoalEvaluationStep<'tcx>), + GoalCandidate(WipGoalCandidate<'tcx>), +} + +impl<'tcx> From<WipGoalEvaluation<'tcx>> for DebugSolver<'tcx> { + fn from(g: WipGoalEvaluation<'tcx>) -> DebugSolver<'tcx> { + DebugSolver::GoalEvaluation(g) + } +} + +impl<'tcx> From<WipAddedGoalsEvaluation<'tcx>> for DebugSolver<'tcx> { + fn from(g: WipAddedGoalsEvaluation<'tcx>) -> DebugSolver<'tcx> { + DebugSolver::AddedGoalsEvaluation(g) + } +} + +impl<'tcx> From<WipGoalEvaluationStep<'tcx>> for DebugSolver<'tcx> { + fn from(g: WipGoalEvaluationStep<'tcx>) -> DebugSolver<'tcx> { + DebugSolver::GoalEvaluationStep(g) + } +} + +impl<'tcx> From<WipGoalCandidate<'tcx>> for DebugSolver<'tcx> { + fn from(g: WipGoalCandidate<'tcx>) -> DebugSolver<'tcx> { + DebugSolver::GoalCandidate(g) + } +} + +pub struct ProofTreeBuilder<'tcx> { + state: Option<Box<BuilderData<'tcx>>>, +} + +struct BuilderData<'tcx> { + tree: DebugSolver<'tcx>, + use_global_cache: UseGlobalCache, +} + +impl<'tcx> ProofTreeBuilder<'tcx> { + fn new( + state: impl Into<DebugSolver<'tcx>>, + use_global_cache: UseGlobalCache, + ) -> ProofTreeBuilder<'tcx> { + ProofTreeBuilder { + state: Some(Box::new(BuilderData { tree: state.into(), use_global_cache })), + } + } + + fn nested(&self, state: impl Into<DebugSolver<'tcx>>) -> Self { + match &self.state { + Some(prev_state) => Self { + state: Some(Box::new(BuilderData { + tree: state.into(), + use_global_cache: prev_state.use_global_cache, + })), + }, + None => Self { state: None }, + } + } + + fn as_mut(&mut self) -> Option<&mut DebugSolver<'tcx>> { + self.state.as_mut().map(|boxed| &mut boxed.tree) + } + + pub fn finalize(self) -> Option<inspect::GoalEvaluation<'tcx>> { + match self.state?.tree { + DebugSolver::GoalEvaluation(wip_goal_evaluation) => { + Some(wip_goal_evaluation.finalize()) + } + root => unreachable!("unexpected proof tree builder root node: {:?}", root), + } + } + + pub fn use_global_cache(&self) -> bool { + self.state + .as_ref() + .map(|state| matches!(state.use_global_cache, UseGlobalCache::Yes)) + .unwrap_or(true) + } + + pub fn new_maybe_root( + tcx: TyCtxt<'tcx>, + generate_proof_tree: GenerateProofTree, + ) -> ProofTreeBuilder<'tcx> { + let generate_proof_tree = match ( + tcx.sess.opts.unstable_opts.dump_solver_proof_tree, + tcx.sess.opts.unstable_opts.dump_solver_proof_tree_use_cache, + generate_proof_tree, + ) { + (_, Some(use_cache), GenerateProofTree::Yes(_)) => { + GenerateProofTree::Yes(UseGlobalCache::from_bool(use_cache)) + } + + (DumpSolverProofTree::Always, use_cache, GenerateProofTree::No) => { + let use_cache = use_cache.unwrap_or(true); + GenerateProofTree::Yes(UseGlobalCache::from_bool(use_cache)) + } + + (_, None, GenerateProofTree::Yes(_)) => generate_proof_tree, + (DumpSolverProofTree::Never, _, _) => generate_proof_tree, + (DumpSolverProofTree::OnError, _, _) => generate_proof_tree, + }; + + match generate_proof_tree { + GenerateProofTree::No => ProofTreeBuilder::new_noop(), + GenerateProofTree::Yes(global_cache_disabled) => { + ProofTreeBuilder::new_root(global_cache_disabled) + } + } + } + + pub fn new_root(use_global_cache: UseGlobalCache) -> ProofTreeBuilder<'tcx> { + ProofTreeBuilder::new(DebugSolver::Root, use_global_cache) + } + + pub fn new_noop() -> ProofTreeBuilder<'tcx> { + ProofTreeBuilder { state: None } + } + + pub fn is_noop(&self) -> bool { + self.state.is_none() + } + + pub fn new_goal_evaluation( + &mut self, + goal: Goal<'tcx, ty::Predicate<'tcx>>, + is_normalizes_to_hack: IsNormalizesToHack, + ) -> ProofTreeBuilder<'tcx> { + if self.state.is_none() { + return ProofTreeBuilder { state: None }; + } + + self.nested(WipGoalEvaluation { + uncanonicalized_goal: goal, + canonicalized_goal: None, + evaluation_steps: vec![], + is_normalizes_to_hack, + cache_hit: None, + returned_goals: vec![], + result: None, + }) + } + + pub fn canonicalized_goal(&mut self, canonical_goal: CanonicalInput<'tcx>) { + if let Some(this) = self.as_mut() { + match this { + DebugSolver::GoalEvaluation(goal_evaluation) => { + assert_eq!(goal_evaluation.canonicalized_goal.replace(canonical_goal), None); + } + _ => unreachable!(), + } + } + } + + pub fn cache_hit(&mut self, cache_hit: CacheHit) { + if let Some(this) = self.as_mut() { + match this { + DebugSolver::GoalEvaluation(goal_evaluation) => { + assert_eq!(goal_evaluation.cache_hit.replace(cache_hit), None); + } + _ => unreachable!(), + }; + } + } + + pub fn returned_goals(&mut self, goals: &[Goal<'tcx, ty::Predicate<'tcx>>]) { + if let Some(this) = self.as_mut() { + match this { + DebugSolver::GoalEvaluation(evaluation) => { + assert!(evaluation.returned_goals.is_empty()); + evaluation.returned_goals.extend(goals); + } + _ => unreachable!(), + } + } + } + pub fn goal_evaluation(&mut self, goal_evaluation: ProofTreeBuilder<'tcx>) { + if let Some(this) = self.as_mut() { + match (this, goal_evaluation.state.unwrap().tree) { + ( + DebugSolver::AddedGoalsEvaluation(WipAddedGoalsEvaluation { + evaluations, .. + }), + DebugSolver::GoalEvaluation(goal_evaluation), + ) => evaluations.last_mut().unwrap().push(goal_evaluation), + (this @ DebugSolver::Root, goal_evaluation) => *this = goal_evaluation, + _ => unreachable!(), + } + } + } + + pub fn new_goal_evaluation_step( + &mut self, + instantiated_goal: QueryInput<'tcx, ty::Predicate<'tcx>>, + ) -> ProofTreeBuilder<'tcx> { + if self.state.is_none() { + return ProofTreeBuilder { state: None }; + } + + self.nested(WipGoalEvaluationStep { + instantiated_goal, + nested_goal_evaluations: vec![], + candidates: vec![], + result: None, + }) + } + pub fn goal_evaluation_step(&mut self, goal_eval_step: ProofTreeBuilder<'tcx>) { + if let Some(this) = self.as_mut() { + match (this, goal_eval_step.state.unwrap().tree) { + (DebugSolver::GoalEvaluation(goal_eval), DebugSolver::GoalEvaluationStep(step)) => { + goal_eval.evaluation_steps.push(step); + } + _ => unreachable!(), + } + } + } + + pub fn new_goal_candidate(&mut self) -> ProofTreeBuilder<'tcx> { + if self.state.is_none() { + return ProofTreeBuilder { state: None }; + } + + self.nested(WipGoalCandidate { + nested_goal_evaluations: vec![], + candidates: vec![], + kind: None, + }) + } + + pub fn candidate_kind(&mut self, candidate_kind: CandidateKind<'tcx>) { + if let Some(this) = self.as_mut() { + match this { + DebugSolver::GoalCandidate(this) => { + assert_eq!(this.kind.replace(candidate_kind), None) + } + _ => unreachable!(), + } + } + } + + pub fn goal_candidate(&mut self, candidate: ProofTreeBuilder<'tcx>) { + if let Some(this) = self.as_mut() { + match (this, candidate.state.unwrap().tree) { + ( + DebugSolver::GoalCandidate(WipGoalCandidate { candidates, .. }) + | DebugSolver::GoalEvaluationStep(WipGoalEvaluationStep { candidates, .. }), + DebugSolver::GoalCandidate(candidate), + ) => candidates.push(candidate), + _ => unreachable!(), + } + } + } + + pub fn new_evaluate_added_goals(&mut self) -> ProofTreeBuilder<'tcx> { + if self.state.is_none() { + return ProofTreeBuilder { state: None }; + } + + self.nested(WipAddedGoalsEvaluation { evaluations: vec![], result: None }) + } + + pub fn evaluate_added_goals_loop_start(&mut self) { + if let Some(this) = self.as_mut() { + match this { + DebugSolver::AddedGoalsEvaluation(this) => { + this.evaluations.push(vec![]); + } + _ => unreachable!(), + } + } + } + + pub fn eval_added_goals_result(&mut self, result: Result<Certainty, NoSolution>) { + if let Some(this) = self.as_mut() { + match this { + DebugSolver::AddedGoalsEvaluation(this) => { + assert_eq!(this.result.replace(result), None); + } + _ => unreachable!(), + } + } + } + + pub fn added_goals_evaluation(&mut self, goals_evaluation: ProofTreeBuilder<'tcx>) { + if let Some(this) = self.as_mut() { + match (this, goals_evaluation.state.unwrap().tree) { + ( + DebugSolver::GoalEvaluationStep(WipGoalEvaluationStep { + nested_goal_evaluations, + .. + }) + | DebugSolver::GoalCandidate(WipGoalCandidate { + nested_goal_evaluations, .. + }), + DebugSolver::AddedGoalsEvaluation(added_goals_evaluation), + ) => nested_goal_evaluations.push(added_goals_evaluation), + _ => unreachable!(), + } + } + } + + pub fn query_result(&mut self, result: QueryResult<'tcx>) { + if let Some(this) = self.as_mut() { + match this { + DebugSolver::GoalEvaluation(goal_evaluation) => { + assert_eq!(goal_evaluation.result.replace(result), None); + } + DebugSolver::GoalEvaluationStep(evaluation_step) => { + assert_eq!(evaluation_step.result.replace(result), None); + } + DebugSolver::Root + | DebugSolver::AddedGoalsEvaluation(_) + | DebugSolver::GoalCandidate(_) => unreachable!(), + } + } + } +} diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs index 56a254d9c..77809d8d2 100644 --- a/compiler/rustc_trait_selection/src/solve/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/mod.rs @@ -20,17 +20,24 @@ use rustc_middle::ty::{ CoercePredicate, RegionOutlivesPredicate, SubtypePredicate, TypeOutlivesPredicate, }; +mod alias_relate; mod assembly; mod canonicalize; mod eval_ctxt; mod fulfill; +pub mod inspect; +mod normalize; mod opaques; mod project_goals; mod search_graph; mod trait_goals; +mod weak_types; -pub use eval_ctxt::{EvalCtxt, InferCtxtEvalExt}; +pub use eval_ctxt::{ + EvalCtxt, GenerateProofTree, InferCtxtEvalExt, InferCtxtSelectExt, UseGlobalCache, +}; pub use fulfill::FulfillmentCtxt; +pub(crate) use normalize::deeply_normalize; #[derive(Debug, Clone, Copy)] enum SolverMode { @@ -154,139 +161,40 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { } } - #[instrument(level = "debug", skip(self), ret)] - fn compute_alias_relate_goal( + #[instrument(level = "debug", skip(self))] + fn compute_const_evaluatable_goal( &mut self, - goal: Goal<'tcx, (ty::Term<'tcx>, ty::Term<'tcx>, ty::AliasRelationDirection)>, + Goal { param_env, predicate: ct }: Goal<'tcx, ty::Const<'tcx>>, ) -> QueryResult<'tcx> { - let tcx = self.tcx(); - // We may need to invert the alias relation direction if dealing an alias on the RHS. - #[derive(Debug)] - enum Invert { - No, - Yes, - } - let evaluate_normalizes_to = - |ecx: &mut EvalCtxt<'_, 'tcx>, alias, other, direction, invert| { - let span = tracing::span!( - tracing::Level::DEBUG, - "compute_alias_relate_goal(evaluate_normalizes_to)", - ?alias, - ?other, - ?direction, - ?invert - ); - let _enter = span.enter(); - let result = ecx.probe(|ecx| { - let other = match direction { - // This is purely an optimization. - ty::AliasRelationDirection::Equate => other, - - ty::AliasRelationDirection::Subtype => { - let fresh = ecx.next_term_infer_of_kind(other); - let (sub, sup) = match invert { - Invert::No => (fresh, other), - Invert::Yes => (other, fresh), - }; - ecx.sub(goal.param_env, sub, sup)?; - fresh - } - }; - ecx.add_goal(goal.with( - tcx, - ty::Binder::dummy(ty::ProjectionPredicate { - projection_ty: alias, - term: other, - }), - )); - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - }); - debug!(?result); - result - }; - - let (lhs, rhs, direction) = goal.predicate; - - if lhs.is_infer() || rhs.is_infer() { - bug!( - "`AliasRelate` goal with an infer var on lhs or rhs which should have been instantiated" - ); - } - - match (lhs.to_alias_ty(tcx), rhs.to_alias_ty(tcx)) { - (None, None) => bug!("`AliasRelate` goal without an alias on either lhs or rhs"), - - // RHS is not a projection, only way this is true is if LHS normalizes-to RHS - (Some(alias_lhs), None) => { - evaluate_normalizes_to(self, alias_lhs, rhs, direction, Invert::No) - } - - // LHS is not a projection, only way this is true is if RHS normalizes-to LHS - (None, Some(alias_rhs)) => { - evaluate_normalizes_to(self, alias_rhs, lhs, direction, Invert::Yes) - } - - (Some(alias_lhs), Some(alias_rhs)) => { - debug!("both sides are aliases"); - - let mut candidates = Vec::new(); - // LHS normalizes-to RHS - candidates.extend(evaluate_normalizes_to( - self, - alias_lhs, - rhs, - direction, - Invert::No, - )); - // RHS normalizes-to RHS - candidates.extend(evaluate_normalizes_to( - self, - alias_rhs, - lhs, - direction, - Invert::Yes, - )); - // Relate via substs - let subst_relate_response = self.probe(|ecx| { - let span = tracing::span!( - tracing::Level::DEBUG, - "compute_alias_relate_goal(relate_via_substs)", - ?alias_lhs, - ?alias_rhs, - ?direction - ); - let _enter = span.enter(); - - match direction { - ty::AliasRelationDirection::Equate => { - ecx.eq(goal.param_env, alias_lhs, alias_rhs)?; - } - ty::AliasRelationDirection::Subtype => { - ecx.sub(goal.param_env, alias_lhs, alias_rhs)?; - } - } - - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - }); - candidates.extend(subst_relate_response); - debug!(?candidates); - - if let Some(merged) = self.try_merge_responses(&candidates) { - Ok(merged) + match ct.kind() { + ty::ConstKind::Unevaluated(uv) => { + // We never return `NoSolution` here as `try_const_eval_resolve` emits an + // error itself when failing to evaluate, so emitting an additional fulfillment + // error in that case is unnecessary noise. This may change in the future once + // evaluation failures are allowed to impact selection, e.g. generic const + // expressions in impl headers or `where`-clauses. + + // FIXME(generic_const_exprs): Implement handling for generic + // const expressions here. + if let Some(_normalized) = self.try_const_eval_resolve(param_env, uv, ct.ty()) { + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } else { - // When relating two aliases and we have ambiguity, we prefer - // relating the generic arguments of the aliases over normalizing - // them. This is necessary for inference during typeck. - // - // As this is incomplete, we must not do so during coherence. - match (self.solver_mode(), subst_relate_response) { - (SolverMode::Normal, Ok(response)) => Ok(response), - (SolverMode::Normal, Err(NoSolution)) | (SolverMode::Coherence, _) => { - self.flounder(&candidates) - } - } + self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) } } + ty::ConstKind::Infer(_) => { + self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) + } + ty::ConstKind::Placeholder(_) | ty::ConstKind::Value(_) | ty::ConstKind::Error(_) => { + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } + // We can freely ICE here as: + // - `Param` gets replaced with a placeholder during canonicalization + // - `Bound` cannot exist as we don't have a binder around the self Type + // - `Expr` is part of `feature(generic_const_exprs)` and is not implemented yet + ty::ConstKind::Param(_) | ty::ConstKind::Bound(_, _) | ty::ConstKind::Expr(_) => { + bug!("unexpect const kind: {:?}", ct) + } } } diff --git a/compiler/rustc_trait_selection/src/solve/normalize.rs b/compiler/rustc_trait_selection/src/solve/normalize.rs new file mode 100644 index 000000000..c388850d8 --- /dev/null +++ b/compiler/rustc_trait_selection/src/solve/normalize.rs @@ -0,0 +1,220 @@ +use crate::traits::error_reporting::TypeErrCtxtExt; +use crate::traits::query::evaluate_obligation::InferCtxtExt; +use crate::traits::{needs_normalization, BoundVarReplacer, PlaceholderReplacer}; +use rustc_data_structures::stack::ensure_sufficient_stack; +use rustc_infer::infer::at::At; +use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_infer::traits::TraitEngineExt; +use rustc_infer::traits::{FulfillmentError, Obligation, TraitEngine}; +use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; +use rustc_middle::traits::Reveal; +use rustc_middle::ty::{self, AliasTy, Ty, TyCtxt, UniverseIndex}; +use rustc_middle::ty::{FallibleTypeFolder, TypeSuperFoldable}; +use rustc_middle::ty::{TypeFoldable, TypeVisitableExt}; + +use super::FulfillmentCtxt; + +/// Deeply normalize all aliases in `value`. This does not handle inference and expects +/// its input to be already fully resolved. +pub(crate) fn deeply_normalize<'tcx, T: TypeFoldable<TyCtxt<'tcx>>>( + at: At<'_, 'tcx>, + value: T, +) -> Result<T, Vec<FulfillmentError<'tcx>>> { + let fulfill_cx = FulfillmentCtxt::new(at.infcx); + let mut folder = NormalizationFolder { at, fulfill_cx, depth: 0, universes: Vec::new() }; + + value.try_fold_with(&mut folder) +} + +struct NormalizationFolder<'me, 'tcx> { + at: At<'me, 'tcx>, + fulfill_cx: FulfillmentCtxt<'tcx>, + depth: usize, + universes: Vec<Option<UniverseIndex>>, +} + +impl<'tcx> NormalizationFolder<'_, 'tcx> { + fn normalize_alias_ty( + &mut self, + alias: AliasTy<'tcx>, + ) -> Result<Ty<'tcx>, Vec<FulfillmentError<'tcx>>> { + let infcx = self.at.infcx; + let tcx = infcx.tcx; + let recursion_limit = tcx.recursion_limit(); + if !recursion_limit.value_within_limit(self.depth) { + self.at.infcx.err_ctxt().report_overflow_error( + &alias.to_ty(tcx), + self.at.cause.span, + true, + |_| {}, + ); + } + + self.depth += 1; + + let new_infer_ty = infcx.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::NormalizeProjectionType, + span: self.at.cause.span, + }); + let obligation = Obligation::new( + tcx, + self.at.cause.clone(), + self.at.param_env, + ty::Binder::dummy(ty::ProjectionPredicate { + projection_ty: alias, + term: new_infer_ty.into(), + }), + ); + + // Do not emit an error if normalization is known to fail but instead + // keep the projection unnormalized. This is the case for projections + // with a `T: Trait` where-clause and opaque types outside of the defining + // scope. + let result = if infcx.predicate_may_hold(&obligation) { + self.fulfill_cx.register_predicate_obligation(infcx, obligation); + let errors = self.fulfill_cx.select_all_or_error(infcx); + if !errors.is_empty() { + return Err(errors); + } + let ty = infcx.resolve_vars_if_possible(new_infer_ty); + ty.try_fold_with(self)? + } else { + alias.to_ty(tcx).try_super_fold_with(self)? + }; + + self.depth -= 1; + Ok(result) + } + + fn normalize_unevaluated_const( + &mut self, + ty: Ty<'tcx>, + uv: ty::UnevaluatedConst<'tcx>, + ) -> Result<ty::Const<'tcx>, Vec<FulfillmentError<'tcx>>> { + let infcx = self.at.infcx; + let tcx = infcx.tcx; + let recursion_limit = tcx.recursion_limit(); + if !recursion_limit.value_within_limit(self.depth) { + self.at.infcx.err_ctxt().report_overflow_error( + &ty::Const::new_unevaluated(tcx, uv, ty), + self.at.cause.span, + true, + |_| {}, + ); + } + + self.depth += 1; + + let new_infer_ct = infcx.next_const_var( + ty, + ConstVariableOrigin { + kind: ConstVariableOriginKind::MiscVariable, + span: self.at.cause.span, + }, + ); + let obligation = Obligation::new( + tcx, + self.at.cause.clone(), + self.at.param_env, + ty::Binder::dummy(ty::ProjectionPredicate { + projection_ty: tcx.mk_alias_ty(uv.def, uv.substs), + term: new_infer_ct.into(), + }), + ); + + let result = if infcx.predicate_may_hold(&obligation) { + self.fulfill_cx.register_predicate_obligation(infcx, obligation); + let errors = self.fulfill_cx.select_all_or_error(infcx); + if !errors.is_empty() { + return Err(errors); + } + let ct = infcx.resolve_vars_if_possible(new_infer_ct); + ct.try_fold_with(self)? + } else { + ty::Const::new_unevaluated(tcx, uv, ty).try_super_fold_with(self)? + }; + + self.depth -= 1; + Ok(result) + } +} + +impl<'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for NormalizationFolder<'_, 'tcx> { + type Error = Vec<FulfillmentError<'tcx>>; + + fn interner(&self) -> TyCtxt<'tcx> { + self.at.infcx.tcx + } + + fn try_fold_binder<T: TypeFoldable<TyCtxt<'tcx>>>( + &mut self, + t: ty::Binder<'tcx, T>, + ) -> Result<ty::Binder<'tcx, T>, Self::Error> { + self.universes.push(None); + let t = t.try_super_fold_with(self)?; + self.universes.pop(); + Ok(t) + } + + fn try_fold_ty(&mut self, ty: Ty<'tcx>) -> Result<Ty<'tcx>, Self::Error> { + let reveal = self.at.param_env.reveal(); + let infcx = self.at.infcx; + debug_assert_eq!(ty, infcx.shallow_resolve(ty)); + if !needs_normalization(&ty, reveal) { + return Ok(ty); + } + + // We don't normalize opaque types unless we have + // `Reveal::All`, even if we're in the defining scope. + let data = match *ty.kind() { + ty::Alias(kind, alias_ty) if kind != ty::Opaque || reveal == Reveal::All => alias_ty, + _ => return ty.try_super_fold_with(self), + }; + + if data.has_escaping_bound_vars() { + let (data, mapped_regions, mapped_types, mapped_consts) = + BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, data); + let result = ensure_sufficient_stack(|| self.normalize_alias_ty(data))?; + Ok(PlaceholderReplacer::replace_placeholders( + infcx, + mapped_regions, + mapped_types, + mapped_consts, + &mut self.universes, + result, + )) + } else { + ensure_sufficient_stack(|| self.normalize_alias_ty(data)) + } + } + + fn try_fold_const(&mut self, ct: ty::Const<'tcx>) -> Result<ty::Const<'tcx>, Self::Error> { + let reveal = self.at.param_env.reveal(); + let infcx = self.at.infcx; + debug_assert_eq!(ct, infcx.shallow_resolve(ct)); + if !needs_normalization(&ct, reveal) { + return Ok(ct); + } + + let uv = match ct.kind() { + ty::ConstKind::Unevaluated(ct) => ct, + _ => return ct.try_super_fold_with(self), + }; + + if uv.has_escaping_bound_vars() { + let (uv, mapped_regions, mapped_types, mapped_consts) = + BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, uv); + let result = ensure_sufficient_stack(|| self.normalize_unevaluated_const(ct.ty(), uv))?; + Ok(PlaceholderReplacer::replace_placeholders( + infcx, + mapped_regions, + mapped_types, + mapped_consts, + &mut self.universes, + result, + )) + } else { + ensure_sufficient_stack(|| self.normalize_unevaluated_const(ct.ty(), uv)) + } + } +} diff --git a/compiler/rustc_trait_selection/src/solve/opaques.rs b/compiler/rustc_trait_selection/src/solve/opaques.rs index a5de4ddee..16194f5ad 100644 --- a/compiler/rustc_trait_selection/src/solve/opaques.rs +++ b/compiler/rustc_trait_selection/src/solve/opaques.rs @@ -20,8 +20,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { let Some(opaque_ty_def_id) = opaque_ty.def_id.as_local() else { return Err(NoSolution); }; - let opaque_ty = - ty::OpaqueTypeKey { def_id: opaque_ty_def_id, substs: opaque_ty.substs }; // FIXME: at some point we should call queries without defining // new opaque types but having the existing opaque type definitions. // This will require moving this below "Prefer opaques registered already". @@ -41,7 +39,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { Ok(()) => {} } // Prefer opaques registered already. - let matches = self.unify_existing_opaque_tys(goal.param_env, opaque_ty, expected); + let opaque_type_key = + ty::OpaqueTypeKey { def_id: opaque_ty_def_id, substs: opaque_ty.substs }; + let matches = + self.unify_existing_opaque_tys(goal.param_env, opaque_type_key, expected); if !matches.is_empty() { if let Some(response) = self.try_merge_responses(&matches) { return Ok(response); @@ -50,10 +51,24 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { } } // Otherwise, define a new opaque type - self.register_opaque_ty(opaque_ty, expected, goal.param_env)?; + self.insert_hidden_type(opaque_type_key, goal.param_env, expected)?; + self.add_item_bounds_for_hidden_type( + opaque_ty.def_id, + opaque_ty.substs, + goal.param_env, + expected, + ); self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } (Reveal::UserFacing, SolverMode::Coherence) => { + // An impossible opaque type bound is the only way this goal will fail + // e.g. assigning `impl Copy := NotCopy` + self.add_item_bounds_for_hidden_type( + opaque_ty.def_id, + opaque_ty.substs, + goal.param_env, + expected, + ); self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) } (Reveal::All, _) => { diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs index 7d7dfa2c8..e53b784a7 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -9,6 +9,7 @@ use rustc_hir::LangItem; use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::specialization_graph::LeafDef; use rustc_infer::traits::Reveal; +use rustc_middle::traits::solve::inspect::CandidateKind; use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, QueryResult}; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; use rustc_middle::ty::ProjectionPredicate; @@ -22,25 +23,66 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { &mut self, goal: Goal<'tcx, ProjectionPredicate<'tcx>>, ) -> QueryResult<'tcx> { - match goal.predicate.projection_ty.kind(self.tcx()) { - ty::AliasKind::Projection => { + let def_id = goal.predicate.def_id(); + match self.tcx().def_kind(def_id) { + DefKind::AssocTy | DefKind::AssocConst => { // To only compute normalization once for each projection we only - // normalize if the expected term is an unconstrained inference variable. + // assemble normalization candidates if the expected term is an + // unconstrained inference variable. + // + // Why: For better cache hits, since if we have an unconstrained RHS then + // there are only as many cache keys as there are (canonicalized) alias + // types in each normalizes-to goal. This also weakens inference in a + // forwards-compatible way so we don't use the value of the RHS term to + // affect candidate assembly for projections. // // E.g. for `<T as Trait>::Assoc == u32` we recursively compute the goal // `exists<U> <T as Trait>::Assoc == U` and then take the resulting type for // `U` and equate it with `u32`. This means that we don't need a separate - // projection cache in the solver. + // projection cache in the solver, since we're piggybacking off of regular + // goal caching. if self.term_is_fully_unconstrained(goal) { - let candidates = self.assemble_and_evaluate_candidates(goal); - self.merge_candidates(candidates) + match self.tcx().associated_item(def_id).container { + ty::AssocItemContainer::TraitContainer => { + let candidates = self.assemble_and_evaluate_candidates(goal); + self.merge_candidates(candidates) + } + ty::AssocItemContainer::ImplContainer => { + bug!("IATs not supported here yet") + } + } } else { self.set_normalizes_to_hack_goal(goal); self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } } - ty::AliasKind::Opaque => self.normalize_opaque_type(goal), - ty::AliasKind::Inherent => bug!("IATs not supported here yet"), + DefKind::AnonConst => self.normalize_anon_const(goal), + DefKind::OpaqueTy => self.normalize_opaque_type(goal), + DefKind::TyAlias => self.normalize_weak_type(goal), + kind => bug!("unknown DefKind {} in projection goal: {goal:#?}", kind.descr(def_id)), + } + } + + #[instrument(level = "debug", skip(self), ret)] + fn normalize_anon_const( + &mut self, + goal: Goal<'tcx, ty::ProjectionPredicate<'tcx>>, + ) -> QueryResult<'tcx> { + if let Some(normalized_const) = self.try_const_eval_resolve( + goal.param_env, + ty::UnevaluatedConst::new( + goal.predicate.projection_ty.def_id, + goal.predicate.projection_ty.substs, + ), + self.tcx() + .type_of(goal.predicate.projection_ty.def_id) + .no_bound_vars() + .expect("const ty should not rely on other generics"), + ) { + self.eq(goal.param_env, normalized_const, goal.predicate.term.ct().unwrap())?; + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } else { + self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) } } } @@ -65,24 +107,26 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { fn probe_and_match_goal_against_assumption( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - assumption: ty::Predicate<'tcx>, + assumption: ty::Clause<'tcx>, then: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> QueryResult<'tcx>, ) -> QueryResult<'tcx> { - if let Some(poly_projection_pred) = assumption.to_opt_poly_projection_pred() - && poly_projection_pred.projection_def_id() == goal.predicate.def_id() - { - ecx.probe(|ecx| { - let assumption_projection_pred = - ecx.instantiate_binder_with_infer(poly_projection_pred); - ecx.eq( - goal.param_env, - goal.predicate.projection_ty, - assumption_projection_pred.projection_ty, - )?; - ecx.eq(goal.param_env, goal.predicate.term, assumption_projection_pred.term) - .expect("expected goal term to be fully unconstrained"); - then(ecx) - }) + if let Some(projection_pred) = assumption.as_projection_clause() { + if projection_pred.projection_def_id() == goal.predicate.def_id() { + ecx.probe_candidate("assumption").enter(|ecx| { + let assumption_projection_pred = + ecx.instantiate_binder_with_infer(projection_pred); + ecx.eq( + goal.param_env, + goal.predicate.projection_ty, + assumption_projection_pred.projection_ty, + )?; + ecx.eq(goal.param_env, goal.predicate.term, assumption_projection_pred.term) + .expect("expected goal term to be fully unconstrained"); + then(ecx) + }) + } else { + Err(NoSolution) + } } else { Err(NoSolution) } @@ -102,101 +146,100 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { return Err(NoSolution); } - ecx.probe(|ecx| { - let impl_substs = ecx.fresh_substs_for_item(impl_def_id); - let impl_trait_ref = impl_trait_ref.subst(tcx, impl_substs); - - ecx.eq(goal.param_env, goal_trait_ref, impl_trait_ref)?; - - let where_clause_bounds = tcx - .predicates_of(impl_def_id) - .instantiate(tcx, impl_substs) - .predicates - .into_iter() - .map(|pred| goal.with(tcx, pred)); - ecx.add_goals(where_clause_bounds); - - // In case the associated item is hidden due to specialization, we have to - // return ambiguity this would otherwise be incomplete, resulting in - // unsoundness during coherence (#105782). - let Some(assoc_def) = fetch_eligible_assoc_item_def( - ecx, - goal.param_env, - goal_trait_ref, - goal.predicate.def_id(), - impl_def_id - )? else { - return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS); - }; + ecx.probe( + |r| CandidateKind::Candidate { name: "impl".into(), result: *r }).enter( + |ecx| { + let impl_substs = ecx.fresh_substs_for_item(impl_def_id); + let impl_trait_ref = impl_trait_ref.subst(tcx, impl_substs); + + ecx.eq(goal.param_env, goal_trait_ref, impl_trait_ref)?; + + let where_clause_bounds = tcx + .predicates_of(impl_def_id) + .instantiate(tcx, impl_substs) + .predicates + .into_iter() + .map(|pred| goal.with(tcx, pred)); + ecx.add_goals(where_clause_bounds); + + // In case the associated item is hidden due to specialization, we have to + // return ambiguity this would otherwise be incomplete, resulting in + // unsoundness during coherence (#105782). + let Some(assoc_def) = fetch_eligible_assoc_item_def( + ecx, + goal.param_env, + goal_trait_ref, + goal.predicate.def_id(), + impl_def_id + )? else { + return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS); + }; - if !assoc_def.item.defaultness(tcx).has_value() { - let guar = tcx.sess.delay_span_bug( - tcx.def_span(assoc_def.item.def_id), - "missing value for assoc item in impl", - ); - let error_term = match assoc_def.item.kind { - ty::AssocKind::Const => tcx - .const_error( + if !assoc_def.item.defaultness(tcx).has_value() { + let guar = tcx.sess.delay_span_bug( + tcx.def_span(assoc_def.item.def_id), + "missing value for assoc item in impl", + ); + let error_term = match assoc_def.item.kind { + ty::AssocKind::Const => ty::Const::new_error(tcx, + guar, tcx.type_of(goal.predicate.def_id()) .subst(tcx, goal.predicate.projection_ty.substs), - guar, - ) - .into(), - ty::AssocKind::Type => tcx.ty_error(guar).into(), - ty::AssocKind::Fn => unreachable!(), - }; - ecx.eq(goal.param_env, goal.predicate.term, error_term) - .expect("expected goal term to be fully unconstrained"); - return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes); - } + ) + .into(), + ty::AssocKind::Type => Ty::new_error(tcx,guar).into(), + ty::AssocKind::Fn => unreachable!(), + }; + ecx.eq(goal.param_env, goal.predicate.term, error_term) + .expect("expected goal term to be fully unconstrained"); + return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes); + } - // Getting the right substitutions here is complex, e.g. given: - // - a goal `<Vec<u32> as Trait<i32>>::Assoc<u64>` - // - the applicable impl `impl<T> Trait<i32> for Vec<T>` - // - and the impl which defines `Assoc` being `impl<T, U> Trait<U> for Vec<T>` - // - // We first rebase the goal substs onto the impl, going from `[Vec<u32>, i32, u64]` - // to `[u32, u64]`. - // - // And then map these substs to the substs of the defining impl of `Assoc`, going - // from `[u32, u64]` to `[u32, i32, u64]`. - let impl_substs_with_gat = goal.predicate.projection_ty.substs.rebase_onto( - tcx, - goal_trait_ref.def_id, - impl_substs, - ); - let substs = ecx.translate_substs( - goal.param_env, - impl_def_id, - impl_substs_with_gat, - assoc_def.defining_node, - ); - - // Finally we construct the actual value of the associated type. - let is_const = matches!(tcx.def_kind(assoc_def.item.def_id), DefKind::AssocConst); - let ty = tcx.type_of(assoc_def.item.def_id); - let term: ty::EarlyBinder<ty::Term<'tcx>> = if is_const { - let identity_substs = - ty::InternalSubsts::identity_for_item(tcx, assoc_def.item.def_id); - let did = assoc_def.item.def_id; - let kind = - ty::ConstKind::Unevaluated(ty::UnevaluatedConst::new(did, identity_substs)); - ty.map_bound(|ty| tcx.mk_const(kind, ty).into()) - } else { - ty.map_bound(|ty| ty.into()) - }; + // Getting the right substitutions here is complex, e.g. given: + // - a goal `<Vec<u32> as Trait<i32>>::Assoc<u64>` + // - the applicable impl `impl<T> Trait<i32> for Vec<T>` + // - and the impl which defines `Assoc` being `impl<T, U> Trait<U> for Vec<T>` + // + // We first rebase the goal substs onto the impl, going from `[Vec<u32>, i32, u64]` + // to `[u32, u64]`. + // + // And then map these substs to the substs of the defining impl of `Assoc`, going + // from `[u32, u64]` to `[u32, i32, u64]`. + let impl_substs_with_gat = goal.predicate.projection_ty.substs.rebase_onto( + tcx, + goal_trait_ref.def_id, + impl_substs, + ); + let substs = ecx.translate_substs( + goal.param_env, + impl_def_id, + impl_substs_with_gat, + assoc_def.defining_node, + ); - ecx.eq(goal.param_env, goal.predicate.term, term.subst(tcx, substs)) - .expect("expected goal term to be fully unconstrained"); - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - }) + // Finally we construct the actual value of the associated type. + let term = match assoc_def.item.kind { + ty::AssocKind::Type => tcx.type_of(assoc_def.item.def_id).map_bound(|ty| ty.into()), + ty::AssocKind::Const => bug!("associated const projection is not supported yet"), + ty::AssocKind::Fn => unreachable!("we should never project to a fn"), + }; + + ecx.eq(goal.param_env, goal.predicate.term, term.subst(tcx, substs)) + .expect("expected goal term to be fully unconstrained"); + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }, + ) } fn consider_auto_trait_candidate( - _ecx: &mut EvalCtxt<'_, 'tcx>, + ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx> { - bug!("auto traits do not have associated types: {:?}", goal); + ecx.tcx().sess.delay_span_bug( + ecx.tcx().def_span(goal.predicate.def_id()), + "associated types not allowed on auto traits", + ); + Err(NoSolution) } fn consider_trait_alias_candidate( @@ -280,7 +323,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { goal: Goal<'tcx, Self>, ) -> QueryResult<'tcx> { let tcx = ecx.tcx(); - ecx.probe(|ecx| { + ecx.probe_candidate("builtin pointee").enter(|ecx| { let metadata_ty = match goal.predicate.self_ty().kind() { ty::Bool | ty::Char @@ -300,7 +343,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { | ty::Never | ty::Foreign(..) => tcx.types.unit, - ty::Error(e) => tcx.ty_error(*e), + ty::Error(e) => Ty::new_error(tcx, *e), ty::Str | ty::Slice(_) => tcx.types.usize, @@ -323,7 +366,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { } ty::Adt(def, substs) if def.is_struct() => { - match def.non_enum_variant().fields.raw.last() { + match def.non_enum_variant().tail_opt() { None => tcx.types.unit, Some(field_def) => { let self_ty = field_def.ty(tcx, substs); @@ -497,7 +540,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { ), }; - ecx.probe(|ecx| { + ecx.probe_candidate("builtin discriminant kind").enter(|ecx| { ecx.eq(goal.param_env, goal.predicate.term, discriminant_ty.into()) .expect("expected goal term to be fully unconstrained"); ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) diff --git a/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs b/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs index 19e4b2300..f00456e26 100644 --- a/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs @@ -2,6 +2,7 @@ mod cache; mod overflow; pub(super) use overflow::OverflowHandler; +use rustc_middle::traits::solve::inspect::CacheHit; use self::cache::ProvisionalEntry; use cache::ProvisionalCache; @@ -12,6 +13,7 @@ use rustc_middle::traits::solve::{CanonicalInput, Certainty, MaybeCause, QueryRe use rustc_middle::ty::TyCtxt; use std::{collections::hash_map::Entry, mem}; +use super::inspect::ProofTreeBuilder; use super::SolverMode; rustc_index::newtype_index! { @@ -88,11 +90,12 @@ impl<'tcx> SearchGraph<'tcx> { /// Tries putting the new goal on the stack, returning an error if it is already cached. /// /// This correctly updates the provisional cache if there is a cycle. - #[instrument(level = "debug", skip(self, tcx), ret)] + #[instrument(level = "debug", skip(self, tcx, inspect), ret)] fn try_push_stack( &mut self, tcx: TyCtxt<'tcx>, input: CanonicalInput<'tcx>, + inspect: &mut ProofTreeBuilder<'tcx>, ) -> Result<(), QueryResult<'tcx>> { // Look at the provisional cache to check for cycles. let cache = &mut self.provisional_cache; @@ -119,6 +122,8 @@ impl<'tcx> SearchGraph<'tcx> { // Finally we can return either the provisional response for that goal if we have a // coinductive cycle or an ambiguous result if the cycle is inductive. Entry::Occupied(entry_index) => { + inspect.cache_hit(CacheHit::Provisional); + let entry_index = *entry_index.get(); let stack_depth = cache.depth(entry_index); @@ -205,16 +210,18 @@ impl<'tcx> SearchGraph<'tcx> { &mut self, tcx: TyCtxt<'tcx>, canonical_input: CanonicalInput<'tcx>, - mut loop_body: impl FnMut(&mut Self) -> QueryResult<'tcx>, + inspect: &mut ProofTreeBuilder<'tcx>, + mut loop_body: impl FnMut(&mut Self, &mut ProofTreeBuilder<'tcx>) -> QueryResult<'tcx>, ) -> QueryResult<'tcx> { - if self.should_use_global_cache() { + if self.should_use_global_cache() && inspect.use_global_cache() { if let Some(result) = tcx.new_solver_evaluation_cache.get(&canonical_input, tcx) { debug!(?canonical_input, ?result, "cache hit"); + inspect.cache_hit(CacheHit::Global); return result; } } - match self.try_push_stack(tcx, canonical_input) { + match self.try_push_stack(tcx, canonical_input, inspect) { Ok(()) => {} // Our goal is already on the stack, eager return. Err(response) => return response, @@ -231,7 +238,7 @@ impl<'tcx> SearchGraph<'tcx> { result }, |this| { - let result = loop_body(this); + let result = loop_body(this, inspect); this.try_finalize_goal(canonical_input, result).then(|| result) }, ) diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index f722f2813..ef5f25b1f 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -7,6 +7,7 @@ use rustc_hir::{LangItem, Movability}; use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::util::supertraits; use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, QueryResult}; +use rustc_middle::traits::Reveal; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams, TreatProjections}; use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt}; use rustc_middle::ty::{TraitPredicate, TypeVisitableExt}; @@ -61,7 +62,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { }, }; - ecx.probe(|ecx| { + ecx.probe_candidate("impl").enter(|ecx| { let impl_substs = ecx.fresh_substs_for_item(impl_def_id); let impl_trait_ref = impl_trait_ref.subst(tcx, impl_substs); @@ -81,24 +82,26 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { fn probe_and_match_goal_against_assumption( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, - assumption: ty::Predicate<'tcx>, + assumption: ty::Clause<'tcx>, then: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> QueryResult<'tcx>, ) -> QueryResult<'tcx> { - if let Some(poly_trait_pred) = assumption.to_opt_poly_trait_pred() - && poly_trait_pred.def_id() == goal.predicate.def_id() - && poly_trait_pred.polarity() == goal.predicate.polarity - { - // FIXME: Constness - ecx.probe(|ecx| { - let assumption_trait_pred = - ecx.instantiate_binder_with_infer(poly_trait_pred); - ecx.eq( - goal.param_env, - goal.predicate.trait_ref, - assumption_trait_pred.trait_ref, - )?; - then(ecx) - }) + if let Some(trait_clause) = assumption.as_trait_clause() { + if trait_clause.def_id() == goal.predicate.def_id() + && trait_clause.polarity() == goal.predicate.polarity + { + // FIXME: Constness + ecx.probe_candidate("assumption").enter(|ecx| { + let assumption_trait_pred = ecx.instantiate_binder_with_infer(trait_clause); + ecx.eq( + goal.param_env, + goal.predicate.trait_ref, + assumption_trait_pred.trait_ref, + )?; + then(ecx) + }) + } else { + Err(NoSolution) + } } else { Err(NoSolution) } @@ -116,6 +119,32 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { return result; } + // Don't call `type_of` on a local TAIT that's in the defining scope, + // since that may require calling `typeck` on the same item we're + // currently type checking, which will result in a fatal cycle that + // ideally we want to avoid, since we can make progress on this goal + // via an alias bound or a locally-inferred hidden type instead. + // + // Also, don't call `type_of` on a TAIT in `Reveal::All` mode, since + // we already normalize the self type in + // `assemble_candidates_after_normalizing_self_ty`, and we'd + // just be registering an identical candidate here. + // + // Returning `Err(NoSolution)` here is ok in `SolverMode::Coherence` + // since we'll always be registering an ambiguous candidate in + // `assemble_candidates_after_normalizing_self_ty` due to normalizing + // the TAIT. + if let ty::Alias(ty::Opaque, opaque_ty) = goal.predicate.self_ty().kind() { + if matches!(goal.param_env.reveal(), Reveal::All) + || opaque_ty + .def_id + .as_local() + .is_some_and(|def_id| ecx.can_define_opaque_ty(def_id)) + { + return Err(NoSolution); + } + } + ecx.probe_and_evaluate_goal_for_constituent_tys( goal, structural_traits::instantiate_constituent_tys_for_auto_trait, @@ -132,7 +161,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { let tcx = ecx.tcx(); - ecx.probe(|ecx| { + ecx.probe_candidate("trait alias").enter(|ecx| { let nested_obligations = tcx .predicates_of(goal.predicate.def_id()) .instantiate(tcx, goal.predicate.trait_ref.substs); @@ -344,7 +373,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { if b_ty.is_ty_var() { return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS); } - ecx.probe(|ecx| { + ecx.probe_candidate("builtin unsize").enter(|ecx| { match (a_ty.kind(), b_ty.kind()) { // Trait upcasting, or `dyn Trait + Auto + 'a` -> `dyn Trait + 'b` (&ty::Dynamic(_, _, ty::Dyn), &ty::Dynamic(_, _, ty::Dyn)) => { @@ -396,12 +425,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { return Err(NoSolution); } - let tail_field = a_def - .non_enum_variant() - .fields - .raw - .last() - .expect("expected unsized ADT to have a tail field"); + let tail_field = a_def.non_enum_variant().tail(); let tail_field_ty = tcx.type_of(tail_field.did); let a_tail_ty = tail_field_ty.subst(tcx, a_substs); @@ -414,7 +438,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { tcx.mk_substs_from_iter(a_substs.iter().enumerate().map(|(i, a)| { if unsizing_params.contains(i as u32) { b_substs[i] } else { a } })); - let unsized_a_ty = tcx.mk_adt(a_def, new_a_substs); + let unsized_a_ty = Ty::new_adt(tcx, a_def, new_a_substs); // Finally, we require that `TailA: Unsize<TailB>` for the tail field // types. @@ -434,7 +458,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { // Substitute just the tail field of B., and require that they're equal. let unsized_a_ty = - tcx.mk_tup_from_iter(a_rest_tys.iter().chain([b_last_ty]).copied()); + Ty::new_tup_from_iter(tcx, a_rest_tys.iter().chain([b_last_ty]).copied()); ecx.eq(goal.param_env, unsized_a_ty, b_ty)?; // Similar to ADTs, require that the rest of the fields are equal. @@ -476,7 +500,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { } let mut unsize_dyn_to_principal = |principal: Option<ty::PolyExistentialTraitRef<'tcx>>| { - ecx.probe(|ecx| -> Result<_, NoSolution> { + ecx.probe_candidate("upcast dyn to principle").enter(|ecx| -> Result<_, NoSolution> { // Require that all of the trait predicates from A match B, except for // the auto traits. We do this by constructing a new A type with B's // auto traits, and equating these types. @@ -493,7 +517,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { .map(ty::Binder::dummy), ); let new_a_data = tcx.mk_poly_existential_predicates_from_iter(new_a_data); - let new_a_ty = tcx.mk_dynamic(new_a_data, b_region, ty::Dyn); + let new_a_ty = Ty::new_dynamic(tcx, new_a_data, b_region, ty::Dyn); // We also require that A's lifetime outlives B's lifetime. ecx.eq(goal.param_env, new_a_ty, b_ty)?; @@ -618,7 +642,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { ty::Dynamic(..) | ty::Param(..) | ty::Foreign(..) - | ty::Alias(ty::Projection | ty::Inherent, ..) + | ty::Alias(ty::Projection | ty::Weak | ty::Inherent, ..) | ty::Placeholder(..) => Some(Err(NoSolution)), ty::Infer(_) | ty::Bound(_, _) => bug!("unexpected type `{self_ty}`"), @@ -698,7 +722,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { goal: Goal<'tcx, TraitPredicate<'tcx>>, constituent_tys: impl Fn(&EvalCtxt<'_, 'tcx>, Ty<'tcx>) -> Result<Vec<Ty<'tcx>>, NoSolution>, ) -> QueryResult<'tcx> { - self.probe(|ecx| { + self.probe_candidate("constituent tys").enter(|ecx| { ecx.add_goals( constituent_tys(ecx, goal.predicate.self_ty())? .into_iter() diff --git a/compiler/rustc_trait_selection/src/solve/weak_types.rs b/compiler/rustc_trait_selection/src/solve/weak_types.rs new file mode 100644 index 000000000..b095b54c5 --- /dev/null +++ b/compiler/rustc_trait_selection/src/solve/weak_types.rs @@ -0,0 +1,19 @@ +use rustc_middle::traits::solve::{Certainty, Goal, QueryResult}; +use rustc_middle::ty; + +use super::EvalCtxt; + +impl<'tcx> EvalCtxt<'_, 'tcx> { + pub(super) fn normalize_weak_type( + &mut self, + goal: Goal<'tcx, ty::ProjectionPredicate<'tcx>>, + ) -> QueryResult<'tcx> { + let tcx = self.tcx(); + let weak_ty = goal.predicate.projection_ty; + let expected = goal.predicate.term.ty().expect("no such thing as a const alias"); + + let actual = tcx.type_of(weak_ty.def_id).subst(tcx, weak_ty.substs); + self.eq(goal.param_env, expected, actual)?; + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } +} diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs index 62d2aad52..cb38d0ac8 100644 --- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs +++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs @@ -95,7 +95,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { tcx, ObligationCause::dummy(), orig_env, - ty::Binder::dummy(ty::TraitPredicate { + ty::TraitPredicate { trait_ref, constness: ty::BoundConstness::NotConst, polarity: if polarity { @@ -103,7 +103,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { } else { ImplPolarity::Negative }, - }), + }, )); if let Ok(Some(ImplSource::UserDefined(_))) = result { debug!( @@ -255,7 +255,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { // that are already in the `ParamEnv` (modulo regions): we already // know that they must hold. for predicate in param_env.caller_bounds() { - fresh_preds.insert(self.clean_pred(infcx, predicate)); + fresh_preds.insert(self.clean_pred(infcx, predicate.as_predicate())); } let mut select = SelectionContext::new(&infcx); @@ -270,8 +270,9 @@ impl<'tcx> AutoTraitFinder<'tcx> { polarity: ty::ImplPolarity::Positive, })); - let computed_preds = param_env.caller_bounds().iter(); - let mut user_computed_preds: FxIndexSet<_> = user_env.caller_bounds().iter().collect(); + let computed_preds = param_env.caller_bounds().iter().map(|c| c.as_predicate()); + let mut user_computed_preds: FxIndexSet<_> = + user_env.caller_bounds().iter().map(|c| c.as_predicate()).collect(); let mut new_env = param_env; let dummy_cause = ObligationCause::dummy(); @@ -291,7 +292,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { new_env, pred, )); - let result = select.select(&obligation); + let result = select.poly_select(&obligation); match result { Ok(Some(ref impl_source)) => { @@ -349,14 +350,14 @@ impl<'tcx> AutoTraitFinder<'tcx> { let normalized_preds = elaborate(tcx, computed_preds.clone().chain(user_computed_preds.iter().cloned())); new_env = ty::ParamEnv::new( - tcx.mk_predicates_from_iter(normalized_preds), + tcx.mk_clauses_from_iter(normalized_preds.filter_map(|p| p.as_clause())), param_env.reveal(), param_env.constness(), ); } let final_user_env = ty::ParamEnv::new( - tcx.mk_predicates_from_iter(user_computed_preds.into_iter()), + tcx.mk_clauses_from_iter(user_computed_preds.into_iter().filter_map(|p| p.as_clause())), user_env.reveal(), user_env.constness(), ); @@ -400,8 +401,8 @@ impl<'tcx> AutoTraitFinder<'tcx> { let mut should_add_new = true; user_computed_preds.retain(|&old_pred| { if let ( - ty::PredicateKind::Clause(ty::Clause::Trait(new_trait)), - ty::PredicateKind::Clause(ty::Clause::Trait(old_trait)), + ty::PredicateKind::Clause(ty::ClauseKind::Trait(new_trait)), + ty::PredicateKind::Clause(ty::ClauseKind::Trait(old_trait)), ) = (new_pred.kind().skip_binder(), old_pred.kind().skip_binder()) { if new_trait.def_id() == old_trait.def_id() { @@ -621,14 +622,14 @@ impl<'tcx> AutoTraitFinder<'tcx> { let bound_predicate = predicate.kind(); match bound_predicate.skip_binder() { - ty::PredicateKind::Clause(ty::Clause::Trait(p)) => { + ty::PredicateKind::Clause(ty::ClauseKind::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::Clause(ty::Clause::Projection(p)) => { + ty::PredicateKind::Clause(ty::ClauseKind::Projection(p)) => { let p = bound_predicate.rebind(p); debug!( "evaluate_nested_obligations: examining projection predicate {:?}", @@ -758,11 +759,11 @@ impl<'tcx> AutoTraitFinder<'tcx> { } } } - ty::PredicateKind::Clause(ty::Clause::RegionOutlives(binder)) => { + ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(binder)) => { let binder = bound_predicate.rebind(binder); selcx.infcx.region_outlives_predicate(&dummy_cause, binder) } - ty::PredicateKind::Clause(ty::Clause::TypeOutlives(binder)) => { + ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(binder)) => { let binder = bound_predicate.rebind(binder); match ( binder.no_bound_vars(), @@ -793,7 +794,7 @@ impl<'tcx> AutoTraitFinder<'tcx> { unevaluated, Some(obligation.cause.span), ) { - Ok(Some(valtree)) => Ok(selcx.tcx().mk_const(valtree, c.ty())), + Ok(Some(valtree)) => Ok(ty::Const::new_value(selcx.tcx(),valtree, c.ty())), Ok(None) => { let tcx = self.tcx; let reported = @@ -826,18 +827,15 @@ impl<'tcx> AutoTraitFinder<'tcx> { // we start out with a `ParamEnv` with no inference variables, // and these don't correspond to adding any new bounds to // the `ParamEnv`. - ty::PredicateKind::WellFormed(..) - | ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..)) + ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(..)) + | ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(..)) | ty::PredicateKind::AliasRelate(..) | ty::PredicateKind::ObjectSafe(..) | ty::PredicateKind::ClosureKind(..) | ty::PredicateKind::Subtype(..) // FIXME(generic_const_exprs): you can absolutely add this as a where clauses - | ty::PredicateKind::ConstEvaluatable(..) + | ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..)) | ty::PredicateKind::Coerce(..) => {} - ty::PredicateKind::TypeWellFormedFromEnv(..) => { - bug!("predicate should only exist in the environment: {bound_predicate:?}") - } ty::PredicateKind::Ambiguous => return false, }; } diff --git a/compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs b/compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs deleted file mode 100644 index 28967e1cc..000000000 --- a/compiler/rustc_trait_selection/src/traits/chalk_fulfill.rs +++ /dev/null @@ -1,151 +0,0 @@ -//! Defines a Chalk-based `TraitEngine` - -use crate::infer::canonical::OriginalQueryValues; -use crate::infer::InferCtxt; -use crate::traits::query::NoSolution; -use crate::traits::{ - ChalkEnvironmentAndGoal, FulfillmentError, FulfillmentErrorCode, PredicateObligation, - SelectionError, TraitEngine, -}; -use rustc_data_structures::fx::FxIndexSet; -use rustc_middle::ty::TypeVisitableExt; - -pub struct FulfillmentContext<'tcx> { - obligations: FxIndexSet<PredicateObligation<'tcx>>, - - usable_in_snapshot: bool, -} - -impl FulfillmentContext<'_> { - pub(super) fn new() -> Self { - FulfillmentContext { obligations: FxIndexSet::default(), usable_in_snapshot: false } - } - - 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>, - ) { - if !self.usable_in_snapshot { - assert!(!infcx.is_in_snapshot()); - } - let obligation = infcx.resolve_vars_if_possible(obligation); - - self.obligations.insert(obligation); - } - - fn collect_remaining_errors( - &mut self, - _infcx: &InferCtxt<'tcx>, - ) -> Vec<FulfillmentError<'tcx>> { - // any remaining obligations are errors - self.obligations - .iter() - .map(|obligation| FulfillmentError { - obligation: obligation.clone(), - code: FulfillmentErrorCode::CodeAmbiguity { overflow: false }, - // FIXME - does Chalk have a notation of 'root obligation'? - // This is just for diagnostics, so it's okay if this is wrong - root_obligation: obligation.clone(), - }) - .collect() - } - - fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>> { - if !self.usable_in_snapshot { - assert!(!infcx.is_in_snapshot()); - } - - let mut errors = Vec::new(); - let mut next_round = FxIndexSet::default(); - let mut making_progress; - - loop { - making_progress = false; - - // We iterate over all obligations, and record if we are able - // to unambiguously prove at least one obligation. - for obligation in self.obligations.drain(..) { - let obligation = infcx.resolve_vars_if_possible(obligation); - let environment = obligation.param_env.caller_bounds(); - let goal = ChalkEnvironmentAndGoal { environment, goal: obligation.predicate }; - let mut orig_values = OriginalQueryValues::default(); - if goal.references_error() { - continue; - } - - let canonical_goal = - infcx.canonicalize_query_preserving_universes(goal, &mut orig_values); - - match infcx.tcx.evaluate_goal(canonical_goal) { - Ok(response) => { - if response.is_proven() { - making_progress = true; - - match infcx.instantiate_query_response_and_region_obligations( - &obligation.cause, - obligation.param_env, - &orig_values, - &response, - ) { - Ok(infer_ok) => next_round.extend( - infer_ok.obligations.into_iter().map(|obligation| { - assert!(!infcx.is_in_snapshot()); - infcx.resolve_vars_if_possible(obligation) - }), - ), - - Err(_err) => errors.push(FulfillmentError { - obligation: obligation.clone(), - code: FulfillmentErrorCode::CodeSelectionError( - SelectionError::Unimplemented, - ), - // FIXME - does Chalk have a notation of 'root obligation'? - // This is just for diagnostics, so it's okay if this is wrong - root_obligation: obligation, - }), - } - } else { - // Ambiguous: retry at next round. - next_round.insert(obligation); - } - } - - Err(NoSolution) => errors.push(FulfillmentError { - obligation: obligation.clone(), - code: FulfillmentErrorCode::CodeSelectionError( - SelectionError::Unimplemented, - ), - // FIXME - does Chalk have a notation of 'root obligation'? - // This is just for diagnostics, so it's okay if this is wrong - root_obligation: obligation, - }), - } - } - next_round = std::mem::replace(&mut self.obligations, next_round); - - if !making_progress { - break; - } - } - - errors - } - - fn drain_unstalled_obligations( - &mut self, - _: &InferCtxt<'tcx>, - ) -> Vec<PredicateObligation<'tcx>> { - unimplemented!() - } - - fn pending_obligations(&self) -> Vec<PredicateObligation<'tcx>> { - self.obligations.iter().cloned().collect() - } -} diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index e8c5a8fab..1b1285e1b 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -5,7 +5,7 @@ //! [trait-specialization]: https://rustc-dev-guide.rust-lang.org/traits/specialization.html use crate::infer::outlives::env::OutlivesEnvironment; -use crate::infer::{CombinedSnapshot, InferOk}; +use crate::infer::InferOk; use crate::traits::outlives_bounds::InferCtxtExt as _; use crate::traits::select::IntercrateAmbiguityCause; use crate::traits::util::impl_subject_and_oblig; @@ -23,13 +23,14 @@ use rustc_middle::traits::specialization_graph::OverlapMode; use rustc_middle::traits::DefiningAnchor; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; use rustc_middle::ty::visit::{TypeVisitable, TypeVisitableExt}; -use rustc_middle::ty::{self, ImplSubject, Ty, TyCtxt, TypeVisitor}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitor}; use rustc_span::symbol::sym; use rustc_span::DUMMY_SP; use std::fmt::Debug; use std::iter; use std::ops::ControlFlow; +use super::query::evaluate_obligation::InferCtxtExt; use super::NormalizeExt; /// Whether we do the orphan check relative to this crate or @@ -62,6 +63,21 @@ pub fn add_placeholder_note(err: &mut Diagnostic) { ); } +#[derive(Debug, Clone, Copy)] +enum TrackAmbiguityCauses { + Yes, + No, +} + +impl TrackAmbiguityCauses { + fn is_yes(self) -> bool { + match self { + TrackAmbiguityCauses::Yes => true, + TrackAmbiguityCauses::No => false, + } + } +} + /// If there are types that satisfy both impls, returns `Some` /// with a suitably-freshened `ImplHeader` with those types /// substituted. Otherwise, returns `None`. @@ -97,29 +113,28 @@ pub fn overlapping_impls( return None; } - let infcx = tcx - .infer_ctxt() - .with_opaque_type_inference(DefiningAnchor::Bubble) - .intercrate(true) - .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 { - return None; - } + let _overlap_with_bad_diagnostics = overlap( + tcx, + TrackAmbiguityCauses::No, + skip_leak_check, + impl1_def_id, + impl2_def_id, + overlap_mode, + )?; // 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() - .with_opaque_type_inference(DefiningAnchor::Bubble) - .intercrate(true) - .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()) + let overlap = overlap( + tcx, + TrackAmbiguityCauses::Yes, + skip_leak_check, + impl1_def_id, + impl2_def_id, + overlap_mode, + ) + .unwrap(); + Some(overlap) } fn with_fresh_ty_vars<'cx, 'tcx>( @@ -134,7 +149,12 @@ fn with_fresh_ty_vars<'cx, 'tcx>( impl_def_id, self_ty: tcx.type_of(impl_def_id).subst(tcx, impl_substs), trait_ref: tcx.impl_trait_ref(impl_def_id).map(|i| i.subst(tcx, impl_substs)), - predicates: tcx.predicates_of(impl_def_id).instantiate(tcx, impl_substs).predicates, + predicates: tcx + .predicates_of(impl_def_id) + .instantiate(tcx, impl_substs) + .iter() + .map(|(c, _)| c.as_predicate()) + .collect(), }; let InferOk { value: mut header, obligations } = @@ -146,40 +166,35 @@ fn with_fresh_ty_vars<'cx, 'tcx>( /// Can both impl `a` and impl `b` be satisfied by a common type (including /// where-clauses)? If so, returns an `ImplHeader` that unifies the two impls. -fn overlap<'cx, 'tcx>( - selcx: &mut SelectionContext<'cx, 'tcx>, +#[instrument(level = "debug", skip(tcx))] +fn overlap<'tcx>( + tcx: TyCtxt<'tcx>, + track_ambiguity_causes: TrackAmbiguityCauses, skip_leak_check: SkipLeakCheck, impl1_def_id: DefId, impl2_def_id: DefId, overlap_mode: OverlapMode, ) -> Option<OverlapResult<'tcx>> { - debug!( - "overlap(impl1_def_id={:?}, impl2_def_id={:?}, overlap_mode={:?})", - impl1_def_id, impl2_def_id, overlap_mode - ); - - 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) - }) -} - -fn overlap_within_probe<'cx, 'tcx>( - selcx: &mut SelectionContext<'cx, 'tcx>, - impl1_def_id: DefId, - impl2_def_id: DefId, - overlap_mode: OverlapMode, - snapshot: &CombinedSnapshot<'tcx>, -) -> Option<OverlapResult<'tcx>> { - let infcx = selcx.infcx; - if overlap_mode.use_negative_impl() { - if negative_impl(infcx.tcx, impl1_def_id, impl2_def_id) - || negative_impl(infcx.tcx, impl2_def_id, impl1_def_id) + if impl_intersection_has_negative_obligation(tcx, impl1_def_id, impl2_def_id) + || impl_intersection_has_negative_obligation(tcx, impl2_def_id, impl1_def_id) { return None; } } + let infcx = tcx + .infer_ctxt() + .with_opaque_type_inference(DefiningAnchor::Bubble) + .skip_leak_check(skip_leak_check.is_yes()) + .intercrate(true) + .with_next_trait_solver(tcx.next_trait_solver_in_coherence()) + .build(); + let selcx = &mut SelectionContext::new(&infcx); + if track_ambiguity_causes.is_yes() { + selcx.enable_tracking_intercrate_ambiguity_causes(); + } + // For the purposes of this check, we don't bring any placeholder // types into scope; instead, we replace the generic types with // fresh type variables, and hence we do our evaluations in an @@ -189,27 +204,40 @@ fn overlap_within_probe<'cx, 'tcx>( let impl1_header = with_fresh_ty_vars(selcx, param_env, impl1_def_id); let impl2_header = with_fresh_ty_vars(selcx, param_env, impl2_def_id); - let obligations = equate_impl_headers(selcx.infcx, &impl1_header, &impl2_header)?; + // Equate the headers to find their intersection (the general type, with infer vars, + // that may apply both impls). + let equate_obligations = equate_impl_headers(selcx.infcx, &impl1_header, &impl2_header)?; debug!("overlap: unification check succeeded"); - if overlap_mode.use_implicit_negative() { - if implicit_negative(selcx, param_env, &impl1_header, impl2_header, obligations) { - return None; - } + if overlap_mode.use_implicit_negative() + && impl_intersection_has_impossible_obligation( + selcx, + param_env, + &impl1_header, + impl2_header, + equate_obligations, + ) + { + return None; } - // We disable the leak when creating the `snapshot` by using - // `infcx.probe_maybe_disable_leak_check`. - if infcx.leak_check(true, snapshot).is_err() { + // We toggle the `leak_check` by using `skip_leak_check` when constructing the + // inference context, so this may be a noop. + if infcx.leak_check(ty::UniverseIndex::ROOT, None).is_err() { debug!("overlap: leak check failed"); return None; } let intercrate_ambiguity_causes = selcx.take_intercrate_ambiguity_causes(); debug!("overlap: intercrate_ambiguity_causes={:#?}", intercrate_ambiguity_causes); - - let involves_placeholder = - matches!(selcx.infcx.region_constraints_added_in_snapshot(snapshot), Some(true)); + let involves_placeholder = infcx + .inner + .borrow_mut() + .unwrap_region_constraints() + .data() + .constraints + .iter() + .any(|c| c.0.involves_placeholders()); let impl_header = selcx.infcx.resolve_vars_if_possible(impl1_header); Some(OverlapResult { impl_header, intercrate_ambiguity_causes, involves_placeholder }) @@ -236,55 +264,55 @@ fn equate_impl_headers<'tcx>( result.map(|infer_ok| infer_ok.obligations).ok() } -/// Given impl1 and impl2 check if both impls can be satisfied by a common type (including -/// where-clauses) If so, return false, otherwise return true, they are disjoint. -fn implicit_negative<'cx, 'tcx>( +/// Check if both impls can be satisfied by a common type by considering whether +/// any of either impl's obligations is not known to hold. +/// +/// For example, given these two impls: +/// `impl From<MyLocalType> for Box<dyn Error>` (in my crate) +/// `impl<E> From<E> for Box<dyn Error> where E: Error` (in libstd) +/// +/// After replacing both impl headers with inference vars (which happens before +/// this function is called), we get: +/// `Box<dyn Error>: From<MyLocalType>` +/// `Box<dyn Error>: From<?E>` +/// +/// This gives us `?E = MyLocalType`. We then certainly know that `MyLocalType: Error` +/// never holds in intercrate mode since a local impl does not exist, and a +/// downstream impl cannot be added -- therefore can consider the intersection +/// of the two impls above to be empty. +/// +/// Importantly, this works even if there isn't a `impl !Error for MyLocalType`. +fn impl_intersection_has_impossible_obligation<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, param_env: ty::ParamEnv<'tcx>, impl1_header: &ty::ImplHeader<'tcx>, impl2_header: ty::ImplHeader<'tcx>, obligations: PredicateObligations<'tcx>, ) -> bool { - // There's no overlap if obligations are unsatisfiable or if the obligation negated is - // satisfied. - // - // For example, given these two impl headers: - // - // `impl<'a> From<&'a str> for Box<dyn Error>` - // `impl<E> From<E> for Box<dyn Error> where E: Error` - // - // So we have: - // - // `Box<dyn Error>: From<&'?a str>` - // `Box<dyn Error>: From<?E>` - // - // After equating the two headers: - // - // `Box<dyn Error> = Box<dyn Error>` - // So, `?E = &'?a str` and then given the where clause `&'?a str: Error`. - // - // If the obligation `&'?a str: Error` holds, it means that there's overlap. If that doesn't - // hold we need to check if `&'?a str: !Error` holds, if doesn't hold there's overlap because - // at some point an impl for `&'?a str: Error` could be added. - debug!( - "implicit_negative(impl1_header={:?}, impl2_header={:?}, obligations={:?})", - impl1_header, impl2_header, obligations - ); let infcx = selcx.infcx; - let opt_failing_obligation = impl1_header - .predicates - .iter() - .copied() - .chain(impl2_header.predicates) - .map(|p| infcx.resolve_vars_if_possible(p)) - .map(|p| Obligation { - cause: ObligationCause::dummy(), - param_env, - recursion_depth: 0, - predicate: p, + + let obligation_guaranteed_to_fail = move |obligation: &PredicateObligation<'tcx>| { + if infcx.next_trait_solver() { + infcx.evaluate_obligation(obligation).map_or(false, |result| !result.may_apply()) + } else { + // We use `evaluate_root_obligation` to correctly track + // intercrate ambiguity clauses. We do not need this in the + // new solver. + selcx.evaluate_root_obligation(obligation).map_or( + false, // Overflow has occurred, and treat the obligation as possibly holding. + |result| !result.may_apply(), + ) + } + }; + + let opt_failing_obligation = [&impl1_header.predicates, &impl2_header.predicates] + .into_iter() + .flatten() + .map(|&predicate| { + Obligation::new(infcx.tcx, ObligationCause::dummy(), param_env, predicate) }) .chain(obligations) - .find(|o| !selcx.predicate_may_hold_fatal(o)); + .find(obligation_guaranteed_to_fail); if let Some(failing_obligation) = opt_failing_obligation { debug!("overlap: obligation unsatisfiable {:?}", failing_obligation); @@ -294,9 +322,27 @@ 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(tcx: TyCtxt<'_>, impl1_def_id: DefId, impl2_def_id: DefId) -> bool { +/// Check if both impls can be satisfied by a common type by considering whether +/// any of first impl's obligations is known not to hold *via a negative predicate*. +/// +/// For example, given these two impls: +/// `struct MyCustomBox<T: ?Sized>(Box<T>);` +/// `impl From<&str> for MyCustomBox<dyn Error>` (in my crate) +/// `impl<E> From<E> for MyCustomBox<dyn Error> where E: Error` (in my crate) +/// +/// After replacing the second impl's header with inference vars, we get: +/// `MyCustomBox<dyn Error>: From<&str>` +/// `MyCustomBox<dyn Error>: From<?E>` +/// +/// This gives us `?E = &str`. We then try to prove the first impl's predicates +/// after negating, giving us `&str: !Error`. This is a negative impl provided by +/// libstd, and therefore we can guarantee for certain that libstd will never add +/// a positive impl for `&str: Error` (without it being a breaking change). +fn impl_intersection_has_negative_obligation( + tcx: TyCtxt<'_>, + impl1_def_id: DefId, + impl2_def_id: DefId, +) -> bool { debug!("negative_impl(impl1_def_id={:?}, impl2_def_id={:?})", impl1_def_id, impl2_def_id); // Create an infcx, taking the predicates of impl1 as assumptions: @@ -322,57 +368,45 @@ fn negative_impl(tcx: TyCtxt<'_>, impl1_def_id: DefId, impl2_def_id: DefId) -> b // Attempt to prove that impl2 applies, given all of the above. let selcx = &mut SelectionContext::new(&infcx); let impl2_substs = infcx.fresh_substs_for_item(DUMMY_SP, impl2_def_id); - let (subject2, obligations) = + let (subject2, normalization_obligations) = impl_subject_and_oblig(selcx, impl_env, impl2_def_id, impl2_substs, |_, _| { ObligationCause::dummy() }); - !equate(&infcx, impl_env, subject1, subject2, obligations, impl1_def_id) -} - -fn equate<'tcx>( - infcx: &InferCtxt<'tcx>, - impl_env: ty::ParamEnv<'tcx>, - subject1: ImplSubject<'tcx>, - subject2: ImplSubject<'tcx>, - obligations: impl Iterator<Item = PredicateObligation<'tcx>>, - body_def_id: DefId, -) -> bool { - // do the impls unify? If not, not disjoint. - let Ok(InferOk { obligations: more_obligations, .. }) = + // do the impls unify? If not, then it's not currently possible to prove any + // obligations about their intersection. + let Ok(InferOk { obligations: equate_obligations, .. }) = infcx.at(&ObligationCause::dummy(), impl_env).eq(DefineOpaqueTypes::No,subject1, subject2) else { debug!("explicit_disjoint: {:?} does not unify with {:?}", subject1, subject2); - return true; + return false; }; - let opt_failing_obligation = obligations - .into_iter() - .chain(more_obligations) - .find(|o| negative_impl_exists(infcx, o, body_def_id)); - - if let Some(failing_obligation) = opt_failing_obligation { - debug!("overlap: obligation unsatisfiable {:?}", failing_obligation); - false - } else { - true + for obligation in normalization_obligations.into_iter().chain(equate_obligations) { + if negative_impl_exists(&infcx, &obligation, impl1_def_id) { + debug!("overlap: obligation unsatisfiable {:?}", obligation); + return true; + } } + + false } -/// Try to prove that a negative impl exist for the given obligation and its super predicates. +/// Try to prove that a negative impl exist for the obligation or its supertraits. +/// +/// If such a negative impl exists, then the obligation definitely must not hold +/// due to coherence, even if it's not necessarily "knowable" in this crate. Any +/// valid impl downstream would not be able to exist due to the overlapping +/// negative impl. #[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(infcx.fork(), o, body_def_id) { - return true; - } - // Try to prove a negative obligation exists for super predicates for pred in util::elaborate(infcx.tcx, iter::once(o.predicate)) { - if resolve_negative_obligation(infcx.fork(), &o.with(infcx.tcx, pred), body_def_id) { + if prove_negated_obligation(infcx.fork(), &o.with(infcx.tcx, pred), body_def_id) { return true; } } @@ -381,7 +415,7 @@ fn negative_impl_exists<'tcx>( } #[instrument(level = "debug", skip(infcx))] -fn resolve_negative_obligation<'tcx>( +fn prove_negated_obligation<'tcx>( infcx: InferCtxt<'tcx>, o: &PredicateObligation<'tcx>, body_def_id: DefId, @@ -403,7 +437,11 @@ fn resolve_negative_obligation<'tcx>( let body_def_id = body_def_id.as_local().unwrap_or(CRATE_DEF_ID); let ocx = ObligationCtxt::new(&infcx); - let wf_tys = ocx.assumed_wf_types(param_env, DUMMY_SP, body_def_id); + let Ok(wf_tys) = ocx.assumed_wf_types(param_env, body_def_id) + else { + return false; + }; + let outlives_env = OutlivesEnvironment::with_bounds( param_env, infcx.implied_bounds_tys(param_env, body_def_id, wf_tys), @@ -676,7 +714,9 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for OrphanChecker<'tcx> { | ty::RawPtr(..) | ty::Never | ty::Tuple(..) - | ty::Alias(ty::Projection | ty::Inherent, ..) => self.found_non_local_ty(ty), + | ty::Alias(ty::Projection | ty::Inherent | ty::Weak, ..) => { + self.found_non_local_ty(ty) + } ty::Param(..) => self.found_param_ty(ty), diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index bd1ea43a7..8dc13b827 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -176,7 +176,7 @@ fn satisfied_from_param_env<'tcx>( fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> { debug!("is_const_evaluatable: candidate={:?}", c); if self.infcx.probe(|_| { - let ocx = ObligationCtxt::new_in_snapshot(self.infcx); + let ocx = ObligationCtxt::new(self.infcx); ocx.eq(&ObligationCause::dummy(), self.param_env, c.ty(), self.ct.ty()).is_ok() && ocx.eq(&ObligationCause::dummy(), self.param_env, c, self.ct).is_ok() && ocx.select_all_or_error().is_empty() @@ -207,7 +207,7 @@ fn satisfied_from_param_env<'tcx>( for pred in param_env.caller_bounds() { match pred.kind().skip_binder() { - ty::PredicateKind::ConstEvaluatable(ce) => { + ty::ClauseKind::ConstEvaluatable(ce) => { let b_ct = tcx.expand_abstract_consts(ce); let mut v = Visitor { ct, infcx, param_env, single_match }; let _ = b_ct.visit_with(&mut v); @@ -219,7 +219,7 @@ fn satisfied_from_param_env<'tcx>( } if let Some(Ok(c)) = single_match { - let ocx = ObligationCtxt::new_in_snapshot(infcx); + let ocx = ObligationCtxt::new(infcx); assert!(ocx.eq(&ObligationCause::dummy(), param_env, c.ty(), ct.ty()).is_ok()); assert!(ocx.eq(&ObligationCause::dummy(), param_env, c, ct).is_ok()); assert!(ocx.select_all_or_error().is_empty()); diff --git a/compiler/rustc_trait_selection/src/traits/engine.rs b/compiler/rustc_trait_selection/src/traits/engine.rs index 2c5ffd664..61f693e1b 100644 --- a/compiler/rustc_trait_selection/src/traits/engine.rs +++ b/compiler/rustc_trait_selection/src/traits/engine.rs @@ -1,9 +1,10 @@ use std::cell::RefCell; use std::fmt::Debug; +use super::FulfillmentContext; use super::TraitEngine; -use super::{ChalkFulfillmentContext, FulfillmentContext}; use crate::solve::FulfillmentCtxt as NextFulfillmentCtxt; +use crate::traits::error_reporting::TypeErrCtxtExt; use crate::traits::NormalizeExt; use rustc_data_structures::fx::FxIndexSet; use rustc_errors::ErrorGuaranteed; @@ -24,27 +25,25 @@ use rustc_middle::ty::ToPredicate; use rustc_middle::ty::TypeFoldable; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_session::config::TraitSolver; -use rustc_span::Span; pub trait TraitEngineExt<'tcx> { - fn new(tcx: TyCtxt<'tcx>) -> Box<Self>; - fn new_in_snapshot(tcx: TyCtxt<'tcx>) -> Box<Self>; + fn new(infcx: &InferCtxt<'tcx>) -> Box<Self>; } impl<'tcx> TraitEngineExt<'tcx> for dyn TraitEngine<'tcx> { - fn new(tcx: TyCtxt<'tcx>) -> Box<Self> { - match tcx.sess.opts.unstable_opts.trait_solver { - TraitSolver::Classic => Box::new(FulfillmentContext::new()), - TraitSolver::Chalk => Box::new(ChalkFulfillmentContext::new()), - TraitSolver::Next => Box::new(NextFulfillmentCtxt::new()), - } - } - - fn new_in_snapshot(tcx: TyCtxt<'tcx>) -> Box<Self> { - match tcx.sess.opts.unstable_opts.trait_solver { - TraitSolver::Classic => Box::new(FulfillmentContext::new_in_snapshot()), - TraitSolver::Chalk => Box::new(ChalkFulfillmentContext::new_in_snapshot()), - TraitSolver::Next => Box::new(NextFulfillmentCtxt::new()), + fn new(infcx: &InferCtxt<'tcx>) -> Box<Self> { + match (infcx.tcx.sess.opts.unstable_opts.trait_solver, infcx.next_trait_solver()) { + (TraitSolver::Classic, false) | (TraitSolver::NextCoherence, false) => { + Box::new(FulfillmentContext::new(infcx)) + } + (TraitSolver::Next | TraitSolver::NextCoherence, true) => { + Box::new(NextFulfillmentCtxt::new(infcx)) + } + _ => bug!( + "incompatible combination of -Ztrait-solver flag ({:?}) and InferCtxt::next_trait_solver ({:?})", + infcx.tcx.sess.opts.unstable_opts.trait_solver, + infcx.next_trait_solver() + ), } } } @@ -58,11 +57,7 @@ pub struct ObligationCtxt<'a, 'tcx> { impl<'a, 'tcx> ObligationCtxt<'a, 'tcx> { pub fn new(infcx: &'a InferCtxt<'tcx>) -> Self { - Self { infcx, engine: RefCell::new(<dyn TraitEngine<'_>>::new(infcx.tcx)) } - } - - pub fn new_in_snapshot(infcx: &'a InferCtxt<'tcx>) -> Self { - Self { infcx, engine: RefCell::new(<dyn TraitEngine<'_>>::new_in_snapshot(infcx.tcx)) } + Self { infcx, engine: RefCell::new(<dyn TraitEngine<'_>>::new(infcx)) } } pub fn register_obligation(&self, obligation: PredicateObligation<'tcx>) { @@ -202,17 +197,24 @@ impl<'a, 'tcx> ObligationCtxt<'a, 'tcx> { } } + pub fn assumed_wf_types_and_report_errors( + &self, + param_env: ty::ParamEnv<'tcx>, + def_id: LocalDefId, + ) -> Result<FxIndexSet<Ty<'tcx>>, ErrorGuaranteed> { + self.assumed_wf_types(param_env, def_id) + .map_err(|errors| self.infcx.err_ctxt().report_fulfillment_errors(&errors)) + } + pub fn assumed_wf_types( &self, param_env: ty::ParamEnv<'tcx>, - span: Span, def_id: LocalDefId, - ) -> FxIndexSet<Ty<'tcx>> { + ) -> Result<FxIndexSet<Ty<'tcx>>, Vec<FulfillmentError<'tcx>>> { let tcx = self.infcx.tcx; - let assumed_wf_types = tcx.assumed_wf_types(def_id); let mut implied_bounds = FxIndexSet::default(); - let cause = ObligationCause::misc(span, def_id); - for ty in assumed_wf_types { + let mut errors = Vec::new(); + for &(ty, span) in tcx.assumed_wf_types(def_id) { // FIXME(@lcnr): rustc currently does not check wf for types // pre-normalization, meaning that implied bounds are sometimes // incorrect. See #100910 for more details. @@ -225,10 +227,19 @@ impl<'a, 'tcx> ObligationCtxt<'a, 'tcx> { // sound and then uncomment this line again. // implied_bounds.insert(ty); - let normalized = self.normalize(&cause, param_env, ty); - implied_bounds.insert(normalized); + let cause = ObligationCause::misc(span, def_id); + match self + .infcx + .at(&cause, param_env) + .deeply_normalize(ty, &mut **self.engine.borrow_mut()) + { + // Insert well-formed types, ignoring duplicates. + Ok(normalized) => drop(implied_bounds.insert(normalized)), + Err(normalization_errors) => errors.extend(normalization_errors), + }; } - implied_bounds + + if errors.is_empty() { Ok(implied_bounds) } else { Err(errors) } } pub fn make_canonicalized_query_response<T>( diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs index 7ab652761..f785c4eaf 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs @@ -1,7 +1,7 @@ use rustc_hir::def_id::DefId; use rustc_infer::infer::{InferCtxt, LateBoundRegionConversionTime}; use rustc_infer::traits::util::elaborate; -use rustc_infer::traits::{Obligation, ObligationCause, TraitObligation}; +use rustc_infer::traits::{Obligation, ObligationCause, PolyTraitObligation}; use rustc_middle::ty; use rustc_span::{Span, DUMMY_SP}; @@ -14,13 +14,13 @@ pub enum Ambiguity { pub fn recompute_applicable_impls<'tcx>( infcx: &InferCtxt<'tcx>, - obligation: &TraitObligation<'tcx>, + obligation: &PolyTraitObligation<'tcx>, ) -> Vec<Ambiguity> { let tcx = infcx.tcx; let param_env = obligation.param_env; let impl_may_apply = |impl_def_id| { - let ocx = ObligationCtxt::new_in_snapshot(infcx); + let ocx = ObligationCtxt::new(infcx); let placeholder_obligation = infcx.instantiate_binder_with_placeholders(obligation.predicate); let obligation_trait_ref = @@ -45,7 +45,7 @@ pub fn recompute_applicable_impls<'tcx>( }; let param_env_candidate_may_apply = |poly_trait_predicate: ty::PolyTraitPredicate<'tcx>| { - let ocx = ObligationCtxt::new_in_snapshot(infcx); + let ocx = ObligationCtxt::new(infcx); let placeholder_obligation = infcx.instantiate_binder_with_placeholders(obligation.predicate); let obligation_trait_ref = @@ -84,7 +84,7 @@ pub fn recompute_applicable_impls<'tcx>( tcx.predicates_of(obligation.cause.body_id.to_def_id()).instantiate_identity(tcx); for (pred, span) in elaborate(tcx, predicates.into_iter()) { let kind = pred.kind(); - if let ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred)) = kind.skip_binder() + if let ty::ClauseKind::Trait(trait_pred) = kind.skip_binder() && param_env_candidate_may_apply(kind.rebind(trait_pred)) { if kind.rebind(trait_pred.trait_ref) == ty::Binder::dummy(ty::TraitRef::identity(tcx, trait_pred.def_id())) { 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 a10ececbb..f34218059 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -5,13 +5,13 @@ pub mod suggestions; use super::{ FulfillmentError, FulfillmentErrorCode, MismatchedProjectionTypes, Obligation, ObligationCause, ObligationCauseCode, ObligationCtxt, OutputTypeParameterMismatch, Overflow, - PredicateObligation, SelectionContext, SelectionError, TraitNotObjectSafe, + PredicateObligation, SelectionError, TraitNotObjectSafe, }; use crate::infer::error_reporting::{TyCategory, TypeAnnotationNeeded as ErrorCode}; use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use crate::infer::{self, InferCtxt}; +use crate::solve::{GenerateProofTree, InferCtxtEvalExt, UseGlobalCache}; 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::{AppendConstMessage, OnUnimplementedNote, TypeErrCtxtExt as _}; @@ -28,21 +28,24 @@ use rustc_hir::{GenericParam, Item, Node}; use rustc_infer::infer::error_reporting::TypeErrCtxt; use rustc_infer::infer::{InferOk, TypeTrace}; use rustc_middle::traits::select::OverflowError; -use rustc_middle::traits::SelectionOutputTypeParameterMismatch; +use rustc_middle::traits::solve::Goal; +use rustc_middle::traits::{DefiningAnchor, SelectionOutputTypeParameterMismatch}; use rustc_middle::ty::abstract_const::NotConstEvaluatable; use rustc_middle::ty::error::{ExpectedFound, TypeError}; -use rustc_middle::ty::fold::{TypeFolder, TypeSuperFoldable}; +use rustc_middle::ty::fold::{BottomUpFolder, TypeFolder, TypeSuperFoldable}; use rustc_middle::ty::print::{with_forced_trimmed_paths, FmtPrinter, Print}; use rustc_middle::ty::{ self, SubtypePredicate, ToPolyTraitRef, ToPredicate, TraitRef, Ty, TyCtxt, TypeFoldable, TypeVisitable, TypeVisitableExt, }; -use rustc_session::config::TraitSolver; +use rustc_session::config::{DumpSolverProofTree, TraitSolver}; use rustc_session::Limit; use rustc_span::def_id::LOCAL_CRATE; use rustc_span::symbol::sym; use rustc_span::{ExpnKind, Span, DUMMY_SP}; +use std::borrow::Cow; use std::fmt; +use std::io::Write; use std::iter; use std::ops::ControlFlow; use suggestions::TypeErrCtxtExt as _; @@ -59,12 +62,17 @@ pub enum CandidateSimilarity { Fuzzy { ignoring_lifetimes: bool }, } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct ImplCandidate<'tcx> { pub trait_ref: ty::TraitRef<'tcx>, pub similarity: CandidateSimilarity, } +enum GetSafeTransmuteErrorAndReason { + Silent, + Error { err_msg: String, safe_transmute_explanation: String }, +} + pub trait InferCtxtExt<'tcx> { /// Given some node representing a fn-like thing in the HIR map, /// returns a span and `ArgKind` information that describes the @@ -148,6 +156,12 @@ pub trait TypeErrCtxtExt<'tcx> { root_obligation: &PredicateObligation<'tcx>, error: &SelectionError<'tcx>, ); + + fn report_const_param_not_wf( + &self, + ty: Ty<'tcx>, + obligation: &PredicateObligation<'tcx>, + ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>; } impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { @@ -365,7 +379,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { param_env, ty.rebind(ty::TraitPredicate { trait_ref, constness, polarity }), ); - let ocx = ObligationCtxt::new_in_snapshot(self); + let ocx = ObligationCtxt::new(self); ocx.register_obligation(obligation); if ocx.select_all_or_error().is_empty() { return Ok(( @@ -618,6 +632,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { error: &SelectionError<'tcx>, ) { let tcx = self.tcx; + + if tcx.sess.opts.unstable_opts.dump_solver_proof_tree == DumpSolverProofTree::OnError { + dump_proof_tree(root_obligation, self.infcx); + } + let mut span = obligation.cause.span; // FIXME: statically guarantee this by tainting after the diagnostic is emitted self.set_tainted_by_errors( @@ -640,6 +659,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { span = obligation.cause.span; } } + if let ObligationCauseCode::CompareImplItemObligation { impl_item_def_id, trait_item_def_id, @@ -656,9 +676,16 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { return; } + // Report a const-param specific error + if let ObligationCauseCode::ConstParam(ty) = *obligation.cause.code().peel_derives() + { + self.report_const_param_not_wf(ty, &obligation).emit(); + return; + } + let bound_predicate = obligation.predicate.kind(); match bound_predicate.skip_binder() { - ty::PredicateKind::Clause(ty::Clause::Trait(trait_predicate)) => { + ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_predicate)) => { let trait_predicate = bound_predicate.rebind(trait_predicate); let mut trait_predicate = self.resolve_vars_if_possible(trait_predicate); @@ -724,11 +751,17 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { == self.tcx.lang_items().transmute_trait() { // Recompute the safe transmute reason and use that for the error reporting - self.get_safe_transmute_error_and_reason( + match self.get_safe_transmute_error_and_reason( obligation.clone(), trait_ref, span, - ) + ) { + GetSafeTransmuteErrorAndReason::Silent => return, + GetSafeTransmuteErrorAndReason::Error { + err_msg, + safe_transmute_explanation, + } => (err_msg, Some(safe_transmute_explanation)), + } } else { (err_msg, None) }; @@ -940,7 +973,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { && self.fallback_has_occurred { let predicate = trait_predicate.map_bound(|trait_pred| { - trait_pred.with_self_ty(self.tcx, self.tcx.mk_unit()) + trait_pred.with_self_ty(self.tcx, Ty::new_unit(self.tcx)) }); let unit_obligation = obligation.with(tcx, predicate); if self.predicate_may_hold(&unit_obligation) { @@ -995,8 +1028,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { span_bug!(span, "coerce requirement gave wrong error: `{:?}`", predicate) } - ty::PredicateKind::Clause(ty::Clause::RegionOutlives(..)) - | ty::PredicateKind::Clause(ty::Clause::TypeOutlives(..)) => { + ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(..)) + | ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(..)) => { span_bug!( span, "outlives clauses should not error outside borrowck. obligation: `{:?}`", @@ -1004,7 +1037,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ) } - ty::PredicateKind::Clause(ty::Clause::Projection(..)) => { + ty::PredicateKind::Clause(ty::ClauseKind::Projection(..)) => { span_bug!( span, "projection clauses should be implied from elsewhere. obligation: `{:?}`", @@ -1022,7 +1055,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { self.report_closure_error(&obligation, closure_def_id, found_kind, kind) } - ty::PredicateKind::WellFormed(ty) => { + ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(ty)) => { + let ty = self.resolve_vars_if_possible(ty); match self.tcx.sess.opts.unstable_opts.trait_solver { TraitSolver::Classic => { // WF predicates cannot themselves make @@ -1032,7 +1066,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // (which may fail). span_bug!(span, "WF predicate not satisfied for {:?}", ty); } - TraitSolver::Chalk | TraitSolver::Next => { + TraitSolver::Next | TraitSolver::NextCoherence => { // FIXME: we'll need a better message which takes into account // which bounds actually failed to hold. self.tcx.sess.struct_span_err( @@ -1043,7 +1077,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } } - ty::PredicateKind::ConstEvaluatable(..) => { + ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..)) => { // Errors for `ConstEvaluatable` predicates show up as // `SelectionError::ConstEvalFailure`, // not `Unimplemented`. @@ -1067,17 +1101,12 @@ 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" - ), - ty::PredicateKind::AliasRelate(..) => span_bug!( span, "AliasRelate predicate should never be the predicate cause of a SelectionError" ), - ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(ct, ty)) => { + ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, ty)) => { let mut diag = self.tcx.sess.struct_span_err( span, format!("the constant `{}` is not of type `{}`", ct, ty), @@ -1122,6 +1151,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } } + SelectionError::OpaqueTypeAutoTraitLeakageUnknown(def_id) => self.report_opaque_type_auto_trait_leakage( + &obligation, + def_id, + ), + TraitNotObjectSafe(did) => { let violations = self.tcx.object_safety_violations(did); report_object_safety_error(self.tcx, span, did, violations) @@ -1140,16 +1174,10 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } // Already reported in the query. - SelectionError::NotConstEvaluatable(NotConstEvaluatable::Error(_)) => { - // FIXME(eddyb) remove this once `ErrorGuaranteed` becomes a proof token. - self.tcx.sess.delay_span_bug(span, "`ErrorGuaranteed` without an error"); - return; - } + SelectionError::NotConstEvaluatable(NotConstEvaluatable::Error(_)) | // Already reported. - Overflow(OverflowError::Error(_)) => { - self.tcx.sess.delay_span_bug(span, "`OverflowError` has been reported"); - return; - } + Overflow(OverflowError::Error(_)) => return, + Overflow(_) => { bug!("overflow should be handled before the `report_selection_error` path"); } @@ -1162,6 +1190,102 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { self.point_at_returns_when_relevant(&mut err, &obligation); err.emit(); } + + fn report_const_param_not_wf( + &self, + ty: Ty<'tcx>, + obligation: &PredicateObligation<'tcx>, + ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { + let span = obligation.cause.span; + + let mut diag = match ty.kind() { + _ if ty.has_param() => { + span_bug!(span, "const param tys cannot mention other generic parameters"); + } + ty::Float(_) => { + struct_span_err!( + self.tcx.sess, + span, + E0741, + "`{ty}` is forbidden as the type of a const generic parameter", + ) + } + ty::FnPtr(_) => { + struct_span_err!( + self.tcx.sess, + span, + E0741, + "using function pointers as const generic parameters is forbidden", + ) + } + ty::RawPtr(_) => { + struct_span_err!( + self.tcx.sess, + span, + E0741, + "using raw pointers as const generic parameters is forbidden", + ) + } + ty::Adt(def, _) => { + // We should probably see if we're *allowed* to derive `ConstParamTy` on the type... + let mut diag = struct_span_err!( + self.tcx.sess, + span, + E0741, + "`{ty}` must implement `ConstParamTy` to be used as the type of a const generic parameter", + ); + // Only suggest derive if this isn't a derived obligation, + // and the struct is local. + if let Some(span) = self.tcx.hir().span_if_local(def.did()) + && obligation.cause.code().parent().is_none() + { + if ty.is_structural_eq_shallow(self.tcx) { + diag.span_suggestion( + span, + "add `#[derive(ConstParamTy)]` to the struct", + "#[derive(ConstParamTy)]\n", + Applicability::MachineApplicable, + ); + } else { + // FIXME(adt_const_params): We should check there's not already an + // overlapping `Eq`/`PartialEq` impl. + diag.span_suggestion( + span, + "add `#[derive(ConstParamTy, PartialEq, Eq)]` to the struct", + "#[derive(ConstParamTy, PartialEq, Eq)]\n", + Applicability::MachineApplicable, + ); + } + } + diag + } + _ => { + struct_span_err!( + self.tcx.sess, + span, + E0741, + "`{ty}` can't be used as a const parameter type", + ) + } + }; + + let mut code = obligation.cause.code(); + let mut pred = obligation.predicate.to_opt_poly_trait_pred(); + while let Some((next_code, next_pred)) = code.parent() { + if let Some(pred) = pred { + let pred = self.instantiate_binder_with_placeholders(pred); + diag.note(format!( + "`{}` must implement `{}`, but it does not", + pred.self_ty(), + pred.print_modifiers_and_trait_path() + )); + } + code = next_code; + pred = next_pred; + } + + diag + } } trait InferCtxtPrivExt<'tcx> { @@ -1292,7 +1416,7 @@ trait InferCtxtPrivExt<'tcx> { obligation: PredicateObligation<'tcx>, trait_ref: ty::PolyTraitRef<'tcx>, span: Span, - ) -> (String, Option<String>); + ) -> GetSafeTransmuteErrorAndReason; fn add_tuple_trait_message( &self, @@ -1345,6 +1469,12 @@ trait InferCtxtPrivExt<'tcx> { terr: TypeError<'tcx>, ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>; + fn report_opaque_type_auto_trait_leakage( + &self, + obligation: &PredicateObligation<'tcx>, + def_id: DefId, + ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>; + fn report_type_parameter_mismatch_error( &self, obligation: &PredicateObligation<'tcx>, @@ -1372,8 +1502,8 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let bound_error = error.kind(); let (cond, error) = match (cond.kind().skip_binder(), bound_error.skip_binder()) { ( - ty::PredicateKind::Clause(ty::Clause::Trait(..)), - ty::PredicateKind::Clause(ty::Clause::Trait(error)), + ty::PredicateKind::Clause(ty::ClauseKind::Trait(..)), + ty::PredicateKind::Clause(ty::ClauseKind::Trait(error)), ) => (cond, bound_error.rebind(error)), _ => { // FIXME: make this work in other cases too. @@ -1383,7 +1513,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { for pred in super::elaborate(self.tcx, std::iter::once(cond)) { let bound_predicate = pred.kind(); - if let ty::PredicateKind::Clause(ty::Clause::Trait(implication)) = + if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(implication)) = bound_predicate.skip_binder() { let error = error.to_poly_trait_ref(); @@ -1404,6 +1534,10 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { #[instrument(skip(self), level = "debug")] fn report_fulfillment_error(&self, error: &FulfillmentError<'tcx>) { + if self.tcx.sess.opts.unstable_opts.dump_solver_proof_tree == DumpSolverProofTree::OnError { + dump_proof_tree(&error.root_obligation, self.infcx); + } + match error.code { FulfillmentErrorCode::CodeSelectionError(ref selection_error) => { self.report_selection_error( @@ -1474,14 +1608,14 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } self.probe(|_| { - let ocx = ObligationCtxt::new_in_snapshot(self); + let ocx = ObligationCtxt::new(self); // try to find the mismatched types to report the error with. // // this can fail if the problem was higher-ranked, in which // cause I have no idea for a good error message. let bound_predicate = predicate.kind(); - let (values, err) = if let ty::PredicateKind::Clause(ty::Clause::Projection(data)) = + let (values, err) = if let ty::PredicateKind::Clause(ty::ClauseKind::Projection(data)) = bound_predicate.skip_binder() { let data = self.instantiate_binder_with_fresh_vars( @@ -1490,20 +1624,21 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { bound_predicate.rebind(data), ); let unnormalized_term = match data.term.unpack() { - ty::TermKind::Ty(_) => self - .tcx - .mk_projection(data.projection_ty.def_id, data.projection_ty.substs) - .into(), - ty::TermKind::Const(ct) => self - .tcx - .mk_const( - ty::UnevaluatedConst { - def: data.projection_ty.def_id, - substs: data.projection_ty.substs, - }, - ct.ty(), - ) - .into(), + ty::TermKind::Ty(_) => Ty::new_projection( + self.tcx, + data.projection_ty.def_id, + data.projection_ty.substs, + ) + .into(), + ty::TermKind::Const(ct) => ty::Const::new_unevaluated( + self.tcx, + ty::UnevaluatedConst { + def: data.projection_ty.def_id, + substs: data.projection_ty.substs, + }, + ct.ty(), + ) + .into(), }; let normalized_term = ocx.normalize(&obligation.cause, obligation.param_env, unnormalized_term); @@ -1564,7 +1699,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let mut diag = struct_span_err!(self.tcx.sess, obligation.cause.span, E0271, "{msg}"); let secondary_span = (|| { - let ty::PredicateKind::Clause(ty::Clause::Projection(proj)) = + let ty::PredicateKind::Clause(ty::ClauseKind::Projection(proj)) = predicate.kind().skip_binder() else { return None; @@ -1602,7 +1737,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { }), ) => Some(( ty.span, - with_forced_trimmed_paths!(format!( + with_forced_trimmed_paths!(Cow::from(format!( "type mismatch resolving `{}`", self.resolve_vars_if_possible(predicate) .print(FmtPrinter::new_with_limit( @@ -1612,7 +1747,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { )) .unwrap() .into_buffer() - )), + ))), )), _ => None, } @@ -1702,12 +1837,13 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ty::Alias(ty::Projection, ..) => Some(12), ty::Alias(ty::Inherent, ..) => Some(13), ty::Alias(ty::Opaque, ..) => Some(14), - ty::Never => Some(15), - ty::Adt(..) => Some(16), - ty::Generator(..) => Some(17), - ty::Foreign(..) => Some(18), - ty::GeneratorWitness(..) => Some(19), - ty::GeneratorWitnessMIR(..) => Some(20), + ty::Alias(ty::Weak, ..) => Some(15), + ty::Never => Some(16), + ty::Adt(..) => Some(17), + ty::Generator(..) => Some(18), + ty::Foreign(..) => Some(19), + ty::GeneratorWitness(..) => Some(20), + ty::GeneratorWitnessMIR(..) => Some(21), ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) | ty::Error(_) => None, } } @@ -1775,6 +1911,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { || !trait_pred .skip_binder() .is_constness_satisfied_by(self.tcx.constness(def_id)) + || !self.tcx.is_user_visible_dep(def_id.krate) { return None; } @@ -1803,10 +1940,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { other: bool, ) -> bool { let other = if other { "other " } else { "" }; - let report = |mut candidates: Vec<TraitRef<'tcx>>, err: &mut Diagnostic| { - candidates.sort(); - candidates.dedup(); - let len = candidates.len(); + let report = |candidates: Vec<TraitRef<'tcx>>, err: &mut Diagnostic| { if candidates.is_empty() { return false; } @@ -1835,11 +1969,14 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { candidates.iter().map(|c| c.print_only_trait_path().to_string()).collect(); traits.sort(); traits.dedup(); + // FIXME: this could use a better heuristic, like just checking + // that substs[1..] is the same. + let all_traits_equal = traits.len() == 1; - let mut candidates: Vec<String> = candidates + let candidates: Vec<String> = candidates .into_iter() .map(|c| { - if traits.len() == 1 { + if all_traits_equal { format!("\n {}", c.self_ty()) } else { format!("\n {}", c) @@ -1847,14 +1984,16 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { }) .collect(); - candidates.sort(); - candidates.dedup(); let end = if candidates.len() <= 9 { candidates.len() } else { 8 }; err.help(format!( "the following {other}types implement trait `{}`:{}{}", trait_ref.print_only_trait_path(), candidates[..end].join(""), - if len > 9 { format!("\nand {} others", len - 8) } else { String::new() } + if candidates.len() > 9 { + format!("\nand {} others", candidates.len() - 8) + } else { + String::new() + } )); true }; @@ -1868,7 +2007,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // Mentioning implementers of `Copy`, `Debug` and friends is not useful. return false; } - let normalized_impl_candidates: Vec<_> = self + let mut impl_candidates: Vec<_> = self .tcx .all_impls(def_id) // Ignore automatically derived impls and `!Trait` impls. @@ -1895,7 +2034,10 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } }) .collect(); - return report(normalized_impl_candidates, err); + + impl_candidates.sort(); + impl_candidates.dedup(); + return report(impl_candidates, err); } // Sort impl candidates so that ordering is consistent for UI tests. @@ -1904,27 +2046,25 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // // Prefer more similar candidates first, then sort lexicographically // by their normalized string representation. - let mut normalized_impl_candidates_and_similarities = impl_candidates + let mut impl_candidates: Vec<_> = impl_candidates .iter() - .copied() - .map(|ImplCandidate { trait_ref, similarity }| { - // 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) + .cloned() + .map(|mut cand| { + // Fold the consts so that they shows up as, e.g., `10` + // instead of `core::::array::{impl#30}::{constant#0}`. + cand.trait_ref = cand.trait_ref.fold_with(&mut BottomUpFolder { + tcx: self.tcx, + ty_op: |ty| ty, + lt_op: |lt| lt, + ct_op: |ct| ct.eval(self.tcx, ty::ParamEnv::empty()), + }); + cand }) - .collect::<Vec<_>>(); - normalized_impl_candidates_and_similarities.sort(); - normalized_impl_candidates_and_similarities.dedup(); - - let normalized_impl_candidates = normalized_impl_candidates_and_similarities - .into_iter() - .map(|(_, normalized)| normalized) - .collect::<Vec<_>>(); + .collect(); + impl_candidates.sort_by_key(|cand| (cand.similarity, cand.trait_ref)); + impl_candidates.dedup(); - report(normalized_impl_candidates, err) + report(impl_candidates.into_iter().map(|cand| cand.trait_ref).collect(), err) } fn report_similar_impl_candidates_for_root_obligation( @@ -2075,7 +2215,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let bound_predicate = predicate.kind(); let mut err = match bound_predicate.skip_binder() { - ty::PredicateKind::Clause(ty::Clause::Trait(data)) => { + ty::PredicateKind::Clause(ty::ClauseKind::Trait(data)) => { let trait_ref = bound_predicate.rebind(data.trait_ref); debug!(?trait_ref); @@ -2145,55 +2285,40 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ) }; - let obligation = obligation.with(self.tcx, trait_ref); - let mut selcx = SelectionContext::new(&self); - match selcx.select_from_obligation(&obligation) { - Ok(None) => { - let ambiguities = - ambiguity::recompute_applicable_impls(self.infcx, &obligation); - let has_non_region_infer = trait_ref - .skip_binder() - .substs - .types() - .any(|t| !t.is_ty_or_numeric_infer()); - // It doesn't make sense to talk about applicable impls if there are more - // than a handful of them. - if ambiguities.len() > 1 && ambiguities.len() < 10 && has_non_region_infer { - if self.tainted_by_errors().is_some() && subst.is_none() { - // If `subst.is_none()`, then this is probably two param-env - // candidates or impl candidates that are equal modulo lifetimes. - // Therefore, if we've already emitted an error, just skip this - // one, since it's not particularly actionable. - err.cancel(); - return; - } - self.annotate_source_of_ambiguity(&mut err, &ambiguities, predicate); - } else { - if self.tainted_by_errors().is_some() { - err.cancel(); - return; - } - err.note(format!("cannot satisfy `{}`", predicate)); - let impl_candidates = self.find_similar_impl_candidates( - predicate.to_opt_poly_trait_pred().unwrap(), - ); - if impl_candidates.len() < 10 { - self.report_similar_impl_candidates( - impl_candidates.as_slice(), - trait_ref, - obligation.cause.body_id, - &mut err, - false, - ); - } - } + let ambiguities = ambiguity::recompute_applicable_impls( + self.infcx, + &obligation.with(self.tcx, trait_ref), + ); + let has_non_region_infer = + trait_ref.skip_binder().substs.types().any(|t| !t.is_ty_or_numeric_infer()); + // It doesn't make sense to talk about applicable impls if there are more + // than a handful of them. + if ambiguities.len() > 1 && ambiguities.len() < 10 && has_non_region_infer { + if self.tainted_by_errors().is_some() && subst.is_none() { + // If `subst.is_none()`, then this is probably two param-env + // candidates or impl candidates that are equal modulo lifetimes. + // Therefore, if we've already emitted an error, just skip this + // one, since it's not particularly actionable. + err.cancel(); + return; } - _ => { - if self.tainted_by_errors().is_some() { - err.cancel(); - return; - } - err.note(format!("cannot satisfy `{}`", predicate)); + self.annotate_source_of_ambiguity(&mut err, &ambiguities, predicate); + } else { + if self.tainted_by_errors().is_some() { + err.cancel(); + return; + } + err.note(format!("cannot satisfy `{}`", predicate)); + let impl_candidates = self + .find_similar_impl_candidates(predicate.to_opt_poly_trait_pred().unwrap()); + if impl_candidates.len() < 10 { + self.report_similar_impl_candidates( + impl_candidates.as_slice(), + trait_ref, + obligation.cause.body_id, + &mut err, + false, + ); } } @@ -2258,17 +2383,21 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { && let Some(impl_def_id) = trait_impls.non_blanket_impls().values().flatten().next() { let non_blanket_impl_count = trait_impls.non_blanket_impls().values().flatten().count(); - let message = if non_blanket_impl_count == 1 { - "use the fully-qualified path to the only available implementation".to_string() - } else { + // If there is only one implementation of the trait, suggest using it. + // Otherwise, use a placeholder comment for the implementation. + let (message, impl_suggestion) = if non_blanket_impl_count == 1 {( + "use the fully-qualified path to the only available implementation".to_string(), + format!("<{} as ", self.tcx.type_of(impl_def_id).subst_identity()) + )} else {( format!( "use a fully-qualified path to a specific available implementation ({} found)", non_blanket_impl_count - ) - }; + ), + "</* self type */ as ".to_string() + )}; let mut suggestions = vec![( path.span.shrink_to_lo(), - format!("<{} as ", self.tcx.type_of(impl_def_id).subst_identity()) + impl_suggestion )]; if let Some(generic_arg) = trait_path_segment.args { let between_span = trait_path_segment.ident.span.between(generic_arg.span_ext); @@ -2291,7 +2420,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { err } - ty::PredicateKind::WellFormed(arg) => { + ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => { // Same hacky approach as above to avoid deluging user // with error messages. if arg.references_error() @@ -2329,7 +2458,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { true, ) } - ty::PredicateKind::Clause(ty::Clause::Projection(data)) => { + ty::PredicateKind::Clause(ty::ClauseKind::Projection(data)) => { if predicate.references_error() || self.tainted_by_errors().is_some() { return; } @@ -2363,7 +2492,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } } - ty::PredicateKind::ConstEvaluatable(data) => { + ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(data)) => { if predicate.references_error() || self.tainted_by_errors().is_some() { return; } @@ -2526,11 +2655,11 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - if let ty::Param(ty::ParamTy { name, .. }) = *ty.kind() { + if let ty::Param(_) = *ty.kind() { let infcx = self.infcx; *self.var_map.entry(ty).or_insert_with(|| { infcx.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::TypeParameterDefinition(name, None), + kind: TypeVariableOriginKind::MiscVariable, span: DUMMY_SP, }) }) @@ -2577,7 +2706,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { err: &mut Diagnostic, obligation: &PredicateObligation<'tcx>, ) { - let ty::PredicateKind::Clause(ty::Clause::Trait(pred)) = obligation.predicate.kind().skip_binder() else { return; }; + let ty::PredicateKind::Clause(ty::ClauseKind::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; }; @@ -2738,7 +2867,9 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { obligation: PredicateObligation<'tcx>, trait_ref: ty::PolyTraitRef<'tcx>, span: Span, - ) -> (String, Option<String>) { + ) -> GetSafeTransmuteErrorAndReason { + use rustc_transmute::Answer; + // Erase regions because layout code doesn't particularly care about regions. let trait_ref = self.tcx.erase_regions(self.tcx.erase_late_bound_regions(trait_ref)); @@ -2751,19 +2882,20 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { rustc_transmute::Assume::from_const(self.infcx.tcx, obligation.param_env, trait_ref.substs.const_at(3)) else { span_bug!(span, "Unable to construct rustc_transmute::Assume where it was previously possible"); }; + match rustc_transmute::TransmuteTypeEnv::new(self.infcx).is_transmutable( obligation.cause, src_and_dst, scope, assume, ) { - rustc_transmute::Answer::No(reason) => { + Answer::No(reason) => { let dst = trait_ref.substs.type_at(0); let src = trait_ref.substs.type_at(1); - let custom_err_msg = format!( + let err_msg = format!( "`{src}` cannot be safely transmuted into `{dst}` in the defining scope of `{scope}`" ); - let reason_msg = match reason { + let safe_transmute_explanation = match reason { rustc_transmute::Reason::SrcIsUnspecified => { format!("`{src}` does not have a well-specified layout") } @@ -2779,19 +2911,39 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { rustc_transmute::Reason::DstIsPrivate => format!( "`{dst}` is or contains a type or field that is not visible in that scope" ), - // FIXME(bryangarza): Include the number of bytes of src and dst rustc_transmute::Reason::DstIsTooBig => { format!("The size of `{src}` is smaller than the size of `{dst}`") } + rustc_transmute::Reason::DstHasStricterAlignment { + src_min_align, + dst_min_align, + } => { + format!( + "The minimum alignment of `{src}` ({src_min_align}) should be greater than that of `{dst}` ({dst_min_align})" + ) + } + rustc_transmute::Reason::DstIsMoreUnique => { + format!("`{src}` is a shared reference, but `{dst}` is a unique reference") + } + // Already reported by rustc + rustc_transmute::Reason::TypeError => { + return GetSafeTransmuteErrorAndReason::Silent; + } + rustc_transmute::Reason::SrcLayoutUnknown => { + format!("`{src}` has an unknown layout") + } + rustc_transmute::Reason::DstLayoutUnknown => { + format!("`{dst}` has an unknown layout") + } }; - (custom_err_msg, Some(reason_msg)) + GetSafeTransmuteErrorAndReason::Error { err_msg, safe_transmute_explanation } } // Should never get a Yes at this point! We already ran it before, and did not get a Yes. - rustc_transmute::Answer::Yes => span_bug!( + Answer::Yes => span_bug!( span, "Inconsistent rustc_transmute::is_transmutable(...) result, got Yes", ), - _ => span_bug!(span, "Unsupported rustc_transmute::Reason variant"), + other => span_bug!(span, "Unsupported rustc_transmute::Answer variant: `{other:?}`"), } } @@ -3049,6 +3201,39 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ) } + fn report_opaque_type_auto_trait_leakage( + &self, + obligation: &PredicateObligation<'tcx>, + def_id: DefId, + ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { + let name = match self.tcx.opaque_type_origin(def_id.expect_local()) { + hir::OpaqueTyOrigin::FnReturn(_) | hir::OpaqueTyOrigin::AsyncFn(_) => { + format!("opaque type") + } + hir::OpaqueTyOrigin::TyAlias { .. } => { + format!("`{}`", self.tcx.def_path_debug_str(def_id)) + } + }; + let mut err = self.tcx.sess.struct_span_err( + obligation.cause.span, + format!("cannot check whether the hidden type of {name} satisfies auto traits"), + ); + err.span_note(self.tcx.def_span(def_id), "opaque type is declared here"); + match self.defining_use_anchor { + DefiningAnchor::Bubble | DefiningAnchor::Error => {} + DefiningAnchor::Bind(bind) => { + err.span_note( + self.tcx.def_ident_span(bind).unwrap_or_else(|| self.tcx.def_span(bind)), + "this item depends on auto traits of the hidden type, \ + but may also be registering the hidden type. \ + This is not supported right now. \ + You can try moving the opaque type and the item that actually registers a hidden type into a new submodule".to_string(), + ); + } + }; + err + } + fn report_type_parameter_mismatch_error( &self, obligation: &PredicateObligation<'tcx>, @@ -3178,7 +3363,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } match obligation.predicate.kind().skip_binder() { - ty::PredicateKind::ConstEvaluatable(ct) => { + ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(ct)) => { let ty::ConstKind::Unevaluated(uv) = ct.kind() else { bug!("const evaluatable failed for non-unevaluated const `{ct:?}`"); }; @@ -3360,3 +3545,16 @@ pub enum DefIdOrName { DefId(DefId), Name(&'static str), } + +pub fn dump_proof_tree<'tcx>(o: &Obligation<'tcx, ty::Predicate<'tcx>>, infcx: &InferCtxt<'tcx>) { + infcx.probe(|_| { + let goal = Goal { predicate: o.predicate, param_env: o.param_env }; + let tree = infcx + .evaluate_root_goal(goal, GenerateProofTree::Yes(UseGlobalCache::No)) + .1 + .expect("proof tree should have been generated"); + let mut lock = std::io::stdout().lock(); + let _ = lock.write_fmt(format_args!("{tree:?}")); + let _ = lock.flush(); + }); +} 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 10bd027b6..b16d2eb5f 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 @@ -41,7 +41,6 @@ pub trait TypeErrCtxtExt<'tcx> { static ALLOWED_FORMAT_SYMBOLS: &[Symbol] = &[ kw::SelfUpper, sym::ItemContext, - sym::from_method, sym::from_desugaring, sym::direct, sym::cause, @@ -172,23 +171,6 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } } - if let ObligationCauseCode::ItemObligation(item) - | ObligationCauseCode::BindingObligation(item, _) - | ObligationCauseCode::ExprItemObligation(item, ..) - | ObligationCauseCode::ExprBindingObligation(item, ..) = *obligation.cause.code() - { - // FIXME: maybe also have some way of handling methods - // from other traits? That would require name resolution, - // which we might want to be some sort of hygienic. - // - // Currently I'm leaving it for what I need for `try`. - if self.tcx.trait_of_item(item) == Some(trait_ref.def_id) { - let method = self.tcx.item_name(item); - flags.push((sym::from_method, None)); - flags.push((sym::from_method, Some(method.to_string()))); - } - } - if let Some(k) = obligation.cause.span.desugaring_kind() { flags.push((sym::from_desugaring, None)); flags.push((sym::from_desugaring, Some(format!("{:?}", k)))); @@ -278,7 +260,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // Arrays give us `[]`, `[{ty}; _]` and `[{ty}; N]` if let ty::Array(aty, len) = self_ty.kind() { flags.push((sym::_Self, Some("[]".to_string()))); - let len = len.kind().try_to_value().and_then(|v| v.try_to_target_usize(self.tcx)); + let len = len.try_to_value().and_then(|v| v.try_to_target_usize(self.tcx)); flags.push((sym::_Self, Some(format!("[{}; _]", aty)))); if let Some(n) = len { flags.push((sym::_Self, Some(format!("[{}; {}]", aty, n)))); @@ -672,7 +654,7 @@ impl<'tcx> OnUnimplementedFormatString { None => { if let Some(val) = options.get(&s) { val - } else if s == sym::from_desugaring || s == sym::from_method { + } else if s == sym::from_desugaring { // don't break messages using these two arguments incorrectly &empty_string } else if s == sym::ItemContext { 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 82bad96ea..9ac1ba027 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -13,7 +13,7 @@ use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::{ error_code, pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, - ErrorGuaranteed, MultiSpan, Style, + ErrorGuaranteed, MultiSpan, Style, SuggestionStyle, }; use rustc_hir as hir; use rustc_hir::def::DefKind; @@ -38,6 +38,7 @@ use rustc_span::def_id::LocalDefId; use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::{BytePos, DesugaringKind, ExpnKind, MacroKind, Span, DUMMY_SP}; use rustc_target::spec::abi; +use std::borrow::Cow; use std::iter; use std::ops::Deref; @@ -186,7 +187,12 @@ pub trait TypeErrCtxtExt<'tcx> { trait_pred: ty::PolyTraitPredicate<'tcx>, ) -> bool; - fn get_closure_name(&self, def_id: DefId, err: &mut Diagnostic, msg: &str) -> Option<Symbol>; + fn get_closure_name( + &self, + def_id: DefId, + err: &mut Diagnostic, + msg: Cow<'static, str>, + ) -> Option<Symbol>; fn suggest_fn_call( &self, @@ -356,6 +362,15 @@ pub trait TypeErrCtxtExt<'tcx> { err: &mut Diagnostic, trait_pred: ty::PolyTraitPredicate<'tcx>, ); + + fn suggest_option_method_if_applicable( + &self, + failed_pred: ty::Predicate<'tcx>, + param_env: ty::ParamEnv<'tcx>, + err: &mut Diagnostic, + expr: &hir::Expr<'_>, + ); + fn note_function_argument_obligation( &self, body_id: LocalDefId, @@ -771,7 +786,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { if let Some(steps) = autoderef.into_iter().enumerate().find_map(|(steps, (ty, obligations))| { // Re-add the `&` - let ty = self.tcx.mk_ref(region, TypeAndMut { ty, mutbl }); + let ty = Ty::new_ref(self.tcx, region, TypeAndMut { ty, mutbl }); // Remapping bound vars here let real_trait_pred_and_ty = @@ -857,7 +872,12 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { /// Given a closure's `DefId`, return the given name of the closure. /// /// This doesn't account for reassignments, but it's only used for suggestions. - fn get_closure_name(&self, def_id: DefId, err: &mut Diagnostic, msg: &str) -> Option<Symbol> { + fn get_closure_name( + &self, + def_id: DefId, + err: &mut Diagnostic, + msg: Cow<'static, str>, + ) -> Option<Symbol> { let get_name = |err: &mut Diagnostic, kind: &hir::PatKind<'_>| -> Option<Symbol> { // Get the local name of this closure. This can be inaccurate because // of the possibility of reassignment, but this should be good enough. @@ -900,7 +920,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { return false; } - if let ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred)) = obligation.predicate.kind().skip_binder() + if let ty::PredicateKind::Clause(ty::ClauseKind::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 @@ -934,17 +954,17 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let msg = match def_id_or_name { DefIdOrName::DefId(def_id) => match self.tcx.def_kind(def_id) { DefKind::Ctor(CtorOf::Struct, _) => { - "use parentheses to construct this tuple struct".to_string() + Cow::from("use parentheses to construct this tuple struct") } DefKind::Ctor(CtorOf::Variant, _) => { - "use parentheses to construct this tuple variant".to_string() + Cow::from("use parentheses to construct this tuple variant") } - kind => format!( + kind => Cow::from(format!( "use parentheses to call this {}", self.tcx.def_kind_descr(kind, def_id) - ), + )), }, - DefIdOrName::Name(name) => format!("use parentheses to call this {name}"), + DefIdOrName::Name(name) => Cow::from(format!("use parentheses to call this {name}")), }; let args = inputs @@ -979,7 +999,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { .. })) => { err.span_label(*fn_decl_span, "consider calling this closure"); - let Some(name) = self.get_closure_name(def_id, err, &msg) else { + let Some(name) = self.get_closure_name(def_id, err, msg.clone()) else { return false; }; name.to_string() @@ -1137,7 +1157,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => { self.tcx.item_bounds(def_id).subst(self.tcx, substs).iter().find_map(|pred| { - if let ty::PredicateKind::Clause(ty::Clause::Projection(proj)) = pred.kind().skip_binder() + if let ty::ClauseKind::Projection(proj) = pred.kind().skip_binder() && Some(proj.projection_ty.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() @@ -1181,7 +1201,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { DefIdOrName::Name("type parameter") }; param_env.caller_bounds().iter().find_map(|pred| { - if let ty::PredicateKind::Clause(ty::Clause::Projection(proj)) = pred.kind().skip_binder() + if let ty::ClauseKind::Projection(proj) = pred.kind().skip_binder() && Some(proj.projection_ty.def_id) == self.tcx.lang_items().fn_once_output() && proj.projection_ty.self_ty() == found // args tuple will always be substs[1] @@ -1278,13 +1298,13 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let trait_pred_and_imm_ref = old_pred.map_bound(|trait_pred| { ( trait_pred, - self.tcx.mk_imm_ref(self.tcx.lifetimes.re_static, trait_pred.self_ty()), + Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_static, trait_pred.self_ty()), ) }); let trait_pred_and_mut_ref = old_pred.map_bound(|trait_pred| { ( trait_pred, - self.tcx.mk_mut_ref(self.tcx.lifetimes.re_static, trait_pred.self_ty()), + Ty::new_mut_ref(self.tcx, self.tcx.lifetimes.re_static, trait_pred.self_ty()), ) }); @@ -1341,7 +1361,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { err.note(msg); } else { err.message = - vec![(rustc_errors::DiagnosticMessage::Str(msg), Style::NoStyle)]; + vec![(rustc_errors::DiagnosticMessage::from(msg), Style::NoStyle)]; } err.span_label( span, @@ -1445,7 +1465,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ) { let ty::Ref(_, object_ty, hir::Mutability::Not) = target_ty.kind() else { return; }; let ty::Dynamic(predicates, _, ty::Dyn) = object_ty.kind() else { return; }; - let self_ref_ty = self.tcx.mk_imm_ref(self.tcx.lifetimes.re_erased, self_ty); + let self_ref_ty = Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_erased, self_ty); for predicate in predicates.iter() { if !self.predicate_must_hold_modulo_regions( @@ -1619,7 +1639,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::Clause(ty::Clause::Trait(pred)) = + if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) = obligation.predicate.kind().skip_binder() { err.span_label(*span, format!("this call returns `{}`", pred.self_ty())); @@ -1686,8 +1706,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { if let ty::Ref(region, t_type, mutability) = *trait_pred.skip_binder().self_ty().kind() { let suggested_ty = match mutability { - hir::Mutability::Mut => self.tcx.mk_imm_ref(region, t_type), - hir::Mutability::Not => self.tcx.mk_mut_ref(region, t_type), + hir::Mutability::Mut => Ty::new_imm_ref(self.tcx, region, t_type), + hir::Mutability::Not => Ty::new_mut_ref(self.tcx, region, t_type), }; // Remapping bound vars here @@ -1931,7 +1951,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ), }; - infcx.tcx.mk_fn_ptr(trait_ref.rebind(sig)) + Ty::new_fn_ptr(infcx.tcx, trait_ref.rebind(sig)) } let argument_kind = match expected.skip_binder().self_ty().kind() { @@ -1981,7 +2001,7 @@ 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::Clause(ty::Clause::Trait(trait_pred)) = pred.kind().skip_binder() + && let ty::ClauseKind::Trait(trait_pred) = pred.kind().skip_binder() && self.tcx.is_fn_trait(trait_pred.def_id()) { let expected_self = @@ -1995,7 +2015,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let other_pred = predicates.into_iter() .enumerate() .find(|(other_idx, (pred, _))| match pred.kind().skip_binder() { - ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred)) + ty::ClauseKind::Trait(trait_pred) if self.tcx.is_fn_trait(trait_pred.def_id()) && other_idx != idx // Make sure that the self type matches @@ -2121,7 +2141,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::Clause(ty::Clause::Trait(p)) => (Some(p), Some(p.self_ty())), + ty::PredicateKind::Clause(ty::ClauseKind::Trait(p)) => (Some(p), Some(p.self_ty())), _ => (None, None), }; let mut generator = None; @@ -2643,8 +2663,15 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { | ObligationCauseCode::LetElse | ObligationCauseCode::BinOp { .. } | ObligationCauseCode::AscribeUserTypeProvePredicate(..) - | ObligationCauseCode::RustCall - | ObligationCauseCode::DropImpl => {} + | ObligationCauseCode::DropImpl + | ObligationCauseCode::ConstParam(_) => {} + ObligationCauseCode::RustCall => { + if let Some(pred) = predicate.to_opt_poly_trait_pred() + && Some(pred.def_id()) == self.tcx.lang_items().sized_trait() + { + err.note("argument required to be sized due to `extern \"rust-call\"` ABI"); + } + } ObligationCauseCode::SliceOrArrayElem => { err.note("slice and array elements must have `Sized` type"); } @@ -2697,6 +2724,32 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let msg = format!("required by this bound in `{short_item_name}`"); multispan.push_span_label(span, msg); err.span_note(multispan, descr); + if let ty::PredicateKind::Clause(clause) = predicate.kind().skip_binder() + && let ty::ClauseKind::Trait(trait_pred) = clause + { + let def_id = trait_pred.def_id(); + let visible_item = if let Some(local) = def_id.as_local() { + // Check for local traits being reachable. + let vis = &self.tcx.resolutions(()).effective_visibilities; + // Account for non-`pub` traits in the root of the local crate. + let is_locally_reachable = self.tcx.parent(def_id).is_crate_root(); + vis.is_reachable(local) || is_locally_reachable + } else { + // Check for foreign traits being reachable. + self.tcx.visible_parent_map(()).get(&def_id).is_some() + }; + if let DefKind::Trait = tcx.def_kind(item_def_id) && !visible_item { + // FIXME(estebank): extend this to search for all the types that do + // implement this trait and list them. + err.note(format!( + "`{short_item_name}` is a \"sealed trait\", because to implement \ + it you also need to implelement `{}`, which is not accessible; \ + this is usually done to force you to use one of the provided \ + types that already implement it", + with_no_trimmed_paths!(tcx.def_path_str(def_id)), + )); + } + } } else { err.span_note(tcx.def_span(item_def_id), descr); } @@ -2786,10 +2839,10 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { err.help("unsized locals are gated as an unstable feature"); } } - ObligationCauseCode::SizedArgumentType(sp) => { - if let Some(span) = sp { + ObligationCauseCode::SizedArgumentType(ty_span) => { + if let Some(span) = ty_span { if let ty::PredicateKind::Clause(clause) = predicate.kind().skip_binder() - && let ty::Clause::Trait(trait_pred) = clause + && let ty::ClauseKind::Trait(trait_pred) = clause && let ty::Dynamic(..) = trait_pred.self_ty().kind() { let span = if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) @@ -2958,7 +3011,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { for ty in bound_tys.skip_binder() { with_forced_trimmed_paths!(write!(msg, "`{}`, ", ty).unwrap()); } - err.note(msg.trim_end_matches(", ")) + err.note(msg.trim_end_matches(", ").to_string()) } ty::GeneratorWitnessMIR(def_id, substs) => { use std::fmt::Write; @@ -2972,7 +3025,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let ty = bty.subst(tcx, substs); write!(msg, "`{}`, ", ty).unwrap(); } - err.note(msg.trim_end_matches(", ")) + err.note(msg.trim_end_matches(", ").to_string()) } ty::Generator(def_id, _, _) => { let sp = self.tcx.def_span(def_id); @@ -3171,6 +3224,29 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ) }); } + ObligationCauseCode::TypeAlias(ref nested, span, def_id) => { + // #74711: avoid a stack overflow + ensure_sufficient_stack(|| { + self.note_obligation_cause_code( + body_id, + err, + predicate, + param_env, + nested, + obligated_types, + seen_requirements, + ) + }); + let mut multispan = MultiSpan::from(span); + multispan.push_span_label(span, "required by this bound"); + err.span_note( + multispan, + format!( + "required by a bound on the type alias `{}`", + self.infcx.tcx.item_name(def_id) + ), + ); + } ObligationCauseCode::FunctionArgumentObligation { arg_hir_id, call_hir_id, @@ -3271,7 +3347,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let item_def_id = self.tcx.associated_item_def_ids(future_trait)[0]; // `<T as Future>::Output` let projection_ty = trait_pred.map_bound(|trait_pred| { - self.tcx.mk_projection( + Ty::new_projection( + self.tcx, item_def_id, // Future::Output has no substs [trait_pred.self_ty()], @@ -3425,7 +3502,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { { if let hir::Expr { kind: hir::ExprKind::Block(..), .. } = expr { let expr = expr.peel_blocks(); - let ty = typeck_results.expr_ty_adjusted_opt(expr).unwrap_or(tcx.ty_error_misc()); + let ty = typeck_results.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(tcx,)); let span = expr.span; if Some(span) != err.span.primary_span() { err.span_label( @@ -3450,7 +3527,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { && let where_clauses = self.tcx.predicates_of(def_id).instantiate(self.tcx, node_substs) && let Some(where_pred) = where_clauses.predicates.get(*idx) { - if let Some(where_pred) = where_pred.to_opt_poly_trait_pred() + if let Some(where_pred) = where_pred.as_trait_clause() && let Some(failed_pred) = failed_pred.to_opt_poly_trait_pred() { let where_pred = self.instantiate_binder_with_placeholders(where_pred); @@ -3473,13 +3550,13 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } }) }; - } else if let Some(where_pred) = where_pred.to_opt_poly_projection_pred() + } else if let Some(where_pred) = where_pred.as_projection_clause() && let Some(failed_pred) = failed_pred.to_opt_poly_projection_pred() && let Some(found) = failed_pred.skip_binder().term.ty() { type_diffs = vec![ Sorts(ty::error::ExpectedFound { - expected: self.tcx.mk_alias(ty::Projection, where_pred.skip_binder().projection_ty), + expected: Ty::new_alias(self.tcx,ty::Projection, where_pred.skip_binder().projection_ty), found, }), ]; @@ -3509,15 +3586,93 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { err.replace_span_with(path.ident.span, true); } } - if let Some(Node::Expr(hir::Expr { - kind: - hir::ExprKind::Call(hir::Expr { span, .. }, _) - | hir::ExprKind::MethodCall(hir::PathSegment { ident: Ident { span, .. }, .. }, ..), - .. - })) = hir.find(call_hir_id) + + if let Some(Node::Expr(expr)) = hir.find(call_hir_id) { + if let hir::ExprKind::Call(hir::Expr { span, .. }, _) + | hir::ExprKind::MethodCall( + hir::PathSegment { ident: Ident { span, .. }, .. }, + .., + ) = expr.kind + { + if Some(*span) != err.span.primary_span() { + err.span_label(*span, "required by a bound introduced by this call"); + } + } + + if let hir::ExprKind::MethodCall(_, expr, ..) = expr.kind { + self.suggest_option_method_if_applicable(failed_pred, param_env, err, expr); + } + } + } + + fn suggest_option_method_if_applicable( + &self, + failed_pred: ty::Predicate<'tcx>, + param_env: ty::ParamEnv<'tcx>, + err: &mut Diagnostic, + expr: &hir::Expr<'_>, + ) { + let tcx = self.tcx; + let infcx = self.infcx; + let Some(typeck_results) = self.typeck_results.as_ref() else { return }; + + // Make sure we're dealing with the `Option` type. + let Some(option_ty_adt) = typeck_results.expr_ty_adjusted(expr).ty_adt_def() else { return }; + if !tcx.is_diagnostic_item(sym::Option, option_ty_adt.did()) { + return; + } + + // Given the predicate `fn(&T): FnOnce<(U,)>`, extract `fn(&T)` and `(U,)`, + // then suggest `Option::as_deref(_mut)` if `U` can deref to `T` + if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(ty::TraitPredicate { trait_ref, .. })) + = failed_pred.kind().skip_binder() + && tcx.is_fn_trait(trait_ref.def_id) + && let [self_ty, found_ty] = trait_ref.substs.as_slice() + && let Some(fn_ty) = self_ty.as_type().filter(|ty| ty.is_fn()) + && let fn_sig @ ty::FnSig { + abi: abi::Abi::Rust, + c_variadic: false, + unsafety: hir::Unsafety::Normal, + .. + } = fn_ty.fn_sig(tcx).skip_binder() + + // Extract first param of fn sig with peeled refs, e.g. `fn(&T)` -> `T` + && let Some(&ty::Ref(_, target_ty, needs_mut)) = fn_sig.inputs().first().map(|t| t.kind()) + && !target_ty.has_escaping_bound_vars() + + // Extract first tuple element out of fn trait, e.g. `FnOnce<(U,)>` -> `U` + && let Some(ty::Tuple(tys)) = found_ty.as_type().map(Ty::kind) + && let &[found_ty] = tys.as_slice() + && !found_ty.has_escaping_bound_vars() + + // Extract `<U as Deref>::Target` assoc type and check that it is `T` + && let Some(deref_target_did) = tcx.lang_items().deref_target() + && let projection = Ty::new_projection(tcx,deref_target_did, tcx.mk_substs(&[ty::GenericArg::from(found_ty)])) + && let InferOk { value: deref_target, obligations } = infcx.at(&ObligationCause::dummy(), param_env).normalize(projection) + && obligations.iter().all(|obligation| infcx.predicate_must_hold_modulo_regions(obligation)) + && infcx.can_eq(param_env, deref_target, target_ty) { - if Some(*span) != err.span.primary_span() { - err.span_label(*span, "required by a bound introduced by this call"); + let help = if let hir::Mutability::Mut = needs_mut + && let Some(deref_mut_did) = tcx.lang_items().deref_mut_trait() + && infcx + .type_implements_trait(deref_mut_did, iter::once(found_ty), param_env) + .must_apply_modulo_regions() + { + Some(("call `Option::as_deref_mut()` first", ".as_deref_mut()")) + } else if let hir::Mutability::Not = needs_mut { + Some(("call `Option::as_deref()` first", ".as_deref()")) + } else { + None + }; + + if let Some((msg, sugg)) = help { + err.span_suggestion_with_style( + expr.span.shrink_to_hi(), + msg, + sugg, + Applicability::MaybeIncorrect, + SuggestionStyle::ShowAlways + ); } } } @@ -3539,7 +3694,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let mut assocs = vec![]; let mut expr = expr; let mut prev_ty = self.resolve_vars_if_possible( - typeck_results.expr_ty_adjusted_opt(expr).unwrap_or(tcx.ty_error_misc()), + typeck_results.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(tcx)), ); while let hir::ExprKind::MethodCall(_path_segment, rcvr_expr, _args, span) = expr.kind { // Point at every method call in the chain with the resulting type. @@ -3550,7 +3705,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { self.probe_assoc_types_at_expr(&type_diffs, span, prev_ty, expr.hir_id, param_env); assocs.push(assocs_in_this_method); prev_ty = self.resolve_vars_if_possible( - typeck_results.expr_ty_adjusted_opt(expr).unwrap_or(tcx.ty_error_misc()), + typeck_results.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(tcx)), ); if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind @@ -3568,7 +3723,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { if let hir::Node::Param(param) = parent { // ...and it is a an fn argument. let prev_ty = self.resolve_vars_if_possible( - typeck_results.node_type_opt(param.hir_id).unwrap_or(tcx.ty_error_misc()), + typeck_results.node_type_opt(param.hir_id).unwrap_or(Ty::new_misc_error(tcx,)), ); let assocs_in_this_method = self.probe_assoc_types_at_expr(&type_diffs, param.ty_span, prev_ty, param.hir_id, param_env); if assocs_in_this_method.iter().any(|a| a.is_some()) { @@ -3671,7 +3826,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { body_id: hir::HirId, param_env: ty::ParamEnv<'tcx>, ) -> Vec<Option<(Span, (DefId, Ty<'tcx>))>> { - let ocx = ObligationCtxt::new_in_snapshot(self.infcx); + let ocx = ObligationCtxt::new(self.infcx); let mut assocs_in_this_method = Vec::with_capacity(type_diffs.len()); for diff in type_diffs { let Sorts(expected_found) = diff else { continue; }; @@ -3698,12 +3853,12 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // in. For example, this would be what `Iterator::Item` is here. let ty_var = self.infcx.next_ty_var(origin); // This corresponds to `<ExprTy as Iterator>::Item = _`. - let projection = ty::Binder::dummy(ty::PredicateKind::Clause(ty::Clause::Projection( - ty::ProjectionPredicate { + let projection = ty::Binder::dummy(ty::PredicateKind::Clause( + ty::ClauseKind::Projection(ty::ProjectionPredicate { projection_ty: self.tcx.mk_alias_ty(proj.def_id, substs), term: ty_var.into(), - }, - ))); + }), + )); let body_def_id = self.tcx.hir().enclosing_body_owner(body_id); // Add `<ExprTy as Iterator>::Item = _` obligation. ocx.register_obligation(Obligation::misc( diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 2f85c32b5..cf9d9315f 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -4,7 +4,7 @@ use rustc_data_structures::obligation_forest::{Error, ForestObligation, Outcome} use rustc_data_structures::obligation_forest::{ObligationForest, ObligationProcessor}; use rustc_infer::infer::DefineOpaqueTypes; use rustc_infer::traits::ProjectionCacheKey; -use rustc_infer::traits::{SelectionError, TraitEngine, TraitObligation}; +use rustc_infer::traits::{PolyTraitObligation, SelectionError, TraitEngine}; use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::ty::abstract_const::NotConstEvaluatable; use rustc_middle::ty::error::{ExpectedFound, TypeError}; @@ -50,20 +50,15 @@ impl<'tcx> ForestObligation for PendingPredicateObligation<'tcx> { /// method `select_all_or_error` can be used to report any remaining /// ambiguous cases as errors. pub struct FulfillmentContext<'tcx> { - // A list of all obligations that have been registered with this - // fulfillment context. + /// A list of all obligations that have been registered with this + /// fulfillment context. predicates: ObligationForest<PendingPredicateObligation<'tcx>>, - // Is it OK to register obligations into this infcx inside - // an infcx snapshot? - // - // The "primary fulfillment" in many cases in typeck lives - // outside of any snapshot, so any use of it inside a snapshot - // will lead to trouble and therefore is checked against, but - // other fulfillment contexts sometimes do live inside of - // a snapshot (they don't *straddle* a snapshot, so there - // is no trouble there). - usable_in_snapshot: bool, + /// The snapshot in which this context was created. Using the context + /// outside of this snapshot leads to subtle bugs if the snapshot + /// gets rolled back. Because of this we explicitly check that we only + /// use the context in exactly this snapshot. + usable_in_snapshot: usize, } #[derive(Clone, Debug)] @@ -80,18 +75,17 @@ pub struct PendingPredicateObligation<'tcx> { #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] static_assert_size!(PendingPredicateObligation<'_>, 72); -impl<'a, 'tcx> FulfillmentContext<'tcx> { +impl<'tcx> FulfillmentContext<'tcx> { /// Creates a new fulfillment context. - pub(super) fn new() -> FulfillmentContext<'tcx> { - FulfillmentContext { predicates: ObligationForest::new(), usable_in_snapshot: false } - } - - pub(super) fn new_in_snapshot() -> FulfillmentContext<'tcx> { - FulfillmentContext { predicates: ObligationForest::new(), usable_in_snapshot: true } + pub(super) fn new(infcx: &InferCtxt<'tcx>) -> FulfillmentContext<'tcx> { + FulfillmentContext { + predicates: ObligationForest::new(), + usable_in_snapshot: infcx.num_open_snapshots(), + } } /// Attempts to select obligations using `selcx`. - fn select(&mut self, selcx: SelectionContext<'a, 'tcx>) -> Vec<FulfillmentError<'tcx>> { + fn select(&mut self, selcx: SelectionContext<'_, 'tcx>) -> Vec<FulfillmentError<'tcx>> { let span = debug_span!("select", obligation_forest_size = ?self.predicates.len()); let _enter = span.enter(); @@ -116,19 +110,19 @@ impl<'a, 'tcx> FulfillmentContext<'tcx> { } impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> { + #[inline] fn register_predicate_obligation( &mut self, infcx: &InferCtxt<'tcx>, obligation: PredicateObligation<'tcx>, ) { + assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots()); // this helps to reduce duplicate errors, as well as making // debug output much nicer to read and so on. let obligation = infcx.resolve_vars_if_possible(obligation); debug!(?obligation, "register_predicate_obligation"); - assert!(!infcx.is_in_snapshot() || self.usable_in_snapshot); - self.predicates .register_obligation(PendingPredicateObligation { obligation, stalled_on: vec![] }); } @@ -332,7 +326,7 @@ 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::Clause(ty::Clause::Trait(trait_ref)) => { + ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_ref)) => { let trait_obligation = obligation.with(infcx.tcx, binder.rebind(trait_ref)); self.process_trait_obligation( @@ -341,7 +335,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { &mut pending_obligation.stalled_on, ) } - ty::PredicateKind::Clause(ty::Clause::Projection(data)) => { + ty::PredicateKind::Clause(ty::ClauseKind::Projection(data)) => { let project_obligation = obligation.with(infcx.tcx, binder.rebind(data)); self.process_projection_obligation( @@ -350,30 +344,27 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { &mut pending_obligation.stalled_on, ) } - ty::PredicateKind::Clause(ty::Clause::RegionOutlives(_)) - | ty::PredicateKind::Clause(ty::Clause::TypeOutlives(_)) - | ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..)) - | ty::PredicateKind::WellFormed(_) + ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(_)) + | ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(_)) + | ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(..)) + | ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(_)) | ty::PredicateKind::ObjectSafe(_) | ty::PredicateKind::ClosureKind(..) | ty::PredicateKind::Subtype(_) | ty::PredicateKind::Coerce(_) - | ty::PredicateKind::ConstEvaluatable(..) + | ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..)) | ty::PredicateKind::ConstEquate(..) => { let pred = ty::Binder::dummy(infcx.instantiate_binder_with_placeholders(binder)); 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") - } ty::PredicateKind::AliasRelate(..) => { bug!("AliasRelate is only used for new solver") } }, Some(pred) => match pred { - ty::PredicateKind::Clause(ty::Clause::Trait(data)) => { + ty::PredicateKind::Clause(ty::ClauseKind::Trait(data)) => { let trait_obligation = obligation.with(infcx.tcx, Binder::dummy(data)); self.process_trait_obligation( @@ -383,7 +374,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { ) } - ty::PredicateKind::Clause(ty::Clause::RegionOutlives(data)) => { + ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(data)) => { if infcx.considering_regions { infcx.region_outlives_predicate(&obligation.cause, Binder::dummy(data)); } @@ -391,7 +382,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { ProcessResult::Changed(vec![]) } - ty::PredicateKind::Clause(ty::Clause::TypeOutlives(ty::OutlivesPredicate( + ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate( t_a, r_b, ))) => { @@ -401,7 +392,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { ProcessResult::Changed(vec![]) } - ty::PredicateKind::Clause(ty::Clause::Projection(ref data)) => { + ty::PredicateKind::Clause(ty::ClauseKind::Projection(ref data)) => { let project_obligation = obligation.with(infcx.tcx, Binder::dummy(*data)); self.process_projection_obligation( @@ -432,7 +423,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { } } - ty::PredicateKind::WellFormed(arg) => { + ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => { match wf::obligations( self.selcx.infcx, obligation.param_env, @@ -497,7 +488,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { } } - ty::PredicateKind::ConstEvaluatable(uv) => { + ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(uv)) => { match const_evaluatable::is_const_evaluatable( self.selcx.infcx, uv, @@ -537,7 +528,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { debug!("equating consts:\nc1= {:?}\nc2= {:?}", c1, c2); use rustc_hir::def::DefKind; - use ty::ConstKind::Unevaluated; + use ty::Unevaluated; match (c1.kind(), c2.kind()) { (Unevaluated(a), Unevaluated(b)) if a.def == b.def && tcx.def_kind(a.def) == DefKind::AssocConst => @@ -633,13 +624,10 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { } } ty::PredicateKind::Ambiguous => ProcessResult::Unchanged, - ty::PredicateKind::TypeWellFormedFromEnv(..) => { - bug!("TypeWellFormedFromEnv is only used for Chalk") - } ty::PredicateKind::AliasRelate(..) => { bug!("AliasRelate is only used for new solver") } - ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(ct, ty)) => { + ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, ty)) => { match self.selcx.infcx.at(&obligation.cause, obligation.param_env).eq( DefineOpaqueTypes::No, ct.ty(), @@ -679,7 +667,7 @@ impl<'a, 'tcx> FulfillProcessor<'a, 'tcx> { fn process_trait_obligation( &mut self, obligation: &PredicateObligation<'tcx>, - trait_obligation: TraitObligation<'tcx>, + trait_obligation: PolyTraitObligation<'tcx>, stalled_on: &mut Vec<TyOrConstInferVar<'tcx>>, ) -> ProcessResult<PendingPredicateObligation<'tcx>, FulfillmentErrorCode<'tcx>> { let infcx = self.selcx.infcx; @@ -695,7 +683,7 @@ impl<'a, 'tcx> FulfillProcessor<'a, 'tcx> { } } - match self.selcx.select(&trait_obligation) { + match self.selcx.poly_select(&trait_obligation) { Ok(Some(impl_source)) => { debug!("selecting trait at depth {} yielded Ok(Some)", obligation.recursion_depth); ProcessResult::Changed(mk_pending(impl_source.nested_obligations())) diff --git a/compiler/rustc_trait_selection/src/traits/misc.rs b/compiler/rustc_trait_selection/src/traits/misc.rs index 2210ef975..e9cfd63e2 100644 --- a/compiler/rustc_trait_selection/src/traits/misc.rs +++ b/compiler/rustc_trait_selection/src/traits/misc.rs @@ -100,7 +100,8 @@ pub fn type_allowed_to_implement_const_param_ty<'tcx>( | ty::Str | ty::Array(..) | ty::Slice(_) - | ty::Ref(.., hir::Mutability::Not) => return Ok(()), + | ty::Ref(.., hir::Mutability::Not) + | ty::Tuple(_) => return Ok(()), &ty::Adt(adt, substs) => (adt, substs), diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index f265230ff..1af8323b6 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -3,7 +3,6 @@ //! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/resolution.html pub mod auto_trait; -mod chalk_fulfill; pub(crate) mod coherence; pub mod const_evaluatable; mod engine; @@ -12,14 +11,16 @@ mod fulfill; pub mod misc; mod object_safety; pub mod outlives_bounds; -mod project; +pub mod project; pub mod query; +#[cfg_attr(not(bootstrap), allow(hidden_glob_reexports))] mod select; mod specialize; mod structural_match; mod structural_normalize; +#[cfg_attr(not(bootstrap), allow(hidden_glob_reexports))] mod util; -mod vtable; +pub mod vtable; pub mod wf; use crate::infer::outlives::env::OutlivesEnvironment; @@ -30,7 +31,7 @@ use rustc_errors::ErrorGuaranteed; use rustc_middle::query::Providers; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::visit::{TypeVisitable, TypeVisitableExt}; -use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt, TypeSuperVisitable}; +use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt, TypeFolder, TypeSuperVisitable}; use rustc_middle::ty::{InternalSubsts, SubstsRef}; use rustc_span::def_id::DefId; use rustc_span::Span; @@ -38,6 +39,8 @@ use rustc_span::Span; use std::fmt::Debug; use std::ops::ControlFlow; +pub(crate) use self::project::{needs_normalization, BoundVarReplacer, PlaceholderReplacer}; + pub use self::FulfillmentErrorCode::*; pub use self::ImplSource::*; pub use self::ObligationCauseCode::*; @@ -60,19 +63,15 @@ pub use self::specialize::specialization_graph::FutureCompatOverlapErrorKind; pub use self::specialize::{ specialization_graph, translate_substs, translate_substs_with_cause, OverlapError, }; -pub use self::structural_match::{ - search_for_adt_const_param_violation, search_for_structural_match_violation, -}; +pub use self::structural_match::search_for_structural_match_violation; pub use self::structural_normalize::StructurallyNormalizeExt; pub use self::util::elaborate; -pub use self::util::{expand_trait_aliases, TraitAliasExpander}; -pub use self::util::{get_vtable_index_of_object_method, impl_item_is_final, upcast_choices}; pub use self::util::{ - supertrait_def_ids, supertraits, transitive_bounds, transitive_bounds_that_define_assoc_item, - SupertraitDefIds, + check_substs_compatible, supertrait_def_ids, supertraits, transitive_bounds, + transitive_bounds_that_define_assoc_item, SupertraitDefIds, }; - -pub use self::chalk_fulfill::FulfillmentContext as ChalkFulfillmentContext; +pub use self::util::{expand_trait_aliases, TraitAliasExpander}; +pub use self::util::{get_vtable_index_of_object_method, impl_item_is_final, upcast_choices}; pub use rustc_infer::traits::*; @@ -114,11 +113,11 @@ pub fn predicates_for_generics<'tcx>( param_env: ty::ParamEnv<'tcx>, generic_bounds: ty::InstantiatedPredicates<'tcx>, ) -> impl Iterator<Item = PredicateObligation<'tcx>> { - generic_bounds.into_iter().enumerate().map(move |(idx, (predicate, span))| Obligation { + generic_bounds.into_iter().enumerate().map(move |(idx, (clause, span))| Obligation { cause: cause(idx, span), recursion_depth: 0, param_env, - predicate, + predicate: clause.as_predicate(), }) } @@ -161,7 +160,7 @@ fn pred_known_to_hold_modulo_regions<'tcx>( // the we do no inference in the process of checking this obligation. let goal = infcx.resolve_vars_if_possible((obligation.predicate, obligation.param_env)); infcx.probe(|_| { - let ocx = ObligationCtxt::new_in_snapshot(infcx); + let ocx = ObligationCtxt::new(infcx); ocx.register_obligation(obligation); let errors = ocx.select_all_or_error(); @@ -185,8 +184,8 @@ fn do_normalize_predicates<'tcx>( tcx: TyCtxt<'tcx>, cause: ObligationCause<'tcx>, elaborated_env: ty::ParamEnv<'tcx>, - predicates: Vec<ty::Predicate<'tcx>>, -) -> Result<Vec<ty::Predicate<'tcx>>, ErrorGuaranteed> { + predicates: Vec<ty::Clause<'tcx>>, +) -> Result<Vec<ty::Clause<'tcx>>, ErrorGuaranteed> { let span = cause.span; // FIXME. We should really... do something with these region // obligations. But this call just continues the older @@ -270,13 +269,67 @@ pub fn normalize_param_env_or_error<'tcx>( // parameter environments once for every fn as it goes, // and errors will get reported then; so outside of type inference we // can be sure that no errors should occur. - let mut predicates: Vec<_> = - util::elaborate(tcx, unnormalized_env.caller_bounds().into_iter()).collect(); + let mut predicates: Vec<_> = util::elaborate( + tcx, + unnormalized_env.caller_bounds().into_iter().map(|predicate| { + if tcx.features().generic_const_exprs { + return predicate; + } + + struct ConstNormalizer<'tcx>(TyCtxt<'tcx>); + + impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ConstNormalizer<'tcx> { + fn interner(&self) -> TyCtxt<'tcx> { + self.0 + } + + fn fold_const(&mut self, c: ty::Const<'tcx>) -> ty::Const<'tcx> { + // While it is pretty sus to be evaluating things with an empty param env, it + // should actually be okay since without `feature(generic_const_exprs)` the only + // const arguments that have a non-empty param env are array repeat counts. These + // do not appear in the type system though. + c.eval(self.0, ty::ParamEnv::empty()) + } + } + + // This whole normalization step is a hack to work around the fact that + // `normalize_param_env_or_error` is fundamentally broken from using an + // unnormalized param env with a trait solver that expects the param env + // to be normalized. + // + // When normalizing the param env we can end up evaluating obligations + // that have been normalized but can only be proven via a where clause + // which is still in its unnormalized form. example: + // + // Attempting to prove `T: Trait<<u8 as Identity>::Assoc>` in a param env + // with a `T: Trait<<u8 as Identity>::Assoc>` where clause will fail because + // we first normalize obligations before proving them so we end up proving + // `T: Trait<u8>`. Since lazy normalization is not implemented equating `u8` + // with `<u8 as Identity>::Assoc` fails outright so we incorrectly believe that + // we cannot prove `T: Trait<u8>`. + // + // The same thing is true for const generics- attempting to prove + // `T: Trait<ConstKind::Unevaluated(...)>` with the same thing as a where clauses + // will fail. After normalization we may be attempting to prove `T: Trait<4>` with + // the unnormalized where clause `T: Trait<ConstKind::Unevaluated(...)>`. In order + // for the obligation to hold `4` must be equal to `ConstKind::Unevaluated(...)` + // but as we do not have lazy norm implemented, equating the two consts fails outright. + // + // Ideally we would not normalize consts here at all but it is required for backwards + // compatibility. Eventually when lazy norm is implemented this can just be removed. + // We do not normalize types here as there is no backwards compatibility requirement + // for us to do so. + // + // FIXME(-Ztrait-solver=next): remove this hack since we have deferred projection equality + predicate.fold_with(&mut ConstNormalizer(tcx)) + }), + ) + .collect(); debug!("normalize_param_env_or_error: elaborated-predicates={:?}", predicates); let elaborated_env = ty::ParamEnv::new( - tcx.mk_predicates(&predicates), + tcx.mk_clauses(&predicates), unnormalized_env.reveal(), unnormalized_env.constness(), ); @@ -300,11 +353,8 @@ pub fn normalize_param_env_or_error<'tcx>( // This works fairly well because trait matching does not actually care about param-env // TypeOutlives predicates - these are normally used by regionck. let outlives_predicates: Vec<_> = predicates - .drain_filter(|predicate| { - matches!( - predicate.kind().skip_binder(), - ty::PredicateKind::Clause(ty::Clause::TypeOutlives(..)) - ) + .extract_if(|predicate| { + matches!(predicate.kind().skip_binder(), ty::ClauseKind::TypeOutlives(..)) }) .collect(); @@ -330,7 +380,7 @@ pub fn normalize_param_env_or_error<'tcx>( // predicates here anyway. Keeping them here anyway because it seems safer. let outlives_env = non_outlives_predicates.iter().chain(&outlives_predicates).cloned(); let outlives_env = ty::ParamEnv::new( - tcx.mk_predicates_from_iter(outlives_env), + tcx.mk_clauses_from_iter(outlives_env), unnormalized_env.reveal(), unnormalized_env.constness(), ); @@ -350,13 +400,18 @@ pub fn normalize_param_env_or_error<'tcx>( predicates.extend(outlives_predicates); debug!("normalize_param_env_or_error: final predicates={:?}", predicates); ty::ParamEnv::new( - tcx.mk_predicates(&predicates), + tcx.mk_clauses(&predicates), unnormalized_env.reveal(), unnormalized_env.constness(), ) } -/// Normalize a type and process all resulting obligations, returning any errors +/// Normalize a type and process all resulting obligations, returning any errors. +/// +/// FIXME(-Ztrait-solver=next): This should be replaced by `At::deeply_normalize` +/// which has the same behavior with the new solver. Because using a separate +/// fulfillment context worsens caching in the old solver, `At::deeply_normalize` +/// is still lazy with the old solver as it otherwise negatively impacts perf. #[instrument(skip_all)] pub fn fully_normalize<'tcx, T>( infcx: &InferCtxt<'tcx>, @@ -385,10 +440,7 @@ where /// Normalizes the predicates and checks whether they hold in an empty environment. If this /// returns true, then either normalize encountered an error or one of the predicates did not /// hold. Used when creating vtables to check for unsatisfiable methods. -pub fn impossible_predicates<'tcx>( - tcx: TyCtxt<'tcx>, - predicates: Vec<ty::Predicate<'tcx>>, -) -> bool { +pub fn impossible_predicates<'tcx>(tcx: TyCtxt<'tcx>, predicates: Vec<ty::Clause<'tcx>>) -> bool { debug!("impossible_predicates(predicates={:?})", predicates); let infcx = tcx.infer_ctxt().build(); @@ -485,7 +537,7 @@ fn is_impossible_method(tcx: TyCtxt<'_>, (impl_def_id, trait_item_def_id): (DefI tcx, ObligationCause::dummy_with_span(*span), param_env, - ty::EarlyBinder(*pred).subst(tcx, impl_trait_ref.substs), + ty::EarlyBinder::bind(*pred).subst(tcx, impl_trait_ref.substs), ) }) }); diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs index c81bf6ebc..c31944c16 100644 --- a/compiler/rustc_trait_selection/src/traits/object_safety.rs +++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs @@ -101,7 +101,7 @@ pub fn is_vtable_safe_method(tcx: TyCtxt<'_>, trait_def_id: DefId, method: ty::A debug_assert!(tcx.generics_of(trait_def_id).has_self); debug!("is_vtable_safe_method({:?}, {:?})", trait_def_id, method); // Any method that has a `Self: Sized` bound cannot be called. - if generics_require_sized_self(tcx, method.def_id) { + if tcx.generics_require_sized_self(method.def_id) { return false; } @@ -115,15 +115,11 @@ fn object_safety_violations_for_trait( tcx: TyCtxt<'_>, trait_def_id: DefId, ) -> Vec<ObjectSafetyViolation> { - // Check methods for violations. + // Check assoc items for violations. let mut violations: Vec<_> = tcx .associated_items(trait_def_id) .in_definition_order() - .filter(|item| item.kind == ty::AssocKind::Fn) - .filter_map(|&item| { - object_safety_violation_for_method(tcx, trait_def_id, item) - .map(|(code, span)| ObjectSafetyViolation::Method(item.name, code, span)) - }) + .filter_map(|&item| object_safety_violation_for_assoc_item(tcx, trait_def_id, item)) .collect(); // Check the trait itself. @@ -145,30 +141,6 @@ fn object_safety_violations_for_trait( violations.push(ObjectSafetyViolation::SupertraitNonLifetimeBinder(spans)); } - violations.extend( - tcx.associated_items(trait_def_id) - .in_definition_order() - .filter(|item| item.kind == ty::AssocKind::Const) - .map(|item| { - let ident = item.ident(tcx); - ObjectSafetyViolation::AssocConst(ident.name, ident.span) - }), - ); - - if !tcx.features().generic_associated_types_extended { - violations.extend( - tcx.associated_items(trait_def_id) - .in_definition_order() - .filter(|item| item.kind == ty::AssocKind::Type) - .filter(|item| !tcx.generics_of(item.def_id).params.is_empty()) - .filter(|item| tcx.opt_rpitit_info(item.def_id).is_none()) - .map(|item| { - let ident = item.ident(tcx); - ObjectSafetyViolation::GAT(ident.name, ident.span) - }), - ); - } - debug!( "object_safety_violations_for_trait(trait_def_id={:?}) = {:?}", trait_def_id, violations @@ -299,22 +271,22 @@ fn bounds_reference_self(tcx: TyCtxt<'_>, trait_def_id: DefId) -> SmallVec<[Span .in_definition_order() .filter(|item| item.kind == ty::AssocKind::Type) .flat_map(|item| tcx.explicit_item_bounds(item.def_id).subst_identity_iter_copied()) - .filter_map(|pred_span| predicate_references_self(tcx, pred_span)) + .filter_map(|c| predicate_references_self(tcx, c)) .collect() } fn predicate_references_self<'tcx>( tcx: TyCtxt<'tcx>, - (predicate, sp): (ty::Predicate<'tcx>, Span), + (predicate, sp): (ty::Clause<'tcx>, Span), ) -> Option<Span> { 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::Clause(ty::Clause::Trait(ref data)) => { + ty::ClauseKind::Trait(ref data) => { // In the case of a trait predicate, we can skip the "self" type. data.trait_ref.substs[1..].iter().any(has_self_ty).then_some(sp) } - ty::PredicateKind::Clause(ty::Clause::Projection(ref data)) => { + ty::ClauseKind::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 @@ -332,24 +304,14 @@ fn predicate_references_self<'tcx>( // possible alternatives. data.projection_ty.substs[1..].iter().any(has_self_ty).then_some(sp) } - ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(_ct, ty)) => { - has_self_ty(&ty.into()).then_some(sp) - } - - ty::PredicateKind::AliasRelate(..) => bug!("`AliasRelate` not allowed as assumption"), + ty::ClauseKind::ConstArgHasType(_ct, ty) => has_self_ty(&ty.into()).then_some(sp), - ty::PredicateKind::WellFormed(..) - | ty::PredicateKind::ObjectSafe(..) - | ty::PredicateKind::Clause(ty::Clause::TypeOutlives(..)) - | ty::PredicateKind::Clause(ty::Clause::RegionOutlives(..)) - | ty::PredicateKind::ClosureKind(..) - | ty::PredicateKind::Subtype(..) - | ty::PredicateKind::Coerce(..) + ty::ClauseKind::WellFormed(..) + | ty::ClauseKind::TypeOutlives(..) + | ty::ClauseKind::RegionOutlives(..) // FIXME(generic_const_exprs): this can mention `Self` - | ty::PredicateKind::ConstEvaluatable(..) - | ty::PredicateKind::ConstEquate(..) - | ty::PredicateKind::Ambiguous - | ty::PredicateKind::TypeWellFormedFromEnv(..) => None, + | ty::ClauseKind::ConstEvaluatable(..) + => None, } } @@ -369,7 +331,7 @@ fn super_predicates_have_non_lifetime_binders( } fn trait_has_sized_self(tcx: TyCtxt<'_>, trait_def_id: DefId) -> bool { - generics_require_sized_self(tcx, trait_def_id) + tcx.generics_require_sized_self(trait_def_id) } fn generics_require_sized_self(tcx: TyCtxt<'_>, def_id: DefId) -> bool { @@ -381,54 +343,66 @@ fn generics_require_sized_self(tcx: TyCtxt<'_>, def_id: DefId) -> bool { let predicates = tcx.predicates_of(def_id); let predicates = predicates.instantiate_identity(tcx).predicates; elaborate(tcx, predicates.into_iter()).any(|pred| match pred.kind().skip_binder() { - ty::PredicateKind::Clause(ty::Clause::Trait(ref trait_pred)) => { + ty::ClauseKind::Trait(ref trait_pred) => { trait_pred.def_id() == sized_def_id && trait_pred.self_ty().is_param(0) } - ty::PredicateKind::Clause(ty::Clause::Projection(..)) - | ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..)) - | ty::PredicateKind::Subtype(..) - | ty::PredicateKind::Coerce(..) - | ty::PredicateKind::Clause(ty::Clause::RegionOutlives(..)) - | ty::PredicateKind::WellFormed(..) - | ty::PredicateKind::ObjectSafe(..) - | ty::PredicateKind::ClosureKind(..) - | ty::PredicateKind::Clause(ty::Clause::TypeOutlives(..)) - | ty::PredicateKind::ConstEvaluatable(..) - | ty::PredicateKind::ConstEquate(..) - | ty::PredicateKind::AliasRelate(..) - | ty::PredicateKind::Ambiguous - | ty::PredicateKind::TypeWellFormedFromEnv(..) => false, + ty::ClauseKind::RegionOutlives(_) + | ty::ClauseKind::TypeOutlives(_) + | ty::ClauseKind::Projection(_) + | ty::ClauseKind::ConstArgHasType(_, _) + | ty::ClauseKind::WellFormed(_) + | ty::ClauseKind::ConstEvaluatable(_) => false, }) } -/// Returns `Some(_)` if this method makes the containing trait not object safe. -fn object_safety_violation_for_method( +/// Returns `Some(_)` if this item makes the containing trait not object safe. +#[instrument(level = "debug", skip(tcx), ret)] +fn object_safety_violation_for_assoc_item( tcx: TyCtxt<'_>, trait_def_id: DefId, - method: ty::AssocItem, -) -> Option<(MethodViolationCode, Span)> { - debug!("object_safety_violation_for_method({:?}, {:?})", trait_def_id, method); - // Any method that has a `Self : Sized` requisite is otherwise + item: ty::AssocItem, +) -> Option<ObjectSafetyViolation> { + // Any item that has a `Self : Sized` requisite is otherwise // exempt from the regulations. - if generics_require_sized_self(tcx, method.def_id) { + if tcx.generics_require_sized_self(item.def_id) { return None; } - let violation = virtual_call_violation_for_method(tcx, trait_def_id, method); - // Get an accurate span depending on the violation. - violation.map(|v| { - let node = tcx.hir().get_if_local(method.def_id); - 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()) + match item.kind { + // Associated consts are never object safe, as they can't have `where` bounds yet at all, + // and associated const bounds in trait objects aren't a thing yet either. + ty::AssocKind::Const => { + Some(ObjectSafetyViolation::AssocConst(item.name, item.ident(tcx).span)) + } + ty::AssocKind::Fn => virtual_call_violation_for_method(tcx, trait_def_id, item).map(|v| { + let node = tcx.hir().get_if_local(item.def_id); + // Get an accurate span depending on the violation. + 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(item.ident(tcx).span, |decl| decl.output.span()) + } + _ => item.ident(tcx).span, + }; + + ObjectSafetyViolation::Method(item.name, v, span) + }), + // Associated types can only be object safe if they have `Self: Sized` bounds. + ty::AssocKind::Type => { + if !tcx.features().generic_associated_types_extended + && !tcx.generics_of(item.def_id).params.is_empty() + && item.opt_rpitit_info.is_none() + { + Some(ObjectSafetyViolation::GAT(item.name, item.ident(tcx).span)) + } else { + // We will permit associated types if they are explicitly mentioned in the trait object. + // We can't check this here, as here we only check if it is guaranteed to not be possible. + None } - _ => method.ident(tcx).span, - }; - (v, span) - }) + } + } } /// Returns `Some(_)` if this method cannot be called on a trait @@ -526,7 +500,7 @@ fn virtual_call_violation_for_method<'tcx>( // #78372 tcx.sess.delay_span_bug( tcx.def_span(method.def_id), - format!("error: {}\n while computing layout for type {:?}", err, ty), + format!("error: {err}\n while computing layout for type {ty:?}"), ); None } @@ -535,7 +509,7 @@ fn virtual_call_violation_for_method<'tcx>( // e.g., `Rc<()>` let unit_receiver_ty = - receiver_for_self_ty(tcx, receiver_ty, tcx.mk_unit(), method.def_id); + receiver_for_self_ty(tcx, receiver_ty, Ty::new_unit(tcx), method.def_id); match abi_of_ty(unit_receiver_ty) { Some(Abi::Scalar(..)) => (), @@ -583,7 +557,7 @@ fn virtual_call_violation_for_method<'tcx>( // because a trait object can't claim to live longer than the concrete // type. If the lifetime bound holds on dyn Trait then it's guaranteed // to hold as well on the concrete type. - if pred.to_opt_type_outlives().is_some() { + if pred.as_type_outlives_clause().is_some() { return false; } @@ -600,11 +574,11 @@ fn virtual_call_violation_for_method<'tcx>( // only if the autotrait is one of the trait object's trait bounds, like // in `dyn Trait + AutoTrait`. This guarantees that trait objects only // implement auto traits if the underlying type does as well. - if let ty::PredicateKind::Clause(ty::Clause::Trait(ty::TraitPredicate { + if let ty::ClauseKind::Trait(ty::TraitPredicate { trait_ref: pred_trait_ref, constness: ty::BoundConstness::NotConst, polarity: ty::ImplPolarity::Positive, - })) = pred.kind().skip_binder() + }) = pred.kind().skip_binder() && pred_trait_ref.self_ty() == tcx.types.self_param && tcx.trait_is_auto(pred_trait_ref.def_id) { @@ -642,7 +616,7 @@ fn receiver_for_self_ty<'tcx>( if param.index == 0 { self_ty.into() } else { tcx.mk_param_from_def(param) } }); - let result = EarlyBinder(receiver_ty).subst(tcx, substs); + let result = EarlyBinder::bind(receiver_ty).subst(tcx, substs); debug!( "receiver_for_self_ty({:?}, {:?}, {:?}) = {:?}", receiver_ty, self_ty, method_def_id, result @@ -690,7 +664,7 @@ fn object_ty_for_trait<'tcx>( ); debug!(?existential_predicates); - tcx.mk_dynamic(existential_predicates, lifetime, ty::Dyn) + Ty::new_dynamic(tcx, existential_predicates, lifetime, ty::Dyn) } /// Checks the method's receiver (the `self` argument) can be dispatched on when `Self` is a @@ -758,7 +732,7 @@ fn receiver_is_dispatchable<'tcx>( // FIXME(mikeyhew) this is a total hack. Once object_safe_for_dispatch is stabilized, we can // replace this with `dyn Trait` let unsized_self_ty: Ty<'tcx> = - tcx.mk_ty_param(u32::MAX, Symbol::intern("RustaceansAreAwesome")); + Ty::new_param(tcx, u32::MAX, Symbol::intern("RustaceansAreAwesome")); // `Receiver[Self => U]` let unsized_receiver_ty = @@ -772,7 +746,6 @@ fn receiver_is_dispatchable<'tcx>( // Self: Unsize<U> let unsize_predicate = ty::TraitRef::new(tcx, unsize_did, [tcx.types.self_param, unsized_self_ty]) - .without_const() .to_predicate(tcx); // U: Trait<Arg1, ..., ArgN> @@ -789,7 +762,7 @@ fn receiver_is_dispatchable<'tcx>( param_env.caller_bounds().iter().chain([unsize_predicate, trait_predicate]); ty::ParamEnv::new( - tcx.mk_predicates_from_iter(caller_bounds), + tcx.mk_clauses_from_iter(caller_bounds), param_env.reveal(), param_env.constness(), ) @@ -949,5 +922,10 @@ pub fn contains_illegal_impl_trait_in_trait<'tcx>( } pub fn provide(providers: &mut Providers) { - *providers = Providers { object_safety_violations, check_is_object_safe, ..*providers }; + *providers = Providers { + object_safety_violations, + check_is_object_safe, + generics_require_sized_self, + ..*providers + }; } diff --git a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs index f8d056e32..ae6fc7cf8 100644 --- a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs @@ -9,7 +9,7 @@ use rustc_span::def_id::LocalDefId; pub use rustc_middle::traits::query::OutlivesBound; -type Bounds<'a, 'tcx: 'a> = impl Iterator<Item = OutlivesBound<'tcx>> + 'a; +pub type Bounds<'a, 'tcx: 'a> = impl Iterator<Item = OutlivesBound<'tcx>> + 'a; pub trait InferCtxtExt<'a, 'tcx> { fn implied_outlives_bounds( &self, @@ -57,16 +57,12 @@ impl<'a, 'tcx: 'a> InferCtxtExt<'a, 'tcx> for InferCtxt<'tcx> { let ty = OpportunisticRegionResolver::new(self).fold_ty(ty); // We do not expect existential variables in implied bounds. - // We may however encounter unconstrained lifetime variables in invalid - // code. See #110161 for context. + // We may however encounter unconstrained lifetime variables + // in very rare cases. + // + // See `ui/implied-bounds/implied-bounds-unconstrained-2.rs` for + // an example. assert!(!ty.has_non_region_infer()); - if ty.has_infer() { - self.tcx.sess.delay_span_bug( - self.tcx.def_span(body_id), - "skipped implied_outlives_bounds due to unconstrained lifetimes", - ); - return vec![]; - } let mut canonical_var_values = OriginalQueryValues::default(); let canonical_ty = diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 510698971..a10bca31f 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -1,8 +1,10 @@ //! Code for projecting associated types out of trait references. +use super::check_substs_compatible; use super::specialization_graph; use super::translate_substs; use super::util; +use super::ImplSourceUserDefinedData; use super::MismatchedProjectionTypes; use super::Obligation; use super::ObligationCause; @@ -10,10 +12,6 @@ use super::PredicateObligation; use super::Selection; use super::SelectionContext; use super::SelectionError; -use super::{ - ImplSourceClosureData, ImplSourceFnPointerData, ImplSourceFutureData, ImplSourceGeneratorData, - ImplSourceUserDefinedData, -}; use super::{Normalized, NormalizedTy, ProjectionCacheEntry, ProjectionCacheKey}; use crate::errors::InherentProjectionNormalizationOverflow; @@ -30,7 +28,9 @@ use rustc_hir::lang_items::LangItem; use rustc_infer::infer::at::At; use rustc_infer::infer::resolve::OpportunisticRegionResolver; use rustc_infer::infer::DefineOpaqueTypes; -use rustc_infer::traits::ImplSourceBuiltinData; +use rustc_infer::traits::FulfillmentError; +use rustc_infer::traits::ObligationCauseCode; +use rustc_infer::traits::TraitEngine; use rustc_middle::traits::select::OverflowError; use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; use rustc_middle::ty::visit::{MaxUniverse, TypeVisitable, TypeVisitableExt}; @@ -55,14 +55,55 @@ pub trait NormalizeExt<'tcx> { /// This normalization should be used when the type contains inference variables or the /// projection may be fallible. fn normalize<T: TypeFoldable<TyCtxt<'tcx>>>(&self, t: T) -> InferOk<'tcx, T>; + + /// Deeply normalizes `value`, replacing all aliases which can by normalized in + /// the current environment. In the new solver this errors in case normalization + /// fails or is ambiguous. This only normalizes opaque types with `Reveal::All`. + /// + /// In the old solver this simply uses `normalizes` and adds the nested obligations + /// to the `fulfill_cx`. This is necessary as we otherwise end up recomputing the + /// same goals in both a temporary and the shared context which negatively impacts + /// performance as these don't share caching. + /// + /// FIXME(-Ztrait-solver=next): This has the same behavior as `traits::fully_normalize` + /// in the new solver, but because of performance reasons, we currently reuse an + /// existing fulfillment context in the old solver. Once we also eagerly prove goals with + /// the old solver or have removed the old solver, remove `traits::fully_normalize` and + /// rename this function to `At::fully_normalize`. + fn deeply_normalize<T: TypeFoldable<TyCtxt<'tcx>>>( + self, + value: T, + fulfill_cx: &mut dyn TraitEngine<'tcx>, + ) -> Result<T, Vec<FulfillmentError<'tcx>>>; } impl<'tcx> NormalizeExt<'tcx> for At<'_, 'tcx> { fn normalize<T: TypeFoldable<TyCtxt<'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 } + if self.infcx.next_trait_solver() { + InferOk { value, obligations: Vec::new() } + } else { + 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 } + } + } + + fn deeply_normalize<T: TypeFoldable<TyCtxt<'tcx>>>( + self, + value: T, + fulfill_cx: &mut dyn TraitEngine<'tcx>, + ) -> Result<T, Vec<FulfillmentError<'tcx>>> { + if self.infcx.next_trait_solver() { + crate::solve::deeply_normalize(self, value) + } else { + let value = self + .normalize(value) + .into_value_registering_obligations(self.infcx, &mut *fulfill_cx); + let errors = fulfill_cx.select_where_possible(self.infcx); + let value = self.infcx.resolve_vars_if_possible(value); + if errors.is_empty() { Ok(value) } else { Err(errors) } + } } } @@ -406,6 +447,7 @@ impl<'a, 'b, 'tcx> AssocTypeNormalizer<'a, 'b, 'tcx> { depth: usize, obligations: &'a mut Vec<PredicateObligation<'tcx>>, ) -> AssocTypeNormalizer<'a, 'b, 'tcx> { + debug_assert!(!selcx.infcx.next_trait_solver()); AssocTypeNormalizer { selcx, param_env, @@ -502,10 +544,7 @@ impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTypeNormalizer<'a, 'b, 'tcx // to make sure we don't forget to fold the substs regardless. match kind { - // This is really important. While we *can* handle this, this has - // severe performance implications for large opaque types with - // late-bound regions. See `issue-88862` benchmark. - ty::Opaque if !data.substs.has_escaping_bound_vars() => { + ty::Opaque => { // Only normalize `impl Trait` outside of type inference, usually in codegen. match self.param_env.reveal() { Reveal::UserFacing => ty.super_fold_with(self), @@ -531,7 +570,6 @@ impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTypeNormalizer<'a, 'b, 'tcx } } } - ty::Opaque => ty.super_fold_with(self), ty::Projection if !data.has_escaping_bound_vars() => { // This branch is *mostly* just an optimization: when we don't @@ -621,6 +659,30 @@ impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTypeNormalizer<'a, 'b, 'tcx ); normalized_ty } + ty::Weak => { + let infcx = self.selcx.infcx; + self.obligations.extend( + infcx + .tcx + .predicates_of(data.def_id) + .instantiate_own(infcx.tcx, data.substs) + .map(|(mut predicate, span)| { + if data.has_escaping_bound_vars() { + (predicate, ..) = BoundVarReplacer::replace_bound_vars( + infcx, + &mut self.universes, + predicate, + ); + } + let mut cause = self.cause.clone(); + cause.map_code(|code| { + ObligationCauseCode::TypeAlias(code, span, data.def_id) + }); + Obligation::new(infcx.tcx, cause, self.param_env, predicate) + }), + ); + infcx.tcx.type_of(data.def_id).subst(infcx.tcx, data.substs).fold_with(self) + } ty::Inherent if !data.has_escaping_bound_vars() => { // This branch is *mostly* just an optimization: when we don't @@ -672,7 +734,9 @@ impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'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() || !needs_normalization(&constant, self.param_env.reveal()) { + if tcx.features().generic_const_exprs + || !needs_normalization(&constant, self.param_env.reveal()) + { constant } else { let constant = constant.super_fold_with(self); @@ -824,7 +888,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for BoundVarReplacer<'_, 'tcx> { let universe = self.universe_for(debruijn); let p = ty::PlaceholderRegion { universe, bound: br }; self.mapped_regions.insert(p, br); - self.infcx.tcx.mk_re_placeholder(p) + ty::Region::new_placeholder(self.infcx.tcx, p) } _ => r, } @@ -842,7 +906,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for BoundVarReplacer<'_, 'tcx> { let universe = self.universe_for(debruijn); let p = ty::PlaceholderType { universe, bound: bound_ty }; self.mapped_types.insert(p, bound_ty); - self.infcx.tcx.mk_placeholder(p) + Ty::new_placeholder(self.infcx.tcx, p) } _ if t.has_vars_bound_at_or_above(self.current_index) => t.super_fold_with(self), _ => t, @@ -861,7 +925,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for BoundVarReplacer<'_, 'tcx> { let universe = self.universe_for(debruijn); let p = ty::PlaceholderConst { universe, bound: bound_const }; self.mapped_consts.insert(p, bound_const); - self.infcx.tcx.mk_const(p, ct.ty()) + ty::Const::new_placeholder(self.infcx.tcx, p, ct.ty()) } _ => ct.super_fold_with(self), } @@ -945,7 +1009,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for PlaceholderReplacer<'_, 'tcx> { let db = ty::DebruijnIndex::from_usize( self.universe_indices.len() - index + self.current_index.as_usize() - 1, ); - self.interner().mk_re_late_bound(db, *replace_var) + ty::Region::new_late_bound(self.interner(), db, *replace_var) } None => r1, } @@ -972,7 +1036,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for PlaceholderReplacer<'_, 'tcx> { let db = ty::DebruijnIndex::from_usize( self.universe_indices.len() - index + self.current_index.as_usize() - 1, ); - self.interner().mk_bound(db, *replace_var) + Ty::new_bound(self.infcx.tcx, db, *replace_var) } None => ty, } @@ -996,7 +1060,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for PlaceholderReplacer<'_, 'tcx> { let db = ty::DebruijnIndex::from_usize( self.universe_indices.len() - index + self.current_index.as_usize() - 1, ); - self.interner().mk_const(ty::ConstKind::Bound(db, *replace_var), ct.ty()) + ty::Const::new_bound(self.infcx.tcx, db, *replace_var, ct.ty()) } None => ct, } @@ -1059,6 +1123,7 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>( obligations: &mut Vec<PredicateObligation<'tcx>>, ) -> Result<Option<Term<'tcx>>, InProgress> { let infcx = selcx.infcx; + debug_assert!(!selcx.infcx.next_trait_solver()); // 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. @@ -1168,11 +1233,11 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>( }; let mut deduped: SsoHashSet<_> = Default::default(); - result.obligations.drain_filter(|projected_obligation| { + result.obligations.retain(|projected_obligation| { if !deduped.insert(projected_obligation.clone()) { - return true; + return false; } - false + true }); if use_cache { @@ -1375,7 +1440,7 @@ struct Progress<'tcx> { impl<'tcx> Progress<'tcx> { fn error(tcx: TyCtxt<'tcx>, guar: ErrorGuaranteed) -> Self { - Progress { term: tcx.ty_error(guar).into(), obligations: vec![] } + Progress { term: Ty::new_error(tcx, guar).into(), obligations: vec![] } } fn with_addl_obligations(mut self, mut obligations: Vec<PredicateObligation<'tcx>>) -> Self { @@ -1434,19 +1499,22 @@ fn project<'cx, 'tcx>( ProjectionCandidateSet::None => { let tcx = selcx.tcx(); let term = match tcx.def_kind(obligation.predicate.def_id) { - DefKind::AssocTy | DefKind::ImplTraitPlaceholder => tcx - .mk_projection(obligation.predicate.def_id, obligation.predicate.substs) - .into(), - DefKind::AssocConst => tcx - .mk_const( - ty::ConstKind::Unevaluated(ty::UnevaluatedConst::new( - obligation.predicate.def_id, - obligation.predicate.substs, - )), - tcx.type_of(obligation.predicate.def_id) - .subst(tcx, obligation.predicate.substs), - ) - .into(), + DefKind::AssocTy | DefKind::ImplTraitPlaceholder => Ty::new_projection( + tcx, + obligation.predicate.def_id, + obligation.predicate.substs, + ) + .into(), + DefKind::AssocConst => ty::Const::new_unevaluated( + tcx, + ty::UnevaluatedConst::new( + obligation.predicate.def_id, + obligation.predicate.substs, + ), + tcx.type_of(obligation.predicate.def_id) + .subst(tcx, obligation.predicate.substs), + ) + .into(), kind => { bug!("unknown projection def-id: {}", kind.descr(obligation.predicate.def_id)) } @@ -1477,7 +1545,6 @@ fn assemble_candidate_for_impl_trait_in_trait<'cx, 'tcx>( let trait_def_id = tcx.parent(trait_fn_def_id); let trait_substs = obligation.predicate.substs.truncate_to(tcx, tcx.generics_of(trait_def_id)); - // FIXME(named-returns): Binders let trait_predicate = ty::TraitRef::new(tcx, trait_def_id, trait_substs); let _ = selcx.infcx.commit_if_ok(|_| { @@ -1543,7 +1610,7 @@ fn assemble_candidates_from_trait_def<'cx, 'tcx>( // Check whether the self-type is itself a projection. // If so, extract what we know from the trait and try to come up with a good answer. let bounds = match *obligation.predicate.self_ty().kind() { - // Excluding IATs here as they don't have meaningful item bounds. + // Excluding IATs and type aliases here as they don't have meaningful item bounds. ty::Alias(ty::Projection | ty::Opaque, ref data) => { tcx.item_bounds(data.def_id).subst(tcx, data.substs) } @@ -1584,6 +1651,10 @@ fn assemble_candidates_from_object_ty<'cx, 'tcx>( let tcx = selcx.tcx(); + if !tcx.trait_def(obligation.predicate.trait_def_id(tcx)).implement_via_object { + return; + } + let self_ty = obligation.predicate.self_ty(); let object_ty = selcx.infcx.shallow_resolve(self_ty); let data = match object_ty.kind() { @@ -1620,15 +1691,13 @@ fn assemble_candidates_from_predicates<'cx, 'tcx>( obligation: &ProjectionTyObligation<'tcx>, candidate_set: &mut ProjectionCandidateSet<'tcx>, ctor: fn(ty::PolyProjectionPredicate<'tcx>) -> ProjectionCandidate<'tcx>, - env_predicates: impl Iterator<Item = ty::Predicate<'tcx>>, + env_predicates: impl Iterator<Item = ty::Clause<'tcx>>, potentially_unnormalized_candidates: bool, ) { let infcx = selcx.infcx; for predicate in env_predicates { let bound_predicate = predicate.kind(); - if let ty::PredicateKind::Clause(ty::Clause::Projection(data)) = - predicate.kind().skip_binder() - { + if let ty::ClauseKind::Projection(data) = predicate.kind().skip_binder() { let data = bound_predicate.rebind(data); if data.projection_def_id() != obligation.predicate.def_id { continue; @@ -1677,8 +1746,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(selcx.tcx(), poly_trait_ref); + let trait_ref = obligation.predicate.trait_ref(selcx.tcx()); + let trait_obligation = obligation.with(selcx.tcx(), trait_ref); let _ = selcx.infcx.commit_if_ok(|_| { let impl_source = match selcx.select(&trait_obligation) { Ok(Some(impl_source)) => impl_source, @@ -1694,11 +1763,6 @@ 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) => { // We have to be careful when projecting out of an // impl because of specialization. If we are not in @@ -1737,7 +1801,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(trait_ref); !poly_trait_ref.still_further_specializable() } else { debug!( @@ -1756,7 +1820,11 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( 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()) { + if [lang_items.gen_trait(), lang_items.future_trait()].contains(&Some(trait_ref.def_id)) + || selcx.tcx().fn_trait_kind_from_def_id(trait_ref.def_id).is_some() + { + true + } else if lang_items.discriminant_kind_trait() == Some(trait_ref.def_id) { match self_ty.kind() { ty::Bool | ty::Char @@ -1791,7 +1859,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( | ty::Infer(..) | ty::Error(_) => false, } - } else if lang_items.pointee_trait() == Some(poly_trait_ref.def_id()) { + } else if lang_items.pointee_trait() == Some(trait_ref.def_id) { let tail = selcx.tcx().struct_tail_with_normalize( self_ty, |ty| { @@ -1866,7 +1934,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( } } } else { - bug!("unexpected builtin trait with associated type: {poly_trait_ref:?}") + bug!("unexpected builtin trait with associated type: {trait_ref:?}") } } super::ImplSource::Param(..) => { @@ -1903,9 +1971,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( // why we special case object types. false } - super::ImplSource::AutoImpl(..) - | super::ImplSource::TraitUpcasting(_) - | super::ImplSource::ConstDestruct(_) => { + | super::ImplSource::TraitUpcasting(_) => { // These traits have no associated types. selcx.tcx().sess.delay_span_bug( obligation.cause.span, @@ -1969,17 +2035,26 @@ fn confirm_select_candidate<'cx, 'tcx>( ) -> Progress<'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::Builtin(data) => confirm_builtin_candidate(selcx, obligation, data), + super::ImplSource::Builtin(data) => { + let trait_def_id = obligation.predicate.trait_def_id(selcx.tcx()); + let lang_items = selcx.tcx().lang_items(); + if lang_items.gen_trait() == Some(trait_def_id) { + confirm_generator_candidate(selcx, obligation, data) + } else if lang_items.future_trait() == Some(trait_def_id) { + confirm_future_candidate(selcx, obligation, data) + } else if selcx.tcx().fn_trait_kind_from_def_id(trait_def_id).is_some() { + if obligation.predicate.self_ty().is_closure() { + confirm_closure_candidate(selcx, obligation, data) + } else { + confirm_fn_pointer_candidate(selcx, obligation, data) + } + } else { + confirm_builtin_candidate(selcx, obligation, data) + } + } super::ImplSource::Object(_) - | super::ImplSource::AutoImpl(..) | super::ImplSource::Param(..) - | super::ImplSource::TraitUpcasting(_) - | super::ImplSource::TraitAlias(..) - | super::ImplSource::ConstDestruct(_) => { + | super::ImplSource::TraitUpcasting(_) => { // we don't create Select candidates with this kind of resolution span_bug!( obligation.cause.span, @@ -1993,9 +2068,14 @@ fn confirm_select_candidate<'cx, 'tcx>( fn confirm_generator_candidate<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionTyObligation<'tcx>, - impl_source: ImplSourceGeneratorData<'tcx, PredicateObligation<'tcx>>, + nested: Vec<PredicateObligation<'tcx>>, ) -> Progress<'tcx> { - let gen_sig = impl_source.substs.as_generator().poly_sig(); + let ty::Generator(_, substs, _) = + selcx.infcx.shallow_resolve(obligation.predicate.self_ty()).kind() + else { + unreachable!() + }; + let gen_sig = substs.as_generator().poly_sig(); let Normalized { value: gen_sig, obligations } = normalize_with_depth( selcx, obligation.param_env, @@ -2033,16 +2113,21 @@ fn confirm_generator_candidate<'cx, 'tcx>( }); confirm_param_env_candidate(selcx, obligation, predicate, false) - .with_addl_obligations(impl_source.nested) + .with_addl_obligations(nested) .with_addl_obligations(obligations) } fn confirm_future_candidate<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionTyObligation<'tcx>, - impl_source: ImplSourceFutureData<'tcx, PredicateObligation<'tcx>>, + nested: Vec<PredicateObligation<'tcx>>, ) -> Progress<'tcx> { - let gen_sig = impl_source.substs.as_generator().poly_sig(); + let ty::Generator(_, substs, _) = + selcx.infcx.shallow_resolve(obligation.predicate.self_ty()).kind() + else { + unreachable!() + }; + let gen_sig = substs.as_generator().poly_sig(); let Normalized { value: gen_sig, obligations } = normalize_with_depth( selcx, obligation.param_env, @@ -2072,14 +2157,14 @@ fn confirm_future_candidate<'cx, 'tcx>( }); confirm_param_env_candidate(selcx, obligation, predicate, false) - .with_addl_obligations(impl_source.nested) + .with_addl_obligations(nested) .with_addl_obligations(obligations) } fn confirm_builtin_candidate<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionTyObligation<'tcx>, - data: ImplSourceBuiltinData<PredicateObligation<'tcx>>, + data: Vec<PredicateObligation<'tcx>>, ) -> Progress<'tcx> { let tcx = selcx.tcx(); let self_ty = obligation.predicate.self_ty(); @@ -2127,15 +2212,15 @@ fn confirm_builtin_candidate<'cx, 'tcx>( confirm_param_env_candidate(selcx, obligation, ty::Binder::dummy(predicate), false) .with_addl_obligations(obligations) - .with_addl_obligations(data.nested) + .with_addl_obligations(data) } fn confirm_fn_pointer_candidate<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionTyObligation<'tcx>, - fn_pointer_impl_source: ImplSourceFnPointerData<'tcx, PredicateObligation<'tcx>>, + nested: Vec<PredicateObligation<'tcx>>, ) -> Progress<'tcx> { - let fn_type = selcx.infcx.shallow_resolve(fn_pointer_impl_source.fn_ty); + let fn_type = selcx.infcx.shallow_resolve(obligation.predicate.self_ty()); let sig = fn_type.fn_sig(selcx.tcx()); let Normalized { value: sig, obligations } = normalize_with_depth( selcx, @@ -2146,16 +2231,21 @@ fn confirm_fn_pointer_candidate<'cx, 'tcx>( ); confirm_callable_candidate(selcx, obligation, sig, util::TupleArgumentsFlag::Yes) - .with_addl_obligations(fn_pointer_impl_source.nested) + .with_addl_obligations(nested) .with_addl_obligations(obligations) } fn confirm_closure_candidate<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionTyObligation<'tcx>, - impl_source: ImplSourceClosureData<'tcx, PredicateObligation<'tcx>>, + nested: Vec<PredicateObligation<'tcx>>, ) -> Progress<'tcx> { - let closure_sig = impl_source.substs.as_closure().sig(); + let ty::Closure(_, substs) = + selcx.infcx.shallow_resolve(obligation.predicate.self_ty()).kind() + else { + unreachable!() + }; + let closure_sig = substs.as_closure().sig(); let Normalized { value: closure_sig, obligations } = normalize_with_depth( selcx, obligation.param_env, @@ -2167,7 +2257,7 @@ fn confirm_closure_candidate<'cx, 'tcx>( debug!(?obligation, ?closure_sig, ?obligations, "confirm_closure_candidate"); confirm_callable_candidate(selcx, obligation, closure_sig, util::TupleArgumentsFlag::No) - .with_addl_obligations(impl_source.nested) + .with_addl_obligations(nested) .with_addl_obligations(obligations) } @@ -2263,7 +2353,7 @@ fn confirm_param_env_candidate<'cx, 'tcx>( obligation, poly_cache_entry, e, ); debug!("confirm_param_env_candidate: {}", msg); - let err = infcx.tcx.ty_error_with_message(obligation.cause.span, msg); + let err = Ty::new_error_with_message(infcx.tcx, obligation.cause.span, msg); Progress { term: err.into(), obligations: vec![] } } } @@ -2295,7 +2385,7 @@ fn confirm_impl_candidate<'cx, 'tcx>( "confirm_impl_candidate: no associated type {:?} for {:?}", assoc_ty.item.name, obligation.predicate ); - return Progress { term: tcx.ty_error_misc().into(), obligations: nested }; + return Progress { term: Ty::new_misc_error(tcx).into(), obligations: nested }; } // If we're trying to normalize `<Vec<u32> as X>::A<S>` using //`impl<T> X for Vec<T> { type A<Y> = Box<Y>; }`, then: @@ -2311,13 +2401,14 @@ fn confirm_impl_candidate<'cx, 'tcx>( let term: ty::EarlyBinder<ty::Term<'tcx>> = if is_const { let did = assoc_ty.item.def_id; let identity_substs = crate::traits::InternalSubsts::identity_for_item(tcx, did); - let kind = ty::ConstKind::Unevaluated(ty::UnevaluatedConst::new(did, identity_substs)); - ty.map_bound(|ty| tcx.mk_const(kind, ty).into()) + let uv = ty::UnevaluatedConst::new(did, identity_substs); + ty.map_bound(|ty| ty::Const::new_unevaluated(tcx, uv, ty).into()) } else { ty.map_bound(|ty| ty.into()) }; if !check_substs_compatible(tcx, assoc_ty.item, substs) { - let err = tcx.ty_error_with_message( + let err = Ty::new_error_with_message( + tcx, obligation.cause.span, "impl item and trait item have different parameters", ); @@ -2328,47 +2419,6 @@ 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_item: ty::AssocItem, - substs: ty::SubstsRef<'tcx>, -) -> bool { - fn check_substs_compatible_inner<'tcx>( - tcx: TyCtxt<'tcx>, - generics: &'tcx ty::Generics, - args: &'tcx [ty::GenericArg<'tcx>], - ) -> bool { - if generics.count() != args.len() { - return false; - } - - let (parent_args, own_args) = args.split_at(generics.parent_count); - - if let Some(parent) = generics.parent - && let parent_generics = tcx.generics_of(parent) - && !check_substs_compatible_inner(tcx, parent_generics, parent_args) { - return false; - } - - for (param, arg) in std::iter::zip(&generics.params, own_args) { - match (¶m.kind, arg.unpack()) { - (ty::GenericParamDefKind::Type { .. }, ty::GenericArgKind::Type(_)) - | (ty::GenericParamDefKind::Lifetime, ty::GenericArgKind::Lifetime(_)) - | (ty::GenericParamDefKind::Const { .. }, ty::GenericArgKind::Const(_)) => {} - _ => return false, - } - } - - true - } - - 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>( selcx: &mut SelectionContext<'_, 'tcx>, obligation: &ProjectionTyObligation<'tcx>, @@ -2385,13 +2435,14 @@ fn confirm_impl_trait_in_trait_candidate<'tcx>( // We don't support specialization for RPITITs anyways... yet. // Also don't try to project to an RPITIT that has no value if !leaf_def.is_final() || !leaf_def.item.defaultness(tcx).has_value() { - return Progress { term: tcx.ty_error_misc().into(), obligations }; + return Progress { term: Ty::new_misc_error(tcx).into(), obligations }; } // Use the default `impl Trait` for the trait, e.g., for a default trait body if leaf_def.item.container == ty::AssocItemContainer::TraitContainer { return Progress { - term: tcx.mk_opaque(obligation.predicate.def_id, obligation.predicate.substs).into(), + term: Ty::new_opaque(tcx, obligation.predicate.def_id, obligation.predicate.substs) + .into(), obligations, }; } @@ -2409,7 +2460,8 @@ fn confirm_impl_trait_in_trait_candidate<'tcx>( ); if !check_substs_compatible(tcx, leaf_def.item, impl_fn_substs) { - let err = tcx.ty_error_with_message( + let err = Ty::new_error_with_message( + tcx, obligation.cause.span, "impl method and trait method have different parameters", ); @@ -2455,7 +2507,7 @@ fn confirm_impl_trait_in_trait_candidate<'tcx>( cause.clone(), obligation.recursion_depth + 1, tcx.collect_return_position_impl_trait_in_trait_tys(impl_fn_def_id).map_or_else( - |guar| tcx.ty_error(guar), + |guar| Ty::new_error(tcx, guar), |tys| tys[&obligation.predicate.def_id].subst(tcx, impl_fn_substs), ), &mut obligations, diff --git a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs index 4e4172e7f..709c3f432 100644 --- a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs +++ b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs @@ -307,13 +307,13 @@ pub fn dtorck_constraint_for_ty_inner<'tcx>( // there, but that needs some way to handle cycles. constraints .dtorck_types - .extend(dtorck_types.iter().map(|t| EarlyBinder(*t).subst(tcx, substs))); + .extend(dtorck_types.iter().map(|t| EarlyBinder::bind(*t).subst(tcx, substs))); constraints .outlives - .extend(outlives.iter().map(|t| EarlyBinder(*t).subst(tcx, substs))); + .extend(outlives.iter().map(|t| EarlyBinder::bind(*t).subst(tcx, substs))); constraints .overflows - .extend(overflows.iter().map(|t| EarlyBinder(*t).subst(tcx, substs))); + .extend(overflows.iter().map(|t| EarlyBinder::bind(*t).subst(tcx, substs))); } // Objects must be alive in order for their destructor 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 edbe2de81..a50644bb7 100644 --- a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs +++ b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs @@ -67,7 +67,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::Clause(ty::Clause::Trait(pred)) => { + ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => { // we ignore the value set to it. let mut _constness = pred.constness; obligation @@ -78,9 +78,9 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { _ => obligation.param_env.without_const(), }; - if self.tcx.trait_solver_next() { + if self.next_trait_solver() { self.probe(|snapshot| { - let mut fulfill_cx = crate::solve::FulfillmentCtxt::new(); + let mut fulfill_cx = crate::solve::FulfillmentCtxt::new(self); fulfill_cx.register_predicate_obligation(self, obligation.clone()); // True errors // FIXME(-Ztrait-solver=next): Overflows are reported as ambig here, is that OK? @@ -90,13 +90,14 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { Ok(EvaluationResult::EvaluatedToAmbig) } else if self.opaque_types_added_in_snapshot(snapshot) { Ok(EvaluationResult::EvaluatedToOkModuloOpaqueTypes) - } else if self.region_constraints_added_in_snapshot(snapshot).is_some() { + } else if self.region_constraints_added_in_snapshot(snapshot) { Ok(EvaluationResult::EvaluatedToOkModuloRegions) } else { Ok(EvaluationResult::EvaluatedToOk) } }) } else { + assert!(!self.intercrate); let c_pred = self.canonicalize_query_keep_static( param_env.and(obligation.predicate), &mut _orig_values, diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs index 8bf934cb7..7fe79fd86 100644 --- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -30,7 +30,7 @@ pub trait QueryNormalizeExt<'tcx> { /// /// 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> + fn query_normalize<T>(self, value: T) -> Result<Normalized<'tcx, T>, NoSolution> where T: TypeFoldable<TyCtxt<'tcx>>; } @@ -49,7 +49,7 @@ impl<'cx, 'tcx> QueryNormalizeExt<'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 query_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<TyCtxt<'tcx>>, { @@ -60,6 +60,16 @@ impl<'cx, 'tcx> QueryNormalizeExt<'tcx> for At<'cx, 'tcx> { self.param_env, self.cause, ); + + if self.infcx.next_trait_solver() { + match crate::solve::deeply_normalize(self, value) { + Ok(value) => return Ok(Normalized { value, obligations: vec![] }), + Err(_errors) => { + return Err(NoSolution); + } + } + } + if !needs_normalization(&value, self.param_env.reveal()) { return Ok(Normalized { value, obligations: vec![] }); } @@ -211,10 +221,7 @@ impl<'cx, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'cx, 'tcx> // Wrap this in a closure so we don't accidentally return from the outer function let res = match kind { - // This is really important. While we *can* handle this, this has - // severe performance implications for large opaque types with - // late-bound regions. See `issue-88862` benchmark. - ty::Opaque if !data.substs.has_escaping_bound_vars() => { + ty::Opaque => { // Only normalize `impl Trait` outside of type inference, usually in codegen. match self.param_env.reveal() { Reveal::UserFacing => ty.try_super_fold_with(self)?, @@ -255,9 +262,7 @@ impl<'cx, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'cx, 'tcx> } } - ty::Opaque => ty.try_super_fold_with(self)?, - - ty::Projection | ty::Inherent => { + ty::Projection | ty::Inherent | ty::Weak => { // See note in `rustc_trait_selection::traits::project` let infcx = self.infcx; @@ -282,6 +287,7 @@ impl<'cx, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'cx, 'tcx> debug!("QueryNormalizer: orig_values = {:#?}", orig_values); let result = match kind { ty::Projection => tcx.normalize_projection_ty(c_data), + ty::Weak => tcx.normalize_weak_ty(c_data), ty::Inherent => tcx.normalize_inherent_projection_ty(c_data), _ => unreachable!(), }?; @@ -321,8 +327,12 @@ impl<'cx, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'cx, 'tcx> }; // `tcx.normalize_projection_ty` may normalize to a type that still has // unevaluated consts, so keep normalizing here if that's the case. - if res != ty && res.has_type_flags(ty::TypeFlags::HAS_CT_PROJECTION) { - res.try_super_fold_with(self)? + // Similarly, `tcx.normalize_weak_ty` will only unwrap one layer of type + // and we need to continue folding it to reveal the TAIT behind it. + if res != ty + && (res.has_type_flags(ty::TypeFlags::HAS_CT_PROJECTION) || kind == ty::Weak) + { + res.try_fold_with(self)? } else { res } diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs index 01d7a1e79..44671a076 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs @@ -67,7 +67,8 @@ fn relate_mir_and_user_ty<'tcx>( ocx.eq(&cause, param_env, mir_ty, user_ty)?; // FIXME(#104764): We should check well-formedness before normalization. - let predicate = ty::Binder::dummy(ty::PredicateKind::WellFormed(user_ty.into())); + let predicate = + ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(user_ty.into()))); ocx.register_obligation(Obligation::new(ocx.infcx.tcx, cause, param_env, predicate)); Ok(()) } @@ -119,7 +120,9 @@ fn relate_mir_and_user_substs<'tcx>( let impl_self_ty = ocx.normalize(&cause, param_env, impl_self_ty); ocx.eq(&cause, param_env, self_ty, impl_self_ty)?; - let predicate = ty::Binder::dummy(ty::PredicateKind::WellFormed(impl_self_ty.into())); + let predicate = ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed( + impl_self_ty.into(), + ))); ocx.register_obligation(Obligation::new(tcx, cause.clone(), param_env, predicate)); } @@ -134,7 +137,8 @@ fn relate_mir_and_user_substs<'tcx>( // them? This would only be relevant if some input // type were ill-formed but did not appear in `ty`, // which...could happen with normalization... - let predicate = ty::Binder::dummy(ty::PredicateKind::WellFormed(ty.into())); + let predicate = + ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(ty.into()))); ocx.register_obligation(Obligation::new(tcx, cause, param_env, predicate)); Ok(()) } diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs index 6d8d2103f..5420caee3 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs @@ -5,6 +5,7 @@ use crate::traits::ObligationCtxt; use rustc_errors::ErrorGuaranteed; use rustc_infer::infer::region_constraints::RegionConstraintData; use rustc_middle::traits::query::NoSolution; +use rustc_middle::ty::{TyCtxt, TypeFoldable}; use rustc_span::source_map::DUMMY_SP; use rustc_span::Span; @@ -24,9 +25,10 @@ impl<F> CustomTypeOp<F> { } } -impl<'tcx, F, R: fmt::Debug> super::TypeOp<'tcx> for CustomTypeOp<F> +impl<'tcx, F, R> super::TypeOp<'tcx> for CustomTypeOp<F> where F: FnOnce(&ObligationCtxt<'_, 'tcx>) -> Result<R, NoSolution>, + R: fmt::Debug + TypeFoldable<TyCtxt<'tcx>>, { type Output = R; /// We can't do any custom error reporting for `CustomTypeOp`, so @@ -57,12 +59,16 @@ impl<F> fmt::Debug for CustomTypeOp<F> { /// Executes `op` and then scrapes out all the "old style" region /// constraints that result, creating query-region-constraints. -pub fn scrape_region_constraints<'tcx, Op: super::TypeOp<'tcx, Output = R>, R>( +pub fn scrape_region_constraints<'tcx, Op, R>( infcx: &InferCtxt<'tcx>, op: impl FnOnce(&ObligationCtxt<'_, 'tcx>) -> Result<R, NoSolution>, name: &'static str, span: Span, -) -> Result<(TypeOpOutput<'tcx, Op>, RegionConstraintData<'tcx>), ErrorGuaranteed> { +) -> Result<(TypeOpOutput<'tcx, Op>, RegionConstraintData<'tcx>), ErrorGuaranteed> +where + R: TypeFoldable<TyCtxt<'tcx>>, + Op: super::TypeOp<'tcx, Output = R>, +{ // During NLL, we expect that nobody will register region // obligations **except** as part of a custom type op (and, at the // end of each custom type op, we scrape out the region @@ -76,7 +82,7 @@ pub fn scrape_region_constraints<'tcx, Op: super::TypeOp<'tcx, Output = R>, R>( ); let value = infcx.commit_if_ok(|_| { - let ocx = ObligationCtxt::new_in_snapshot(infcx); + let ocx = ObligationCtxt::new(infcx); let value = op(&ocx).map_err(|_| { infcx.tcx.sess.delay_span_bug(span, format!("error performing operation: {name}")) })?; @@ -91,6 +97,9 @@ pub fn scrape_region_constraints<'tcx, Op: super::TypeOp<'tcx, Output = R>, R>( } })?; + // Next trait solver performs operations locally, and normalize goals should resolve vars. + let value = infcx.resolve_vars_if_possible(value); + let region_obligations = infcx.take_registered_region_obligations(); let region_constraint_data = infcx.take_and_reset_region_constraints(); let region_constraints = query_response::make_query_region_constraints( diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs index 9989fc9c4..979498fb6 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs @@ -1,3 +1,4 @@ +use crate::solve; use crate::traits::query::NoSolution; use crate::traits::wf; use crate::traits::ObligationCtxt; @@ -6,6 +7,7 @@ use rustc_infer::infer::canonical::Canonical; use rustc_infer::infer::outlives::components::{push_outlives_components, Component}; use rustc_infer::traits::query::OutlivesBound; use rustc_middle::infer::canonical::CanonicalQueryResponse; +use rustc_middle::traits::ObligationCause; use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt, TypeVisitableExt}; use rustc_span::def_id::CRATE_DEF_ID; use rustc_span::source_map::DUMMY_SP; @@ -108,7 +110,7 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>( // learn anything new from those. if obligation.predicate.has_non_region_infer() { match obligation.predicate.kind().skip_binder() { - ty::PredicateKind::Clause(ty::Clause::Projection(..)) + ty::PredicateKind::Clause(ty::ClauseKind::Projection(..)) | ty::PredicateKind::AliasRelate(..) => { ocx.register_obligation(obligation.clone()); } @@ -121,33 +123,33 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>( Some(pred) => pred, }; match pred { - ty::PredicateKind::Clause(ty::Clause::Trait(..)) + ty::PredicateKind::Clause(ty::ClauseKind::Trait(..)) // FIXME(const_generics): Make sure that `<'a, 'b, const N: &'a &'b u32>` is sound // if we ever support that - | ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..)) + | ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(..)) | ty::PredicateKind::Subtype(..) | ty::PredicateKind::Coerce(..) - | ty::PredicateKind::Clause(ty::Clause::Projection(..)) + | ty::PredicateKind::Clause(ty::ClauseKind::Projection(..)) | ty::PredicateKind::ClosureKind(..) | ty::PredicateKind::ObjectSafe(..) - | ty::PredicateKind::ConstEvaluatable(..) + | ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..)) | ty::PredicateKind::ConstEquate(..) | ty::PredicateKind::Ambiguous | ty::PredicateKind::AliasRelate(..) - | ty::PredicateKind::TypeWellFormedFromEnv(..) => {} + => {} // We need to search through *all* WellFormed predicates - ty::PredicateKind::WellFormed(arg) => { + ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => { wf_args.push(arg); } // We need to register region relationships - ty::PredicateKind::Clause(ty::Clause::RegionOutlives(ty::OutlivesPredicate( + ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate( r_a, r_b, ))) => outlives_bounds.push(ty::OutlivesPredicate(r_a.into(), r_b)), - ty::PredicateKind::Clause(ty::Clause::TypeOutlives(ty::OutlivesPredicate( + ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate( ty_a, r_b, ))) => outlives_bounds.push(ty::OutlivesPredicate(ty_a.into(), r_b)), @@ -164,19 +166,29 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>( // We lazily compute the outlives components as // `select_all_or_error` constrains inference variables. - let implied_bounds = outlives_bounds - .into_iter() - .flat_map(|ty::OutlivesPredicate(a, r_b)| match a.unpack() { - ty::GenericArgKind::Lifetime(r_a) => vec![OutlivesBound::RegionSubRegion(r_b, r_a)], + let mut implied_bounds = Vec::new(); + for ty::OutlivesPredicate(a, r_b) in outlives_bounds { + match a.unpack() { + ty::GenericArgKind::Lifetime(r_a) => { + implied_bounds.push(OutlivesBound::RegionSubRegion(r_b, r_a)) + } ty::GenericArgKind::Type(ty_a) => { - let ty_a = ocx.infcx.resolve_vars_if_possible(ty_a); + let mut ty_a = ocx.infcx.resolve_vars_if_possible(ty_a); + // Need to manually normalize in the new solver as `wf::obligations` does not. + if ocx.infcx.next_trait_solver() { + ty_a = solve::deeply_normalize( + ocx.infcx.at(&ObligationCause::dummy(), param_env), + ty_a, + ) + .map_err(|_errs| NoSolution)?; + } let mut components = smallvec![]; push_outlives_components(tcx, ty_a, &mut components); - implied_bounds_from_components(r_b, components) + implied_bounds.extend(implied_bounds_from_components(r_b, components)) } ty::GenericArgKind::Const(_) => unreachable!(), - }) - .collect(); + } + } Ok(implied_bounds) } diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs index 642fdec2d..9d7933e23 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs @@ -146,7 +146,7 @@ where infcx: &InferCtxt<'tcx>, span: Span, ) -> Result<TypeOpOutput<'tcx, Self>, ErrorGuaranteed> { - if infcx.tcx.trait_solver_next() { + if infcx.next_trait_solver() { return Ok(scrape_region_constraints( infcx, |ocx| QueryTypeOp::perform_locally_in_new_solver(ocx, self), diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs index 57ca14aa4..9559f5002 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs @@ -50,12 +50,12 @@ impl<'tcx> Normalizable<'tcx> for Ty<'tcx> { } } -impl<'tcx> Normalizable<'tcx> for ty::Predicate<'tcx> { +impl<'tcx> Normalizable<'tcx> for ty::Clause<'tcx> { fn type_op_method( tcx: TyCtxt<'tcx>, canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Normalize<Self>>>, ) -> Result<CanonicalQueryResponse<'tcx, Self>, NoSolution> { - tcx.type_op_normalize_predicate(canonicalized) + tcx.type_op_normalize_clause(canonicalized) } } 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 47850bc33..789ef6472 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 @@ -18,7 +18,7 @@ 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::Clause(ty::Clause::Trait(trait_ref)) = + if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_ref)) = key.value.predicate.kind().skip_binder() { if let Some(sized_def_id) = tcx.lang_items().sized_trait() { 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 8bc82b9f5..d5f6aaa7f 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -10,7 +10,7 @@ use hir::def_id::DefId; use hir::LangItem; use rustc_hir as hir; use rustc_infer::traits::ObligationCause; -use rustc_infer::traits::{Obligation, SelectionError, TraitObligation}; +use rustc_infer::traits::{Obligation, PolyTraitObligation, SelectionError}; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; use rustc_middle::ty::{self, Ty, TypeVisitableExt}; @@ -137,13 +137,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { #[instrument(level = "debug", skip(self, candidates))] fn assemble_candidates_from_projected_tys( &mut self, - obligation: &TraitObligation<'tcx>, + obligation: &PolyTraitObligation<'tcx>, candidates: &mut SelectionCandidateSet<'tcx>, ) { // Before we go into the whole placeholder thing, just // quickly check if the self-type is a projection at all. match obligation.predicate.skip_binder().trait_ref.self_ty().kind() { - // Excluding IATs here as they don't have meaningful item bounds. + // Excluding IATs and type aliases here as they don't have meaningful item bounds. ty::Alias(ty::Projection | ty::Opaque, _) => {} ty::Infer(ty::TyVar(_)) => { span_bug!( @@ -181,7 +181,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { .caller_bounds() .iter() .filter(|p| !p.references_error()) - .filter_map(|p| p.to_opt_poly_trait_pred()); + .filter_map(|p| p.as_trait_clause()); // Micro-optimization: filter out predicates relating to different traits. let matching_bounds = @@ -206,7 +206,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { fn assemble_generator_candidates( &mut self, - obligation: &TraitObligation<'tcx>, + obligation: &PolyTraitObligation<'tcx>, candidates: &mut SelectionCandidateSet<'tcx>, ) { // Okay to skip binder because the substs on generator types never @@ -231,7 +231,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { fn assemble_future_candidates( &mut self, - obligation: &TraitObligation<'tcx>, + obligation: &PolyTraitObligation<'tcx>, candidates: &mut SelectionCandidateSet<'tcx>, ) { let self_ty = obligation.self_ty().skip_binder(); @@ -254,7 +254,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /// unified during the confirmation step. fn assemble_closure_candidates( &mut self, - obligation: &TraitObligation<'tcx>, + obligation: &PolyTraitObligation<'tcx>, candidates: &mut SelectionCandidateSet<'tcx>, ) { let Some(kind) = self.tcx().fn_trait_kind_from_def_id(obligation.predicate.def_id()) else { @@ -292,7 +292,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /// Implements one of the `Fn()` family for a fn pointer. fn assemble_fn_pointer_candidates( &mut self, - obligation: &TraitObligation<'tcx>, + obligation: &PolyTraitObligation<'tcx>, candidates: &mut SelectionCandidateSet<'tcx>, ) { // We provide impl of all fn traits for fn pointers. @@ -334,7 +334,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { #[instrument(level = "debug", skip(self, candidates))] fn assemble_candidates_from_impls( &mut self, - obligation: &TraitObligation<'tcx>, + obligation: &PolyTraitObligation<'tcx>, candidates: &mut SelectionCandidateSet<'tcx>, ) { // Essentially any user-written impl will match with an error type, @@ -360,7 +360,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // consider a "quick reject". This avoids creating more types // and so forth that we need to. let impl_trait_ref = self.tcx().impl_trait_ref(impl_def_id).unwrap(); - if !drcx.substs_refs_may_unify(obligation_substs, impl_trait_ref.0.substs) { + if !drcx + .substs_refs_may_unify(obligation_substs, impl_trait_ref.skip_binder().substs) + { return; } if self.reject_fn_ptr_impls( @@ -386,9 +388,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /// `FnPtr`, when we wanted to report that it doesn't implement `Trait`. #[instrument(level = "trace", skip(self), ret)] fn reject_fn_ptr_impls( - &self, + &mut self, impl_def_id: DefId, - obligation: &TraitObligation<'tcx>, + obligation: &PolyTraitObligation<'tcx>, impl_self_ty: Ty<'tcx>, ) -> bool { // Let `impl<T: FnPtr> Trait for Vec<T>` go through the normal rejection path. @@ -400,7 +402,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { }; for &(predicate, _) in self.tcx().predicates_of(impl_def_id).predicates { - let ty::PredicateKind::Clause(ty::Clause::Trait(pred)) + let ty::ClauseKind::Trait(pred) = predicate.kind().skip_binder() else { continue }; if fn_ptr_trait != pred.trait_ref.def_id { continue; @@ -415,17 +417,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // Fast path to avoid evaluating an obligation that trivially holds. // There may be more bounds, but these are checked by the regular path. ty::FnPtr(..) => return false, + // These may potentially implement `FnPtr` ty::Placeholder(..) | ty::Dynamic(_, _, _) | ty::Alias(_, _) | ty::Infer(_) - | ty::Param(..) => {} + | ty::Param(..) + | ty::Bound(_, _) => {} - ty::Bound(_, _) => span_bug!( - obligation.cause.span(), - "cannot have escaping bound var in self type of {obligation:#?}" - ), // These can't possibly implement `FnPtr` as they are concrete types // and not `FnPtr` ty::Bool @@ -461,10 +461,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { self.tcx().mk_predicate(obligation.predicate.map_bound(|mut pred| { pred.trait_ref = ty::TraitRef::new(self.tcx(), fn_ptr_trait, [pred.trait_ref.self_ty()]); - ty::PredicateKind::Clause(ty::Clause::Trait(pred)) + ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) })), ); - if let Ok(r) = self.infcx.evaluate_obligation(&obligation) { + if let Ok(r) = self.evaluate_root_obligation(&obligation) { if !r.may_apply() { return true; } @@ -475,7 +475,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { fn assemble_candidates_from_auto_impls( &mut self, - obligation: &TraitObligation<'tcx>, + obligation: &PolyTraitObligation<'tcx>, candidates: &mut SelectionCandidateSet<'tcx>, ) { // Okay to skip binder here because the tests we do below do not involve bound regions. @@ -544,7 +544,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /// Searches for impls that might apply to `obligation`. fn assemble_candidates_from_object_ty( &mut self, - obligation: &TraitObligation<'tcx>, + obligation: &PolyTraitObligation<'tcx>, candidates: &mut SelectionCandidateSet<'tcx>, ) { debug!( @@ -552,6 +552,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { "assemble_candidates_from_object_ty", ); + if !self.tcx().trait_def(obligation.predicate.def_id()).implement_via_object { + return; + } + self.infcx.probe(|_snapshot| { if obligation.has_non_region_late_bound() { return; @@ -664,7 +668,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /// Searches for unsizing that might apply to `obligation`. fn assemble_candidates_for_unsizing( &mut self, - obligation: &TraitObligation<'tcx>, + obligation: &PolyTraitObligation<'tcx>, candidates: &mut SelectionCandidateSet<'tcx>, ) { // We currently never consider higher-ranked obligations e.g. @@ -778,7 +782,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { #[instrument(level = "debug", skip(self, obligation, candidates))] fn assemble_candidates_for_transmutability( &mut self, - obligation: &TraitObligation<'tcx>, + obligation: &PolyTraitObligation<'tcx>, candidates: &mut SelectionCandidateSet<'tcx>, ) { if obligation.predicate.has_non_region_param() { @@ -796,7 +800,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { #[instrument(level = "debug", skip(self, obligation, candidates))] fn assemble_candidates_for_trait_alias( &mut self, - obligation: &TraitObligation<'tcx>, + obligation: &PolyTraitObligation<'tcx>, candidates: &mut SelectionCandidateSet<'tcx>, ) { // Okay to skip binder here because the tests we do below do not involve bound regions. @@ -833,7 +837,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { fn assemble_const_destruct_candidates( &mut self, - obligation: &TraitObligation<'tcx>, + obligation: &PolyTraitObligation<'tcx>, candidates: &mut SelectionCandidateSet<'tcx>, ) { // If the predicate is `~const Destruct` in a non-const environment, we don't actually need @@ -920,7 +924,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { fn assemble_candidate_for_tuple( &mut self, - obligation: &TraitObligation<'tcx>, + obligation: &PolyTraitObligation<'tcx>, candidates: &mut SelectionCandidateSet<'tcx>, ) { let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder()); @@ -962,7 +966,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { fn assemble_candidate_for_pointer_like( &mut self, - obligation: &TraitObligation<'tcx>, + obligation: &PolyTraitObligation<'tcx>, candidates: &mut SelectionCandidateSet<'tcx>, ) { // The regions of a type don't affect the size of the type @@ -987,7 +991,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { fn assemble_candidates_for_fn_ptr_trait( &mut self, - obligation: &TraitObligation<'tcx>, + obligation: &PolyTraitObligation<'tcx>, candidates: &mut SelectionCandidateSet<'tcx>, ) { let self_ty = self.infcx.shallow_resolve(obligation.self_ty()); diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 0d9f55d4c..7adc29bbb 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -6,6 +6,7 @@ //! //! [rustc dev guide]: //! https://rustc-dev-guide.rust-lang.org/traits/resolution.html#confirmation +use rustc_ast::Mutability; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir::lang_items::LangItem; use rustc_infer::infer::LateBoundRegionConversionTime::HigherRankedType; @@ -13,9 +14,8 @@ use rustc_infer::infer::{DefineOpaqueTypes, InferOk}; use rustc_middle::traits::SelectionOutputTypeParameterMismatch; use rustc_middle::ty::{ self, Binder, GenericParamDefKind, InternalSubsts, SubstsRef, ToPolyTraitRef, ToPredicate, - TraitRef, Ty, TyCtxt, TypeVisitableExt, + TraitPredicate, TraitRef, Ty, TyCtxt, TypeVisitableExt, }; -use rustc_session::config::TraitSolver; use rustc_span::def_id::DefId; use crate::traits::project::{normalize_with_depth, normalize_with_depth_to}; @@ -26,12 +26,9 @@ use crate::traits::vtable::{ }; use crate::traits::{ BuiltinDerivedObligation, ImplDerivedObligation, ImplDerivedObligationCause, ImplSource, - ImplSourceAutoImplData, ImplSourceBuiltinData, ImplSourceClosureData, - ImplSourceConstDestructData, ImplSourceFnPointerData, ImplSourceFutureData, - ImplSourceGeneratorData, ImplSourceObjectData, ImplSourceTraitAliasData, - ImplSourceTraitUpcastingData, ImplSourceUserDefinedData, Normalized, Obligation, - ObligationCause, OutputTypeParameterMismatch, PredicateObligation, Selection, SelectionError, - TraitNotObjectSafe, TraitObligation, Unimplemented, + ImplSourceObjectData, ImplSourceTraitUpcastingData, ImplSourceUserDefinedData, Normalized, + Obligation, ObligationCause, OutputTypeParameterMismatch, PolyTraitObligation, + PredicateObligation, Selection, SelectionError, TraitNotObjectSafe, Unimplemented, }; use super::BuiltinImplConditions; @@ -45,7 +42,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { #[instrument(level = "debug", skip(self))] pub(super) fn confirm_candidate( &mut self, - obligation: &TraitObligation<'tcx>, + obligation: &PolyTraitObligation<'tcx>, candidate: SelectionCandidate<'tcx>, ) -> Result<Selection<'tcx>, SelectionError<'tcx>> { let mut impl_src = match candidate { @@ -70,8 +67,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } AutoImplCandidate => { - let data = self.confirm_auto_impl_candidate(obligation); - ImplSource::AutoImpl(data) + let data = self.confirm_auto_impl_candidate(obligation)?; + ImplSource::Builtin(data) } ProjectionCandidate(idx, constness) => { @@ -86,34 +83,34 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ClosureCandidate { .. } => { let vtable_closure = self.confirm_closure_candidate(obligation)?; - ImplSource::Closure(vtable_closure) + ImplSource::Builtin(vtable_closure) } GeneratorCandidate => { let vtable_generator = self.confirm_generator_candidate(obligation)?; - ImplSource::Generator(vtable_generator) + ImplSource::Builtin(vtable_generator) } FutureCandidate => { let vtable_future = self.confirm_future_candidate(obligation)?; - ImplSource::Future(vtable_future) + ImplSource::Builtin(vtable_future) } FnPointerCandidate { is_const } => { let data = self.confirm_fn_pointer_candidate(obligation, is_const)?; - ImplSource::FnPointer(data) + ImplSource::Builtin(data) } TraitAliasCandidate => { let data = self.confirm_trait_alias_candidate(obligation); - ImplSource::TraitAlias(data) + ImplSource::Builtin(data) } BuiltinObjectCandidate => { // This indicates something like `Trait + Send: Send`. In this case, we know that // this holds because that's what the object type is telling us, and there's really // no additional obligations to prove and no types in particular to unify, etc. - ImplSource::Param(Vec::new(), ty::BoundConstness::NotConst) + ImplSource::Builtin(Vec::new()) } BuiltinUnsizeCandidate => { @@ -128,7 +125,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ConstDestructCandidate(def_id) => { let data = self.confirm_const_destruct_candidate(obligation, def_id)?; - ImplSource::ConstDestruct(data) + ImplSource::Builtin(data) } }; @@ -151,7 +148,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { fn confirm_projection_candidate( &mut self, - obligation: &TraitObligation<'tcx>, + obligation: &PolyTraitObligation<'tcx>, idx: usize, ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> { let tcx = self.tcx(); @@ -162,7 +159,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { 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() { - // Excluding IATs here as they don't have meaningful item bounds. + // Excluding IATs and type aliases here as they don't have meaningful item bounds. ty::Alias(ty::Projection | ty::Opaque, ty::AliasTy { def_id, substs, .. }) => { (def_id, substs) } @@ -171,7 +168,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let candidate_predicate = tcx.item_bounds(def_id).map_bound(|i| i[idx]).subst(tcx, substs); let candidate = candidate_predicate - .to_opt_poly_trait_pred() + .as_trait_clause() .expect("projection candidate is not a trait predicate") .map_bound(|t| t.trait_ref); let mut obligations = Vec::new(); @@ -218,7 +215,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { fn confirm_param_candidate( &mut self, - obligation: &TraitObligation<'tcx>, + obligation: &PolyTraitObligation<'tcx>, param: ty::PolyTraitRef<'tcx>, ) -> Vec<PredicateObligation<'tcx>> { debug!(?obligation, ?param, "confirm_param_candidate"); @@ -241,9 +238,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { fn confirm_builtin_candidate( &mut self, - obligation: &TraitObligation<'tcx>, + obligation: &PolyTraitObligation<'tcx>, has_nested: bool, - ) -> ImplSourceBuiltinData<PredicateObligation<'tcx>> { + ) -> Vec<PredicateObligation<'tcx>> { debug!(?obligation, ?has_nested, "confirm_builtin_candidate"); let lang_items = self.tcx().lang_items(); @@ -276,14 +273,63 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { debug!(?obligations); - ImplSourceBuiltinData { nested: obligations } + obligations } + #[instrument(level = "debug", skip(self))] fn confirm_transmutability_candidate( &mut self, - obligation: &TraitObligation<'tcx>, - ) -> Result<ImplSourceBuiltinData<PredicateObligation<'tcx>>, SelectionError<'tcx>> { - debug!(?obligation, "confirm_transmutability_candidate"); + obligation: &PolyTraitObligation<'tcx>, + ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> { + use rustc_transmute::{Answer, Condition}; + #[instrument(level = "debug", skip(tcx, obligation, predicate))] + fn flatten_answer_tree<'tcx>( + tcx: TyCtxt<'tcx>, + obligation: &PolyTraitObligation<'tcx>, + predicate: TraitPredicate<'tcx>, + cond: Condition<rustc_transmute::layout::rustc::Ref<'tcx>>, + ) -> Vec<PredicateObligation<'tcx>> { + match cond { + // FIXME(bryangarza): Add separate `IfAny` case, instead of treating as `IfAll` + // Not possible until the trait solver supports disjunctions of obligations + Condition::IfAll(conds) | Condition::IfAny(conds) => conds + .into_iter() + .flat_map(|cond| flatten_answer_tree(tcx, obligation, predicate, cond)) + .collect(), + Condition::IfTransmutable { src, dst } => { + let trait_def_id = obligation.predicate.def_id(); + let scope = predicate.trait_ref.substs.type_at(2); + let assume_const = predicate.trait_ref.substs.const_at(3); + let make_obl = |from_ty, to_ty| { + let trait_ref1 = ty::TraitRef::new( + tcx, + trait_def_id, + [ + ty::GenericArg::from(to_ty), + ty::GenericArg::from(from_ty), + ty::GenericArg::from(scope), + ty::GenericArg::from(assume_const), + ], + ); + Obligation::with_depth( + tcx, + obligation.cause.clone(), + obligation.recursion_depth + 1, + obligation.param_env, + trait_ref1, + ) + }; + + // If Dst is mutable, check bidirectionally. + // For example, transmuting bool -> u8 is OK as long as you can't update that u8 + // to be > 1, because you could later transmute the u8 back to a bool and get UB. + match dst.mutability { + Mutability::Not => vec![make_obl(src.ty, dst.ty)], + Mutability::Mut => vec![make_obl(src.ty, dst.ty), make_obl(dst.ty, src.ty)], + } + } + } + } // We erase regions here because transmutability calls layout queries, // which does not handle inference regions and doesn't particularly @@ -301,21 +347,25 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { return Err(Unimplemented); }; + let dst = predicate.trait_ref.substs.type_at(0); + let src = predicate.trait_ref.substs.type_at(1); + debug!(?src, ?dst); let mut transmute_env = rustc_transmute::TransmuteTypeEnv::new(self.infcx); let maybe_transmutable = transmute_env.is_transmutable( obligation.cause.clone(), - rustc_transmute::Types { - dst: predicate.trait_ref.substs.type_at(0), - src: predicate.trait_ref.substs.type_at(1), - }, + rustc_transmute::Types { dst, src }, predicate.trait_ref.substs.type_at(2), assume, ); - match maybe_transmutable { - rustc_transmute::Answer::Yes => Ok(ImplSourceBuiltinData { nested: vec![] }), - _ => Err(Unimplemented), - } + let fully_flattened = match maybe_transmutable { + Answer::No(_) => Err(Unimplemented)?, + Answer::If(cond) => flatten_answer_tree(self.tcx(), obligation, predicate, cond), + Answer::Yes => vec![], + }; + + debug!(?fully_flattened); + Ok(fully_flattened) } /// This handles the case where an `auto trait Foo` impl is being used. @@ -325,22 +375,22 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /// 2. For each where-clause `C` declared on `Foo`, `[Self => X] C` holds. fn confirm_auto_impl_candidate( &mut self, - obligation: &TraitObligation<'tcx>, - ) -> ImplSourceAutoImplData<PredicateObligation<'tcx>> { + obligation: &PolyTraitObligation<'tcx>, + ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> { debug!(?obligation, "confirm_auto_impl_candidate"); let self_ty = self.infcx.shallow_resolve(obligation.predicate.self_ty()); - let types = self.constituent_types_for_ty(self_ty); - self.vtable_auto_impl(obligation, obligation.predicate.def_id(), types) + let types = self.constituent_types_for_ty(self_ty)?; + Ok(self.vtable_auto_impl(obligation, obligation.predicate.def_id(), types)) } /// See `confirm_auto_impl_candidate`. fn vtable_auto_impl( &mut self, - obligation: &TraitObligation<'tcx>, + obligation: &PolyTraitObligation<'tcx>, trait_def_id: DefId, nested: ty::Binder<'tcx, Vec<Ty<'tcx>>>, - ) -> ImplSourceAutoImplData<PredicateObligation<'tcx>> { + ) -> Vec<PredicateObligation<'tcx>> { debug!(?nested, "vtable_auto_impl"); ensure_sufficient_stack(|| { let cause = obligation.derived_cause(BuiltinDerivedObligation); @@ -370,13 +420,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { debug!(?obligations, "vtable_auto_impl"); - ImplSourceAutoImplData { trait_def_id, nested: obligations } + obligations }) } fn confirm_impl_candidate( &mut self, - obligation: &TraitObligation<'tcx>, + obligation: &PolyTraitObligation<'tcx>, impl_def_id: DefId, ) -> ImplSourceUserDefinedData<'tcx, PredicateObligation<'tcx>> { debug!(?obligation, ?impl_def_id, "confirm_impl_candidate"); @@ -431,9 +481,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { fn confirm_object_candidate( &mut self, - obligation: &TraitObligation<'tcx>, + obligation: &PolyTraitObligation<'tcx>, index: usize, - ) -> Result<ImplSourceObjectData<'tcx, PredicateObligation<'tcx>>, SelectionError<'tcx>> { + ) -> Result<ImplSourceObjectData<PredicateObligation<'tcx>>, SelectionError<'tcx>> { let tcx = self.tcx(); debug!(?obligation, ?index, "confirm_object_candidate"); @@ -527,9 +577,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { substs.extend(trait_predicate.trait_ref.substs.iter()); let mut bound_vars: smallvec::SmallVec<[ty::BoundVariableKind; 8]> = smallvec::SmallVec::with_capacity( - bound.0.kind().bound_vars().len() + defs.count(), + bound.skip_binder().kind().bound_vars().len() + defs.count(), ); - bound_vars.extend(bound.0.kind().bound_vars().into_iter()); + bound_vars.extend(bound.skip_binder().kind().bound_vars().into_iter()); InternalSubsts::fill_single(&mut substs, defs, &mut |param, _| match param .kind { @@ -537,7 +587,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let kind = ty::BoundTyKind::Param(param.def_id, param.name); let bound_var = ty::BoundVariableKind::Ty(kind); bound_vars.push(bound_var); - tcx.mk_bound( + Ty::new_bound( + tcx, ty::INNERMOST, ty::BoundTy { var: ty::BoundVar::from_usize(bound_vars.len() - 1), @@ -550,7 +601,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let kind = ty::BoundRegionKind::BrNamed(param.def_id, param.name); let bound_var = ty::BoundVariableKind::Region(kind); bound_vars.push(bound_var); - tcx.mk_re_late_bound( + ty::Region::new_late_bound( + tcx, ty::INNERMOST, ty::BoundRegion { var: ty::BoundVar::from_usize(bound_vars.len() - 1), @@ -562,11 +614,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { GenericParamDefKind::Const { .. } => { let bound_var = ty::BoundVariableKind::Const; bound_vars.push(bound_var); - tcx.mk_const( - ty::ConstKind::Bound( - ty::INNERMOST, - ty::BoundVar::from_usize(bound_vars.len() - 1), - ), + ty::Const::new_bound( + tcx, + ty::INNERMOST, + ty::BoundVar::from_usize(bound_vars.len() - 1), tcx.type_of(param.def_id) .no_bound_vars() .expect("const parameter types cannot be generic"), @@ -578,7 +629,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let assoc_ty_substs = tcx.mk_substs(&substs); let bound = bound.map_bound(|b| b.kind().skip_binder()).subst(tcx, assoc_ty_substs); - tcx.mk_predicate(ty::Binder::bind_with_vars(bound, bound_vars)) + ty::Binder::bind_with_vars(bound, bound_vars).to_predicate(tcx) }; let normalized_bound = normalize_with_depth_to( self, @@ -599,15 +650,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { (unnormalized_upcast_trait_ref, ty::Binder::dummy(object_trait_ref)), ); - Ok(ImplSourceObjectData { upcast_trait_ref, vtable_base, nested }) + Ok(ImplSourceObjectData { vtable_base, nested }) } fn confirm_fn_pointer_candidate( &mut self, - obligation: &TraitObligation<'tcx>, + obligation: &PolyTraitObligation<'tcx>, is_const: bool, - ) -> Result<ImplSourceFnPointerData<'tcx, PredicateObligation<'tcx>>, SelectionError<'tcx>> - { + ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> { debug!(?obligation, "confirm_fn_pointer_candidate"); let tcx = self.tcx(); @@ -659,16 +709,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let tr = ty::TraitRef::from_lang_item(self.tcx(), LangItem::Sized, cause.span, [output_ty]); nested.push(Obligation::new(self.infcx.tcx, cause, obligation.param_env, tr)); - Ok(ImplSourceFnPointerData { fn_ty: self_ty, nested }) + Ok(nested) } fn confirm_trait_alias_candidate( &mut self, - obligation: &TraitObligation<'tcx>, - ) -> ImplSourceTraitAliasData<'tcx, PredicateObligation<'tcx>> { + obligation: &PolyTraitObligation<'tcx>, + ) -> Vec<PredicateObligation<'tcx>> { debug!(?obligation, "confirm_trait_alias_candidate"); - let alias_def_id = obligation.predicate.def_id(); let predicate = self.infcx.instantiate_binder_with_placeholders(obligation.predicate); let trait_ref = predicate.trait_ref; let trait_def_id = trait_ref.def_id; @@ -685,14 +734,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { debug!(?trait_def_id, ?trait_obligations, "trait alias obligations"); - ImplSourceTraitAliasData { alias_def_id, substs, nested: trait_obligations } + trait_obligations } fn confirm_generator_candidate( &mut self, - obligation: &TraitObligation<'tcx>, - ) -> Result<ImplSourceGeneratorData<'tcx, PredicateObligation<'tcx>>, SelectionError<'tcx>> - { + obligation: &PolyTraitObligation<'tcx>, + ) -> Result<Vec<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. @@ -725,13 +773,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let nested = self.confirm_poly_trait_refs(obligation, trait_ref)?; debug!(?trait_ref, ?nested, "generator candidate obligations"); - Ok(ImplSourceGeneratorData { generator_def_id, substs, nested }) + Ok(nested) } fn confirm_future_candidate( &mut self, - obligation: &TraitObligation<'tcx>, - ) -> Result<ImplSourceFutureData<'tcx, PredicateObligation<'tcx>>, SelectionError<'tcx>> { + obligation: &PolyTraitObligation<'tcx>, + ) -> Result<Vec<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. @@ -755,14 +803,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let nested = self.confirm_poly_trait_refs(obligation, trait_ref)?; debug!(?trait_ref, ?nested, "future candidate obligations"); - Ok(ImplSourceFutureData { generator_def_id, substs, nested }) + Ok(nested) } #[instrument(skip(self), level = "debug")] fn confirm_closure_candidate( &mut self, - obligation: &TraitObligation<'tcx>, - ) -> Result<ImplSourceClosureData<'tcx, PredicateObligation<'tcx>>, SelectionError<'tcx>> { + obligation: &PolyTraitObligation<'tcx>, + ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> { let kind = self .tcx() .fn_trait_kind_from_def_id(obligation.predicate.def_id()) @@ -781,15 +829,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { debug!(?closure_def_id, ?trait_ref, ?nested, "confirm closure candidate obligations"); - // FIXME: Chalk - if self.tcx().sess.opts.unstable_opts.trait_solver != TraitSolver::Chalk { - nested.push(obligation.with( - self.tcx(), - ty::Binder::dummy(ty::PredicateKind::ClosureKind(closure_def_id, substs, kind)), - )); - } + nested.push(obligation.with( + self.tcx(), + ty::Binder::dummy(ty::PredicateKind::ClosureKind(closure_def_id, substs, kind)), + )); - Ok(ImplSourceClosureData { closure_def_id, substs, nested }) + Ok(nested) } /// In the case of closure types and fn pointers, @@ -820,7 +865,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { #[instrument(skip(self), level = "trace")] fn confirm_poly_trait_refs( &mut self, - obligation: &TraitObligation<'tcx>, + obligation: &PolyTraitObligation<'tcx>, self_ty_trait_ref: ty::PolyTraitRef<'tcx>, ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> { let obligation_trait_ref = obligation.predicate.to_poly_trait_ref(); @@ -855,10 +900,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { fn confirm_trait_upcasting_unsize_candidate( &mut self, - obligation: &TraitObligation<'tcx>, + obligation: &PolyTraitObligation<'tcx>, idx: usize, - ) -> Result<ImplSourceTraitUpcastingData<'tcx, PredicateObligation<'tcx>>, SelectionError<'tcx>> - { + ) -> Result<ImplSourceTraitUpcastingData<PredicateObligation<'tcx>>, SelectionError<'tcx>> { let tcx = self.tcx(); // `assemble_candidates_for_unsizing` should ensure there are no late-bound @@ -903,7 +947,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { .map(ty::Binder::dummy), ); let existential_predicates = tcx.mk_poly_existential_predicates_from_iter(iter); - let source_trait = tcx.mk_dynamic(existential_predicates, r_b, repr_a); + let source_trait = Ty::new_dynamic(tcx, existential_predicates, r_b, repr_a); // Require that the traits involved in this upcast are **equal**; // only the **lifetime bound** is changed. @@ -955,13 +999,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let vtable_vptr_slot = prepare_vtable_segments(tcx, source_trait_ref, vtable_segment_callback).unwrap(); - Ok(ImplSourceTraitUpcastingData { upcast_trait_ref, vtable_vptr_slot, nested }) + Ok(ImplSourceTraitUpcastingData { vtable_vptr_slot, nested }) } fn confirm_builtin_unsize_candidate( &mut self, - obligation: &TraitObligation<'tcx>, - ) -> Result<ImplSourceBuiltinData<PredicateObligation<'tcx>>, SelectionError<'tcx>> { + obligation: &PolyTraitObligation<'tcx>, + ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> { let tcx = self.tcx(); // `assemble_candidates_for_unsizing` should ensure there are no late-bound @@ -996,7 +1040,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { .map(ty::Binder::dummy), ); let existential_predicates = tcx.mk_poly_existential_predicates_from_iter(iter); - let source_trait = tcx.mk_dynamic(existential_predicates, r_b, dyn_a); + let source_trait = Ty::new_dynamic(tcx, existential_predicates, r_b, dyn_a); // Require that the traits involved in this upcast are **equal**; // only the **lifetime bound** is changed. @@ -1054,12 +1098,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation.cause.span, [source], ); - nested.push(predicate_to_obligation(tr.without_const().to_predicate(tcx))); + nested.push(predicate_to_obligation(tr.to_predicate(tcx))); // If the type is `Foo + 'a`, ensure that the type // being cast to `Foo + 'a` outlives `'a`: let outlives = ty::OutlivesPredicate(source, r); - nested.push(predicate_to_obligation(ty::Binder::dummy(outlives).to_predicate(tcx))); + nested.push(predicate_to_obligation( + ty::Binder::dummy(ty::ClauseKind::TypeOutlives(outlives)).to_predicate(tcx), + )); } // `[T; n]` -> `[T]` @@ -1079,12 +1125,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { return Err(Unimplemented); } - let tail_field = def - .non_enum_variant() - .fields - .raw - .last() - .expect("expected unsized ADT to have a tail field"); + let tail_field = def.non_enum_variant().tail(); let tail_field_ty = tcx.type_of(tail_field.did); // Extract `TailField<T>` and `TailField<U>` from `Struct<T>` and `Struct<U>`, @@ -1112,7 +1153,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let substs = tcx.mk_substs_from_iter(substs_a.iter().enumerate().map(|(i, k)| { if unsizing_params.contains(i as u32) { substs_b[i] } else { k } })); - let new_struct = tcx.mk_adt(def, substs); + let new_struct = Ty::new_adt(tcx, def, substs); let InferOk { obligations, .. } = self .infcx .at(&obligation.cause, obligation.param_env) @@ -1143,7 +1184,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // Check that the source tuple with the target's // last element is equal to the target. let new_tuple = - tcx.mk_tup_from_iter(a_mid.iter().copied().chain(iter::once(b_last))); + Ty::new_tup_from_iter(tcx, a_mid.iter().copied().chain(iter::once(b_last))); let InferOk { obligations, .. } = self .infcx .at(&obligation.cause, obligation.param_env) @@ -1162,17 +1203,17 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { _ => bug!("source: {source}, target: {target}"), }; - Ok(ImplSourceBuiltinData { nested }) + Ok(nested) } fn confirm_const_destruct_candidate( &mut self, - obligation: &TraitObligation<'tcx>, + obligation: &PolyTraitObligation<'tcx>, impl_def_id: Option<DefId>, - ) -> Result<ImplSourceConstDestructData<PredicateObligation<'tcx>>, SelectionError<'tcx>> { + ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> { // `~const Destruct` in a non-const environment is always trivially true, since our type is `Drop` if !obligation.is_const() { - return Ok(ImplSourceConstDestructData { nested: vec![] }); + return Ok(vec![]); } let drop_trait = self.tcx().require_lang_item(LangItem::Drop, None); @@ -1326,6 +1367,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } - Ok(ImplSourceConstDestructData { nested }) + Ok(nested) } } diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 3baf1c97c..7f31ab751 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -15,11 +15,12 @@ use super::util::closure_trait_ref_and_return_type; use super::wf; use super::{ ErrorReporting, ImplDerivedObligation, ImplDerivedObligationCause, Normalized, Obligation, - ObligationCause, ObligationCauseCode, Overflow, PredicateObligation, Selection, SelectionError, - SelectionResult, TraitObligation, TraitQueryMode, + ObligationCause, ObligationCauseCode, Overflow, PolyTraitObligation, PredicateObligation, + Selection, SelectionError, SelectionResult, TraitQueryMode, }; use crate::infer::{InferCtxt, InferOk, TypeFreshener}; +use crate::solve::InferCtxtSelectExt; use crate::traits::error_reporting::TypeErrCtxtExt; use crate::traits::project::try_normalize_with_depth_to; use crate::traits::project::ProjectAndUnifyResult; @@ -33,8 +34,7 @@ use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_infer::infer::DefineOpaqueTypes; use rustc_infer::infer::LateBoundRegionConversionTime; -use rustc_infer::traits::TraitEngine; -use rustc_infer::traits::TraitEngineExt; +use rustc_infer::traits::TraitObligation; use rustc_middle::dep_graph::{DepKind, DepNodeIndex}; use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::ty::abstract_const::NotConstEvaluatable; @@ -123,7 +123,7 @@ pub struct SelectionContext<'cx, 'tcx> { // A stack that walks back up the stack frame. struct TraitObligationStack<'prev, 'tcx> { - obligation: &'prev TraitObligation<'tcx>, + obligation: &'prev PolyTraitObligation<'tcx>, /// The trait predicate from `obligation` but "freshened" with the /// selection-context's freshener. Used to check for recursion. @@ -260,10 +260,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /// Attempts to satisfy the obligation. If successful, this will affect the surrounding /// type environment by performing unification. #[instrument(level = "debug", skip(self), ret)] - pub fn select( + pub fn poly_select( &mut self, - obligation: &TraitObligation<'tcx>, + obligation: &PolyTraitObligation<'tcx>, ) -> SelectionResult<'tcx, Selection<'tcx>> { + if self.infcx.next_trait_solver() { + return self.infcx.select_in_new_trait_solver(obligation); + } + let candidate = match self.select_from_obligation(obligation) { Err(SelectionError::Overflow(OverflowError::Canonical)) => { // In standard mode, overflow must have been caught and reported @@ -290,9 +294,21 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } - pub(crate) fn select_from_obligation( + pub fn select( &mut self, obligation: &TraitObligation<'tcx>, + ) -> SelectionResult<'tcx, Selection<'tcx>> { + self.poly_select(&Obligation { + cause: obligation.cause.clone(), + param_env: obligation.param_env, + predicate: ty::Binder::dummy(obligation.predicate), + recursion_depth: obligation.recursion_depth, + }) + } + + fn select_from_obligation( + &mut self, + obligation: &PolyTraitObligation<'tcx>, ) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> { debug_assert!(!obligation.predicate.has_escaping_bound_vars()); @@ -307,6 +323,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut self, stack: &TraitObligationStack<'o, 'tcx>, ) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> { + debug_assert!(!self.infcx.next_trait_solver()); // Watch out for overflow. This intentionally bypasses (and does // not update) the cache. self.check_recursion_limit(&stack.obligation, &stack.obligation)?; @@ -365,7 +382,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } if !candidate_set.ambiguous && no_candidates_apply { - let trait_ref = stack.obligation.predicate.skip_binder().trait_ref; + let trait_ref = self.infcx.resolve_vars_if_possible( + 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!({ @@ -516,37 +535,23 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // The result is "true" if the obligation *may* hold and "false" if // we can be sure it does not. - /// Evaluates whether the obligation `obligation` can be satisfied (by any means). - pub fn predicate_may_hold_fatal(&mut self, obligation: &PredicateObligation<'tcx>) -> bool { - debug!(?obligation, "predicate_may_hold_fatal"); - - // This fatal query is a stopgap that should only be used in standard mode, - // where we do not expect overflow to be propagated. - assert!(self.query_mode == TraitQueryMode::Standard); - - self.evaluate_root_obligation(obligation) - .expect("Overflow should be caught earlier in standard query mode") - .may_apply() - } - /// Evaluates whether the obligation `obligation` can be satisfied /// and returns an `EvaluationResult`. This is meant for the /// *initial* call. + /// + /// Do not use this directly, use `infcx.evaluate_obligation` instead. pub fn evaluate_root_obligation( &mut self, obligation: &PredicateObligation<'tcx>, ) -> Result<EvaluationResult, OverflowError> { + debug_assert!(!self.infcx.next_trait_solver()); self.evaluation_probe(|this| { let goal = this.infcx.resolve_vars_if_possible((obligation.predicate, obligation.param_env)); - let mut result = if this.tcx().trait_solver_next() { - this.evaluate_predicates_recursively_in_new_solver([obligation.clone()])? - } else { - this.evaluate_predicate_recursively( - TraitObligationStackList::empty(&ProvisionalEvaluationCache::default()), - obligation.clone(), - )? - }; + let mut result = this.evaluate_predicate_recursively( + TraitObligationStackList::empty(&ProvisionalEvaluationCache::default()), + obligation.clone(), + )?; // If the predicate has done any inference, then downgrade the // result to ambiguous. if this.infcx.shallow_resolve(goal) != goal { @@ -561,9 +566,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { op: impl FnOnce(&mut Self) -> Result<EvaluationResult, OverflowError>, ) -> Result<EvaluationResult, OverflowError> { self.infcx.probe(|snapshot| -> Result<EvaluationResult, OverflowError> { + let outer_universe = self.infcx.universe(); let result = op(self)?; - match self.infcx.leak_check(true, snapshot) { + match self.infcx.leak_check(outer_universe, Some(snapshot)) { Ok(()) => {} Err(_) => return Ok(EvaluatedToErr), } @@ -572,9 +578,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { return Ok(result.max(EvaluatedToOkModuloOpaqueTypes)); } - match self.infcx.region_constraints_added_in_snapshot(snapshot) { - None => Ok(result), - Some(_) => Ok(result.max(EvaluatedToOkModuloRegions)), + if self.infcx.region_constraints_added_in_snapshot(snapshot) { + Ok(result.max(EvaluatedToOkModuloRegions)) + } else { + Ok(result) } }) } @@ -591,42 +598,19 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { where I: IntoIterator<Item = PredicateObligation<'tcx>> + std::fmt::Debug, { - if self.tcx().trait_solver_next() { - self.evaluate_predicates_recursively_in_new_solver(predicates) - } else { - let mut result = EvaluatedToOk; - for mut obligation in predicates { - obligation.set_depth_from_parent(stack.depth()); - let eval = self.evaluate_predicate_recursively(stack, obligation.clone())?; - if let EvaluatedToErr = eval { - // fast-path - EvaluatedToErr is the top of the lattice, - // so we don't need to look on the other predicates. - return Ok(EvaluatedToErr); - } else { - result = cmp::max(result, eval); - } + let mut result = EvaluatedToOk; + for mut obligation in predicates { + obligation.set_depth_from_parent(stack.depth()); + let eval = self.evaluate_predicate_recursively(stack, obligation.clone())?; + if let EvaluatedToErr = eval { + // fast-path - EvaluatedToErr is the top of the lattice, + // so we don't need to look on the other predicates. + return Ok(EvaluatedToErr); + } else { + result = cmp::max(result, eval); } - Ok(result) } - } - - /// Evaluates the predicates using the new solver when `-Ztrait-solver=next` is enabled - fn evaluate_predicates_recursively_in_new_solver( - &mut self, - predicates: impl IntoIterator<Item = PredicateObligation<'tcx>>, - ) -> Result<EvaluationResult, OverflowError> { - let mut fulfill_cx = crate::solve::FulfillmentCtxt::new(); - fulfill_cx.register_predicate_obligations(self.infcx, predicates); - // True errors - // FIXME(-Ztrait-solver=next): Overflows are reported as ambig here, is that OK? - if !fulfill_cx.select_where_possible(self.infcx).is_empty() { - return Ok(EvaluatedToErr); - } - if !fulfill_cx.select_all_or_error(self.infcx).is_empty() { - return Ok(EvaluatedToAmbig); - } - // Regions and opaques are handled in the `evaluation_probe` by looking at the snapshot - Ok(EvaluatedToOk) + Ok(result) } #[instrument( @@ -640,7 +624,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { previous_stack: TraitObligationStackList<'o, 'tcx>, obligation: PredicateObligation<'tcx>, ) -> Result<EvaluationResult, OverflowError> { - // `previous_stack` stores a `TraitObligation`, while `obligation` is + debug_assert!(!self.infcx.next_trait_solver()); + // `previous_stack` stores a `PolyTraitObligation`, while `obligation` is // a `PredicateObligation`. These are distinct types, so we can't // use any `Option` combinator method that would force them to be // the same. @@ -652,7 +637,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ensure_sufficient_stack(|| { let bound_predicate = obligation.predicate.kind(); match bound_predicate.skip_binder() { - ty::PredicateKind::Clause(ty::Clause::Trait(t)) => { + ty::PredicateKind::Clause(ty::ClauseKind::Trait(t)) => { let t = bound_predicate.rebind(t); debug_assert!(!t.has_escaping_bound_vars()); let obligation = obligation.with(self.tcx(), t); @@ -683,7 +668,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } - ty::PredicateKind::WellFormed(arg) => { + ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => { // So, there is a bit going on here. First, `WellFormed` predicates // are coinductive, like trait predicates with auto traits. // This means that we need to detect if we have recursively @@ -769,7 +754,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } - ty::PredicateKind::Clause(ty::Clause::TypeOutlives(pred)) => { + ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(pred)) => { // A global type with no free lifetimes or generic parameters // outlives anything. if pred.0.has_free_regions() @@ -783,7 +768,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } - ty::PredicateKind::Clause(ty::Clause::RegionOutlives(..)) => { + ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(..)) => { // We do not consider region relationships when evaluating trait matches. Ok(EvaluatedToOkModuloRegions) } @@ -796,7 +781,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } - ty::PredicateKind::Clause(ty::Clause::Projection(data)) => { + ty::PredicateKind::Clause(ty::ClauseKind::Projection(data)) => { let data = bound_predicate.rebind(data); let project_obligation = obligation.with(self.tcx(), data); match project::poly_project_and_unify_type(self, &project_obligation) { @@ -871,7 +856,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } - ty::PredicateKind::ConstEvaluatable(uv) => { + ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(uv)) => { match const_evaluatable::is_const_evaluatable( self.infcx, uv, @@ -901,7 +886,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ); use rustc_hir::def::DefKind; - use ty::ConstKind::Unevaluated; + use ty::Unevaluated; match (c1.kind(), c2.kind()) { (Unevaluated(a), Unevaluated(b)) if a.def == b.def && tcx.def_kind(a.def) == DefKind::AssocConst => @@ -976,14 +961,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } } - ty::PredicateKind::TypeWellFormedFromEnv(..) => { - bug!("TypeWellFormedFromEnv is only used for chalk") - } ty::PredicateKind::AliasRelate(..) => { bug!("AliasRelate is only used for new solver") } ty::PredicateKind::Ambiguous => Ok(EvaluatedToAmbig), - ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(ct, ty)) => { + ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, ty)) => { match self.infcx.at(&obligation.cause, obligation.param_env).eq( DefineOpaqueTypes::No, ct.ty(), @@ -1004,7 +986,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { fn evaluate_trait_predicate_recursively<'o>( &mut self, previous_stack: TraitObligationStackList<'o, 'tcx>, - mut obligation: TraitObligation<'tcx>, + mut obligation: PolyTraitObligation<'tcx>, ) -> Result<EvaluationResult, OverflowError> { if !self.is_intercrate() && obligation.is_global() @@ -1186,6 +1168,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { &mut self, stack: &TraitObligationStack<'o, 'tcx>, ) -> Result<EvaluationResult, OverflowError> { + debug_assert!(!self.infcx.next_trait_solver()); // In intercrate mode, whenever any of the generics are unbound, // there can always be an impl. Even if there are no impls in // this crate, perhaps the type would be unified with @@ -1409,7 +1392,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { fn filter_impls( &mut self, candidates: Vec<SelectionCandidate<'tcx>>, - obligation: &TraitObligation<'tcx>, + obligation: &PolyTraitObligation<'tcx>, ) -> Vec<SelectionCandidate<'tcx>> { trace!("{candidates:#?}"); let tcx = self.tcx(); @@ -1472,7 +1455,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { fn filter_reservation_impls( &mut self, candidate: SelectionCandidate<'tcx>, - obligation: &TraitObligation<'tcx>, + obligation: &PolyTraitObligation<'tcx>, ) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> { let tcx = self.tcx(); // Treat reservation impls as ambiguity. @@ -1644,7 +1627,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { #[instrument(level = "debug", skip(self), ret)] fn match_projection_obligation_against_definition_bounds( &mut self, - obligation: &TraitObligation<'tcx>, + obligation: &PolyTraitObligation<'tcx>, ) -> smallvec::SmallVec<[(usize, ty::BoundConstness); 2]> { let poly_trait_predicate = self.infcx.resolve_vars_if_possible(obligation.predicate); let placeholder_trait_predicate = @@ -1677,9 +1660,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { .enumerate() .filter_map(|(idx, bound)| { let bound_predicate = bound.kind(); - if let ty::PredicateKind::Clause(ty::Clause::Trait(pred)) = - bound_predicate.skip_binder() - { + if let ty::ClauseKind::Trait(pred) = bound_predicate.skip_binder() { let bound = bound_predicate.rebind(pred.trait_ref); if self.infcx.probe(|_| { match self.match_normalize_trait_ref( @@ -1709,7 +1690,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /// variables or placeholders, the normalized bound is returned. fn match_normalize_trait_ref( &mut self, - obligation: &TraitObligation<'tcx>, + obligation: &PolyTraitObligation<'tcx>, trait_bound: ty::PolyTraitRef<'tcx>, placeholder_trait_ref: ty::TraitRef<'tcx>, ) -> Result<Option<ty::PolyTraitRef<'tcx>>, ()> { @@ -2110,7 +2091,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { impl<'tcx> SelectionContext<'_, 'tcx> { fn sized_conditions( &mut self, - obligation: &TraitObligation<'tcx>, + obligation: &PolyTraitObligation<'tcx>, ) -> BuiltinImplConditions<'tcx> { use self::BuiltinImplConditions::{Ambiguous, None, Where}; @@ -2149,13 +2130,11 @@ impl<'tcx> SelectionContext<'_, 'tcx> { ty::Adt(def, substs) => { let sized_crit = def.sized_constraint(self.tcx()); // (*) binder moved here - Where(obligation.predicate.rebind({ - sized_crit - .0 - .iter() - .map(|ty| sized_crit.rebind(*ty).subst(self.tcx(), substs)) - .collect() - })) + Where( + obligation + .predicate + .rebind(sized_crit.subst_iter_copied(self.tcx(), substs).collect()), + ) } ty::Alias(..) | ty::Param(_) | ty::Placeholder(..) => None, @@ -2172,7 +2151,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { fn copy_clone_conditions( &mut self, - obligation: &TraitObligation<'tcx>, + obligation: &PolyTraitObligation<'tcx>, ) -> BuiltinImplConditions<'tcx> { // NOTE: binder moved to (*) let self_ty = self.infcx.shallow_resolve(obligation.predicate.skip_binder().self_ty()); @@ -2305,8 +2284,8 @@ impl<'tcx> SelectionContext<'_, 'tcx> { fn constituent_types_for_ty( &self, t: ty::Binder<'tcx, Ty<'tcx>>, - ) -> ty::Binder<'tcx, Vec<Ty<'tcx>>> { - match *t.skip_binder().kind() { + ) -> Result<ty::Binder<'tcx, Vec<Ty<'tcx>>>, SelectionError<'tcx>> { + Ok(match *t.skip_binder().kind() { ty::Uint(_) | ty::Int(_) | ty::Bool @@ -2319,13 +2298,13 @@ impl<'tcx> SelectionContext<'_, 'tcx> { | ty::Char => ty::Binder::dummy(Vec::new()), // Treat this like `struct str([u8]);` - ty::Str => ty::Binder::dummy(vec![self.tcx().mk_slice(self.tcx().types.u8)]), + ty::Str => ty::Binder::dummy(vec![Ty::new_slice(self.tcx(), self.tcx().types.u8)]), ty::Placeholder(..) | ty::Dynamic(..) | ty::Param(..) | ty::Foreign(..) - | ty::Alias(ty::Projection | ty::Inherent, ..) + | ty::Alias(ty::Projection | ty::Inherent | ty::Weak, ..) | ty::Bound(..) | ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { bug!("asked to assemble constituent types of unexpected type: {:?}", t); @@ -2370,12 +2349,16 @@ impl<'tcx> SelectionContext<'_, 'tcx> { } ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => { + let ty = self.tcx().type_of(def_id); + if ty.skip_binder().references_error() { + return Err(SelectionError::OpaqueTypeAutoTraitLeakageUnknown(def_id)); + } // We can resolve the `impl Trait` to its concrete type, // which enforces a DAG between the functions requiring // the auto trait bounds in question. - t.rebind(vec![self.tcx().type_of(def_id).subst(self.tcx(), substs)]) + t.rebind(vec![ty.subst(self.tcx(), substs)]) } - } + }) } fn collect_predicates_for_types( @@ -2444,7 +2427,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { fn rematch_impl( &mut self, impl_def_id: DefId, - obligation: &TraitObligation<'tcx>, + obligation: &PolyTraitObligation<'tcx>, ) -> Normalized<'tcx, SubstsRef<'tcx>> { let impl_trait_ref = self.tcx().impl_trait_ref(impl_def_id).unwrap(); match self.match_impl(impl_def_id, impl_trait_ref, obligation) { @@ -2465,7 +2448,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { ), ); let value = self.infcx.fresh_substs_for_item(obligation.cause.span, impl_def_id); - let err = self.tcx().ty_error(guar); + let err = Ty::new_error(self.tcx(), guar); let value = value.fold_with(&mut BottomUpFolder { tcx: self.tcx(), ty_op: |_| err, @@ -2482,7 +2465,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { &mut self, impl_def_id: DefId, impl_trait_ref: EarlyBinder<ty::TraitRef<'tcx>>, - obligation: &TraitObligation<'tcx>, + obligation: &PolyTraitObligation<'tcx>, ) -> Result<Normalized<'tcx, SubstsRef<'tcx>>, ()> { let placeholder_obligation = self.infcx.instantiate_binder_with_placeholders(obligation.predicate); @@ -2540,7 +2523,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { /// result from the normalization. fn match_where_clause_trait_ref( &mut self, - obligation: &TraitObligation<'tcx>, + obligation: &PolyTraitObligation<'tcx>, where_clause_trait_ref: ty::PolyTraitRef<'tcx>, ) -> Result<Vec<PredicateObligation<'tcx>>, ()> { self.match_poly_trait_ref(obligation, where_clause_trait_ref) @@ -2551,7 +2534,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { #[instrument(skip(self), level = "debug")] fn match_poly_trait_ref( &mut self, - obligation: &TraitObligation<'tcx>, + obligation: &PolyTraitObligation<'tcx>, poly_trait_ref: ty::PolyTraitRef<'tcx>, ) -> Result<Vec<PredicateObligation<'tcx>>, ()> { self.infcx @@ -2577,7 +2560,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { fn push_stack<'o>( &mut self, previous_stack: TraitObligationStackList<'o, 'tcx>, - obligation: &'o TraitObligation<'tcx>, + obligation: &'o PolyTraitObligation<'tcx>, ) -> TraitObligationStack<'o, 'tcx> { let fresh_trait_pred = obligation.predicate.fold_with(&mut self.freshener); @@ -2596,7 +2579,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { #[instrument(skip(self), level = "debug")] fn closure_trait_ref_unnormalized( &mut self, - obligation: &TraitObligation<'tcx>, + obligation: &PolyTraitObligation<'tcx>, substs: SubstsRef<'tcx>, ) -> ty::PolyTraitRef<'tcx> { let closure_sig = substs.as_closure().sig(); @@ -2670,7 +2653,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { })) }) }; - let predicate = normalize_with_depth_to( + let clause = normalize_with_depth_to( self, param_env, cause.clone(), @@ -2678,7 +2661,12 @@ impl<'tcx> SelectionContext<'_, 'tcx> { predicate, &mut obligations, ); - obligations.push(Obligation { cause, recursion_depth, param_env, predicate }); + obligations.push(Obligation { + cause, + recursion_depth, + param_env, + predicate: clause.as_predicate(), + }); } obligations @@ -3029,7 +3017,7 @@ fn bind_generator_hidden_types_above<'tcx>( kind: ty::BrAnon(None), }; counter += 1; - tcx.mk_re_late_bound(current_depth, br) + ty::Region::new_late_bound(tcx, current_depth, br) } r => bug!("unexpected region: {r:?}"), }) diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs index 9a4b72013..96f128741 100644 --- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs @@ -238,7 +238,7 @@ fn fulfill_implication<'tcx>( // 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); + let ocx = ObligationCtxt::new(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)); @@ -508,21 +508,14 @@ pub(crate) fn to_pretty_impl_header(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Opti Vec::with_capacity(predicates.len() + types_without_default_bounds.len()); for (mut p, _) in predicates { - if let Some(poly_trait_ref) = p.to_opt_poly_trait_pred() { + if let Some(poly_trait_ref) = p.as_trait_clause() { if Some(poly_trait_ref.def_id()) == sized_trait { types_without_default_bounds.remove(&poly_trait_ref.self_ty().skip_binder()); continue; } if ty::BoundConstness::ConstIfConst == poly_trait_ref.skip_binder().constness { - let new_trait_pred = poly_trait_ref.map_bound(|mut trait_pred| { - trait_pred.constness = ty::BoundConstness::NotConst; - trait_pred - }); - - p = tcx.mk_predicate( - new_trait_pred.map_bound(|p| ty::PredicateKind::Clause(ty::Clause::Trait(p))), - ) + p = p.without_const(tcx); } } pretty_predicates.push(p.to_string()); diff --git a/compiler/rustc_trait_selection/src/traits/structural_match.rs b/compiler/rustc_trait_selection/src/traits/structural_match.rs index e38ae9381..420f8c5dc 100644 --- a/compiler/rustc_trait_selection/src/traits/structural_match.rs +++ b/compiler/rustc_trait_selection/src/traits/structural_match.rs @@ -34,24 +34,7 @@ pub fn search_for_structural_match_violation<'tcx>( tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, ) -> Option<Ty<'tcx>> { - ty.visit_with(&mut Search { tcx, span, seen: FxHashSet::default(), adt_const_param: false }) - .break_value() -} - -/// This method traverses the structure of `ty`, trying to find any -/// types that are not allowed to be used in a const generic. -/// -/// This is either because the type does not implement `StructuralEq` -/// and `StructuralPartialEq`, or because the type is intentionally -/// not supported in const generics (such as floats and raw pointers, -/// which are allowed in match blocks). -pub fn search_for_adt_const_param_violation<'tcx>( - span: Span, - tcx: TyCtxt<'tcx>, - ty: Ty<'tcx>, -) -> Option<Ty<'tcx>> { - ty.visit_with(&mut Search { tcx, span, seen: FxHashSet::default(), adt_const_param: true }) - .break_value() + ty.visit_with(&mut Search { tcx, span, seen: FxHashSet::default() }).break_value() } /// This implements the traversal over the structure of a given type to try to @@ -65,11 +48,6 @@ struct Search<'tcx> { /// Tracks ADTs previously encountered during search, so that /// we will not recur on them again. seen: FxHashSet<hir::def_id::DefId>, - - // Additionally deny things that have been allowed in patterns, - // but are not allowed in adt const params, such as floats and - // fn ptrs. - adt_const_param: bool, } impl<'tcx> Search<'tcx> { @@ -124,41 +102,29 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for Search<'tcx> { } ty::FnPtr(..) => { - if !self.adt_const_param { - return ControlFlow::Continue(()); - } else { - return ControlFlow::Break(ty); - } + return ControlFlow::Continue(()); } ty::RawPtr(..) => { - if !self.adt_const_param { - // structural-match ignores substructure of - // `*const _`/`*mut _`, so skip `super_visit_with`. - // - // For example, if you have: - // ``` - // struct NonStructural; - // #[derive(PartialEq, Eq)] - // struct T(*const NonStructural); - // const C: T = T(std::ptr::null()); - // ``` - // - // Even though `NonStructural` does not implement `PartialEq`, - // structural equality on `T` does not recur into the raw - // pointer. Therefore, one can still use `C` in a pattern. - return ControlFlow::Continue(()); - } else { - return ControlFlow::Break(ty); - } + // structural-match ignores substructure of + // `*const _`/`*mut _`, so skip `super_visit_with`. + // + // For example, if you have: + // ``` + // struct NonStructural; + // #[derive(PartialEq, Eq)] + // struct T(*const NonStructural); + // const C: T = T(std::ptr::null()); + // ``` + // + // Even though `NonStructural` does not implement `PartialEq`, + // structural equality on `T` does not recur into the raw + // pointer. Therefore, one can still use `C` in a pattern. + return ControlFlow::Continue(()); } ty::Float(_) => { - if !self.adt_const_param { - return ControlFlow::Continue(()); - } else { - return ControlFlow::Break(ty); - } + return ControlFlow::Continue(()); } ty::Array(..) | ty::Slice(_) | ty::Ref(..) | ty::Tuple(..) => { diff --git a/compiler/rustc_trait_selection/src/traits/structural_normalize.rs b/compiler/rustc_trait_selection/src/traits/structural_normalize.rs index af8dd0da5..84746eba3 100644 --- a/compiler/rustc_trait_selection/src/traits/structural_normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/structural_normalize.rs @@ -21,7 +21,7 @@ impl<'tcx> StructurallyNormalizeExt<'tcx> for At<'_, 'tcx> { ) -> Result<Ty<'tcx>, Vec<FulfillmentError<'tcx>>> { assert!(!ty.is_ty_var(), "should have resolved vars before calling"); - if self.infcx.tcx.trait_solver_next() { + if self.infcx.next_trait_solver() { while let ty::Alias(ty::Projection, projection_ty) = *ty.kind() { let new_infer_ty = self.infcx.next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::NormalizeProjectionType, diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs index 82f3df401..302b6cacf 100644 --- a/compiler/rustc_trait_selection/src/traits/util.rs +++ b/compiler/rustc_trait_selection/src/traits/util.rs @@ -41,7 +41,12 @@ impl<'tcx> TraitAliasExpansionInfo<'tcx> { /// Adds diagnostic labels to `diag` for the expansion path of a trait through all intermediate /// trait aliases. - pub fn label_with_exp_info(&self, diag: &mut Diagnostic, top_label: &str, use_desc: &str) { + pub fn label_with_exp_info( + &self, + diag: &mut Diagnostic, + top_label: &'static str, + use_desc: &str, + ) { diag.span_label(self.top().1, top_label); if self.path.len() > 1 { for (_, sp) in self.path.iter().rev().skip(1).take(self.path.len() - 2) { @@ -120,7 +125,7 @@ impl<'tcx> TraitAliasExpander<'tcx> { let items = predicates.predicates.iter().rev().filter_map(|(pred, span)| { pred.subst_supertrait(tcx, &trait_ref) - .to_opt_poly_trait_pred() + .as_trait_clause() .map(|trait_ref| item.clone_and_push(trait_ref.map_bound(|t| t.trait_ref), *span)) }); debug!("expand_trait_aliases: items={:?}", items.clone().collect::<Vec<_>>()); @@ -177,7 +182,7 @@ impl Iterator for SupertraitDefIds<'_> { predicates .predicates .iter() - .filter_map(|(pred, _)| pred.to_opt_poly_trait_pred()) + .filter_map(|(pred, _)| pred.as_trait_clause()) .map(|trait_ref| trait_ref.def_id()) .filter(|&super_def_id| visited.insert(super_def_id)), ); @@ -238,12 +243,12 @@ pub fn upcast_choices<'tcx>( /// `object.upcast_trait_ref`) within the vtable for `object`. pub fn get_vtable_index_of_object_method<'tcx, N>( tcx: TyCtxt<'tcx>, - object: &super::ImplSourceObjectData<'tcx, N>, + object: &super::ImplSourceObjectData<N>, method_def_id: DefId, ) -> Option<usize> { // Count number of methods preceding the one we are selecting and // add them to the total offset. - tcx.own_existential_vtable_entries(object.upcast_trait_ref.def_id()) + tcx.own_existential_vtable_entries(tcx.parent(method_def_id)) .iter() .copied() .position(|def_id| def_id == method_def_id) @@ -260,7 +265,7 @@ pub fn closure_trait_ref_and_return_type<'tcx>( assert!(!self_ty.has_escaping_bound_vars()); let arguments_tuple = match tuple_arguments { TupleArgumentsFlag::No => sig.skip_binder().inputs()[0], - TupleArgumentsFlag::Yes => tcx.mk_tup(sig.skip_binder().inputs()), + TupleArgumentsFlag::Yes => Ty::new_tup(tcx, sig.skip_binder().inputs()), }; let trait_ref = ty::TraitRef::new(tcx, fn_trait_def_id, [self_ty, arguments_tuple]); sig.map_bound(|sig| (trait_ref, sig.output())) @@ -290,10 +295,51 @@ pub fn future_trait_ref_and_outputs<'tcx>( 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() + && tcx.defaultness(assoc_item.container_id(tcx)).is_final() } pub enum TupleArgumentsFlag { Yes, No, } + +// Verify that the trait item and its implementation have compatible substs lists +pub fn check_substs_compatible<'tcx>( + tcx: TyCtxt<'tcx>, + assoc_item: ty::AssocItem, + substs: ty::SubstsRef<'tcx>, +) -> bool { + fn check_substs_compatible_inner<'tcx>( + tcx: TyCtxt<'tcx>, + generics: &'tcx ty::Generics, + args: &'tcx [ty::GenericArg<'tcx>], + ) -> bool { + if generics.count() != args.len() { + return false; + } + + let (parent_args, own_args) = args.split_at(generics.parent_count); + + if let Some(parent) = generics.parent + && let parent_generics = tcx.generics_of(parent) + && !check_substs_compatible_inner(tcx, parent_generics, parent_args) { + return false; + } + + for (param, arg) in std::iter::zip(&generics.params, own_args) { + match (¶m.kind, arg.unpack()) { + (ty::GenericParamDefKind::Type { .. }, ty::GenericArgKind::Type(_)) + | (ty::GenericParamDefKind::Lifetime, ty::GenericArgKind::Lifetime(_)) + | (ty::GenericParamDefKind::Const { .. }, ty::GenericArgKind::Const(_)) => {} + _ => return false, + } + } + + true + } + + 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) +} diff --git a/compiler/rustc_trait_selection/src/traits/vtable.rs b/compiler/rustc_trait_selection/src/traits/vtable.rs index cc674ceee..1f83f1f44 100644 --- a/compiler/rustc_trait_selection/src/traits/vtable.rs +++ b/compiler/rustc_trait_selection/src/traits/vtable.rs @@ -15,13 +15,13 @@ use std::fmt::Debug; use std::ops::ControlFlow; #[derive(Clone, Debug)] -pub(super) enum VtblSegment<'tcx> { +pub 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>( +pub fn prepare_vtable_segments<'tcx, T>( tcx: TyCtxt<'tcx>, trait_ref: ty::PolyTraitRef<'tcx>, mut segment_visitor: impl FnMut(VtblSegment<'tcx>) -> ControlFlow<T>, @@ -115,7 +115,7 @@ pub(super) fn prepare_vtable_segments<'tcx, T>( .predicates .into_iter() .filter_map(move |(pred, _)| { - pred.subst_supertrait(tcx, &inner_most_trait_ref).to_opt_poly_trait_pred() + pred.subst_supertrait(tcx, &inner_most_trait_ref).as_trait_clause() }); 'diving_in_skip_visited_traits: loop { @@ -362,7 +362,7 @@ pub(crate) fn vtable_trait_upcasting_coercion_new_vptr_slot<'tcx>( let trait_ref = ty::TraitRef::new(tcx, unsize_trait_did, [source, target]); - match tcx.codegen_select_candidate((ty::ParamEnv::reveal_all(), ty::Binder::dummy(trait_ref))) { + match tcx.codegen_select_candidate((ty::ParamEnv::reveal_all(), trait_ref)) { Ok(ImplSource::TraitUpcasting(implsrc_traitcasting)) => { implsrc_traitcasting.vtable_vptr_slot } diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 086ab32b5..d81722ce2 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -77,12 +77,19 @@ pub fn unnormalized_obligations<'tcx>( param_env: ty::ParamEnv<'tcx>, arg: GenericArg<'tcx>, ) -> Option<Vec<traits::PredicateObligation<'tcx>>> { + debug_assert_eq!(arg, infcx.resolve_vars_if_possible(arg)); + + // However, if `arg` IS an unresolved inference variable, returns `None`, + // because we are not able to make any progress at all. This is to prevent + // "livelock" where we say "$0 is WF if $0 is WF". + if arg.is_non_region_infer() { + return None; + } + if let ty::GenericArgKind::Lifetime(..) = arg.unpack() { return Some(vec![]); } - debug_assert_eq!(arg, infcx.resolve_vars_if_possible(arg)); - let mut wf = WfPredicates { infcx, param_env, @@ -142,29 +149,32 @@ 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::Clause(ty::Clause::Trait(t)) => { + ty::PredicateKind::Clause(ty::ClauseKind::Trait(t)) => { wf.compute_trait_pred(&t, Elaborate::None); } - ty::PredicateKind::Clause(ty::Clause::RegionOutlives(..)) => {} - ty::PredicateKind::Clause(ty::Clause::TypeOutlives(ty::OutlivesPredicate(ty, _reg))) => { + ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(..)) => {} + ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate( + ty, + _reg, + ))) => { wf.compute(ty.into()); } - ty::PredicateKind::Clause(ty::Clause::Projection(t)) => { + ty::PredicateKind::Clause(ty::ClauseKind::Projection(t)) => { wf.compute_projection(t.projection_ty); wf.compute(match t.term.unpack() { ty::TermKind::Ty(ty) => ty.into(), ty::TermKind::Const(c) => c.into(), }) } - ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(ct, ty)) => { + ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, ty)) => { wf.compute(ct.into()); wf.compute(ty.into()); } - ty::PredicateKind::WellFormed(arg) => { + ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => { wf.compute(arg); } - ty::PredicateKind::ConstEvaluatable(ct) => { + ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(ct)) => { wf.compute(ct.into()); } @@ -174,8 +184,7 @@ pub fn predicate_obligations<'tcx>( | ty::PredicateKind::Coerce(..) | ty::PredicateKind::ConstEquate(..) | ty::PredicateKind::Ambiguous - | ty::PredicateKind::AliasRelate(..) - | ty::PredicateKind::TypeWellFormedFromEnv(..) => { + | ty::PredicateKind::AliasRelate(..) => { bug!("We should only wf check where clauses, unexpected predicate: {predicate:?}") } } @@ -247,7 +256,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::Clause(ty::Clause::Projection(proj)) => { + ty::PredicateKind::Clause(ty::ClauseKind::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 @@ -264,7 +273,7 @@ fn extend_cause_with_original_assoc_item_obligation<'tcx>( cause.span = impl_item_span; } } - ty::PredicateKind::Clause(ty::Clause::Trait(pred)) => { + ty::PredicateKind::Clause(ty::ClauseKind::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); @@ -293,6 +302,16 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { } fn normalize(self, infcx: &InferCtxt<'tcx>) -> Vec<traits::PredicateObligation<'tcx>> { + // Do not normalize `wf` obligations with the new solver. + // + // The current deep normalization routine with the new solver does not + // handle ambiguity and the new solver correctly deals with unnnormalized goals. + // If the user relies on normalized types, e.g. for `fn implied_outlives_bounds`, + // it is their responsibility to normalize while avoiding ambiguity. + if infcx.next_trait_solver() { + return self.out; + } + let cause = self.cause(traits::WellFormed(None)); let param_env = self.param_env; let mut obligations = Vec::with_capacity(self.out.len()); @@ -386,7 +405,9 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { cause, depth, param_env, - ty::Binder::dummy(ty::PredicateKind::WellFormed(arg)), + ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed( + arg, + ))), ) }), ); @@ -478,7 +499,9 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { cause.clone(), depth, param_env, - ty::Binder::dummy(ty::PredicateKind::WellFormed(arg)), + ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed( + arg, + ))), ) }), ); @@ -521,8 +544,9 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { let obligations = self.nominal_obligations(uv.def, uv.substs); self.out.extend(obligations); - let predicate = - ty::Binder::dummy(ty::PredicateKind::ConstEvaluatable(ct)); + let predicate = ty::Binder::dummy(ty::PredicateKind::Clause( + ty::ClauseKind::ConstEvaluatable(ct), + )); let cause = self.cause(traits::WellFormed(None)); self.out.push(traits::Obligation::with_depth( self.tcx(), @@ -541,7 +565,9 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { cause, self.recursion_depth, self.param_env, - ty::Binder::dummy(ty::PredicateKind::WellFormed(ct.into())), + ty::Binder::dummy(ty::PredicateKind::Clause( + ty::ClauseKind::WellFormed(ct.into()), + )), )); } ty::ConstKind::Expr(_) => { @@ -552,8 +578,9 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { // 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 predicate = ty::Binder::dummy(ty::PredicateKind::Clause( + ty::ClauseKind::ConstEvaluatable(ct), + )); let cause = self.cause(traits::WellFormed(None)); self.out.push(traits::Obligation::with_depth( self.tcx(), @@ -654,9 +681,9 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { cause, depth, param_env, - ty::Binder::dummy(ty::PredicateKind::Clause(ty::Clause::TypeOutlives( - ty::OutlivesPredicate(rty, r), - ))), + ty::Binder::dummy(ty::PredicateKind::Clause( + ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(rty, r)), + )), )); } } @@ -731,6 +758,11 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { } } + ty::Alias(ty::Weak, ty::AliasTy { def_id, substs, .. }) => { + let obligations = self.nominal_obligations(def_id, substs); + self.out.extend(obligations); + } + ty::Dynamic(data, r, _) => { // WfObject // @@ -779,7 +811,9 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { cause, self.recursion_depth, param_env, - ty::Binder::dummy(ty::PredicateKind::WellFormed(ty.into())), + ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed( + ty.into(), + ))), )); } } @@ -918,7 +952,7 @@ pub fn object_region_bounds<'tcx>( // Since we don't actually *know* the self type for an object, // this "open(err)" serves as a kind of dummy standin -- basically // a placeholder type. - let open_ty = tcx.mk_fresh_ty(0); + let open_ty = Ty::new_fresh(tcx, 0); let predicates = existential_predicates.iter().filter_map(|predicate| { if let ty::ExistentialPredicate::Projection(_) = predicate.skip_binder() { @@ -951,7 +985,7 @@ pub fn object_region_bounds<'tcx>( pub(crate) fn required_region_bounds<'tcx>( tcx: TyCtxt<'tcx>, erased_self_ty: Ty<'tcx>, - predicates: impl Iterator<Item = ty::Predicate<'tcx>>, + predicates: impl Iterator<Item = ty::Clause<'tcx>>, ) -> Vec<ty::Region<'tcx>> { assert!(!erased_self_ty.has_escaping_bound_vars()); @@ -959,24 +993,7 @@ pub(crate) fn required_region_bounds<'tcx>( .filter_map(|pred| { debug!(?pred); match pred.kind().skip_binder() { - ty::PredicateKind::Clause(ty::Clause::Projection(..)) - | ty::PredicateKind::Clause(ty::Clause::Trait(..)) - | ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..)) - | ty::PredicateKind::Subtype(..) - | ty::PredicateKind::Coerce(..) - | ty::PredicateKind::WellFormed(..) - | ty::PredicateKind::ObjectSafe(..) - | ty::PredicateKind::ClosureKind(..) - | ty::PredicateKind::Clause(ty::Clause::RegionOutlives(..)) - | ty::PredicateKind::ConstEvaluatable(..) - | ty::PredicateKind::ConstEquate(..) - | ty::PredicateKind::Ambiguous - | ty::PredicateKind::AliasRelate(..) - | ty::PredicateKind::TypeWellFormedFromEnv(..) => None, - ty::PredicateKind::Clause(ty::Clause::TypeOutlives(ty::OutlivesPredicate( - ref t, - ref r, - ))) => { + ty::ClauseKind::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 @@ -992,6 +1009,12 @@ pub(crate) fn required_region_bounds<'tcx>( None } } + ty::ClauseKind::Trait(_) + | ty::ClauseKind::RegionOutlives(_) + | ty::ClauseKind::Projection(_) + | ty::ClauseKind::ConstArgHasType(_, _) + | ty::ClauseKind::WellFormed(_) + | ty::ClauseKind::ConstEvaluatable(_) => None, } }) .collect() |