diff options
Diffstat (limited to 'compiler/rustc_trait_selection/src')
14 files changed, 344 insertions, 207 deletions
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/call_kind.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/call_kind.rs index 1c3e570b676..d8b405e904c 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/call_kind.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/call_kind.rs @@ -81,9 +81,7 @@ pub fn call_kind<'tcx>( } }); - let fn_call = parent.and_then(|p| { - lang_items::FN_TRAITS.iter().filter_map(|&l| tcx.lang_items().get(l)).find(|&id| id == p) - }); + let fn_call = parent.filter(|&p| tcx.fn_trait_kind_from_def_id(p).is_some()); let operator = if !from_hir_call && let Some(p) = parent { lang_items::OPERATORS.iter().filter_map(|&l| tcx.lang_items().get(l)).find(|&id| id == p) diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 5648021f613..df6e8fc4503 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -146,7 +146,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { && leaf_trait_predicate.def_id() != root_pred.def_id() // The root trait is not `Unsize`, as to avoid talking about it in // `tests/ui/coercion/coerce-issue-49593-box-never.rs`. - && Some(root_pred.def_id()) != self.tcx.lang_items().unsize_trait() + && !self.tcx.is_lang_item(root_pred.def_id(), LangItem::Unsize) { ( self.resolve_vars_if_possible( @@ -2274,10 +2274,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // auto-traits or fundamental traits that might not be exactly what // the user might expect to be presented with. Instead this is // useful for less general traits. - if peeled - && !self.tcx.trait_is_auto(def_id) - && !self.tcx.lang_items().iter().any(|(_, id)| id == def_id) - { + if peeled && !self.tcx.trait_is_auto(def_id) && self.tcx.as_lang_item(def_id).is_none() { let impl_candidates = self.find_similar_impl_candidates(trait_pred); self.report_similar_impl_candidates( &impl_candidates, @@ -3013,8 +3010,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // This shouldn't be common unless manually implementing one of the // traits manually, but don't make it more confusing when it does // happen. - if Some(expected_trait_ref.def_id) != self.tcx.lang_items().coroutine_trait() && not_tupled - { + if !self.tcx.is_lang_item(expected_trait_ref.def_id, LangItem::Coroutine) && not_tupled { return Ok(self.report_and_explain_type_error( TypeTrace::trait_refs(&obligation.cause, expected_trait_ref, found_trait_ref), obligation.param_env, diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 7d95a7b3fed..de251ae2893 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -3844,12 +3844,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ); if let ty::PredicateKind::Clause(clause) = failed_pred.kind().skip_binder() && let ty::ClauseKind::Trait(pred) = clause - && [ - tcx.lang_items().fn_once_trait(), - tcx.lang_items().fn_mut_trait(), - tcx.lang_items().fn_trait(), - ] - .contains(&Some(pred.def_id())) + && tcx.fn_trait_kind_from_def_id(pred.def_id()).is_some() { if let [stmt, ..] = block.stmts && let hir::StmtKind::Semi(value) = stmt.kind diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs index 756d9a57b93..1063115ed23 100644 --- a/compiler/rustc_trait_selection/src/errors.rs +++ b/compiler/rustc_trait_selection/src/errors.rs @@ -9,7 +9,7 @@ use rustc_errors::{ use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::{Visitor, VisitorExt, walk_ty}; -use rustc_hir::{self as hir, AmbigArg, FnRetTy, GenericParamKind, IsAnonInPath, Node}; +use rustc_hir::{self as hir, AmbigArg, FnRetTy, GenericParamKind, Node}; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_middle::ty::print::{PrintTraitRefExt as _, TraitRefPrintOnlyTraitPath}; use rustc_middle::ty::{self, Binder, ClosureKind, FnSig, GenericArg, Region, Ty, TyCtxt}; @@ -551,19 +551,6 @@ impl Subdiagnostic for AddLifetimeParamsSuggestion<'_> { impl<'v> Visitor<'v> for ImplicitLifetimeFinder { fn visit_ty(&mut self, ty: &'v hir::Ty<'v, AmbigArg>) { - let make_suggestion = |lifetime: &hir::Lifetime| { - if lifetime.is_anon_in_path == IsAnonInPath::Yes - && lifetime.ident.span.is_empty() - { - format!("{}, ", self.suggestion_param_name) - } else if lifetime.ident.name == kw::UnderscoreLifetime - && lifetime.ident.span.is_empty() - { - format!("{} ", self.suggestion_param_name) - } else { - self.suggestion_param_name.clone() - } - }; match ty.kind { hir::TyKind::Path(hir::QPath::Resolved(_, path)) => { for segment in path.segments { @@ -572,7 +559,7 @@ impl Subdiagnostic for AddLifetimeParamsSuggestion<'_> { matches!( arg, hir::GenericArg::Lifetime(lifetime) - if lifetime.is_anon_in_path == IsAnonInPath::Yes + if lifetime.is_syntactically_hidden() ) }) { self.suggestions.push(( @@ -591,10 +578,10 @@ impl Subdiagnostic for AddLifetimeParamsSuggestion<'_> { if let hir::GenericArg::Lifetime(lifetime) = arg && lifetime.is_anonymous() { - self.suggestions.push(( - lifetime.ident.span, - make_suggestion(lifetime), - )); + self.suggestions.push( + lifetime + .suggestion(&self.suggestion_param_name), + ); } } } @@ -602,7 +589,7 @@ impl Subdiagnostic for AddLifetimeParamsSuggestion<'_> { } } hir::TyKind::Ref(lifetime, ..) if lifetime.is_anonymous() => { - self.suggestions.push((lifetime.ident.span, make_suggestion(lifetime))); + self.suggestions.push(lifetime.suggestion(&self.suggestion_param_name)); } _ => {} } diff --git a/compiler/rustc_trait_selection/src/infer.rs b/compiler/rustc_trait_selection/src/infer.rs index 84ac229b743..0dab3adadb0 100644 --- a/compiler/rustc_trait_selection/src/infer.rs +++ b/compiler/rustc_trait_selection/src/infer.rs @@ -34,7 +34,7 @@ impl<'tcx> InferCtxt<'tcx> { // FIXME(#132279): This should be removed as it causes us to incorrectly // handle opaques in their defining scope. - if !(param_env, ty).has_infer() { + if !self.next_trait_solver() && !(param_env, ty).has_infer() { return self.tcx.type_is_copy_modulo_regions(self.typing_env(param_env), ty); } diff --git a/compiler/rustc_trait_selection/src/solve.rs b/compiler/rustc_trait_selection/src/solve.rs index d425ab50ae0..0c2451a80a7 100644 --- a/compiler/rustc_trait_selection/src/solve.rs +++ b/compiler/rustc_trait_selection/src/solve.rs @@ -9,5 +9,8 @@ mod select; pub(crate) use delegate::SolverDelegate; pub use fulfill::{FulfillmentCtxt, NextSolverError}; pub(crate) use normalize::deeply_normalize_for_diagnostics; -pub use normalize::{deeply_normalize, deeply_normalize_with_skipped_universes}; +pub use normalize::{ + deeply_normalize, deeply_normalize_with_skipped_universes, + deeply_normalize_with_skipped_universes_and_ambiguous_goals, +}; pub use select::InferCtxtSelectExt; diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index 192e632a2d5..848d0646d00 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -1,18 +1,26 @@ use std::marker::PhantomData; use std::mem; +use std::ops::ControlFlow; use rustc_data_structures::thinvec::ExtractIf; +use rustc_hir::def_id::LocalDefId; use rustc_infer::infer::InferCtxt; use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::{ FromSolverError, PredicateObligation, PredicateObligations, TraitEngine, }; +use rustc_middle::ty::{ + self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, TypingMode, +}; use rustc_next_trait_solver::solve::{GenerateProofTree, HasChanged, SolverDelegateEvalExt as _}; +use rustc_span::Span; +use rustc_type_ir::data_structures::DelayedSet; use tracing::instrument; use self::derive_errors::*; use super::Certainty; use super::delegate::SolverDelegate; +use super::inspect::{self, ProofTreeInferCtxtExt}; use crate::traits::{FulfillmentError, ScrubbedTraitError}; mod derive_errors; @@ -39,7 +47,7 @@ pub struct FulfillmentCtxt<'tcx, E: 'tcx> { _errors: PhantomData<E>, } -#[derive(Default)] +#[derive(Default, Debug)] struct ObligationStorage<'tcx> { /// Obligations which resulted in an overflow in fulfillment itself. /// @@ -55,20 +63,23 @@ impl<'tcx> ObligationStorage<'tcx> { self.pending.push(obligation); } + fn has_pending_obligations(&self) -> bool { + !self.pending.is_empty() || !self.overflowed.is_empty() + } + fn clone_pending(&self) -> PredicateObligations<'tcx> { let mut obligations = self.pending.clone(); obligations.extend(self.overflowed.iter().cloned()); obligations } - fn take_pending(&mut self) -> PredicateObligations<'tcx> { - let mut obligations = mem::take(&mut self.pending); - obligations.append(&mut self.overflowed); - obligations - } - - fn unstalled_for_select(&mut self) -> impl Iterator<Item = PredicateObligation<'tcx>> + 'tcx { - mem::take(&mut self.pending).into_iter() + fn drain_pending( + &mut self, + cond: impl Fn(&PredicateObligation<'tcx>) -> bool, + ) -> PredicateObligations<'tcx> { + let (unstalled, pending) = mem::take(&mut self.pending).into_iter().partition(cond); + self.pending = pending; + unstalled } fn on_fulfillment_overflow(&mut self, infcx: &InferCtxt<'tcx>) { @@ -160,7 +171,7 @@ where } let mut has_changed = false; - for obligation in self.obligations.unstalled_for_select() { + for obligation in self.obligations.drain_pending(|_| true) { let goal = obligation.as_goal(); let result = <&SolverDelegate<'tcx>>::from(infcx) .evaluate_root_goal(goal, GenerateProofTree::No, obligation.cause.span) @@ -196,15 +207,95 @@ where } fn has_pending_obligations(&self) -> bool { - !self.obligations.pending.is_empty() || !self.obligations.overflowed.is_empty() + self.obligations.has_pending_obligations() } fn pending_obligations(&self) -> PredicateObligations<'tcx> { self.obligations.clone_pending() } - fn drain_unstalled_obligations(&mut self, _: &InferCtxt<'tcx>) -> PredicateObligations<'tcx> { - self.obligations.take_pending() + fn drain_stalled_obligations_for_coroutines( + &mut self, + infcx: &InferCtxt<'tcx>, + ) -> PredicateObligations<'tcx> { + let stalled_generators = match infcx.typing_mode() { + TypingMode::Analysis { defining_opaque_types_and_generators } => { + defining_opaque_types_and_generators + } + TypingMode::Coherence + | TypingMode::Borrowck { defining_opaque_types: _ } + | TypingMode::PostBorrowckAnalysis { defined_opaque_types: _ } + | TypingMode::PostAnalysis => return Default::default(), + }; + + if stalled_generators.is_empty() { + return Default::default(); + } + + self.obligations.drain_pending(|obl| { + infcx.probe(|_| { + infcx + .visit_proof_tree( + obl.as_goal(), + &mut StalledOnCoroutines { + stalled_generators, + span: obl.cause.span, + cache: Default::default(), + }, + ) + .is_break() + }) + }) + } +} + +/// Detect if a goal is stalled on a coroutine that is owned by the current typeck root. +/// +/// This function can (erroneously) fail to detect a predicate, i.e. it doesn't need to +/// be complete. However, this will lead to ambiguity errors, so we want to make it +/// accurate. +/// +/// This function can be also return false positives, which will lead to poor diagnostics +/// so we want to keep this visitor *precise* too. +struct StalledOnCoroutines<'tcx> { + stalled_generators: &'tcx ty::List<LocalDefId>, + span: Span, + cache: DelayedSet<Ty<'tcx>>, +} + +impl<'tcx> inspect::ProofTreeVisitor<'tcx> for StalledOnCoroutines<'tcx> { + type Result = ControlFlow<()>; + + fn span(&self) -> rustc_span::Span { + self.span + } + + fn visit_goal(&mut self, inspect_goal: &super::inspect::InspectGoal<'_, 'tcx>) -> Self::Result { + inspect_goal.goal().predicate.visit_with(self)?; + + if let Some(candidate) = inspect_goal.unique_applicable_candidate() { + candidate.visit_nested_no_probe(self) + } else { + ControlFlow::Continue(()) + } + } +} + +impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for StalledOnCoroutines<'tcx> { + type Result = ControlFlow<()>; + + fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result { + if !self.cache.insert(ty) { + return ControlFlow::Continue(()); + } + + if let ty::CoroutineWitness(def_id, _) = *ty.kind() + && def_id.as_local().is_some_and(|def_id| self.stalled_generators.contains(&def_id)) + { + return ControlFlow::Break(()); + } + + ty.super_visit_with(self) } } diff --git a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs index d8dcd12aecb..f6c650c68c0 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs @@ -1,5 +1,6 @@ use std::ops::ControlFlow; +use rustc_hir::LangItem; use rustc_infer::infer::InferCtxt; use rustc_infer::traits::solve::{CandidateSource, GoalSource, MaybeCause}; use rustc_infer::traits::{ @@ -109,10 +110,16 @@ pub(super) fn fulfillment_error_for_stalled<'tcx>( false, ), Ok((_, Certainty::Yes)) => { - bug!("did not expect successful goal when collecting ambiguity errors") + bug!( + "did not expect successful goal when collecting ambiguity errors for `{:?}`", + infcx.resolve_vars_if_possible(root_obligation.predicate), + ) } Err(_) => { - bug!("did not expect selection error when collecting ambiguity errors") + bug!( + "did not expect selection error when collecting ambiguity errors for `{:?}`", + infcx.resolve_vars_if_possible(root_obligation.predicate), + ) } } }); @@ -452,9 +459,8 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> { // We do this as a separate loop so that we do not choose to tell the user about some nested // goal before we encounter a `T: FnPtr` nested goal. for nested_goal in &nested_goals { - if let Some(fn_ptr_trait) = tcx.lang_items().fn_ptr_trait() - && let Some(poly_trait_pred) = nested_goal.goal().predicate.as_trait_clause() - && poly_trait_pred.def_id() == fn_ptr_trait + if let Some(poly_trait_pred) = nested_goal.goal().predicate.as_trait_clause() + && tcx.is_lang_item(poly_trait_pred.def_id(), LangItem::FnPtrTrait) && let Err(NoSolution) = nested_goal.result() { return ControlFlow::Break(self.obligation.clone()); diff --git a/compiler/rustc_trait_selection/src/solve/normalize.rs b/compiler/rustc_trait_selection/src/solve/normalize.rs index 79fb044a67f..5f1e63ab225 100644 --- a/compiler/rustc_trait_selection/src/solve/normalize.rs +++ b/compiler/rustc_trait_selection/src/solve/normalize.rs @@ -1,10 +1,10 @@ use std::assert_matches::assert_matches; use std::fmt::Debug; -use std::marker::PhantomData; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_infer::infer::InferCtxt; use rustc_infer::infer::at::At; +use rustc_infer::traits::solve::Goal; use rustc_infer::traits::{FromSolverError, Obligation, TraitEngine}; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::{ @@ -45,11 +45,37 @@ where T: TypeFoldable<TyCtxt<'tcx>>, E: FromSolverError<'tcx, NextSolverError<'tcx>>, { + let (value, goals) = + deeply_normalize_with_skipped_universes_and_ambiguous_goals(at, value, universes)?; + assert_eq!(goals, vec![]); + + Ok(value) +} + +/// Deeply normalize all aliases in `value`. This does not handle inference and expects +/// its input to be already fully resolved. +/// +/// Additionally takes a list of universes which represents the binders which have been +/// entered before passing `value` to the function. This is currently needed for +/// `normalize_erasing_regions`, which skips binders as it walks through a type. +/// +/// This returns a set of stalled obligations if the typing mode of the underlying infcx +/// has any stalled coroutine def ids. +pub fn deeply_normalize_with_skipped_universes_and_ambiguous_goals<'tcx, T, E>( + at: At<'_, 'tcx>, + value: T, + universes: Vec<Option<UniverseIndex>>, +) -> Result<(T, Vec<Goal<'tcx, ty::Predicate<'tcx>>>), Vec<E>> +where + T: TypeFoldable<TyCtxt<'tcx>>, + E: FromSolverError<'tcx, NextSolverError<'tcx>>, +{ let fulfill_cx = FulfillmentCtxt::new(at.infcx); let mut folder = - NormalizationFolder { at, fulfill_cx, depth: 0, universes, _errors: PhantomData }; - - value.try_fold_with(&mut folder) + NormalizationFolder { at, fulfill_cx, depth: 0, universes, stalled_goals: vec![] }; + let value = value.try_fold_with(&mut folder)?; + let errors = folder.fulfill_cx.select_all_or_error(at.infcx); + if errors.is_empty() { Ok((value, folder.stalled_goals)) } else { Err(errors) } } struct NormalizationFolder<'me, 'tcx, E> { @@ -57,7 +83,7 @@ struct NormalizationFolder<'me, 'tcx, E> { fulfill_cx: FulfillmentCtxt<'tcx, E>, depth: usize, universes: Vec<Option<UniverseIndex>>, - _errors: PhantomData<E>, + stalled_goals: Vec<Goal<'tcx, ty::Predicate<'tcx>>>, } impl<'tcx, E> NormalizationFolder<'_, 'tcx, E> @@ -98,10 +124,7 @@ where ); 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); - } + self.select_all_and_stall_coroutine_predicates()?; // Alias is guaranteed to be fully structurally resolved, // so we can super fold here. @@ -139,7 +162,7 @@ where 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); + let errors = self.fulfill_cx.select_where_possible(infcx); if !errors.is_empty() { return Err(errors); } @@ -152,6 +175,27 @@ where self.depth -= 1; Ok(result) } + + fn select_all_and_stall_coroutine_predicates(&mut self) -> Result<(), Vec<E>> { + let errors = self.fulfill_cx.select_where_possible(self.at.infcx); + if !errors.is_empty() { + return Err(errors); + } + + self.stalled_goals.extend( + self.fulfill_cx + .drain_stalled_obligations_for_coroutines(self.at.infcx) + .into_iter() + .map(|obl| obl.as_goal()), + ); + + let errors = self.fulfill_cx.collect_remaining_errors(self.at.infcx); + if !errors.is_empty() { + return Err(errors); + } + + Ok(()) + } } impl<'tcx, E> FallibleTypeFolder<TyCtxt<'tcx>> for NormalizationFolder<'_, 'tcx, E> @@ -254,27 +298,31 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for DeeplyNormalizeForDiagnosticsFolder<'_, fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { let infcx = self.at.infcx; - infcx - .commit_if_ok(|_| { - deeply_normalize_with_skipped_universes( - self.at, - ty, - vec![None; ty.outer_exclusive_binder().as_usize()], - ) - }) - .unwrap_or_else(|_: Vec<ScrubbedTraitError<'tcx>>| ty.super_fold_with(self)) + let result = + infcx.commit_if_ok(|_| { + deeply_normalize_with_skipped_universes_and_ambiguous_goals::< + _, + ScrubbedTraitError<'tcx>, + >(self.at, ty, vec![None; ty.outer_exclusive_binder().as_usize()]) + }); + match result { + Ok((ty, _)) => ty, + Err(_) => ty.super_fold_with(self), + } } fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { let infcx = self.at.infcx; - infcx - .commit_if_ok(|_| { - deeply_normalize_with_skipped_universes( - self.at, - ct, - vec![None; ct.outer_exclusive_binder().as_usize()], - ) - }) - .unwrap_or_else(|_: Vec<ScrubbedTraitError<'tcx>>| ct.super_fold_with(self)) + let result = + infcx.commit_if_ok(|_| { + deeply_normalize_with_skipped_universes_and_ambiguous_goals::< + _, + ScrubbedTraitError<'tcx>, + >(self.at, ct, vec![None; ct.outer_exclusive_binder().as_usize()]) + }); + match result { + Ok((ct, _)) => ct, + Err(_) => ct.super_fold_with(self), + } } } diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 1b76d48e431..a11f8d3a9ec 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -162,7 +162,7 @@ where self.select(selcx) } - fn drain_unstalled_obligations( + fn drain_stalled_obligations_for_coroutines( &mut self, infcx: &InferCtxt<'tcx>, ) -> PredicateObligations<'tcx> { diff --git a/compiler/rustc_trait_selection/src/traits/normalize.rs b/compiler/rustc_trait_selection/src/traits/normalize.rs index 4ac45172a0e..7551ac5aa97 100644 --- a/compiler/rustc_trait_selection/src/traits/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/normalize.rs @@ -77,7 +77,15 @@ impl<'tcx> At<'_, 'tcx> { .into_value_registering_obligations(self.infcx, &mut *fulfill_cx); let errors = fulfill_cx.select_all_or_error(self.infcx); let value = self.infcx.resolve_vars_if_possible(value); - if errors.is_empty() { Ok(value) } else { Err(errors) } + if errors.is_empty() { + Ok(value) + } else { + // Drop pending obligations, since deep normalization may happen + // in a loop and we don't want to trigger the assertion on the next + // iteration due to pending ambiguous obligations we've left over. + let _ = fulfill_cx.collect_remaining_errors(self.infcx); + Err(errors) + } } } } diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 0dce504903c..99fa791b375 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -965,36 +965,38 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( let self_ty = selcx.infcx.shallow_resolve(obligation.predicate.self_ty()); let tcx = selcx.tcx(); - let lang_items = selcx.tcx().lang_items(); - if [ - lang_items.coroutine_trait(), - lang_items.future_trait(), - lang_items.iterator_trait(), - lang_items.async_iterator_trait(), - lang_items.fn_trait(), - lang_items.fn_mut_trait(), - lang_items.fn_once_trait(), - lang_items.async_fn_trait(), - lang_items.async_fn_mut_trait(), - lang_items.async_fn_once_trait(), - ] - .contains(&Some(trait_ref.def_id)) - { - true - } else if tcx.is_lang_item(trait_ref.def_id, LangItem::AsyncFnKindHelper) { - // FIXME(async_closures): Validity constraints here could be cleaned up. - if obligation.predicate.args.type_at(0).is_ty_var() - || obligation.predicate.args.type_at(4).is_ty_var() - || obligation.predicate.args.type_at(5).is_ty_var() - { - candidate_set.mark_ambiguous(); - true - } else { - obligation.predicate.args.type_at(0).to_opt_closure_kind().is_some() - && obligation.predicate.args.type_at(1).to_opt_closure_kind().is_some() + match selcx.tcx().as_lang_item(trait_ref.def_id) { + Some( + LangItem::Coroutine + | LangItem::Future + | LangItem::Iterator + | LangItem::AsyncIterator + | LangItem::Fn + | LangItem::FnMut + | LangItem::FnOnce + | LangItem::AsyncFn + | LangItem::AsyncFnMut + | LangItem::AsyncFnOnce, + ) => true, + Some(LangItem::AsyncFnKindHelper) => { + // FIXME(async_closures): Validity constraints here could be cleaned up. + if obligation.predicate.args.type_at(0).is_ty_var() + || obligation.predicate.args.type_at(4).is_ty_var() + || obligation.predicate.args.type_at(5).is_ty_var() + { + candidate_set.mark_ambiguous(); + true + } else { + obligation.predicate.args.type_at(0).to_opt_closure_kind().is_some() + && obligation + .predicate + .args + .type_at(1) + .to_opt_closure_kind() + .is_some() + } } - } else if tcx.is_lang_item(trait_ref.def_id, LangItem::DiscriminantKind) { - match self_ty.kind() { + Some(LangItem::DiscriminantKind) => match self_ty.kind() { ty::Bool | ty::Char | ty::Int(_) @@ -1031,9 +1033,8 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( | ty::Placeholder(..) | ty::Infer(..) | ty::Error(_) => false, - } - } else if tcx.is_lang_item(trait_ref.def_id, LangItem::AsyncDestruct) { - match self_ty.kind() { + }, + Some(LangItem::AsyncDestruct) => match self_ty.kind() { ty::Bool | ty::Char | ty::Int(_) @@ -1068,101 +1069,104 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( | ty::Placeholder(..) | ty::Infer(_) | ty::Error(_) => false, - } - } else if tcx.is_lang_item(trait_ref.def_id, LangItem::PointeeTrait) { - let tail = selcx.tcx().struct_tail_raw( - self_ty, - |ty| { - // We throw away any obligations we get from this, since we normalize - // and confirm these obligations once again during confirmation - normalize_with_depth( - selcx, - obligation.param_env, - obligation.cause.clone(), - obligation.recursion_depth + 1, - ty, - ) - .value - }, - || {}, - ); + }, + Some(LangItem::PointeeTrait) => { + let tail = selcx.tcx().struct_tail_raw( + self_ty, + |ty| { + // We throw away any obligations we get from this, since we normalize + // and confirm these obligations once again during confirmation + normalize_with_depth( + selcx, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + ty, + ) + .value + }, + || {}, + ); - match tail.kind() { - ty::Bool - | ty::Char - | ty::Int(_) - | ty::Uint(_) - | ty::Float(_) - | ty::Str - | ty::Array(..) - | ty::Pat(..) - | ty::Slice(_) - | ty::RawPtr(..) - | ty::Ref(..) - | ty::FnDef(..) - | ty::FnPtr(..) - | ty::Dynamic(..) - | ty::Closure(..) - | ty::CoroutineClosure(..) - | ty::Coroutine(..) - | ty::CoroutineWitness(..) - | ty::Never - // Extern types have unit metadata, according to RFC 2850 - | ty::Foreign(_) - // If returned by `struct_tail` this is a unit struct - // without any fields, or not a struct, and therefore is Sized. - | ty::Adt(..) - // If returned by `struct_tail` this is the empty tuple. - | ty::Tuple(..) - // Integers and floats are always Sized, and so have unit type metadata. - | ty::Infer(ty::InferTy::IntVar(_) | ty::InferTy::FloatVar(..)) - // This happens if we reach the recursion limit when finding the struct tail. - | ty::Error(..) => true, - - // We normalize from `Wrapper<Tail>::Metadata` to `Tail::Metadata` if able. - // Otherwise, type parameters, opaques, and unnormalized projections have - // unit metadata if they're known (e.g. by the param_env) to be sized. - ty::Param(_) | ty::Alias(..) - if self_ty != tail - || selcx.infcx.predicate_must_hold_modulo_regions( - &obligation.with( - selcx.tcx(), - ty::TraitRef::new( + match tail.kind() { + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Str + | ty::Array(..) + | ty::Pat(..) + | ty::Slice(_) + | ty::RawPtr(..) + | ty::Ref(..) + | ty::FnDef(..) + | ty::FnPtr(..) + | ty::Dynamic(..) + | ty::Closure(..) + | ty::CoroutineClosure(..) + | ty::Coroutine(..) + | ty::CoroutineWitness(..) + | ty::Never + // Extern types have unit metadata, according to RFC 2850 + | ty::Foreign(_) + // If returned by `struct_tail` this is a unit struct + // without any fields, or not a struct, and therefore is Sized. + | ty::Adt(..) + // If returned by `struct_tail` this is the empty tuple. + | ty::Tuple(..) + // Integers and floats are always Sized, and so have unit type metadata. + | ty::Infer(ty::InferTy::IntVar(_) | ty::InferTy::FloatVar(..)) + // This happens if we reach the recursion limit when finding the struct tail. + | ty::Error(..) => true, + + // We normalize from `Wrapper<Tail>::Metadata` to `Tail::Metadata` if able. + // Otherwise, type parameters, opaques, and unnormalized projections have + // unit metadata if they're known (e.g. by the param_env) to be sized. + ty::Param(_) | ty::Alias(..) + if self_ty != tail + || selcx.infcx.predicate_must_hold_modulo_regions( + &obligation.with( selcx.tcx(), - selcx.tcx().require_lang_item( - LangItem::Sized, - Some(obligation.cause.span), + ty::TraitRef::new( + selcx.tcx(), + selcx.tcx().require_lang_item( + LangItem::Sized, + Some(obligation.cause.span), + ), + [self_ty], ), - [self_ty], ), - ), - ) => - { - true - } + ) => + { + true + } - ty::UnsafeBinder(_) => todo!("FIXME(unsafe_binder)"), + ty::UnsafeBinder(_) => todo!("FIXME(unsafe_binder)"), - // FIXME(compiler-errors): are Bound and Placeholder types ever known sized? - ty::Param(_) - | ty::Alias(..) - | ty::Bound(..) - | ty::Placeholder(..) - | ty::Infer(..) => { - if tail.has_infer_types() { - candidate_set.mark_ambiguous(); + // FIXME(compiler-errors): are Bound and Placeholder types ever known sized? + ty::Param(_) + | ty::Alias(..) + | ty::Bound(..) + | ty::Placeholder(..) + | ty::Infer(..) => { + if tail.has_infer_types() { + candidate_set.mark_ambiguous(); + } + false } - false } } - } else if tcx.trait_is_auto(trait_ref.def_id) { - tcx.dcx().span_delayed_bug( - tcx.def_span(obligation.predicate.def_id), - "associated types not allowed on auto traits", - ); - false - } else { - bug!("unexpected builtin trait with associated type: {trait_ref:?}") + _ if tcx.trait_is_auto(trait_ref.def_id) => { + tcx.dcx().span_delayed_bug( + tcx.def_span(obligation.predicate.def_id), + "associated types not allowed on auto traits", + ); + false + } + _ => { + bug!("unexpected builtin trait with associated type: {trait_ref:?}") + } } } ImplSource::Param(..) => { 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 4eecde00eaa..f059bd00768 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 @@ -117,8 +117,7 @@ fn relate_mir_and_user_args<'tcx>( CRATE_DEF_ID, ObligationCauseCode::AscribeUserTypeProvePredicate(predicate_span), ); - let instantiated_predicate = - ocx.normalize(&cause.clone(), param_env, instantiated_predicate); + let instantiated_predicate = ocx.normalize(&cause, param_env, instantiated_predicate); ocx.register_obligation(Obligation::new(tcx, cause, param_env, instantiated_predicate)); } diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 4d88a23250a..c7ce13c8014 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -1498,7 +1498,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // However, if we disqualify *all* goals from being cached, perf suffers. // This is likely fixed by better caching in general in the new solver. // See: <https://github.com/rust-lang/rust/issues/132064>. - TypingMode::Analysis { defining_opaque_types } + TypingMode::Analysis { + defining_opaque_types_and_generators: defining_opaque_types, + } | TypingMode::Borrowck { defining_opaque_types } => { defining_opaque_types.is_empty() || !pred.has_opaque_types() } |
