diff options
Diffstat (limited to 'compiler/rustc_trait_selection/src')
64 files changed, 36061 insertions, 0 deletions
diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs new file mode 100644 index 00000000000..bea6fbd6ac5 --- /dev/null +++ b/compiler/rustc_trait_selection/src/errors.rs @@ -0,0 +1,169 @@ +use crate::fluent_generated as fluent; +use rustc_errors::{ + AddToDiagnostic, Applicability, DiagCtxt, Diagnostic, DiagnosticBuilder, EmissionGuarantee, + IntoDiagnostic, Level, SubdiagnosticMessage, +}; +use rustc_macros::Diagnostic; +use rustc_middle::ty::{self, ClosureKind, PolyTraitRef, Ty}; +use rustc_span::{Span, Symbol}; + +#[derive(Diagnostic)] +#[diag(trait_selection_dump_vtable_entries)] +pub struct DumpVTableEntries<'a> { + #[primary_span] + pub span: Span, + pub trait_ref: PolyTraitRef<'a>, + pub entries: String, +} + +#[derive(Diagnostic)] +#[diag(trait_selection_unable_to_construct_constant_value)] +pub struct UnableToConstructConstantValue<'a> { + #[primary_span] + pub span: Span, + pub unevaluated: ty::UnevaluatedConst<'a>, +} + +#[derive(Diagnostic)] +#[diag(trait_selection_empty_on_clause_in_rustc_on_unimplemented, code = "E0232")] +pub struct EmptyOnClauseInOnUnimplemented { + #[primary_span] + #[label] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(trait_selection_invalid_on_clause_in_rustc_on_unimplemented, code = "E0232")] +pub struct InvalidOnClauseInOnUnimplemented { + #[primary_span] + #[label] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(trait_selection_no_value_in_rustc_on_unimplemented, code = "E0232")] +#[note] +pub struct NoValueInOnUnimplemented { + #[primary_span] + #[label] + pub span: Span, +} + +pub struct NegativePositiveConflict<'tcx> { + pub impl_span: Span, + pub trait_desc: ty::TraitRef<'tcx>, + pub self_ty: Option<Ty<'tcx>>, + pub negative_impl_span: Result<Span, Symbol>, + pub positive_impl_span: Result<Span, Symbol>, +} + +impl<G: EmissionGuarantee> IntoDiagnostic<'_, G> for NegativePositiveConflict<'_> { + #[track_caller] + fn into_diagnostic( + self, + dcx: &DiagCtxt, + level: Level, + ) -> rustc_errors::DiagnosticBuilder<'_, G> { + let mut diag = + DiagnosticBuilder::new(dcx, level, fluent::trait_selection_negative_positive_conflict); + diag.set_arg("trait_desc", self.trait_desc.print_only_trait_path().to_string()); + diag.set_arg( + "self_desc", + self.self_ty.map_or_else(|| "none".to_string(), |ty| ty.to_string()), + ); + diag.set_span(self.impl_span); + diag.code(rustc_errors::error_code!(E0751)); + match self.negative_impl_span { + Ok(span) => { + diag.span_label(span, fluent::trait_selection_negative_implementation_here); + } + Err(cname) => { + diag.note(fluent::trait_selection_negative_implementation_in_crate); + diag.set_arg("negative_impl_cname", cname.to_string()); + } + } + match self.positive_impl_span { + Ok(span) => { + diag.span_label(span, fluent::trait_selection_positive_implementation_here); + } + Err(cname) => { + diag.note(fluent::trait_selection_positive_implementation_in_crate); + diag.set_arg("positive_impl_cname", cname.to_string()); + } + } + diag + } +} + +#[derive(Diagnostic)] +#[diag(trait_selection_inherent_projection_normalization_overflow)] +pub struct InherentProjectionNormalizationOverflow { + #[primary_span] + pub span: Span, + pub ty: String, +} + +pub enum AdjustSignatureBorrow { + Borrow { to_borrow: Vec<(Span, String)> }, + RemoveBorrow { remove_borrow: Vec<(Span, String)> }, +} + +impl AddToDiagnostic for AdjustSignatureBorrow { + fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F) + where + F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage, + { + match self { + AdjustSignatureBorrow::Borrow { to_borrow } => { + diag.set_arg("len", to_borrow.len()); + diag.multipart_suggestion_verbose( + fluent::trait_selection_adjust_signature_borrow, + to_borrow, + Applicability::MaybeIncorrect, + ); + } + AdjustSignatureBorrow::RemoveBorrow { remove_borrow } => { + diag.set_arg("len", remove_borrow.len()); + diag.multipart_suggestion_verbose( + fluent::trait_selection_adjust_signature_remove_borrow, + remove_borrow, + Applicability::MaybeIncorrect, + ); + } + } + } +} + +#[derive(Diagnostic)] +#[diag(trait_selection_closure_kind_mismatch, code = "E0525")] +pub struct ClosureKindMismatch { + #[primary_span] + #[label] + pub closure_span: Span, + pub expected: ClosureKind, + pub found: ClosureKind, + #[label(trait_selection_closure_kind_requirement)] + pub cause_span: Span, + + #[subdiagnostic] + pub fn_once_label: Option<ClosureFnOnceLabel>, + + #[subdiagnostic] + pub fn_mut_label: Option<ClosureFnMutLabel>, +} + +#[derive(Subdiagnostic)] +#[label(trait_selection_closure_fn_once_label)] +pub struct ClosureFnOnceLabel { + #[primary_span] + pub span: Span, + pub place: String, +} + +#[derive(Subdiagnostic)] +#[label(trait_selection_closure_fn_mut_label)] +pub struct ClosureFnMutLabel { + #[primary_span] + pub span: Span, + pub place: String, +} diff --git a/compiler/rustc_trait_selection/src/infer.rs b/compiler/rustc_trait_selection/src/infer.rs new file mode 100644 index 00000000000..251f0628a71 --- /dev/null +++ b/compiler/rustc_trait_selection/src/infer.rs @@ -0,0 +1,198 @@ +use crate::solve::FulfillmentCtxt; +use crate::traits::query::evaluate_obligation::InferCtxtExt as _; +use crate::traits::{self, DefiningAnchor, ObligationCtxt}; + +use rustc_hir::def_id::DefId; +use rustc_hir::lang_items::LangItem; +use rustc_infer::traits::{TraitEngine, TraitEngineExt}; +use rustc_middle::arena::ArenaAllocatable; +use rustc_middle::infer::canonical::{Canonical, CanonicalQueryResponse, QueryResponse}; +use rustc_middle::traits::query::NoSolution; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeVisitableExt}; +use rustc_middle::ty::{GenericArg, ToPredicate}; +use rustc_span::DUMMY_SP; + +use std::fmt::Debug; + +pub use rustc_infer::infer::*; + +pub trait InferCtxtExt<'tcx> { + fn type_is_copy_modulo_regions(&self, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool; + + fn type_is_sized_modulo_regions(&self, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool; + + /// Check whether a `ty` implements given trait(trait_def_id). + /// The inputs are: + /// + /// - the def-id of the trait + /// - the type parameters of the trait, including the self-type + /// - the parameter environment + /// + /// Invokes `evaluate_obligation`, so in the event that evaluating + /// `Ty: Trait` causes overflow, EvaluatedToErrStackDependent (or EvaluatedToAmbigStackDependent) + /// will be returned. + fn type_implements_trait( + &self, + trait_def_id: DefId, + params: impl IntoIterator<Item: Into<GenericArg<'tcx>>>, + param_env: ty::ParamEnv<'tcx>, + ) -> traits::EvaluationResult; + + fn could_impl_trait( + &self, + trait_def_id: DefId, + ty: Ty<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ) -> Option<Vec<traits::FulfillmentError<'tcx>>>; +} + +impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { + fn type_is_copy_modulo_regions(&self, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool { + let ty = self.resolve_vars_if_possible(ty); + + if !(param_env, ty).has_infer() { + return ty.is_copy_modulo_regions(self.tcx, param_env); + } + + let copy_def_id = self.tcx.require_lang_item(LangItem::Copy, None); + + // This can get called from typeck (by euv), and `moves_by_default` + // rightly refuses to work with inference variables, but + // moves_by_default has a cache, which we want to use in other + // cases. + traits::type_known_to_meet_bound_modulo_regions(self, param_env, ty, copy_def_id) + } + + fn type_is_sized_modulo_regions(&self, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool { + let lang_item = self.tcx.require_lang_item(LangItem::Sized, None); + traits::type_known_to_meet_bound_modulo_regions(self, param_env, ty, lang_item) + } + + #[instrument(level = "debug", skip(self, params), ret)] + fn type_implements_trait( + &self, + trait_def_id: DefId, + params: impl IntoIterator<Item: Into<GenericArg<'tcx>>>, + param_env: ty::ParamEnv<'tcx>, + ) -> traits::EvaluationResult { + let trait_ref = ty::TraitRef::new(self.tcx, trait_def_id, params); + + let obligation = traits::Obligation { + cause: traits::ObligationCause::dummy(), + param_env, + recursion_depth: 0, + predicate: ty::Binder::dummy(trait_ref).to_predicate(self.tcx), + }; + self.evaluate_obligation(&obligation).unwrap_or(traits::EvaluationResult::EvaluatedToErr) + } + + fn could_impl_trait( + &self, + trait_def_id: DefId, + ty: Ty<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ) -> Option<Vec<traits::FulfillmentError<'tcx>>> { + self.probe(|_snapshot| { + if let ty::Adt(def, args) = ty.kind() + && let Some((impl_def_id, _)) = self + .tcx + .all_impls(trait_def_id) + .filter_map(|impl_def_id| { + self.tcx.impl_trait_ref(impl_def_id).map(|r| (impl_def_id, r)) + }) + .map(|(impl_def_id, imp)| (impl_def_id, imp.skip_binder())) + .find(|(_, imp)| match imp.self_ty().peel_refs().kind() { + ty::Adt(i_def, _) if i_def.did() == def.did() => true, + _ => false, + }) + { + let mut fulfill_cx = FulfillmentCtxt::new(self); + // We get all obligations from the impl to talk about specific + // trait bounds. + let obligations = self + .tcx + .predicates_of(impl_def_id) + .instantiate(self.tcx, args) + .into_iter() + .map(|(clause, span)| { + traits::Obligation::new( + self.tcx, + traits::ObligationCause::dummy_with_span(span), + param_env, + clause, + ) + }) + .collect::<Vec<_>>(); + fulfill_cx.register_predicate_obligations(self, obligations); + let trait_ref = ty::TraitRef::new(self.tcx, trait_def_id, [ty]); + let obligation = traits::Obligation::new( + self.tcx, + traits::ObligationCause::dummy(), + param_env, + trait_ref, + ); + fulfill_cx.register_predicate_obligation(self, obligation); + let mut errors = fulfill_cx.select_all_or_error(self); + // We remove the last predicate failure, which corresponds to + // the top-level obligation, because most of the type we only + // care about the other ones, *except* when it is the only one. + // This seems to only be relevant for arbitrary self-types. + // Look at `tests/ui/moves/move-fn-self-receiver.rs`. + if errors.len() > 1 { + errors.truncate(errors.len() - 1); + } + Some(errors) + } else { + None + } + }) + } +} + +pub trait InferCtxtBuilderExt<'tcx> { + fn enter_canonical_trait_query<K, R>( + self, + canonical_key: &Canonical<'tcx, K>, + operation: impl FnOnce(&ObligationCtxt<'_, 'tcx>, K) -> Result<R, NoSolution>, + ) -> Result<CanonicalQueryResponse<'tcx, R>, NoSolution> + where + K: TypeFoldable<TyCtxt<'tcx>>, + R: Debug + TypeFoldable<TyCtxt<'tcx>>, + Canonical<'tcx, QueryResponse<'tcx, R>>: ArenaAllocatable<'tcx>; +} + +impl<'tcx> InferCtxtBuilderExt<'tcx> for InferCtxtBuilder<'tcx> { + /// The "main method" for a canonicalized trait query. Given the + /// canonical key `canonical_key`, this method will create a new + /// inference context, instantiate the key, and run your operation + /// `op`. The operation should yield up a result (of type `R`) as + /// well as a set of trait obligations that must be fully + /// satisfied. These obligations will be processed and the + /// canonical result created. + /// + /// Returns `NoSolution` in the event of any error. + /// + /// (It might be mildly nicer to implement this on `TyCtxt`, and + /// not `InferCtxtBuilder`, but that is a bit tricky right now. + /// In part because we would need a `for<'tcx>` sort of + /// bound for the closure and in part because it is convenient to + /// have `'tcx` be free on this function so that we can talk about + /// `K: TypeFoldable<TyCtxt<'tcx>>`.) + fn enter_canonical_trait_query<K, R>( + self, + canonical_key: &Canonical<'tcx, K>, + operation: impl FnOnce(&ObligationCtxt<'_, 'tcx>, K) -> Result<R, NoSolution>, + ) -> Result<CanonicalQueryResponse<'tcx, R>, NoSolution> + where + K: TypeFoldable<TyCtxt<'tcx>>, + R: Debug + TypeFoldable<TyCtxt<'tcx>>, + Canonical<'tcx, QueryResponse<'tcx, R>>: ArenaAllocatable<'tcx>, + { + 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 new file mode 100644 index 00000000000..de2577cca49 --- /dev/null +++ b/compiler/rustc_trait_selection/src/lib.rs @@ -0,0 +1,45 @@ +//! This crate defines the trait resolution method. +//! +//! - **Traits.** Trait resolution is implemented in the `traits` module. +//! +//! For more information about how rustc works, see the [rustc-dev-guide]. +//! +//! [rustc-dev-guide]: https://rustc-dev-guide.rust-lang.org/ +//! +//! # Note +//! +//! This API is completely unstable and subject to change. + +#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] +#![doc(rust_logo)] +#![feature(rustdoc_internals)] +#![allow(internal_features)] +#![feature(associated_type_bounds)] +#![feature(box_patterns)] +#![feature(control_flow_enum)] +#![feature(extract_if)] +#![feature(let_chains)] +#![feature(if_let_guard)] +#![feature(never_type)] +#![feature(type_alias_impl_trait)] +#![feature(min_specialization)] +#![recursion_limit = "512"] // For rustdoc + +#[macro_use] +extern crate rustc_macros; +#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[macro_use] +extern crate rustc_data_structures; +#[macro_use] +extern crate tracing; +#[macro_use] +extern crate rustc_middle; +#[macro_use] +extern crate smallvec; + +pub mod errors; +pub mod infer; +pub mod solve; +pub mod traits; + +rustc_fluent_macro::fluent_messages! { "../messages.ftl" } 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 00000000000..626569fb40f --- /dev/null +++ b/compiler/rustc_trait_selection/src/solve/alias_relate.rs @@ -0,0 +1,152 @@ +//! Implements the `AliasRelate` goal, which is used when unifying aliases. +//! Doing this via a separate goal is called "deferred alias relation" and part +//! of our more general approach to "lazy normalization". +//! +//! This goal, e.g. `A alias-relate B`, may be satisfied by one of three branches: +//! * normalizes-to: If `A` is a projection, we can prove the equivalent +//! projection predicate with B as the right-hand side of the projection. +//! This goal is computed in both directions, if both are aliases. +//! * subst-relate: Equate `A` and `B` by their substs, if they're both +//! aliases with the same def-id. +//! * bidirectional-normalizes-to: If `A` and `B` are both projections, and both +//! may apply, then we can compute the "intersection" of both normalizes-to by +//! performing them together. This is used specifically to resolve ambiguities. +use super::{EvalCtxt, GoalSource}; +use rustc_infer::infer::DefineOpaqueTypes; +use rustc_infer::traits::query::NoSolution; +use rustc_middle::traits::solve::{Certainty, Goal, QueryResult}; +use rustc_middle::ty; + +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; + + let Some(lhs) = self.try_normalize_term(param_env, lhs)? else { + return self.evaluate_added_goals_and_make_canonical_response(Certainty::OVERFLOW); + }; + + let Some(rhs) = self.try_normalize_term(param_env, rhs)? else { + return self.evaluate_added_goals_and_make_canonical_response(Certainty::OVERFLOW); + }; + + let variance = match direction { + ty::AliasRelationDirection::Equate => ty::Variance::Invariant, + ty::AliasRelationDirection::Subtype => ty::Variance::Covariant, + }; + + match (lhs.to_alias_ty(tcx), rhs.to_alias_ty(tcx)) { + (None, None) => { + self.relate(param_env, lhs, variance, rhs)?; + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } + + (Some(alias), None) => { + if rhs.is_infer() { + self.relate(param_env, lhs, variance, rhs)?; + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } else if alias.is_opaque(tcx) { + self.define_opaque(param_env, alias, rhs) + } else { + Err(NoSolution) + } + } + (None, Some(alias)) => { + if lhs.is_infer() { + self.relate(param_env, lhs, variance, rhs)?; + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } else if alias.is_opaque(tcx) { + self.define_opaque(param_env, alias, lhs) + } else { + Err(NoSolution) + } + } + + (Some(alias_lhs), Some(alias_rhs)) => { + self.relate_rigid_alias_or_opaque(param_env, alias_lhs, variance, alias_rhs) + } + } + } + + /// Normalize the `term` to equate it later. This does not define opaque types. + #[instrument(level = "debug", skip(self, param_env), ret)] + fn try_normalize_term( + &mut self, + param_env: ty::ParamEnv<'tcx>, + term: ty::Term<'tcx>, + ) -> Result<Option<ty::Term<'tcx>>, NoSolution> { + match term.unpack() { + ty::TermKind::Ty(ty) => { + // We do no define opaque types here but instead do so in `relate_rigid_alias_or_opaque`. + Ok(self + .try_normalize_ty_recur(param_env, DefineOpaqueTypes::No, 0, ty) + .map(Into::into)) + } + ty::TermKind::Const(_) => { + if let Some(alias) = term.to_alias_ty(self.tcx()) { + let term = self.next_term_infer_of_kind(term); + self.add_goal( + GoalSource::Misc, + Goal::new(self.tcx(), param_env, ty::NormalizesTo { alias, term }), + ); + self.try_evaluate_added_goals()?; + Ok(Some(self.resolve_vars_if_possible(term))) + } else { + Ok(Some(term)) + } + } + } + } + + fn define_opaque( + &mut self, + param_env: ty::ParamEnv<'tcx>, + opaque: ty::AliasTy<'tcx>, + term: ty::Term<'tcx>, + ) -> QueryResult<'tcx> { + self.add_goal( + GoalSource::Misc, + Goal::new(self.tcx(), param_env, ty::NormalizesTo { alias: opaque, term }), + ); + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } + + fn relate_rigid_alias_or_opaque( + &mut self, + param_env: ty::ParamEnv<'tcx>, + lhs: ty::AliasTy<'tcx>, + variance: ty::Variance, + rhs: ty::AliasTy<'tcx>, + ) -> QueryResult<'tcx> { + let tcx = self.tcx(); + let mut candidates = vec![]; + if lhs.is_opaque(tcx) { + candidates.extend( + self.probe_misc_candidate("define-lhs-opaque") + .enter(|ecx| ecx.define_opaque(param_env, lhs, rhs.to_ty(tcx).into())), + ); + } + + if rhs.is_opaque(tcx) { + candidates.extend( + self.probe_misc_candidate("define-rhs-opaque") + .enter(|ecx| ecx.define_opaque(param_env, rhs, lhs.to_ty(tcx).into())), + ); + } + + candidates.extend(self.probe_misc_candidate("args-relate").enter(|ecx| { + ecx.relate(param_env, lhs, variance, rhs)?; + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + })); + + if let Some(result) = self.try_merge_responses(&candidates) { + Ok(result) + } else { + self.flounder(&candidates) + } + } +} diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs new file mode 100644 index 00000000000..caf9470b4c6 --- /dev/null +++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs @@ -0,0 +1,951 @@ +//! Code shared by trait and projection goals for candidate assembly. + +use super::{EvalCtxt, SolverMode}; +use crate::solve::GoalSource; +use crate::traits::coherence; +use rustc_hir::def_id::DefId; +use rustc_infer::traits::query::NoSolution; +use rustc_infer::traits::Reveal; +use rustc_middle::traits::solve::inspect::ProbeKind; +use rustc_middle::traits::solve::{ + CandidateSource, CanonicalResponse, Certainty, Goal, QueryResult, +}; +use rustc_middle::traits::BuiltinImplSource; +use rustc_middle::ty::fast_reject::{SimplifiedType, TreatParams}; +use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{fast_reject, TypeFoldable}; +use rustc_middle::ty::{ToPredicate, TypeVisitableExt}; +use rustc_span::ErrorGuaranteed; +use std::fmt::Debug; + +pub(super) mod structural_traits; + +/// A candidate is a possible way to prove a goal. +/// +/// It consists of both the `source`, which describes how that goal would be proven, +/// and the `result` when using the given `source`. +#[derive(Debug, Clone)] +pub(super) struct Candidate<'tcx> { + pub(super) source: CandidateSource, + pub(super) result: CanonicalResponse<'tcx>, +} + +/// Methods used to assemble candidates for either trait or projection goals. +pub(super) trait GoalKind<'tcx>: + TypeFoldable<TyCtxt<'tcx>> + Copy + Eq + std::fmt::Display +{ + fn self_ty(self) -> Ty<'tcx>; + + fn trait_ref(self, tcx: TyCtxt<'tcx>) -> ty::TraitRef<'tcx>; + + fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self; + + fn trait_def_id(self, tcx: TyCtxt<'tcx>) -> DefId; + + /// Try equating an assumption predicate against a goal's predicate. If it + /// holds, then execute the `then` callback, which should do any additional + /// work, then produce a response (typically by executing + /// [`EvalCtxt::evaluate_added_goals_and_make_canonical_response`]). + fn probe_and_match_goal_against_assumption( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + assumption: ty::Clause<'tcx>, + then: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> QueryResult<'tcx>, + ) -> QueryResult<'tcx>; + + /// Consider a clause, which consists of a "assumption" and some "requirements", + /// to satisfy a goal. If the requirements hold, then attempt to satisfy our + /// goal by equating it with the assumption. + fn consider_implied_clause( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + 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| { + // FIXME(-Znext-solver=coinductive): check whether this should be + // `GoalSource::ImplWhereBound` for any caller. + ecx.add_goals(GoalSource::Misc, requirements); + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }) + } + + /// Consider a bound originating from the item bounds of an alias. For this we + /// require that the well-formed requirements of the self type of the goal + /// are "satisfied from the param-env". + /// See [`EvalCtxt::validate_alias_bound_self_from_param_env`]. + fn consider_alias_bound_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + 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) + }) + } + + /// Consider a clause specifically for a `dyn Trait` self type. This requires + /// additionally checking all of the supertraits and object bounds to hold, + /// since they're not implied by the well-formedness of the object type. + fn consider_object_bound_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + assumption: ty::Clause<'tcx>, + ) -> QueryResult<'tcx> { + Self::probe_and_match_goal_against_assumption(ecx, goal, assumption, |ecx| { + let tcx = ecx.tcx(); + let ty::Dynamic(bounds, _, _) = *goal.predicate.self_ty().kind() else { + bug!("expected object type in `consider_object_bound_candidate`"); + }; + // FIXME(-Znext-solver=coinductive): Should this be `GoalSource::ImplWhereBound`? + ecx.add_goals( + GoalSource::Misc, + structural_traits::predicates_for_object_candidate( + ecx, + goal.param_env, + goal.predicate.trait_ref(tcx), + bounds, + ), + ); + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }) + } + + fn consider_impl_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + impl_def_id: DefId, + ) -> Result<Candidate<'tcx>, NoSolution>; + + /// If the predicate contained an error, we want to avoid emitting unnecessary trait + /// errors but still want to emit errors for other trait goals. We have some special + /// handling for this case. + /// + /// Trait goals always hold while projection goals never do. This is a bit arbitrary + /// but prevents incorrect normalization while hiding any trait errors. + fn consider_error_guaranteed_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + guar: ErrorGuaranteed, + ) -> QueryResult<'tcx>; + + /// A type implements an `auto trait` if its components do as well. + /// + /// These components are given by built-in rules from + /// [`structural_traits::instantiate_constituent_tys_for_auto_trait`]. + fn consider_auto_trait_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx>; + + /// A trait alias holds if the RHS traits and `where` clauses hold. + fn consider_trait_alias_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx>; + + /// A type is `Copy` or `Clone` if its components are `Sized`. + /// + /// These components are given by built-in rules from + /// [`structural_traits::instantiate_constituent_tys_for_sized_trait`]. + fn consider_builtin_sized_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx>; + + /// A type is `Copy` or `Clone` if its components are `Copy` or `Clone`. + /// + /// These components are given by built-in rules from + /// [`structural_traits::instantiate_constituent_tys_for_copy_clone_trait`]. + fn consider_builtin_copy_clone_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx>; + + /// A type is `PointerLike` if we can compute its layout, and that layout + /// matches the layout of `usize`. + fn consider_builtin_pointer_like_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx>; + + /// A type is a `FnPtr` if it is of `FnPtr` type. + fn consider_builtin_fn_ptr_trait_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx>; + + /// A callable type (a closure, fn def, or fn ptr) is known to implement the `Fn<A>` + /// family of traits where `A` is given by the signature of the type. + fn consider_builtin_fn_trait_candidates( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + kind: ty::ClosureKind, + ) -> QueryResult<'tcx>; + + /// `Tuple` is implemented if the `Self` type is a tuple. + fn consider_builtin_tuple_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx>; + + /// `Pointee` is always implemented. + /// + /// See the projection implementation for the `Metadata` types for all of + /// the built-in types. For structs, the metadata type is given by the struct + /// tail. + fn consider_builtin_pointee_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx>; + + /// A coroutine (that comes from an `async` desugaring) is known to implement + /// `Future<Output = O>`, where `O` is given by the coroutine's return type + /// that was computed during type-checking. + fn consider_builtin_future_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx>; + + /// A coroutine (that comes from a `gen` desugaring) is known to implement + /// `Iterator<Item = O>`, where `O` is given by the generator's yield type + /// that was computed during type-checking. + fn consider_builtin_iterator_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx>; + + fn consider_builtin_async_iterator_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx>; + + /// A coroutine (that doesn't come from an `async` or `gen` desugaring) is known to + /// implement `Coroutine<R, Yield = Y, Return = O>`, given the resume, yield, + /// and return types of the coroutine computed during type-checking. + fn consider_builtin_coroutine_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx>; + + fn consider_builtin_discriminant_kind_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx>; + + fn consider_builtin_destruct_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx>; + + fn consider_builtin_transmute_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx>; + + /// Consider (possibly several) candidates to upcast or unsize a type to another + /// type, excluding the coercion of a sized type into a `dyn Trait`. + /// + /// We return the `BuiltinImplSource` for each candidate as it is needed + /// for unsize coercion in hir typeck and because it is difficult to + /// otherwise recompute this for codegen. This is a bit of a mess but the + /// easiest way to maintain the existing behavior for now. + fn consider_structural_builtin_unsize_candidates( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)>; + + /// Consider the `Unsize` candidate corresponding to coercing a sized type + /// into a `dyn Trait`. + /// + /// This is computed separately from the rest of the `Unsize` candidates + /// since it is only done once per self type, and not once per + /// *normalization step* (in `assemble_candidates_via_self_ty`). + fn consider_unsize_to_dyn_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx>; +} + +impl<'tcx> EvalCtxt<'_, 'tcx> { + pub(super) fn assemble_and_evaluate_candidates<G: GoalKind<'tcx>>( + &mut self, + goal: Goal<'tcx, G>, + ) -> Vec<Candidate<'tcx>> { + debug_assert_eq!(goal, self.resolve_vars_if_possible(goal)); + if let Some(ambig) = self.assemble_self_ty_infer_ambiguity_response(goal) { + return vec![ambig]; + } + + let mut candidates = self.assemble_candidates_via_self_ty(goal, 0); + + self.assemble_unsize_to_dyn_candidate(goal, &mut candidates); + + self.assemble_blanket_impl_candidates(goal, &mut candidates); + + self.assemble_param_env_candidates(goal, &mut candidates); + + self.assemble_coherence_unknowable_candidates(goal, &mut candidates); + + candidates + } + + /// `?0: Trait` is ambiguous, because it may be satisfied via a builtin rule, + /// object bound, alias bound, etc. We are unable to determine this until we can at + /// least structurally resolve the type one layer. + /// + /// It would also require us to consider all impls of the trait, which is both pretty + /// bad for perf and would also constrain the self type if there is just a single impl. + fn assemble_self_ty_infer_ambiguity_response<G: GoalKind<'tcx>>( + &mut self, + goal: Goal<'tcx, G>, + ) -> Option<Candidate<'tcx>> { + if goal.predicate.self_ty().is_ty_var() { + debug!("adding self_ty_infer_ambiguity_response"); + let source = CandidateSource::BuiltinImpl(BuiltinImplSource::Misc); + let result = self + .evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) + .unwrap(); + let mut dummy_probe = self.inspect.new_probe(); + dummy_probe.probe_kind(ProbeKind::TraitCandidate { source, result: Ok(result) }); + self.inspect.finish_probe(dummy_probe); + Some(Candidate { source, result }) + } else { + None + } + } + + /// Assemble candidates which apply to the self type. This only looks at candidate which + /// apply to the specific self type and ignores all others. + /// + /// Returns `None` if the self type is still ambiguous. + fn assemble_candidates_via_self_ty<G: GoalKind<'tcx>>( + &mut self, + goal: Goal<'tcx, G>, + num_steps: usize, + ) -> Vec<Candidate<'tcx>> { + debug_assert_eq!(goal, self.resolve_vars_if_possible(goal)); + if let Some(ambig) = self.assemble_self_ty_infer_ambiguity_response(goal) { + return vec![ambig]; + } + + let mut candidates = Vec::new(); + + self.assemble_non_blanket_impl_candidates(goal, &mut candidates); + + self.assemble_builtin_impl_candidates(goal, &mut candidates); + + self.assemble_alias_bound_candidates(goal, &mut candidates); + + self.assemble_object_bound_candidates(goal, &mut candidates); + + self.assemble_candidates_after_normalizing_self_ty(goal, &mut candidates, num_steps); + candidates + } + + /// 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). + /// + /// 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, + goal: Goal<'tcx, G>, + candidates: &mut Vec<Candidate<'tcx>>, + num_steps: usize, + ) { + let tcx = self.tcx(); + let &ty::Alias(_, alias) = goal.predicate.self_ty().kind() else { return }; + + candidates.extend(self.probe(|_| ProbeKind::NormalizedSelfTyAssembly).enter(|ecx| { + if tcx.recursion_limit().value_within_limit(num_steps) { + let normalized_ty = ecx.next_ty_infer(); + let normalizes_to_goal = + goal.with(tcx, ty::NormalizesTo { alias, term: normalized_ty.into() }); + ecx.add_goal(GoalSource::Misc, normalizes_to_goal); + if let Err(NoSolution) = ecx.try_evaluate_added_goals() { + debug!("self type normalization failed"); + return vec![]; + } + 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)); + ecx.assemble_candidates_via_self_ty(goal, num_steps + 1) + } else { + match ecx.evaluate_added_goals_and_make_canonical_response(Certainty::OVERFLOW) { + Ok(result) => vec![Candidate { + source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), + result, + }], + Err(NoSolution) => vec![], + } + } + })); + } + + #[instrument(level = "debug", skip_all)] + fn assemble_non_blanket_impl_candidates<G: GoalKind<'tcx>>( + &mut self, + goal: Goal<'tcx, G>, + candidates: &mut Vec<Candidate<'tcx>>, + ) { + let tcx = self.tcx(); + let self_ty = goal.predicate.self_ty(); + let trait_impls = tcx.trait_impls_of(goal.predicate.trait_def_id(tcx)); + let mut consider_impls_for_simplified_type = |simp| { + if let Some(impls_for_type) = trait_impls.non_blanket_impls().get(&simp) { + for &impl_def_id in impls_for_type { + match G::consider_impl_candidate(self, goal, impl_def_id) { + Ok(candidate) => candidates.push(candidate), + Err(NoSolution) => (), + } + } + } + }; + + match self_ty.kind() { + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Adt(_, _) + | ty::Foreign(_) + | ty::Str + | ty::Array(_, _) + | ty::Slice(_) + | ty::RawPtr(_) + | ty::Ref(_, _, _) + | ty::FnDef(_, _) + | ty::FnPtr(_) + | ty::Dynamic(_, _, _) + | ty::Closure(_, _) + | ty::Coroutine(_, _) + | ty::Never + | ty::Tuple(_) => { + let simp = + fast_reject::simplify_type(tcx, self_ty, TreatParams::ForLookup).unwrap(); + consider_impls_for_simplified_type(simp); + } + + // HACK: For integer and float variables we have to manually look at all impls + // which have some integer or float as a self type. + ty::Infer(ty::IntVar(_)) => { + use ty::IntTy::*; + use ty::UintTy::*; + // This causes a compiler error if any new integer kinds are added. + let (I8 | I16 | I32 | I64 | I128 | Isize): ty::IntTy; + let (U8 | U16 | U32 | U64 | U128 | Usize): ty::UintTy; + let possible_integers = [ + // signed integers + SimplifiedType::Int(I8), + SimplifiedType::Int(I16), + SimplifiedType::Int(I32), + SimplifiedType::Int(I64), + SimplifiedType::Int(I128), + SimplifiedType::Int(Isize), + // unsigned integers + SimplifiedType::Uint(U8), + SimplifiedType::Uint(U16), + SimplifiedType::Uint(U32), + SimplifiedType::Uint(U64), + SimplifiedType::Uint(U128), + SimplifiedType::Uint(Usize), + ]; + for simp in possible_integers { + consider_impls_for_simplified_type(simp); + } + } + + ty::Infer(ty::FloatVar(_)) => { + // This causes a compiler error if any new float kinds are added. + let (ty::FloatTy::F32 | ty::FloatTy::F64); + let possible_floats = [ + SimplifiedType::Float(ty::FloatTy::F32), + SimplifiedType::Float(ty::FloatTy::F64), + ]; + + for simp in possible_floats { + consider_impls_for_simplified_type(simp); + } + } + + // The only traits applying to aliases and placeholders are blanket impls. + // + // Impls which apply to an alias after normalization are handled by + // `assemble_candidates_after_normalizing_self_ty`. + ty::Alias(_, _) | ty::Placeholder(..) | ty::Error(_) => (), + + // FIXME: These should ideally not exist as a self type. It would be nice for + // the builtin auto trait impls of coroutines to instead directly recurse + // into the witness. + ty::CoroutineWitness(..) => (), + + // These variants should not exist as a self type. + ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) + | ty::Param(_) + | ty::Bound(_, _) => bug!("unexpected self type: {self_ty}"), + } + } + + #[instrument(level = "debug", skip_all)] + fn assemble_unsize_to_dyn_candidate<G: GoalKind<'tcx>>( + &mut self, + goal: Goal<'tcx, G>, + candidates: &mut Vec<Candidate<'tcx>>, + ) { + let tcx = self.tcx(); + if tcx.lang_items().unsize_trait() == Some(goal.predicate.trait_def_id(tcx)) { + match G::consider_unsize_to_dyn_candidate(self, goal) { + Ok(result) => candidates.push(Candidate { + source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), + result, + }), + Err(NoSolution) => (), + } + } + } + + #[instrument(level = "debug", skip_all)] + fn assemble_blanket_impl_candidates<G: GoalKind<'tcx>>( + &mut self, + goal: Goal<'tcx, G>, + candidates: &mut Vec<Candidate<'tcx>>, + ) { + let tcx = self.tcx(); + let trait_impls = tcx.trait_impls_of(goal.predicate.trait_def_id(tcx)); + for &impl_def_id in trait_impls.blanket_impls() { + match G::consider_impl_candidate(self, goal, impl_def_id) { + Ok(candidate) => candidates.push(candidate), + Err(NoSolution) => (), + } + } + } + + #[instrument(level = "debug", skip_all)] + fn assemble_builtin_impl_candidates<G: GoalKind<'tcx>>( + &mut self, + goal: Goal<'tcx, G>, + candidates: &mut Vec<Candidate<'tcx>>, + ) { + let tcx = self.tcx(); + let lang_items = tcx.lang_items(); + let trait_def_id = goal.predicate.trait_def_id(tcx); + + // N.B. When assembling built-in candidates for lang items that are also + // `auto` traits, then the auto trait candidate that is assembled in + // `consider_auto_trait_candidate` MUST be disqualified to remain sound. + // + // Instead of adding the logic here, it's a better idea to add it in + // `EvalCtxt::disqualify_auto_trait_candidate_due_to_possible_impl` in + // `solve::trait_goals` instead. + let result = if let Err(guar) = goal.predicate.error_reported() { + G::consider_error_guaranteed_candidate(self, guar) + } else if tcx.trait_is_auto(trait_def_id) { + G::consider_auto_trait_candidate(self, goal) + } else if tcx.trait_is_alias(trait_def_id) { + G::consider_trait_alias_candidate(self, goal) + } else if lang_items.sized_trait() == Some(trait_def_id) { + G::consider_builtin_sized_candidate(self, goal) + } else if lang_items.copy_trait() == Some(trait_def_id) + || lang_items.clone_trait() == Some(trait_def_id) + { + G::consider_builtin_copy_clone_candidate(self, goal) + } else if lang_items.pointer_like() == Some(trait_def_id) { + G::consider_builtin_pointer_like_candidate(self, goal) + } else if lang_items.fn_ptr_trait() == Some(trait_def_id) { + G::consider_builtin_fn_ptr_trait_candidate(self, goal) + } else if let Some(kind) = self.tcx().fn_trait_kind_from_def_id(trait_def_id) { + G::consider_builtin_fn_trait_candidates(self, goal, kind) + } else if lang_items.tuple_trait() == Some(trait_def_id) { + G::consider_builtin_tuple_candidate(self, goal) + } else if lang_items.pointee_trait() == Some(trait_def_id) { + G::consider_builtin_pointee_candidate(self, goal) + } else if lang_items.future_trait() == Some(trait_def_id) { + G::consider_builtin_future_candidate(self, goal) + } else if lang_items.iterator_trait() == Some(trait_def_id) { + G::consider_builtin_iterator_candidate(self, goal) + } else if lang_items.async_iterator_trait() == Some(trait_def_id) { + G::consider_builtin_async_iterator_candidate(self, goal) + } else if lang_items.coroutine_trait() == Some(trait_def_id) { + G::consider_builtin_coroutine_candidate(self, goal) + } else if lang_items.discriminant_kind_trait() == Some(trait_def_id) { + G::consider_builtin_discriminant_kind_candidate(self, goal) + } else if lang_items.destruct_trait() == Some(trait_def_id) { + G::consider_builtin_destruct_candidate(self, goal) + } else if lang_items.transmute_trait() == Some(trait_def_id) { + G::consider_builtin_transmute_candidate(self, goal) + } else { + Err(NoSolution) + }; + + match result { + Ok(result) => candidates.push(Candidate { + source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), + result, + }), + Err(NoSolution) => (), + } + + // There may be multiple unsize candidates for a trait with several supertraits: + // `trait Foo: Bar<A> + Bar<B>` and `dyn Foo: Unsize<dyn Bar<_>>` + if lang_items.unsize_trait() == Some(trait_def_id) { + for (result, source) in G::consider_structural_builtin_unsize_candidates(self, goal) { + candidates.push(Candidate { source: CandidateSource::BuiltinImpl(source), result }); + } + } + } + + #[instrument(level = "debug", skip_all)] + fn assemble_param_env_candidates<G: GoalKind<'tcx>>( + &mut self, + goal: Goal<'tcx, G>, + candidates: &mut Vec<Candidate<'tcx>>, + ) { + for (i, assumption) in goal.param_env.caller_bounds().iter().enumerate() { + match G::consider_implied_clause(self, goal, assumption, []) { + Ok(result) => { + candidates.push(Candidate { source: CandidateSource::ParamEnv(i), result }) + } + Err(NoSolution) => (), + } + } + } + + #[instrument(level = "debug", skip_all)] + fn assemble_alias_bound_candidates<G: GoalKind<'tcx>>( + &mut self, + goal: Goal<'tcx, G>, + candidates: &mut Vec<Candidate<'tcx>>, + ) { + let alias_ty = match goal.predicate.self_ty().kind() { + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Adt(_, _) + | ty::Foreign(_) + | ty::Str + | ty::Array(_, _) + | ty::Slice(_) + | ty::RawPtr(_) + | ty::Ref(_, _, _) + | ty::FnDef(_, _) + | ty::FnPtr(_) + | ty::Dynamic(..) + | ty::Closure(..) + | ty::Coroutine(..) + | ty::CoroutineWitness(..) + | ty::Never + | ty::Tuple(_) + | ty::Param(_) + | 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 and type aliases here as they don't have meaningful item bounds. + ty::Alias(ty::Projection | ty::Opaque, alias_ty) => alias_ty, + }; + + for assumption in + self.tcx().item_bounds(alias_ty.def_id).instantiate(self.tcx(), alias_ty.args) + { + match G::consider_alias_bound_candidate(self, goal, assumption) { + Ok(result) => { + candidates.push(Candidate { source: CandidateSource::AliasBound, result }) + } + Err(NoSolution) => (), + } + } + } + + /// Check that we are allowed to use an alias bound originating from the self + /// type of this goal. This means something different depending on the self type's + /// alias kind. + /// + /// * Projection: Given a goal with a self type such as `<Ty as Trait>::Assoc`, + /// we require that the bound `Ty: Trait` can be proven using either a nested alias + /// bound candidate, or a param-env candidate. + /// + /// * Opaque: The param-env must be in `Reveal::UserFacing` mode. Otherwise, + /// the goal should be proven by using the hidden type instead. + #[instrument(level = "debug", skip(self), ret)] + pub(super) fn validate_alias_bound_self_from_param_env<G: GoalKind<'tcx>>( + &mut self, + goal: Goal<'tcx, G>, + ) -> QueryResult<'tcx> { + match *goal.predicate.self_ty().kind() { + ty::Alias(ty::Projection, projection_ty) => { + let mut param_env_candidates = vec![]; + let self_trait_ref = projection_ty.trait_ref(self.tcx()); + + if self_trait_ref.self_ty().is_ty_var() { + return self + .evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS); + } + + let trait_goal: Goal<'_, ty::TraitPredicate<'tcx>> = goal.with( + self.tcx(), + ty::TraitPredicate { + trait_ref: self_trait_ref, + polarity: ty::ImplPolarity::Positive, + }, + ); + + self.assemble_param_env_candidates(trait_goal, &mut param_env_candidates); + // FIXME: We probably need some sort of recursion depth check here. + // Can't come up with an example yet, though, and the worst case + // we can have is a compiler stack overflow... + self.assemble_alias_bound_candidates(trait_goal, &mut param_env_candidates); + + // FIXME: We must also consider alias-bound candidates for a peculiar + // class of built-in candidates that I'll call "defaulted" built-ins. + // + // For example, we always know that `T: Pointee` is implemented, but + // we do not always know what `<T as Pointee>::Metadata` actually is, + // similar to if we had a user-defined impl with a `default type ...`. + // For these traits, since we're not able to always normalize their + // associated types to a concrete type, we must consider their alias bounds + // instead, so we can prove bounds such as `<T as Pointee>::Metadata: Copy`. + self.assemble_alias_bound_candidates_for_builtin_impl_default_items( + trait_goal, + &mut param_env_candidates, + ); + + self.merge_candidates(param_env_candidates) + } + ty::Alias(ty::Opaque, _opaque_ty) => match goal.param_env.reveal() { + Reveal::UserFacing => { + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } + Reveal::All => return Err(NoSolution), + }, + _ => bug!("only expected to be called on alias tys"), + } + } + + /// Assemble a subset of builtin impl candidates for a class of candidates called + /// "defaulted" built-in traits. + /// + /// For example, we always know that `T: Pointee` is implemented, but we do not + /// always know what `<T as Pointee>::Metadata` actually is! See the comment in + /// [`EvalCtxt::validate_alias_bound_self_from_param_env`] for more detail. + #[instrument(level = "debug", skip_all)] + fn assemble_alias_bound_candidates_for_builtin_impl_default_items<G: GoalKind<'tcx>>( + &mut self, + goal: Goal<'tcx, G>, + candidates: &mut Vec<Candidate<'tcx>>, + ) { + let lang_items = self.tcx().lang_items(); + let trait_def_id = goal.predicate.trait_def_id(self.tcx()); + + // You probably shouldn't add anything to this list unless you + // know what you're doing. + let result = if lang_items.pointee_trait() == Some(trait_def_id) { + G::consider_builtin_pointee_candidate(self, goal) + } else if lang_items.discriminant_kind_trait() == Some(trait_def_id) { + G::consider_builtin_discriminant_kind_candidate(self, goal) + } else { + Err(NoSolution) + }; + + match result { + Ok(result) => candidates.push(Candidate { + source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), + result, + }), + Err(NoSolution) => (), + } + } + + #[instrument(level = "debug", skip_all)] + fn assemble_object_bound_candidates<G: GoalKind<'tcx>>( + &mut self, + 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 + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Adt(_, _) + | ty::Foreign(_) + | ty::Str + | ty::Array(_, _) + | ty::Slice(_) + | ty::RawPtr(_) + | ty::Ref(_, _, _) + | ty::FnDef(_, _) + | ty::FnPtr(_) + | ty::Alias(..) + | ty::Closure(..) + | ty::Coroutine(..) + | ty::CoroutineWitness(..) + | ty::Never + | ty::Tuple(_) + | ty::Param(_) + | ty::Placeholder(..) + | ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) + | ty::Error(_) => return, + ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) + | ty::Bound(..) => bug!("unexpected self type for `{goal:?}`"), + ty::Dynamic(bounds, ..) => bounds, + }; + + // Do not consider built-in object impls for non-object-safe types. + if bounds.principal_def_id().is_some_and(|def_id| !tcx.check_is_object_safe(def_id)) { + return; + } + + // Consider all of the auto-trait and projection bounds, which don't + // need to be recorded as a `BuiltinImplSource::Object` since they don't + // really have a vtable base... + for bound in bounds { + match bound.skip_binder() { + ty::ExistentialPredicate::Trait(_) => { + // Skip principal + } + ty::ExistentialPredicate::Projection(_) + | ty::ExistentialPredicate::AutoTrait(_) => { + match G::consider_object_bound_candidate( + self, + goal, + bound.with_self_ty(tcx, self_ty), + ) { + Ok(result) => candidates.push(Candidate { + source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), + result, + }), + Err(NoSolution) => (), + } + } + } + } + + // FIXME: We only need to do *any* of this if we're considering a trait goal, + // since we don't need to look at any supertrait or anything if we are doing + // a projection goal. + if let Some(principal) = bounds.principal() { + let principal_trait_ref = principal.with_self_ty(tcx, self_ty); + self.walk_vtable(principal_trait_ref, |ecx, assumption, vtable_base, _| { + match G::consider_object_bound_candidate(ecx, goal, assumption.to_predicate(tcx)) { + Ok(result) => candidates.push(Candidate { + source: CandidateSource::BuiltinImpl(BuiltinImplSource::Object { + vtable_base, + }), + result, + }), + Err(NoSolution) => (), + } + }); + } + } + + #[instrument(level = "debug", skip_all)] + fn assemble_coherence_unknowable_candidates<G: GoalKind<'tcx>>( + &mut self, + goal: Goal<'tcx, G>, + candidates: &mut Vec<Candidate<'tcx>>, + ) { + let tcx = self.tcx(); + match self.solver_mode() { + SolverMode::Normal => return, + SolverMode::Coherence => {} + }; + + let result = self.probe_misc_candidate("coherence unknowable").enter(|ecx| { + let trait_ref = goal.predicate.trait_ref(tcx); + #[derive(Debug)] + struct Overflow; + let lazily_normalize_ty = |ty| match ecx.try_normalize_ty(goal.param_env, ty) { + Some(ty) => Ok(ty), + None => Err(Overflow), + }; + + match coherence::trait_ref_is_knowable(tcx, trait_ref, lazily_normalize_ty) { + Err(Overflow) => { + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::OVERFLOW) + } + Ok(Ok(())) => Err(NoSolution), + Ok(Err(_)) => { + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) + } + } + }); + + match result { + Ok(result) => candidates.push(Candidate { + source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), + result, + }), + Err(NoSolution) => {} + } + } + + /// If there are multiple ways to prove a trait or projection goal, we have + /// to somehow try to merge the candidates into one. If that fails, we return + /// ambiguity. + #[instrument(level = "debug", skip(self), ret)] + pub(super) fn merge_candidates( + &mut self, + mut candidates: Vec<Candidate<'tcx>>, + ) -> QueryResult<'tcx> { + // First try merging all candidates. This is complete and fully sound. + let responses = candidates.iter().map(|c| c.result).collect::<Vec<_>>(); + if let Some(result) = self.try_merge_responses(&responses) { + return Ok(result); + } + + // We then check whether we should prioritize `ParamEnv` candidates. + // + // Doing so is incomplete and would therefore be unsound during coherence. + match self.solver_mode() { + SolverMode::Coherence => (), + // Prioritize `ParamEnv` candidates only if they do not guide inference. + // + // This is still incomplete as we may add incorrect region bounds. + SolverMode::Normal => { + let param_env_responses = candidates + .iter() + .filter(|c| { + matches!( + c.source, + CandidateSource::ParamEnv(_) | CandidateSource::AliasBound + ) + }) + .map(|c| c.result) + .collect::<Vec<_>>(); + if let Some(result) = self.try_merge_responses(¶m_env_responses) { + // We strongly prefer alias and param-env bounds here, even if they affect inference. + // See https://github.com/rust-lang/trait-system-refactor-initiative/issues/11. + return Ok(result); + } + } + } + self.flounder(&responses) + } +} diff --git a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs new file mode 100644 index 00000000000..274a75a125c --- /dev/null +++ b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs @@ -0,0 +1,424 @@ +//! Code which is used by built-in goals that match "structurally", such a auto +//! traits, `Copy`/`Clone`. +use rustc_data_structures::fx::FxHashMap; +use rustc_hir::{def_id::DefId, Movability, Mutability}; +use rustc_infer::traits::query::NoSolution; +use rustc_middle::traits::solve::Goal; +use rustc_middle::ty::{ + self, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, +}; + +use crate::solve::EvalCtxt; + +// Calculates the constituent types of a type for `auto trait` purposes. +// +// For types with an "existential" binder, i.e. coroutine witnesses, we also +// instantiate the binder with placeholders eagerly. +#[instrument(level = "debug", skip(ecx), ret)] +pub(in crate::solve) fn instantiate_constituent_tys_for_auto_trait<'tcx>( + ecx: &EvalCtxt<'_, 'tcx>, + ty: Ty<'tcx>, +) -> Result<Vec<Ty<'tcx>>, NoSolution> { + let tcx = ecx.tcx(); + match *ty.kind() { + ty::Uint(_) + | ty::Int(_) + | ty::Bool + | ty::Float(_) + | ty::FnDef(..) + | ty::FnPtr(_) + | ty::Error(_) + | ty::Never + | ty::Char => Ok(vec![]), + + // Treat `str` like it's defined as `struct str([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::Weak, ..) + | ty::Placeholder(..) + | ty::Bound(..) + | ty::Infer(_) => { + bug!("unexpected type `{ty}`") + } + + ty::RawPtr(ty::TypeAndMut { ty: element_ty, .. }) | ty::Ref(_, element_ty, _) => { + Ok(vec![element_ty]) + } + + ty::Array(element_ty, _) | ty::Slice(element_ty) => Ok(vec![element_ty]), + + ty::Tuple(tys) => { + // (T1, ..., Tn) -- meets any bound that all of T1...Tn meet + Ok(tys.iter().collect()) + } + + ty::Closure(_, args) => Ok(vec![args.as_closure().tupled_upvars_ty()]), + + ty::Coroutine(_, args) => { + let coroutine_args = args.as_coroutine(); + Ok(vec![coroutine_args.tupled_upvars_ty(), coroutine_args.witness()]) + } + + ty::CoroutineWitness(def_id, args) => Ok(ecx + .tcx() + .coroutine_hidden_types(def_id) + .map(|bty| { + ecx.instantiate_binder_with_placeholders(replace_erased_lifetimes_with_bound_vars( + tcx, + bty.instantiate(tcx, args), + )) + }) + .collect()), + + // For `PhantomData<T>`, we pass `T`. + ty::Adt(def, args) if def.is_phantom_data() => Ok(vec![args.type_at(0)]), + + ty::Adt(def, args) => Ok(def.all_fields().map(|f| f.ty(tcx, args)).collect()), + + ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => { + // We can resolve the `impl Trait` to its concrete type, + // which enforces a DAG between the functions requiring + // the auto trait bounds in question. + Ok(vec![tcx.type_of(def_id).instantiate(tcx, args)]) + } + } +} + +pub(in crate::solve) fn replace_erased_lifetimes_with_bound_vars<'tcx>( + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, +) -> ty::Binder<'tcx, Ty<'tcx>> { + debug_assert!(!ty.has_bound_regions()); + let mut counter = 0; + let ty = tcx.fold_regions(ty, |r, current_depth| match r.kind() { + ty::ReErased => { + let br = ty::BoundRegion { var: ty::BoundVar::from_u32(counter), kind: ty::BrAnon }; + counter += 1; + ty::Region::new_bound(tcx, current_depth, br) + } + // All free regions should be erased here. + r => bug!("unexpected region: {r:?}"), + }); + let bound_vars = tcx.mk_bound_variable_kinds_from_iter( + (0..counter).map(|_| ty::BoundVariableKind::Region(ty::BrAnon)), + ); + ty::Binder::bind_with_vars(ty, bound_vars) +} + +#[instrument(level = "debug", skip(ecx), ret)] +pub(in crate::solve) fn instantiate_constituent_tys_for_sized_trait<'tcx>( + ecx: &EvalCtxt<'_, 'tcx>, + ty: Ty<'tcx>, +) -> Result<Vec<Ty<'tcx>>, NoSolution> { + match *ty.kind() { + ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) + | ty::Uint(_) + | ty::Int(_) + | ty::Bool + | ty::Float(_) + | ty::FnDef(..) + | ty::FnPtr(_) + | ty::RawPtr(..) + | ty::Char + | ty::Ref(..) + | ty::Coroutine(..) + | ty::CoroutineWitness(..) + | ty::Array(..) + | ty::Closure(..) + | ty::Never + | ty::Dynamic(_, _, ty::DynStar) + | ty::Error(_) => Ok(vec![]), + + ty::Str + | ty::Slice(_) + | ty::Dynamic(..) + | ty::Foreign(..) + | ty::Alias(..) + | ty::Param(_) + | ty::Placeholder(..) => Err(NoSolution), + + ty::Bound(..) + | ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { + bug!("unexpected type `{ty}`") + } + + ty::Tuple(tys) => Ok(tys.to_vec()), + + ty::Adt(def, args) => { + let sized_crit = def.sized_constraint(ecx.tcx()); + Ok(sized_crit.iter_instantiated(ecx.tcx(), args).collect()) + } + } +} + +#[instrument(level = "debug", skip(ecx), ret)] +pub(in crate::solve) fn instantiate_constituent_tys_for_copy_clone_trait<'tcx>( + ecx: &EvalCtxt<'_, 'tcx>, + ty: Ty<'tcx>, +) -> Result<Vec<Ty<'tcx>>, NoSolution> { + match *ty.kind() { + ty::FnDef(..) | ty::FnPtr(_) | ty::Error(_) => Ok(vec![]), + + // Implementations are provided in core + ty::Uint(_) + | ty::Int(_) + | ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) + | ty::Bool + | ty::Float(_) + | ty::Char + | ty::RawPtr(..) + | ty::Never + | ty::Ref(_, _, Mutability::Not) + | ty::Array(..) => Err(NoSolution), + + ty::Dynamic(..) + | ty::Str + | ty::Slice(_) + | ty::Foreign(..) + | ty::Ref(_, _, Mutability::Mut) + | ty::Adt(_, _) + | ty::Alias(_, _) + | ty::Param(_) + | ty::Placeholder(..) => Err(NoSolution), + + ty::Bound(..) + | ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { + bug!("unexpected type `{ty}`") + } + + ty::Tuple(tys) => Ok(tys.to_vec()), + + ty::Closure(_, args) => Ok(vec![args.as_closure().tupled_upvars_ty()]), + + ty::Coroutine(def_id, args) => match ecx.tcx().coroutine_movability(def_id) { + Movability::Static => Err(NoSolution), + Movability::Movable => { + if ecx.tcx().features().coroutine_clone { + let coroutine = args.as_coroutine(); + Ok(vec![coroutine.tupled_upvars_ty(), coroutine.witness()]) + } else { + Err(NoSolution) + } + } + }, + + ty::CoroutineWitness(def_id, args) => Ok(ecx + .tcx() + .coroutine_hidden_types(def_id) + .map(|bty| { + ecx.instantiate_binder_with_placeholders(replace_erased_lifetimes_with_bound_vars( + ecx.tcx(), + bty.instantiate(ecx.tcx(), args), + )) + }) + .collect()), + } +} + +// Returns a binder of the tupled inputs types and output type from a builtin callable type. +pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<'tcx>( + tcx: TyCtxt<'tcx>, + self_ty: Ty<'tcx>, + goal_kind: ty::ClosureKind, +) -> Result<Option<ty::Binder<'tcx, (Ty<'tcx>, Ty<'tcx>)>>, NoSolution> { + match *self_ty.kind() { + // keep this in sync with assemble_fn_pointer_candidates until the old solver is removed. + ty::FnDef(def_id, args) => { + let sig = tcx.fn_sig(def_id); + if sig.skip_binder().is_fn_trait_compatible() + && tcx.codegen_fn_attrs(def_id).target_features.is_empty() + { + Ok(Some( + sig.instantiate(tcx, args) + .map_bound(|sig| (Ty::new_tup(tcx, sig.inputs()), sig.output())), + )) + } else { + Err(NoSolution) + } + } + // 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| (Ty::new_tup(tcx, sig.inputs()), sig.output())))) + } else { + Err(NoSolution) + } + } + ty::Closure(_, args) => { + let closure_args = args.as_closure(); + match closure_args.kind_ty().to_opt_closure_kind() { + // If the closure's kind doesn't extend the goal kind, + // then the closure doesn't implement the trait. + Some(closure_kind) => { + if !closure_kind.extends(goal_kind) { + return Err(NoSolution); + } + } + // Closure kind is not yet determined, so we return ambiguity unless + // the expected kind is `FnOnce` as that is always implemented. + None => { + if goal_kind != ty::ClosureKind::FnOnce { + return Ok(None); + } + } + } + Ok(Some(closure_args.sig().map_bound(|sig| (sig.inputs()[0], sig.output())))) + } + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Adt(_, _) + | ty::Foreign(_) + | ty::Str + | ty::Array(_, _) + | ty::Slice(_) + | ty::RawPtr(_) + | ty::Ref(_, _, _) + | ty::Dynamic(_, _, _) + | ty::Coroutine(_, _) + | ty::CoroutineWitness(..) + | ty::Never + | ty::Tuple(_) + | ty::Alias(_, _) + | ty::Param(_) + | ty::Placeholder(..) + | ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) + | ty::Error(_) => Err(NoSolution), + + ty::Bound(..) + | ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { + bug!("unexpected type `{self_ty}`") + } + } +} + +/// Assemble a list of predicates that would be present on a theoretical +/// user impl for an object type. These predicates must be checked any time +/// we assemble a built-in object candidate for an object type, since they +/// are not implied by the well-formedness of the type. +/// +/// For example, given the following traits: +/// +/// ```rust,ignore (theoretical code) +/// trait Foo: Baz { +/// type Bar: Copy; +/// } +/// +/// trait Baz {} +/// ``` +/// +/// For the dyn type `dyn Foo<Item = Ty>`, we can imagine there being a +/// pair of theoretical impls: +/// +/// ```rust,ignore (theoretical code) +/// impl Foo for dyn Foo<Item = Ty> +/// where +/// Self: Baz, +/// <Self as Foo>::Bar: Copy, +/// { +/// type Bar = Ty; +/// } +/// +/// impl Baz for dyn Foo<Item = Ty> {} +/// ``` +/// +/// However, in order to make such impls well-formed, we need to do an +/// additional step of eagerly folding the associated types in the where +/// clauses of the impl. In this example, that means replacing +/// `<Self as Foo>::Bar` with `Ty` in the first impl. +/// +// FIXME: This is only necessary as `<Self as Trait>::Assoc: ItemBound` +// bounds in impls are trivially proven using the item bound candidates. +// This is unsound in general and once that is fixed, we don't need to +// normalize eagerly here. See https://github.com/lcnr/solver-woes/issues/9 +// for more details. +pub(in crate::solve) fn predicates_for_object_candidate<'tcx>( + ecx: &EvalCtxt<'_, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + trait_ref: ty::TraitRef<'tcx>, + object_bound: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>, +) -> Vec<Goal<'tcx, ty::Predicate<'tcx>>> { + let tcx = ecx.tcx(); + let mut requirements = vec![]; + requirements.extend( + tcx.super_predicates_of(trait_ref.def_id).instantiate(tcx, trait_ref.args).predicates, + ); + for item in tcx.associated_items(trait_ref.def_id).in_definition_order() { + // FIXME(associated_const_equality): Also add associated consts to + // the requirements here. + if item.kind == ty::AssocKind::Type { + // associated types that require `Self: Sized` do not show up in the built-in + // implementation of `Trait for dyn Trait`, and can be dropped here. + if tcx.generics_require_sized_self(item.def_id) { + continue; + } + + requirements + .extend(tcx.item_bounds(item.def_id).iter_instantiated(tcx, trait_ref.args)); + } + } + + let mut replace_projection_with = FxHashMap::default(); + for bound in object_bound { + if let ty::ExistentialPredicate::Projection(proj) = bound.skip_binder() { + let proj = proj.with_self_ty(tcx, trait_ref.self_ty()); + let old_ty = replace_projection_with.insert(proj.def_id(), bound.rebind(proj)); + assert_eq!( + old_ty, + None, + "{} has two substitutions: {} and {}", + proj.projection_ty, + proj.term, + old_ty.unwrap() + ); + } + } + + let mut folder = + ReplaceProjectionWith { ecx, param_env, mapping: replace_projection_with, nested: vec![] }; + let folded_requirements = requirements.fold_with(&mut folder); + + folder + .nested + .into_iter() + .chain(folded_requirements.into_iter().map(|clause| Goal::new(tcx, param_env, clause))) + .collect() +} + +struct ReplaceProjectionWith<'a, 'tcx> { + ecx: &'a EvalCtxt<'a, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + mapping: FxHashMap<DefId, ty::PolyProjectionPredicate<'tcx>>, + nested: Vec<Goal<'tcx, ty::Predicate<'tcx>>>, +} + +impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ReplaceProjectionWith<'_, 'tcx> { + fn interner(&self) -> TyCtxt<'tcx> { + self.ecx.tcx() + } + + fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { + if let ty::Alias(ty::Projection, alias_ty) = *ty.kind() + && let Some(replacement) = self.mapping.get(&alias_ty.def_id) + { + // We may have a case where our object type's projection bound is higher-ranked, + // but the where clauses we instantiated are not. We can solve this by instantiating + // the binder at the usage site. + let proj = self.ecx.instantiate_binder_with_infer(*replacement); + // FIXME: Technically this equate could be fallible... + self.nested.extend( + self.ecx + .eq_and_get_goals(self.param_env, alias_ty, proj.projection_ty) + .expect("expected to be able to unify goal projection with dyn's projection"), + ); + proj.term.ty().unwrap() + } else { + ty.super_fold_with(self) + } + } +} diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs new file mode 100644 index 00000000000..ecdae2521b9 --- /dev/null +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs @@ -0,0 +1,381 @@ +//! Canonicalization is used to separate some goal from its context, +//! throwing away unnecessary information in the process. +//! +//! This is necessary to cache goals containing inference variables +//! and placeholders without restricting them to the current `InferCtxt`. +//! +//! Canonicalization is fairly involved, for more details see the relevant +//! section of the [rustc-dev-guide][c]. +//! +//! [c]: https://rustc-dev-guide.rust-lang.org/solve/canonicalization.html +use super::{CanonicalInput, Certainty, EvalCtxt, Goal}; +use crate::solve::{ + inspect, response_no_constraints_raw, 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::resolve::EagerResolver; +use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk}; +use rustc_middle::infer::canonical::Canonical; +use rustc_middle::traits::query::NoSolution; +use rustc_middle::traits::solve::{ + ExternalConstraintsData, MaybeCause, PredefinedOpaquesData, QueryInput, +}; +use rustc_middle::traits::ObligationCause; +use rustc_middle::ty::{self, BoundVar, GenericArgKind, Ty, TyCtxt, TypeFoldable}; +use rustc_next_trait_solver::canonicalizer::{CanonicalizeMode, Canonicalizer}; +use rustc_span::DUMMY_SP; +use std::iter; +use std::ops::Deref; + +trait ResponseT<'tcx> { + fn var_values(&self) -> CanonicalVarValues<'tcx>; +} + +impl<'tcx> ResponseT<'tcx> for Response<'tcx> { + fn var_values(&self) -> CanonicalVarValues<'tcx> { + self.var_values + } +} + +impl<'tcx, T> ResponseT<'tcx> for inspect::State<'tcx, T> { + fn var_values(&self) -> CanonicalVarValues<'tcx> { + self.var_values + } +} + +impl<'tcx> EvalCtxt<'_, 'tcx> { + /// Canonicalizes the goal remembering the original values + /// for each bound variable. + pub(super) fn canonicalize_goal<T: TypeFoldable<TyCtxt<'tcx>>>( + &self, + goal: Goal<'tcx, T>, + ) -> (Vec<ty::GenericArg<'tcx>>, CanonicalInput<'tcx, T>) { + let opaque_types = self.infcx.clone_opaque_types_for_query_response(); + let (goal, opaque_types) = + (goal, opaque_types).fold_with(&mut EagerResolver::new(self.infcx)); + + let mut orig_values = Default::default(); + let canonical_goal = Canonicalizer::canonicalize( + self.infcx, + CanonicalizeMode::Input, + &mut orig_values, + QueryInput { + goal, + anchor: self.infcx.defining_use_anchor, + predefined_opaques_in_body: self + .tcx() + .mk_predefined_opaques_in_body(PredefinedOpaquesData { opaque_types }), + }, + ); + (orig_values, canonical_goal) + } + + /// To return the constraints of a canonical query to the caller, we canonicalize: + /// + /// - `var_values`: a map from bound variables in the canonical goal to + /// the values inferred while solving the instantiated goal. + /// - `external_constraints`: additional constraints which aren't expressible + /// using simple unification of inference variables. + #[instrument(level = "debug", skip(self))] + pub(in crate::solve) fn evaluate_added_goals_and_make_canonical_response( + &mut self, + certainty: Certainty, + ) -> QueryResult<'tcx> { + let goals_certainty = self.try_evaluate_added_goals()?; + assert_eq!( + self.tainted, + Ok(()), + "EvalCtxt is tainted -- nested goals may have been dropped in a \ + previous call to `try_evaluate_added_goals!`" + ); + + let certainty = certainty.unify_with(goals_certainty); + + let var_values = self.var_values; + let external_constraints = self.compute_external_query_constraints()?; + + let (var_values, mut external_constraints) = + (var_values, external_constraints).fold_with(&mut EagerResolver::new(self.infcx)); + // Remove any trivial region constraints once we've resolved regions + external_constraints + .region_constraints + .outlives + .retain(|(outlives, _)| outlives.0.as_region().map_or(true, |re| re != outlives.1)); + + let canonical = Canonicalizer::canonicalize( + self.infcx, + CanonicalizeMode::Response { max_input_universe: self.max_input_universe }, + &mut Default::default(), + Response { + var_values, + certainty, + external_constraints: self.tcx().mk_external_constraints(external_constraints), + }, + ); + + Ok(canonical) + } + + /// Constructs a totally unconstrained, ambiguous response to a goal. + /// + /// Take care when using this, since often it's useful to respond with + /// ambiguity but return constrained variables to guide inference. + pub(in crate::solve) fn make_ambiguous_response_no_constraints( + &self, + maybe_cause: MaybeCause, + ) -> CanonicalResponse<'tcx> { + response_no_constraints_raw( + self.tcx(), + self.max_input_universe, + self.variables, + Certainty::Maybe(maybe_cause), + ) + } + + /// Computes the region constraints and *new* opaque types registered when + /// proving a goal. + /// + /// If an opaque was already constrained before proving this goal, then the + /// external constraints do not need to record that opaque, since if it is + /// further constrained by inference, that will be passed back in the var + /// values. + #[instrument(level = "debug", skip(self), ret)] + fn compute_external_query_constraints( + &self, + ) -> Result<ExternalConstraintsData<'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 mut region_constraints = self.infcx.with_region_constraints(|region_constraints| { + make_query_region_constraints( + self.tcx(), + region_obligations + .iter() + .map(|r_o| (r_o.sup_type, r_o.sub_region, r_o.origin.to_constraint_category())), + region_constraints, + ) + }); + + 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, _)| { + self.predefined_opaques_in_body.opaque_types.iter().all(|(pa, _)| pa != a) + }); + + Ok(ExternalConstraintsData { region_constraints, opaque_types }) + } + + /// After calling a canonical query, we apply the constraints returned + /// by the query using this function. + /// + /// This happens in three steps: + /// - we instantiate the bound variables of the query response + /// - we unify the `var_values` of the response with the `original_values` + /// - we apply the `external_constraints` returned by the query + pub(super) fn instantiate_and_apply_query_response( + &mut self, + param_env: ty::ParamEnv<'tcx>, + original_values: Vec<ty::GenericArg<'tcx>>, + response: CanonicalResponse<'tcx>, + ) -> Result<(Certainty, Vec<Goal<'tcx, ty::Predicate<'tcx>>>), NoSolution> { + let substitution = + Self::compute_query_response_substitution(self.infcx, &original_values, &response); + + let Response { var_values, external_constraints, certainty } = + response.substitute(self.tcx(), &substitution); + + let nested_goals = + Self::unify_query_var_values(self.infcx, param_env, &original_values, var_values)?; + + let ExternalConstraintsData { region_constraints, opaque_types } = + external_constraints.deref(); + self.register_region_constraints(region_constraints); + self.register_opaque_types(param_env, opaque_types)?; + + Ok((certainty, nested_goals)) + } + + /// This returns the substitutions to instantiate the bound variables of + /// the canonical response. This depends on the `original_values` for the + /// bound variables. + fn compute_query_response_substitution<T: ResponseT<'tcx>>( + infcx: &InferCtxt<'tcx>, + original_values: &[ty::GenericArg<'tcx>], + response: &Canonical<'tcx, T>, + ) -> CanonicalVarValues<'tcx> { + // FIXME: Longterm canonical queries should deal with all placeholders + // created inside of the query directly instead of returning them to the + // caller. + let prev_universe = infcx.universe(); + let universes_created_in_query = response.max_universe.index(); + for _ in 0..universes_created_in_query { + infcx.create_next_universe(); + } + + let var_values = response.value.var_values(); + assert_eq!(original_values.len(), var_values.len()); + + // If the query did not make progress with constraining inference variables, + // we would normally create a new inference variables for bound existential variables + // only then unify this new inference variable with the inference variable from + // the input. + // + // We therefore instantiate the existential variable in the canonical response with the + // inference variable of the input right away, which is more performant. + let mut opt_values = IndexVec::from_elem_n(None, response.variables.len()); + for (original_value, result_value) in iter::zip(original_values, var_values.var_values) { + match result_value.unpack() { + GenericArgKind::Type(t) => { + if let &ty::Bound(debruijn, b) = t.kind() { + assert_eq!(debruijn, ty::INNERMOST); + opt_values[b.var] = Some(*original_value); + } + } + GenericArgKind::Lifetime(r) => { + if let ty::ReBound(debruijn, br) = *r { + assert_eq!(debruijn, ty::INNERMOST); + opt_values[br.var] = Some(*original_value); + } + } + GenericArgKind::Const(c) => { + if let ty::ConstKind::Bound(debruijn, b) = c.kind() { + assert_eq!(debruijn, ty::INNERMOST); + opt_values[b] = Some(*original_value); + } + } + } + } + + let var_values = infcx.tcx.mk_args_from_iter(response.variables.iter().enumerate().map( + |(index, info)| { + if info.universe() != ty::UniverseIndex::ROOT { + // A variable from inside a binder of the query. While ideally these shouldn't + // exist at all (see the FIXME at the start of this method), we have to deal with + // them for now. + infcx.instantiate_canonical_var(DUMMY_SP, info, |idx| { + ty::UniverseIndex::from(prev_universe.index() + idx.index()) + }) + } else if info.is_existential() { + // As an optimization we sometimes avoid creating a new inference variable here. + // + // All new inference variables we create start out in the current universe of the caller. + // This is conceptually wrong as these inference variables would be able to name + // more placeholders then they should be able to. However the inference variables have + // to "come from somewhere", so by equating them with the original values of the caller + // later on, we pull them down into their correct universe again. + if let Some(v) = opt_values[BoundVar::from_usize(index)] { + v + } else { + infcx.instantiate_canonical_var(DUMMY_SP, info, |_| prev_universe) + } + } else { + // For placeholders which were already part of the input, we simply map this + // universal bound variable back the placeholder of the input. + original_values[info.expect_placeholder_index()] + } + }, + )); + + CanonicalVarValues { var_values } + } + + #[instrument(level = "debug", skip(infcx, param_env), ret)] + fn unify_query_var_values( + infcx: &InferCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + original_values: &[ty::GenericArg<'tcx>], + var_values: CanonicalVarValues<'tcx>, + ) -> Result<Vec<Goal<'tcx, ty::Predicate<'tcx>>>, NoSolution> { + assert_eq!(original_values.len(), var_values.len()); + + let mut nested_goals = vec![]; + for (&orig, response) in iter::zip(original_values, var_values.var_values) { + nested_goals.extend( + infcx + .at(&ObligationCause::dummy(), param_env) + .eq(DefineOpaqueTypes::No, orig, response) + .map(|InferOk { value: (), obligations }| { + obligations.into_iter().map(|o| Goal::from(o)) + }) + .map_err(|e| { + debug!(?e, "failed to equate"); + NoSolution + })?, + ); + } + + Ok(nested_goals) + } + + fn register_region_constraints(&mut self, region_constraints: &QueryRegionConstraints<'tcx>) { + for &(ty::OutlivesPredicate(lhs, rhs), _) in ®ion_constraints.outlives { + match lhs.unpack() { + GenericArgKind::Lifetime(lhs) => self.register_region_outlives(lhs, rhs), + GenericArgKind::Type(lhs) => self.register_ty_outlives(lhs, rhs), + GenericArgKind::Const(_) => bug!("const outlives: {lhs:?}: {rhs:?}"), + } + } + + for member_constraint in ®ion_constraints.member_constraints { + // FIXME: Deal with member constraints :< + let _ = member_constraint; + } + } + + fn register_opaque_types( + &mut self, + param_env: ty::ParamEnv<'tcx>, + opaque_types: &[(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)], + ) -> Result<(), NoSolution> { + for &(key, ty) in opaque_types { + self.insert_hidden_type(key, param_env, ty)?; + } + Ok(()) + } +} + +impl<'tcx> inspect::ProofTreeBuilder<'tcx> { + pub fn make_canonical_state<T: TypeFoldable<TyCtxt<'tcx>>>( + ecx: &EvalCtxt<'_, 'tcx>, + data: T, + ) -> inspect::CanonicalState<'tcx, T> { + let state = inspect::State { var_values: ecx.var_values, data }; + let state = state.fold_with(&mut EagerResolver::new(ecx.infcx)); + Canonicalizer::canonicalize( + ecx.infcx, + CanonicalizeMode::Response { max_input_universe: ecx.max_input_universe }, + &mut vec![], + state, + ) + } + + pub fn instantiate_canonical_state<T: TypeFoldable<TyCtxt<'tcx>>>( + infcx: &InferCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + original_values: &[ty::GenericArg<'tcx>], + state: inspect::CanonicalState<'tcx, T>, + ) -> Result<(Vec<Goal<'tcx, ty::Predicate<'tcx>>>, T), NoSolution> { + let substitution = + EvalCtxt::compute_query_response_substitution(infcx, original_values, &state); + + let inspect::State { var_values, data } = state.substitute(infcx.tcx, &substitution); + + let nested_goals = + EvalCtxt::unify_query_var_values(infcx, param_env, original_values, var_values)?; + Ok((nested_goals, data)) + } +} diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/commit_if_ok.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/commit_if_ok.rs new file mode 100644 index 00000000000..67b6801059a --- /dev/null +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/commit_if_ok.rs @@ -0,0 +1,45 @@ +use super::{EvalCtxt, NestedGoals}; +use crate::solve::inspect; +use rustc_middle::traits::query::NoSolution; + +impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { + pub(in crate::solve) fn commit_if_ok<T>( + &mut self, + f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> Result<T, NoSolution>, + ) -> Result<T, NoSolution> { + let mut nested_ecx = EvalCtxt { + infcx: self.infcx, + variables: self.variables, + 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: NestedGoals::new(), + tainted: self.tainted, + inspect: self.inspect.new_probe(), + }; + + let result = nested_ecx.infcx.commit_if_ok(|_| f(&mut nested_ecx)); + if result.is_ok() { + let EvalCtxt { + infcx: _, + variables: _, + var_values: _, + predefined_opaques_in_body: _, + max_input_universe: _, + search_graph: _, + nested_goals, + tainted, + inspect, + } = nested_ecx; + self.nested_goals.extend(nested_goals); + self.tainted = tainted; + self.inspect.integrate_snapshot(inspect); + } else { + nested_ecx.inspect.probe_kind(inspect::ProbeKind::CommitIfOk); + self.inspect.finish_probe(nested_ecx.inspect); + } + + result + } +} diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs new file mode 100644 index 00000000000..76c50a11102 --- /dev/null +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs @@ -0,0 +1,1003 @@ +use rustc_data_structures::stack::ensure_sufficient_stack; +use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_infer::infer::at::ToTrace; +use rustc_infer::infer::canonical::CanonicalVarValues; +use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_infer::infer::{ + BoundRegionConversionTime, DefineOpaqueTypes, InferCtxt, InferOk, TyCtxtInferExt, +}; +use rustc_infer::traits::query::NoSolution; +use rustc_infer::traits::ObligationCause; +use rustc_middle::infer::canonical::CanonicalVarInfos; +use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; +use rustc_middle::traits::solve::inspect; +use rustc_middle::traits::solve::{ + CanonicalInput, CanonicalResponse, Certainty, IsNormalizesToHack, PredefinedOpaques, + PredefinedOpaquesData, QueryResult, +}; +use rustc_middle::traits::{specialization_graph, DefiningAnchor}; +use rustc_middle::ty::{ + 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::iter; +use std::ops::ControlFlow; + +use crate::traits::vtable::{count_own_vtable_entries, prepare_vtable_segments, VtblSegment}; + +use super::inspect::ProofTreeBuilder; +use super::{search_graph, GoalEvaluationKind}; +use super::{search_graph::SearchGraph, Goal}; +use super::{GoalSource, SolverMode}; +pub use select::InferCtxtSelectExt; + +mod canonical; +mod commit_if_ok; +mod probe; +mod select; + +pub struct EvalCtxt<'a, 'tcx> { + /// The inference context that backs (mostly) inference and placeholder terms + /// instantiated while solving goals. + /// + /// NOTE: The `InferCtxt` that backs the `EvalCtxt` is intentionally private, + /// because the `InferCtxt` is much more general than `EvalCtxt`. Methods such + /// as `take_registered_region_obligations` can mess up query responses, + /// using `At::normalize` is totally wrong, calling `evaluate_root_goal` can + /// cause coinductive unsoundness, etc. + /// + /// Methods that are generally of use for trait solving are *intentionally* + /// re-declared through the `EvalCtxt` below, often with cleaner signatures + /// since we don't care about things like `ObligationCause`s and `Span`s here. + /// If some `InferCtxt` method is missing, please first think defensively about + /// the method's compatibility with this solver, or if an existing one does + /// the job already. + infcx: &'a InferCtxt<'tcx>, + + /// The variable info for the `var_values`, only used to make an ambiguous response + /// with no constraints. + variables: CanonicalVarInfos<'tcx>, + pub(super) var_values: CanonicalVarValues<'tcx>, + + predefined_opaques_in_body: PredefinedOpaques<'tcx>, + + /// The highest universe index nameable by the caller. + /// + /// When we enter a new binder inside of the query we create new universes + /// which the caller cannot name. We have to be careful with variables from + /// these new universes when creating the query response. + /// + /// Both because these new universes can prevent us from reaching a fixpoint + /// if we have a coinductive cycle and because that's the only way we can return + /// new placeholders to the caller. + pub(super) max_input_universe: ty::UniverseIndex, + + pub(super) search_graph: &'a mut SearchGraph<'tcx>, + + pub(super) nested_goals: NestedGoals<'tcx>, + + // Has this `EvalCtxt` errored out with `NoSolution` in `try_evaluate_added_goals`? + // + // If so, then it can no longer be used to make a canonical query response, + // since subsequent calls to `try_evaluate_added_goals` have possibly dropped + // ambiguous goals. Instead, a probe needs to be introduced somewhere in the + // evaluation code. + tainted: Result<(), NoSolution>, + + pub(super) inspect: ProofTreeBuilder<'tcx>, +} + +#[derive(Debug, Clone)] +pub(super) struct NestedGoals<'tcx> { + /// This normalizes-to goal that is treated specially during the evaluation + /// loop. In each iteration we take the RHS of the projection, replace it with + /// a fresh inference variable, and only after evaluating that goal do we + /// equate the fresh inference variable with the actual RHS of the predicate. + /// + /// This is both to improve caching, and to avoid using the RHS of the + /// projection predicate to influence the normalizes-to candidate we select. + /// + /// This is not a 'real' nested goal. We must not forget to replace the RHS + /// with a fresh inference variable when we evaluate this goal. That can result + /// in a trait solver cycle. This would currently result in overflow but can be + /// can be unsound with more powerful coinduction in the future. + pub(super) normalizes_to_hack_goal: Option<Goal<'tcx, ty::NormalizesTo<'tcx>>>, + /// The rest of the goals which have not yet processed or remain ambiguous. + pub(super) goals: Vec<(GoalSource, Goal<'tcx, ty::Predicate<'tcx>>)>, +} + +impl<'tcx> NestedGoals<'tcx> { + pub(super) fn new() -> Self { + Self { normalizes_to_hack_goal: None, goals: Vec::new() } + } + + pub(super) fn is_empty(&self) -> bool { + self.normalizes_to_hack_goal.is_none() && self.goals.is_empty() + } + + pub(super) fn extend(&mut self, other: NestedGoals<'tcx>) { + assert_eq!(other.normalizes_to_hack_goal, None); + self.goals.extend(other.goals) + } +} + +#[derive(PartialEq, Eq, Debug, Hash, HashStable, Clone, Copy)] +pub enum GenerateProofTree { + Yes, + IfEnabled, + Never, +} + +pub trait InferCtxtEvalExt<'tcx> { + /// Evaluates a goal from **outside** of the trait solver. + /// + /// Using this while inside of the solver is wrong as it uses a new + /// search graph which would break cycle detection. + fn evaluate_root_goal( + &self, + goal: Goal<'tcx, ty::Predicate<'tcx>>, + 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> { + #[instrument(level = "debug", skip(self))] + fn evaluate_root_goal( + &self, + goal: Goal<'tcx, ty::Predicate<'tcx>>, + 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(GoalEvaluationKind::Root, GoalSource::Misc, goal) + }) + } +} + +impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { + pub(super) fn solver_mode(&self) -> SolverMode { + self.search_graph.solver_mode() + } + + pub(super) fn local_overflow_limit(&self) -> usize { + self.search_graph.local_overflow_limit() + } + + /// 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, + nested_goals: NestedGoals::new(), + inspect: ProofTreeBuilder::new_maybe_root(infcx.tcx, generate_proof_tree), + + // Only relevant when canonicalizing the response, + // which we don't do within this evaluation context. + predefined_opaques_in_body: infcx + .tcx + .mk_predefined_opaques_in_body(PredefinedOpaquesData::default()), + max_input_universe: ty::UniverseIndex::ROOT, + variables: ty::List::empty(), + var_values: CanonicalVarValues::dummy(), + tainted: Ok(()), + }; + let result = f(&mut ecx); + + let tree = ecx.inspect.finalize(); + if let (Some(tree), DumpSolverProofTree::Always) = ( + &tree, + infcx.tcx.sess.opts.unstable_opts.next_solver.map(|c| c.dump_tree).unwrap_or_default(), + ) { + let mut lock = std::io::stdout().lock(); + let _ = lock.write_fmt(format_args!("{tree:?}\n")); + let _ = lock.flush(); + } + + assert!( + ecx.nested_goals.is_empty(), + "root `EvalCtxt` should not have any goals added to it" + ); + + assert!(search_graph.is_empty()); + (result, tree) + } + + /// 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>, + canonical_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, + variables: canonical_input.variables, + 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: canonical_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); + + canonical_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 { + // This seems ok, but fragile. + let _ = infcx.take_opaque_types(); + } + + result + } + + /// The entry point of the solver. + /// + /// This function deals with (coinductive) cycles, overflow, and caching + /// and then calls [`EvalCtxt::compute_goal`] which contains the actual + /// logic of the solver. + /// + /// 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, goal_evaluation), ret)] + fn evaluate_canonical_goal( + tcx: TyCtxt<'tcx>, + search_graph: &'a mut search_graph::SearchGraph<'tcx>, + canonical_input: CanonicalInput<'tcx>, + goal_evaluation: &mut ProofTreeBuilder<'tcx>, + ) -> QueryResult<'tcx> { + let mut canonical_goal_evaluation = + goal_evaluation.new_canonical_goal_evaluation(canonical_input); + + // Deal with overflow, caching, and coinduction. + // + // The actual solver logic happens in `ecx.compute_goal`. + let result = ensure_sufficient_stack(|| { + search_graph.with_new_goal( + tcx, + canonical_input, + &mut canonical_goal_evaluation, + |search_graph, canonical_goal_evaluation| { + EvalCtxt::enter_canonical( + tcx, + search_graph, + canonical_input, + canonical_goal_evaluation, + |ecx, goal| { + let result = ecx.compute_goal(goal); + ecx.inspect.query_result(result); + result + }, + ) + }, + ) + }); + + canonical_goal_evaluation.query_result(result); + goal_evaluation.canonical_goal_evaluation(canonical_goal_evaluation); + result + } + + /// Recursively evaluates `goal`, returning whether any inference vars have + /// been constrained and the certainty of the result. + fn evaluate_goal( + &mut self, + goal_evaluation_kind: GoalEvaluationKind, + source: GoalSource, + 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 mut goal_evaluation = + self.inspect.new_goal_evaluation(goal, &orig_values, goal_evaluation_kind); + let canonical_response = EvalCtxt::evaluate_canonical_goal( + self.tcx(), + self.search_graph, + canonical_goal, + &mut goal_evaluation, + ); + let canonical_response = match canonical_response { + Err(e) => { + self.inspect.goal_evaluation(goal_evaluation); + return Err(e); + } + Ok(response) => response, + }; + + let (certainty, has_changed, nested_goals) = match self + .instantiate_response_discarding_overflow( + goal.param_env, + source, + 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"); + } + + // FIXME: We previously had an assert here that checked that recomputing + // a goal after applying its constraints did not change its response. + // + // This assert was removed as it did not hold for goals constraining + // an inference variable to a recursive alias, e.g. in + // tests/ui/traits/next-solver/overflow/recursive-self-normalization.rs. + // + // Once we have decided on how to handle trait-system-refactor-initiative#75, + // we should re-add an assert here. + + Ok((has_changed, certainty, nested_goals)) + } + + fn instantiate_response_discarding_overflow( + &mut self, + param_env: ty::ParamEnv<'tcx>, + source: GoalSource, + original_values: Vec<ty::GenericArg<'tcx>>, + response: CanonicalResponse<'tcx>, + ) -> Result<(Certainty, bool, Vec<Goal<'tcx, ty::Predicate<'tcx>>>), NoSolution> { + // The old solver did not evaluate nested goals when normalizing. + // It returned the selection constraints allowing a `Projection` + // obligation to not hold in coherence while avoiding the fatal error + // from overflow. + // + // We match this behavior here by considering all constraints + // from nested goals which are not from where-bounds. We will already + // need to track which nested goals are required by impl where-bounds + // for coinductive cycles, so we simply reuse that here. + // + // While we could consider overflow constraints in more cases, this should + // not be necessary for backcompat and results in better perf. It also + // avoids a potential inconsistency which would otherwise require some + // tracking for root goals as well. See #119071 for an example. + let keep_overflow_constraints = || { + self.search_graph.current_goal_is_normalizes_to() + && source != GoalSource::ImplWhereBound + }; + + if response.value.certainty == Certainty::OVERFLOW && !keep_overflow_constraints() { + Ok((Certainty::OVERFLOW, false, Vec::new())) + } else { + let has_changed = !response.value.var_values.is_identity_modulo_regions() + || !response.value.external_constraints.opaque_types.is_empty(); + + let (certainty, nested_goals) = + self.instantiate_and_apply_query_response(param_env, original_values, response)?; + Ok((certainty, has_changed, nested_goals)) + } + } + + fn compute_goal(&mut self, goal: Goal<'tcx, ty::Predicate<'tcx>>) -> QueryResult<'tcx> { + let Goal { param_env, predicate } = goal; + let kind = predicate.kind(); + if let Some(kind) = kind.no_bound_vars() { + match kind { + ty::PredicateKind::Clause(ty::ClauseKind::Trait(predicate)) => { + self.compute_trait_goal(Goal { param_env, predicate }) + } + ty::PredicateKind::Clause(ty::ClauseKind::Projection(predicate)) => { + self.compute_projection_goal(Goal { param_env, predicate }) + } + ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(predicate)) => { + self.compute_type_outlives_goal(Goal { param_env, predicate }) + } + ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(predicate)) => { + self.compute_region_outlives_goal(Goal { param_env, predicate }) + } + 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) => { + self.compute_subtype_goal(Goal { param_env, predicate }) + } + ty::PredicateKind::Coerce(predicate) => { + self.compute_coerce_goal(Goal { param_env, predicate }) + } + ty::PredicateKind::ObjectSafe(trait_def_id) => { + self.compute_object_safe_goal(trait_def_id) + } + ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => { + self.compute_well_formed_goal(Goal { param_env, predicate: arg }) + } + ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(ct)) => { + self.compute_const_evaluatable_goal(Goal { param_env, predicate: ct }) + } + ty::PredicateKind::ConstEquate(_, _) => { + bug!("ConstEquate should not be emitted when `-Znext-solver` is active") + } + ty::PredicateKind::NormalizesTo(predicate) => { + self.compute_normalizes_to_goal(Goal { param_env, predicate }) + } + 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); + let goal = goal.with(self.tcx(), ty::Binder::dummy(kind)); + self.add_goal(GoalSource::Misc, goal); + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } + } + + // Recursively evaluates all the goals added to this `EvalCtxt` to completion, returning + // 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 response = Ok(Certainty::OVERFLOW); + for _ in 0..self.local_overflow_limit() { + // FIXME: This match is a bit ugly, it might be nice to change the inspect + // stuff to use a closure instead. which should hopefully simplify this a bit. + match self.evaluate_added_goals_step() { + Ok(Some(cert)) => { + response = Ok(cert); + break; + } + Ok(None) => {} + Err(NoSolution) => { + response = Err(NoSolution); + break; + } + } + } + + 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); + + response + } + + /// Iterate over all added goals: returning `Ok(Some(_))` in case we can stop rerunning. + /// + /// Goals for the next step get directly added to the nested goals of the `EvalCtxt`. + fn evaluate_added_goals_step(&mut self) -> Result<Option<Certainty>, NoSolution> { + let tcx = self.tcx(); + let mut goals = core::mem::replace(&mut self.nested_goals, NestedGoals::new()); + + self.inspect.evaluate_added_goals_loop_start(); + + fn with_misc_source<'tcx>( + it: impl IntoIterator<Item = Goal<'tcx, ty::Predicate<'tcx>>>, + ) -> impl Iterator<Item = (GoalSource, Goal<'tcx, ty::Predicate<'tcx>>)> { + iter::zip(iter::repeat(GoalSource::Misc), it) + } + + // If this loop did not result in any progress, what's our final certainty. + let mut unchanged_certainty = Some(Certainty::Yes); + if let Some(goal) = goals.normalizes_to_hack_goal.take() { + // Replace the goal with an unconstrained infer var, so the + // RHS does not affect projection candidate assembly. + let unconstrained_rhs = self.next_term_infer_of_kind(goal.predicate.term); + let unconstrained_goal = goal.with( + tcx, + ty::NormalizesTo { alias: goal.predicate.alias, term: unconstrained_rhs }, + ); + + let (_, certainty, instantiate_goals) = self.evaluate_goal( + GoalEvaluationKind::Nested { is_normalizes_to_hack: IsNormalizesToHack::Yes }, + GoalSource::Misc, + unconstrained_goal, + )?; + self.nested_goals.goals.extend(with_misc_source(instantiate_goals)); + + // Finally, equate the goal's RHS with the unconstrained var. + // We put the nested goals from this into goals instead of + // next_goals to avoid needing to process the loop one extra + // time if this goal returns something -- I don't think this + // matters in practice, though. + let eq_goals = + self.eq_and_get_goals(goal.param_env, goal.predicate.term, unconstrained_rhs)?; + goals.goals.extend(with_misc_source(eq_goals)); + + // We only look at the `projection_ty` part here rather than + // looking at the "has changed" return from evaluate_goal, + // because we expect the `unconstrained_rhs` part of the predicate + // to have changed -- that means we actually normalized successfully! + if goal.predicate.alias != self.resolve_vars_if_possible(goal.predicate.alias) { + unchanged_certainty = None; + } + + match certainty { + Certainty::Yes => {} + Certainty::Maybe(_) => { + // We need to resolve vars here so that we correctly + // deal with `has_changed` in the next iteration. + self.set_normalizes_to_hack_goal(self.resolve_vars_if_possible(goal)); + unchanged_certainty = unchanged_certainty.map(|c| c.unify_with(certainty)); + } + } + } + + for (source, goal) in goals.goals.drain(..) { + let (has_changed, certainty, instantiate_goals) = self.evaluate_goal( + GoalEvaluationKind::Nested { is_normalizes_to_hack: IsNormalizesToHack::No }, + source, + goal, + )?; + self.nested_goals.goals.extend(with_misc_source(instantiate_goals)); + if has_changed { + unchanged_certainty = None; + } + + match certainty { + Certainty::Yes => {} + Certainty::Maybe(_) => { + self.nested_goals.goals.push((source, goal)); + unchanged_certainty = unchanged_certainty.map(|c| c.unify_with(certainty)); + } + } + } + + Ok(unchanged_certainty) + } +} + +impl<'tcx> EvalCtxt<'_, 'tcx> { + pub(super) fn tcx(&self) -> TyCtxt<'tcx> { + self.infcx.tcx + } + + pub(super) fn next_ty_infer(&self) -> Ty<'tcx> { + self.infcx.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::MiscVariable, + span: DUMMY_SP, + }) + } + + pub(super) fn next_const_infer(&self, ty: Ty<'tcx>) -> ty::Const<'tcx> { + self.infcx.next_const_var( + ty, + ConstVariableOrigin { kind: ConstVariableOriginKind::MiscVariable, span: DUMMY_SP }, + ) + } + + /// Returns a ty infer or a const infer depending on whether `kind` is a `Ty` or `Const`. + /// If `kind` is an integer inference variable this will still return a ty infer var. + pub(super) fn next_term_infer_of_kind(&self, kind: ty::Term<'tcx>) -> ty::Term<'tcx> { + match kind.unpack() { + ty::TermKind::Ty(_) => self.next_ty_infer().into(), + ty::TermKind::Const(ct) => self.next_const_infer(ct.ty()).into(), + } + } + + /// Is the projection predicate is of the form `exists<T> <Ty as Trait>::Assoc = T`. + /// + /// This is the case if the `term` is an inference variable in the innermost universe + /// and does not occur in any other part of the predicate. + #[instrument(level = "debug", skip(self), ret)] + pub(super) fn term_is_fully_unconstrained( + &self, + goal: Goal<'tcx, ty::NormalizesTo<'tcx>>, + ) -> bool { + let term_is_infer = match goal.predicate.term.unpack() { + ty::TermKind::Ty(ty) => { + if let &ty::Infer(ty::TyVar(vid)) = ty.kind() { + match self.infcx.probe_ty_var(vid) { + Ok(value) => bug!("resolved var in query: {goal:?} {value:?}"), + Err(universe) => universe == self.infcx.universe(), + } + } else { + false + } + } + ty::TermKind::Const(ct) => { + if let ty::ConstKind::Infer(ty::InferConst::Var(vid)) = ct.kind() { + match self.infcx.probe_const_var(vid) { + Ok(value) => bug!("resolved var in query: {goal:?} {value:?}"), + Err(universe) => universe == self.infcx.universe(), + } + } else { + false + } + } + }; + + // Guard against `<T as Trait<?0>>::Assoc = ?0>`. + struct ContainsTerm<'a, 'tcx> { + term: ty::Term<'tcx>, + infcx: &'a InferCtxt<'tcx>, + } + impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ContainsTerm<'_, 'tcx> { + type BreakTy = (); + fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { + if let Some(vid) = t.ty_vid() + && let ty::TermKind::Ty(term) = self.term.unpack() + && let Some(term_vid) = term.ty_vid() + && self.infcx.root_var(vid) == self.infcx.root_var(term_vid) + { + ControlFlow::Break(()) + } else if t.has_non_region_infer() { + t.super_visit_with(self) + } else { + ControlFlow::Continue(()) + } + } + + fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> { + if let ty::ConstKind::Infer(ty::InferConst::Var(vid)) = c.kind() + && let ty::TermKind::Const(term) = self.term.unpack() + && let ty::ConstKind::Infer(ty::InferConst::Var(term_vid)) = term.kind() + && self.infcx.root_const_var(vid) == self.infcx.root_const_var(term_vid) + { + ControlFlow::Break(()) + } else if c.has_non_region_infer() { + c.super_visit_with(self) + } else { + ControlFlow::Continue(()) + } + } + } + + let mut visitor = ContainsTerm { infcx: self.infcx, term: goal.predicate.term }; + + term_is_infer + && goal.predicate.alias.visit_with(&mut visitor).is_continue() + && goal.param_env.visit_with(&mut visitor).is_continue() + } + + #[instrument(level = "debug", skip(self, param_env), ret)] + pub(super) fn eq<T: ToTrace<'tcx>>( + &mut self, + param_env: ty::ParamEnv<'tcx>, + lhs: T, + rhs: T, + ) -> Result<(), NoSolution> { + self.infcx + .at(&ObligationCause::dummy(), param_env) + .eq(DefineOpaqueTypes::No, lhs, rhs) + .map(|InferOk { value: (), obligations }| { + self.add_goals(GoalSource::Misc, obligations.into_iter().map(|o| o.into())); + }) + .map_err(|e| { + debug!(?e, "failed to equate"); + NoSolution + }) + } + + #[instrument(level = "debug", skip(self, param_env), ret)] + pub(super) fn sub<T: ToTrace<'tcx>>( + &mut self, + param_env: ty::ParamEnv<'tcx>, + sub: T, + sup: T, + ) -> Result<(), NoSolution> { + self.infcx + .at(&ObligationCause::dummy(), param_env) + .sub(DefineOpaqueTypes::No, sub, sup) + .map(|InferOk { value: (), obligations }| { + self.add_goals(GoalSource::Misc, obligations.into_iter().map(|o| o.into())); + }) + .map_err(|e| { + debug!(?e, "failed to subtype"); + NoSolution + }) + } + + #[instrument(level = "debug", skip(self, param_env), ret)] + pub(super) fn relate<T: ToTrace<'tcx>>( + &mut self, + param_env: ty::ParamEnv<'tcx>, + lhs: T, + variance: ty::Variance, + rhs: T, + ) -> Result<(), NoSolution> { + self.infcx + .at(&ObligationCause::dummy(), param_env) + .relate(DefineOpaqueTypes::No, lhs, variance, rhs) + .map(|InferOk { value: (), obligations }| { + self.add_goals(GoalSource::Misc, obligations.into_iter().map(|o| o.into())); + }) + .map_err(|e| { + debug!(?e, "failed to relate"); + NoSolution + }) + } + + /// Equates two values returning the nested goals without adding them + /// to the nested goals of the `EvalCtxt`. + /// + /// If possible, try using `eq` instead which automatically handles nested + /// goals correctly. + #[instrument(level = "trace", skip(self, param_env), ret)] + pub(super) fn eq_and_get_goals<T: ToTrace<'tcx>>( + &self, + param_env: ty::ParamEnv<'tcx>, + lhs: T, + rhs: T, + ) -> Result<Vec<Goal<'tcx, ty::Predicate<'tcx>>>, NoSolution> { + self.infcx + .at(&ObligationCause::dummy(), param_env) + .eq(DefineOpaqueTypes::No, lhs, rhs) + .map(|InferOk { value: (), obligations }| { + obligations.into_iter().map(|o| o.into()).collect() + }) + .map_err(|e| { + debug!(?e, "failed to equate"); + NoSolution + }) + } + + pub(super) fn instantiate_binder_with_infer<T: TypeFoldable<TyCtxt<'tcx>> + Copy>( + &self, + value: ty::Binder<'tcx, T>, + ) -> T { + self.infcx.instantiate_binder_with_fresh_vars( + DUMMY_SP, + BoundRegionConversionTime::HigherRankedType, + value, + ) + } + + pub(super) fn instantiate_binder_with_placeholders<T: TypeFoldable<TyCtxt<'tcx>> + Copy>( + &self, + value: ty::Binder<'tcx, T>, + ) -> T { + self.infcx.instantiate_binder_with_placeholders(value) + } + + pub(super) fn resolve_vars_if_possible<T>(&self, value: T) -> T + where + T: TypeFoldable<TyCtxt<'tcx>>, + { + self.infcx.resolve_vars_if_possible(value) + } + + pub(super) fn fresh_args_for_item(&self, def_id: DefId) -> ty::GenericArgsRef<'tcx> { + self.infcx.fresh_args_for_item(DUMMY_SP, def_id) + } + + pub(super) fn translate_args( + &self, + param_env: ty::ParamEnv<'tcx>, + source_impl: DefId, + source_args: ty::GenericArgsRef<'tcx>, + target_node: specialization_graph::Node, + ) -> ty::GenericArgsRef<'tcx> { + crate::traits::translate_args(self.infcx, param_env, source_impl, source_args, target_node) + } + + pub(super) fn register_ty_outlives(&self, ty: Ty<'tcx>, lt: ty::Region<'tcx>) { + self.infcx.register_region_obligation_with_cause(ty, lt, &ObligationCause::dummy()); + } + + pub(super) fn register_region_outlives(&self, a: ty::Region<'tcx>, b: ty::Region<'tcx>) { + // `b : a` ==> `a <= b` + // (inlined from `InferCtxt::region_outlives_predicate`) + self.infcx.sub_regions( + rustc_infer::infer::SubregionOrigin::RelateRegionParamBound(DUMMY_SP), + b, + a, + ); + } + + /// Computes the list of goals required for `arg` to be well-formed + pub(super) fn well_formed_goals( + &self, + param_env: ty::ParamEnv<'tcx>, + arg: ty::GenericArg<'tcx>, + ) -> Option<impl Iterator<Item = Goal<'tcx, ty::Predicate<'tcx>>>> { + crate::traits::wf::unnormalized_obligations(self.infcx, param_env, arg) + .map(|obligations| obligations.into_iter().map(|obligation| obligation.into())) + } + + pub(super) fn is_transmutable( + &self, + src_and_dst: rustc_transmute::Types<'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(), + src_and_dst, + scope, + assume, + ) { + Answer::Yes => Ok(Certainty::Yes), + Answer::No(_) | Answer::If(_) => Err(NoSolution), + } + } + + pub(super) fn can_define_opaque_ty(&self, def_id: LocalDefId) -> bool { + self.infcx.opaque_type_origin(def_id).is_some() + } + + pub(super) fn insert_hidden_type( + &mut self, + opaque_type_key: OpaqueTypeKey<'tcx>, + param_env: ty::ParamEnv<'tcx>, + hidden_ty: Ty<'tcx>, + ) -> Result<(), NoSolution> { + 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(GoalSource::Misc, obligations.into_iter().map(|o| o.into())); + Ok(()) + } + + pub(super) fn add_item_bounds_for_hidden_type( + &mut self, + opaque_def_id: DefId, + opaque_args: ty::GenericArgsRef<'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_args, + ObligationCause::dummy(), + param_env, + hidden_ty, + &mut obligations, + ); + self.add_goals(GoalSource::Misc, 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( + &mut self, + param_env: ty::ParamEnv<'tcx>, + key: ty::OpaqueTypeKey<'tcx>, + ty: Ty<'tcx>, + ) -> Vec<CanonicalResponse<'tcx>> { + // FIXME: Super inefficient to be cloning this... + let opaques = self.infcx.clone_opaque_types_for_query_response(); + + let mut values = vec![]; + for (candidate_key, candidate_ty) in opaques { + if candidate_key.def_id != key.def_id { + continue; + } + values.extend(self.probe_misc_candidate("opaque type storage").enter(|ecx| { + for (a, b) in std::iter::zip(candidate_key.args, key.args) { + ecx.eq(param_env, a, b)?; + } + ecx.eq(param_env, candidate_ty, ty)?; + ecx.add_item_bounds_for_hidden_type( + candidate_key.def_id.to_def_id(), + candidate_key.args, + param_env, + candidate_ty, + ); + 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, + } + } + + /// Walk through the vtable of a principal trait ref, executing a `supertrait_visitor` + /// for every trait ref encountered (including the principal). Passes both the vtable + /// base and the (optional) vptr slot. + pub(super) fn walk_vtable( + &mut self, + principal: ty::PolyTraitRef<'tcx>, + mut supertrait_visitor: impl FnMut(&mut Self, ty::PolyTraitRef<'tcx>, usize, Option<usize>), + ) { + let tcx = self.tcx(); + let mut offset = 0; + prepare_vtable_segments::<()>(tcx, principal, |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(tcx, trait_ref); + + supertrait_visitor( + self, + trait_ref, + offset, + emit_vptr.then(|| offset + own_vtable_entries), + ); + + offset += own_vtable_entries; + if emit_vptr { + offset += 1; + } + } + } + ControlFlow::Continue(()) + }); + } +} 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 00000000000..91fd48807a4 --- /dev/null +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs @@ -0,0 +1,108 @@ +use crate::solve::assembly::Candidate; + +use super::EvalCtxt; +use rustc_middle::traits::{ + query::NoSolution, + solve::{inspect, CandidateSource, 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::ProbeKind<'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, + variables: outer_ecx.variables, + 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_probe(), + }; + let r = nested_ecx.infcx.probe(|_| f(&mut nested_ecx)); + if !outer_ecx.inspect.is_noop() { + let probe_kind = probe_kind(&r); + nested_ecx.inspect.probe_kind(probe_kind); + outer_ecx.inspect.finish_probe(nested_ecx.inspect); + } + r + } +} + +pub(in crate::solve) struct TraitProbeCtxt<'me, 'a, 'tcx, F> { + cx: ProbeCtxt<'me, 'a, 'tcx, F, QueryResult<'tcx>>, + source: CandidateSource, +} + +impl<'tcx, F> TraitProbeCtxt<'_, '_, 'tcx, F> +where + F: FnOnce(&QueryResult<'tcx>) -> inspect::ProbeKind<'tcx>, +{ + pub(in crate::solve) fn enter( + self, + f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> QueryResult<'tcx>, + ) -> Result<Candidate<'tcx>, NoSolution> { + self.cx.enter(|ecx| f(ecx)).map(|result| Candidate { source: self.source, result }) + } +} + +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::ProbeKind<'tcx>, + { + ProbeCtxt { ecx: self, probe_kind, _result: PhantomData } + } + + pub(in crate::solve) fn probe_misc_candidate( + &mut self, + name: &'static str, + ) -> ProbeCtxt< + '_, + 'a, + 'tcx, + impl FnOnce(&QueryResult<'tcx>) -> inspect::ProbeKind<'tcx>, + QueryResult<'tcx>, + > { + ProbeCtxt { + ecx: self, + probe_kind: move |result: &QueryResult<'tcx>| inspect::ProbeKind::MiscCandidate { + name, + result: *result, + }, + _result: PhantomData, + } + } + + pub(in crate::solve) fn probe_trait_candidate( + &mut self, + source: CandidateSource, + ) -> TraitProbeCtxt<'_, 'a, 'tcx, impl FnOnce(&QueryResult<'tcx>) -> inspect::ProbeKind<'tcx>> + { + TraitProbeCtxt { + cx: ProbeCtxt { + ecx: self, + probe_kind: move |result: &QueryResult<'tcx>| inspect::ProbeKind::TraitCandidate { + source, + result: *result, + }, + _result: PhantomData, + }, + source, + } + } +} 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 00000000000..315df06be41 --- /dev/null +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs @@ -0,0 +1,398 @@ +use rustc_hir as hir; +use rustc_hir::def_id::DefId; +use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt}; +use rustc_infer::traits::{ + Obligation, PolyTraitObligation, PredicateObligation, Selection, SelectionResult, TraitEngine, +}; +use rustc_middle::traits::solve::{CandidateSource, CanonicalInput, Certainty, Goal}; +use rustc_middle::traits::{ + BuiltinImplSource, ImplSource, ImplSourceUserDefinedData, ObligationCause, SelectionError, +}; +use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_span::DUMMY_SP; + +use crate::solve::assembly::Candidate; +use crate::solve::eval_ctxt::{EvalCtxt, GenerateProofTree}; +use crate::solve::inspect::ProofTreeBuilder; +use crate::traits::StructurallyNormalizeExt; +use crate::traits::TraitEngineExt; + +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::Never, |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) + } + + // If an unsize goal is ambiguous, then we can manually rematch it to make + // selection progress for coercion during HIR typeck. If it is *not* ambiguous, + // but is `BuiltinImplSource::Misc`, it may have nested `Unsize` goals, + // and we need to rematch those to detect tuple unsizing and trait upcasting. + // FIXME: This will be wrong if we have param-env or where-clause bounds + // with the unsize goal -- we may need to mark those with different impl + // sources. + (Certainty::Maybe(_), CandidateSource::BuiltinImpl(src)) + | (Certainty::Yes, CandidateSource::BuiltinImpl(src @ BuiltinImplSource::Misc)) + if self.tcx.lang_items().unsize_trait() == Some(goal.predicate.def_id()) => + { + rematch_unsize(self, goal, nested_obligations, src, certainty) + } + + // 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(src)) => { + Ok(Some(ImplSource::Builtin(src, 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))) + } + + (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, + + // FIXME: we could prefer earlier vtable bases perhaps... + ( + 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 args = infcx.fresh_args_for_item(DUMMY_SP, impl_def_id); + let impl_trait_ref = + infcx.tcx.impl_trait_ref(impl_def_id).unwrap().instantiate(infcx.tcx, args); + + 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, args).into_iter().map( + |(pred, _)| Obligation::new(infcx.tcx, ObligationCause::dummy(), goal.param_env, pred), + ), + ); + + Ok(Some(ImplSource::UserDefined(ImplSourceUserDefinedData { impl_def_id, args, nested }))) +} + +/// The `Unsize` trait is particularly important to coercion, so we try rematch it. +/// NOTE: This must stay in sync with `consider_builtin_unsize_candidate` in trait +/// goal assembly in the solver, both for soundness and in order to avoid ICEs. +fn rematch_unsize<'tcx>( + infcx: &InferCtxt<'tcx>, + goal: Goal<'tcx, ty::TraitPredicate<'tcx>>, + mut nested: Vec<PredicateObligation<'tcx>>, + source: BuiltinImplSource, + certainty: Certainty, +) -> SelectionResult<'tcx, Selection<'tcx>> { + let tcx = infcx.tcx; + let a_ty = structurally_normalize(goal.predicate.self_ty(), infcx, goal.param_env, &mut nested); + let b_ty = structurally_normalize( + goal.predicate.trait_ref.args.type_at(1), + infcx, + goal.param_env, + &mut nested, + ); + + match (a_ty.kind(), b_ty.kind()) { + // Don't try to coerce `?0` to `dyn Trait` + (ty::Infer(ty::TyVar(_)), _) | (_, ty::Infer(ty::TyVar(_))) => Ok(None), + // Stall any ambiguous upcasting goals, since we can't rematch those + (ty::Dynamic(_, _, ty::Dyn), ty::Dynamic(_, _, ty::Dyn)) => match certainty { + Certainty::Yes => Ok(Some(ImplSource::Builtin(source, nested))), + _ => Ok(None), + }, + // `T` -> `dyn Trait` upcasting + (_, &ty::Dynamic(data, region, ty::Dyn)) => { + // Check that the type implements all of the predicates of the def-id. + // (i.e. the principal, all of the associated types match, and any auto traits) + nested.extend(data.iter().map(|pred| { + Obligation::new( + infcx.tcx, + ObligationCause::dummy(), + goal.param_env, + pred.with_self_ty(tcx, a_ty), + ) + })); + // The type must be Sized to be unsized. + let sized_def_id = tcx.require_lang_item(hir::LangItem::Sized, None); + nested.push(Obligation::new( + infcx.tcx, + ObligationCause::dummy(), + goal.param_env, + ty::TraitRef::new(tcx, sized_def_id, [a_ty]), + )); + // The type must outlive the lifetime of the `dyn` we're unsizing into. + nested.push(Obligation::new( + infcx.tcx, + ObligationCause::dummy(), + goal.param_env, + ty::OutlivesPredicate(a_ty, region), + )); + + Ok(Some(ImplSource::Builtin(source, nested))) + } + // `[T; n]` -> `[T]` unsizing + (&ty::Array(a_elem_ty, ..), &ty::Slice(b_elem_ty)) => { + nested.extend( + infcx + .at(&ObligationCause::dummy(), goal.param_env) + .eq(DefineOpaqueTypes::No, a_elem_ty, b_elem_ty) + .expect("expected rematch to succeed") + .into_obligations(), + ); + + Ok(Some(ImplSource::Builtin(source, nested))) + } + // Struct unsizing `Struct<T>` -> `Struct<U>` where `T: Unsize<U>` + (&ty::Adt(a_def, a_args), &ty::Adt(b_def, b_args)) + if a_def.is_struct() && a_def.did() == b_def.did() => + { + let unsizing_params = tcx.unsizing_params_for_adt(a_def.did()); + // We must be unsizing some type parameters. This also implies + // that the struct has a tail field. + if unsizing_params.is_empty() { + bug!("expected rematch to succeed") + } + + let tail_field = a_def + .non_enum_variant() + .fields + .raw + .last() + .expect("expected unsized ADT to have a tail field"); + let tail_field_ty = tcx.type_of(tail_field.did); + + let a_tail_ty = tail_field_ty.instantiate(tcx, a_args); + let b_tail_ty = tail_field_ty.instantiate(tcx, b_args); + + // Substitute just the unsizing params from B into A. The type after + // this substitution must be equal to B. This is so we don't unsize + // unrelated type parameters. + let new_a_args = tcx.mk_args_from_iter( + a_args + .iter() + .enumerate() + .map(|(i, a)| if unsizing_params.contains(i as u32) { b_args[i] } else { a }), + ); + let unsized_a_ty = Ty::new_adt(tcx, a_def, new_a_args); + + nested.extend( + infcx + .at(&ObligationCause::dummy(), goal.param_env) + .eq(DefineOpaqueTypes::No, unsized_a_ty, b_ty) + .expect("expected rematch to succeed") + .into_obligations(), + ); + + // Finally, we require that `TailA: Unsize<TailB>` for the tail field + // types. + nested.push(Obligation::new( + tcx, + ObligationCause::dummy(), + goal.param_env, + ty::TraitRef::new(tcx, goal.predicate.def_id(), [a_tail_ty, b_tail_ty]), + )); + + Ok(Some(ImplSource::Builtin(source, nested))) + } + // Tuple unsizing `(.., T)` -> `(.., U)` where `T: Unsize<U>` + (&ty::Tuple(a_tys), &ty::Tuple(b_tys)) + if a_tys.len() == b_tys.len() && !a_tys.is_empty() => + { + let (a_last_ty, a_rest_tys) = a_tys.split_last().unwrap(); + let b_last_ty = b_tys.last().unwrap(); + + // Substitute just the tail field of B., and require that they're equal. + let unsized_a_ty = + Ty::new_tup_from_iter(tcx, a_rest_tys.iter().chain([b_last_ty]).copied()); + nested.extend( + infcx + .at(&ObligationCause::dummy(), goal.param_env) + .eq(DefineOpaqueTypes::No, unsized_a_ty, b_ty) + .expect("expected rematch to succeed") + .into_obligations(), + ); + + // Similar to ADTs, require that we can unsize the tail. + nested.push(Obligation::new( + tcx, + ObligationCause::dummy(), + goal.param_env, + ty::TraitRef::new(tcx, goal.predicate.def_id(), [*a_last_ty, *b_last_ty]), + )); + + // We need to be able to detect tuple unsizing to require its feature gate. + assert_eq!( + source, + BuiltinImplSource::TupleUnsizing, + "compiler-errors wants to know if this can ever be triggered..." + ); + Ok(Some(ImplSource::Builtin(source, nested))) + } + _ => { + assert_ne!(certainty, Certainty::Yes); + Ok(None) + } + } +} + +fn structurally_normalize<'tcx>( + ty: Ty<'tcx>, + infcx: &InferCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + nested: &mut Vec<PredicateObligation<'tcx>>, +) -> Ty<'tcx> { + if matches!(ty.kind(), ty::Alias(..)) { + let mut engine = <dyn TraitEngine<'tcx>>::new(infcx); + let normalized_ty = infcx + .at(&ObligationCause::dummy(), param_env) + .structurally_normalize(ty, &mut *engine) + .expect("normalization shouldn't fail if we got to here"); + nested.extend(engine.pending_obligations()); + normalized_ty + } else { + ty + } +} diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs new file mode 100644 index 00000000000..2139210b873 --- /dev/null +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -0,0 +1,192 @@ +use std::mem; + +use rustc_infer::infer::InferCtxt; +use rustc_infer::traits::solve::MaybeCause; +use rustc_infer::traits::Obligation; +use rustc_infer::traits::{ + query::NoSolution, FulfillmentError, FulfillmentErrorCode, MismatchedProjectionTypes, + PredicateObligation, SelectionError, TraitEngine, +}; +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. +/// +/// This is mostly identical to how `evaluate_all` works inside of the +/// solver, except that the requirements are slightly different. +/// +/// Unlike `evaluate_all` it is possible to add new obligations later on +/// and we also have to track diagnostics information by using `Obligation` +/// instead of `Goal`. +/// +/// It is also likely that we want to use slightly different datastructures +/// 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(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>, + obligation: PredicateObligation<'tcx>, + ) { + assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots()); + self.obligations.push(obligation); + } + + fn collect_remaining_errors(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>> { + self.obligations + .drain(..) + .map(|obligation| { + let code = infcx.probe(|_| { + match infcx + .evaluate_root_goal(obligation.clone().into(), GenerateProofTree::IfEnabled) + .0 + { + Ok((_, Certainty::Maybe(MaybeCause::Ambiguity), _)) => { + FulfillmentErrorCode::CodeAmbiguity { overflow: false } + } + Ok((_, Certainty::Maybe(MaybeCause::Overflow), _)) => { + FulfillmentErrorCode::CodeAmbiguity { overflow: true } + } + Ok((_, Certainty::Yes, _)) => { + bug!("did not expect successful goal when collecting ambiguity errors") + } + Err(_) => { + bug!("did not expect selection error when collecting ambiguity errors") + } + } + }); + + FulfillmentError { + obligation: obligation.clone(), + code, + root_obligation: obligation, + } + }) + .collect() + } + + 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) { + unimplemented!("overflowed on pending obligations: {:?}", self.obligations); + } + + 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, GenerateProofTree::IfEnabled).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::NormalizesTo(..) => { + FulfillmentErrorCode::CodeProjectionError( + 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::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| { + Obligation::new( + infcx.tcx, + obligation.cause.clone(), + goal.param_env, + goal.predicate, + ) + })); + has_changed |= changed; + match certainty { + Certainty::Yes => {} + Certainty::Maybe(_) => self.obligations.push(obligation), + } + } + + if !has_changed { + break; + } + } + + errors + } + + fn pending_obligations(&self) -> Vec<PredicateObligation<'tcx>> { + self.obligations.clone() + } + + fn drain_unstalled_obligations( + &mut self, + _: &InferCtxt<'tcx>, + ) -> Vec<PredicateObligation<'tcx>> { + std::mem::take(&mut self.obligations) + } +} diff --git a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs new file mode 100644 index 00000000000..6db53d6ddc4 --- /dev/null +++ b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs @@ -0,0 +1,236 @@ +/// An infrastructure to mechanically analyse proof trees. +/// +/// It is unavoidable that this representation is somewhat +/// lossy as it should hide quite a few semantically relevant things, +/// e.g. canonicalization and the order of nested goals. +/// +/// @lcnr: However, a lot of the weirdness here is not strictly necessary +/// and could be improved in the future. This is mostly good enough for +/// coherence right now and was annoying to implement, so I am leaving it +/// as is until we start using it for something else. +use std::ops::ControlFlow; + +use rustc_infer::infer::InferCtxt; +use rustc_middle::traits::query::NoSolution; +use rustc_middle::traits::solve::{inspect, QueryResult}; +use rustc_middle::traits::solve::{Certainty, Goal}; +use rustc_middle::ty; + +use crate::solve::inspect::ProofTreeBuilder; +use crate::solve::{GenerateProofTree, InferCtxtEvalExt}; + +pub struct InspectGoal<'a, 'tcx> { + infcx: &'a InferCtxt<'tcx>, + depth: usize, + orig_values: &'a [ty::GenericArg<'tcx>], + goal: Goal<'tcx, ty::Predicate<'tcx>>, + evaluation: &'a inspect::GoalEvaluation<'tcx>, +} + +pub struct InspectCandidate<'a, 'tcx> { + goal: &'a InspectGoal<'a, 'tcx>, + kind: inspect::ProbeKind<'tcx>, + nested_goals: Vec<inspect::CanonicalState<'tcx, Goal<'tcx, ty::Predicate<'tcx>>>>, + result: QueryResult<'tcx>, +} + +impl<'a, 'tcx> InspectCandidate<'a, 'tcx> { + pub fn infcx(&self) -> &'a InferCtxt<'tcx> { + self.goal.infcx + } + + pub fn kind(&self) -> inspect::ProbeKind<'tcx> { + self.kind + } + + pub fn result(&self) -> Result<Certainty, NoSolution> { + self.result.map(|c| c.value.certainty) + } + + /// Visit the nested goals of this candidate. + /// + /// FIXME(@lcnr): we have to slightly adapt this API + /// to also use it to compute the most relevant goal + /// for fulfillment errors. Will do that once we actually + /// need it. + pub fn visit_nested<V: ProofTreeVisitor<'tcx>>( + &self, + visitor: &mut V, + ) -> ControlFlow<V::BreakTy> { + // HACK: An arbitrary cutoff to avoid dealing with overflow and cycles. + if self.goal.depth <= 10 { + let infcx = self.goal.infcx; + infcx.probe(|_| { + let mut instantiated_goals = vec![]; + for goal in &self.nested_goals { + let goal = match ProofTreeBuilder::instantiate_canonical_state( + infcx, + self.goal.goal.param_env, + self.goal.orig_values, + *goal, + ) { + Ok((_goals, goal)) => goal, + Err(NoSolution) => { + warn!( + "unexpected failure when instantiating {:?}: {:?}", + goal, self.nested_goals + ); + return ControlFlow::Continue(()); + } + }; + instantiated_goals.push(goal); + } + + for &goal in &instantiated_goals { + let (_, proof_tree) = infcx.evaluate_root_goal(goal, GenerateProofTree::Yes); + let proof_tree = proof_tree.unwrap(); + visitor.visit_goal(&InspectGoal::new( + infcx, + self.goal.depth + 1, + &proof_tree, + ))?; + } + + ControlFlow::Continue(()) + })?; + } + ControlFlow::Continue(()) + } +} + +impl<'a, 'tcx> InspectGoal<'a, 'tcx> { + pub fn infcx(&self) -> &'a InferCtxt<'tcx> { + self.infcx + } + + pub fn goal(&self) -> Goal<'tcx, ty::Predicate<'tcx>> { + self.goal + } + + pub fn result(&self) -> Result<Certainty, NoSolution> { + self.evaluation.evaluation.result.map(|c| c.value.certainty) + } + + fn candidates_recur( + &'a self, + candidates: &mut Vec<InspectCandidate<'a, 'tcx>>, + nested_goals: &mut Vec<inspect::CanonicalState<'tcx, Goal<'tcx, ty::Predicate<'tcx>>>>, + probe: &inspect::Probe<'tcx>, + ) { + for step in &probe.steps { + match step { + &inspect::ProbeStep::AddGoal(_source, goal) => nested_goals.push(goal), + inspect::ProbeStep::NestedProbe(ref probe) => { + // Nested probes have to prove goals added in their parent + // but do not leak them, so we truncate the added goals + // afterwards. + let num_goals = nested_goals.len(); + self.candidates_recur(candidates, nested_goals, probe); + nested_goals.truncate(num_goals); + } + inspect::ProbeStep::EvaluateGoals(_) + | inspect::ProbeStep::CommitIfOkStart + | inspect::ProbeStep::CommitIfOkSuccess => (), + } + } + + match probe.kind { + inspect::ProbeKind::NormalizedSelfTyAssembly + | inspect::ProbeKind::UnsizeAssembly + | inspect::ProbeKind::UpcastProjectionCompatibility + | inspect::ProbeKind::CommitIfOk => (), + // We add a candidate for the root evaluation if there + // is only one way to prove a given goal, e.g. for `WellFormed`. + // + // FIXME: This is currently wrong if we don't even try any + // candidates, e.g. for a trait goal, as in this case `candidates` is + // actually supposed to be empty. + inspect::ProbeKind::Root { result } => { + if candidates.is_empty() { + candidates.push(InspectCandidate { + goal: self, + kind: probe.kind, + nested_goals: nested_goals.clone(), + result, + }); + } + } + inspect::ProbeKind::MiscCandidate { name: _, result } + | inspect::ProbeKind::TraitCandidate { source: _, result } => { + candidates.push(InspectCandidate { + goal: self, + kind: probe.kind, + nested_goals: nested_goals.clone(), + result, + }); + } + } + } + + pub fn candidates(&'a self) -> Vec<InspectCandidate<'a, 'tcx>> { + let mut candidates = vec![]; + let last_eval_step = match self.evaluation.evaluation.kind { + inspect::CanonicalGoalEvaluationKind::Overflow + | inspect::CanonicalGoalEvaluationKind::CycleInStack => { + warn!("unexpected root evaluation: {:?}", self.evaluation); + return vec![]; + } + inspect::CanonicalGoalEvaluationKind::Evaluation { revisions } => { + if let Some(last) = revisions.last() { + last + } else { + return vec![]; + } + } + }; + + let mut nested_goals = vec![]; + self.candidates_recur(&mut candidates, &mut nested_goals, &last_eval_step.evaluation); + + candidates + } + + fn new( + infcx: &'a InferCtxt<'tcx>, + depth: usize, + root: &'a inspect::GoalEvaluation<'tcx>, + ) -> Self { + match root.kind { + inspect::GoalEvaluationKind::Root { ref orig_values } => InspectGoal { + infcx, + depth, + orig_values, + goal: infcx.resolve_vars_if_possible(root.uncanonicalized_goal), + evaluation: root, + }, + inspect::GoalEvaluationKind::Nested { .. } => unreachable!(), + } + } +} + +/// The public API to interact with proof trees. +pub trait ProofTreeVisitor<'tcx> { + type BreakTy; + + fn visit_goal(&mut self, goal: &InspectGoal<'_, 'tcx>) -> ControlFlow<Self::BreakTy>; +} + +pub trait ProofTreeInferCtxtExt<'tcx> { + fn visit_proof_tree<V: ProofTreeVisitor<'tcx>>( + &self, + goal: Goal<'tcx, ty::Predicate<'tcx>>, + visitor: &mut V, + ) -> ControlFlow<V::BreakTy>; +} + +impl<'tcx> ProofTreeInferCtxtExt<'tcx> for InferCtxt<'tcx> { + fn visit_proof_tree<V: ProofTreeVisitor<'tcx>>( + &self, + goal: Goal<'tcx, ty::Predicate<'tcx>>, + visitor: &mut V, + ) -> ControlFlow<V::BreakTy> { + let (_, proof_tree) = self.evaluate_root_goal(goal, GenerateProofTree::Yes); + let proof_tree = proof_tree.unwrap(); + visitor.visit_goal(&InspectGoal::new(self, 0, &proof_tree)) + } +} diff --git a/compiler/rustc_trait_selection/src/solve/inspect/build.rs b/compiler/rustc_trait_selection/src/solve/inspect/build.rs new file mode 100644 index 00000000000..d8caef5b03f --- /dev/null +++ b/compiler/rustc_trait_selection/src/solve/inspect/build.rs @@ -0,0 +1,556 @@ +//! Building proof trees incrementally during trait solving. +//! +//! This code is *a bit* of a mess and can hopefully be +//! mostly ignored. For a general overview of how it works, +//! see the comment on [ProofTreeBuilder]. +use std::mem; + +use rustc_middle::traits::query::NoSolution; +use rustc_middle::traits::solve::{ + CanonicalInput, Certainty, Goal, GoalSource, IsNormalizesToHack, QueryInput, QueryResult, +}; +use rustc_middle::ty::{self, TyCtxt}; +use rustc_session::config::DumpSolverProofTree; + +use crate::solve::{self, inspect, EvalCtxt, GenerateProofTree}; + +/// The core data structure when building proof trees. +/// +/// In case the current evaluation does not generate a proof +/// tree, `state` is simply `None` and we avoid any work. +/// +/// The possible states of the solver are represented via +/// variants of [DebugSolver]. For any nested computation we call +/// `ProofTreeBuilder::new_nested_computation_kind` which +/// creates a new `ProofTreeBuilder` to temporarily replace the +/// current one. Once that nested computation is done, +/// `ProofTreeBuilder::nested_computation_kind` is called +/// to add the finished nested evaluation to the parent. +/// +/// We provide additional information to the current state +/// by calling methods such as `ProofTreeBuilder::probe_kind`. +/// +/// The actual structure closely mirrors the finished proof +/// trees. At the end of trait solving `ProofTreeBuilder::finalize` +/// is called to recursively convert the whole structure to a +/// finished proof tree. +pub(in crate::solve) struct ProofTreeBuilder<'tcx> { + state: Option<Box<DebugSolver<'tcx>>>, +} + +/// The current state of the proof tree builder, at most places +/// in the code, only one or two variants are actually possible. +/// +/// We simply ICE in case that assumption is broken. +#[derive(Debug)] +enum DebugSolver<'tcx> { + Root, + GoalEvaluation(WipGoalEvaluation<'tcx>), + CanonicalGoalEvaluation(WipCanonicalGoalEvaluation<'tcx>), + AddedGoalsEvaluation(WipAddedGoalsEvaluation<'tcx>), + GoalEvaluationStep(WipGoalEvaluationStep<'tcx>), + Probe(WipProbe<'tcx>), +} + +impl<'tcx> From<WipGoalEvaluation<'tcx>> for DebugSolver<'tcx> { + fn from(g: WipGoalEvaluation<'tcx>) -> DebugSolver<'tcx> { + DebugSolver::GoalEvaluation(g) + } +} + +impl<'tcx> From<WipCanonicalGoalEvaluation<'tcx>> for DebugSolver<'tcx> { + fn from(g: WipCanonicalGoalEvaluation<'tcx>) -> DebugSolver<'tcx> { + DebugSolver::CanonicalGoalEvaluation(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<WipProbe<'tcx>> for DebugSolver<'tcx> { + fn from(p: WipProbe<'tcx>) -> DebugSolver<'tcx> { + DebugSolver::Probe(p) + } +} + +#[derive(Eq, PartialEq, Debug)] +struct WipGoalEvaluation<'tcx> { + pub uncanonicalized_goal: Goal<'tcx, ty::Predicate<'tcx>>, + pub kind: WipGoalEvaluationKind<'tcx>, + pub evaluation: Option<WipCanonicalGoalEvaluation<'tcx>>, + pub returned_goals: Vec<Goal<'tcx, ty::Predicate<'tcx>>>, +} + +impl<'tcx> WipGoalEvaluation<'tcx> { + fn finalize(self) -> inspect::GoalEvaluation<'tcx> { + inspect::GoalEvaluation { + uncanonicalized_goal: self.uncanonicalized_goal, + kind: match self.kind { + WipGoalEvaluationKind::Root { orig_values } => { + inspect::GoalEvaluationKind::Root { orig_values } + } + WipGoalEvaluationKind::Nested { is_normalizes_to_hack } => { + inspect::GoalEvaluationKind::Nested { is_normalizes_to_hack } + } + }, + evaluation: self.evaluation.unwrap().finalize(), + returned_goals: self.returned_goals, + } + } +} + +#[derive(Eq, PartialEq, Debug)] +pub(in crate::solve) enum WipGoalEvaluationKind<'tcx> { + Root { orig_values: Vec<ty::GenericArg<'tcx>> }, + Nested { is_normalizes_to_hack: IsNormalizesToHack }, +} + +#[derive(Eq, PartialEq)] +pub(in crate::solve) enum WipCanonicalGoalEvaluationKind<'tcx> { + Overflow, + CycleInStack, + Interned { revisions: &'tcx [inspect::GoalEvaluationStep<'tcx>] }, +} + +impl std::fmt::Debug for WipCanonicalGoalEvaluationKind<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Overflow => write!(f, "Overflow"), + Self::CycleInStack => write!(f, "CycleInStack"), + Self::Interned { revisions: _ } => f.debug_struct("Interned").finish_non_exhaustive(), + } + } +} + +#[derive(Eq, PartialEq, Debug)] +struct WipCanonicalGoalEvaluation<'tcx> { + goal: CanonicalInput<'tcx>, + kind: Option<WipCanonicalGoalEvaluationKind<'tcx>>, + /// Only used for uncached goals. After we finished evaluating + /// the goal, this is interned and moved into `kind`. + revisions: Vec<WipGoalEvaluationStep<'tcx>>, + result: Option<QueryResult<'tcx>>, +} + +impl<'tcx> WipCanonicalGoalEvaluation<'tcx> { + fn finalize(self) -> inspect::CanonicalGoalEvaluation<'tcx> { + assert!(self.revisions.is_empty()); + let kind = match self.kind.unwrap() { + WipCanonicalGoalEvaluationKind::Overflow => { + inspect::CanonicalGoalEvaluationKind::Overflow + } + WipCanonicalGoalEvaluationKind::CycleInStack => { + inspect::CanonicalGoalEvaluationKind::CycleInStack + } + WipCanonicalGoalEvaluationKind::Interned { revisions } => { + inspect::CanonicalGoalEvaluationKind::Evaluation { revisions } + } + }; + + inspect::CanonicalGoalEvaluation { goal: self.goal, kind, result: self.result.unwrap() } + } +} + +#[derive(Eq, PartialEq, Debug)] +struct WipAddedGoalsEvaluation<'tcx> { + evaluations: Vec<Vec<WipGoalEvaluation<'tcx>>>, + result: Option<Result<Certainty, NoSolution>>, +} + +impl<'tcx> WipAddedGoalsEvaluation<'tcx> { + 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)] +struct WipGoalEvaluationStep<'tcx> { + instantiated_goal: QueryInput<'tcx, ty::Predicate<'tcx>>, + + evaluation: WipProbe<'tcx>, +} + +impl<'tcx> WipGoalEvaluationStep<'tcx> { + fn finalize(self) -> inspect::GoalEvaluationStep<'tcx> { + let evaluation = self.evaluation.finalize(); + match evaluation.kind { + inspect::ProbeKind::Root { .. } => (), + _ => unreachable!("unexpected root evaluation: {evaluation:?}"), + } + inspect::GoalEvaluationStep { instantiated_goal: self.instantiated_goal, evaluation } + } +} + +#[derive(Eq, PartialEq, Debug)] +struct WipProbe<'tcx> { + pub steps: Vec<WipProbeStep<'tcx>>, + pub kind: Option<inspect::ProbeKind<'tcx>>, +} + +impl<'tcx> WipProbe<'tcx> { + fn finalize(self) -> inspect::Probe<'tcx> { + inspect::Probe { + steps: self.steps.into_iter().map(WipProbeStep::finalize).collect(), + kind: self.kind.unwrap(), + } + } +} + +#[derive(Eq, PartialEq, Debug)] +enum WipProbeStep<'tcx> { + AddGoal(GoalSource, inspect::CanonicalState<'tcx, Goal<'tcx, ty::Predicate<'tcx>>>), + EvaluateGoals(WipAddedGoalsEvaluation<'tcx>), + NestedProbe(WipProbe<'tcx>), + CommitIfOkStart, + CommitIfOkSuccess, +} + +impl<'tcx> WipProbeStep<'tcx> { + fn finalize(self) -> inspect::ProbeStep<'tcx> { + match self { + WipProbeStep::AddGoal(source, goal) => inspect::ProbeStep::AddGoal(source, goal), + WipProbeStep::EvaluateGoals(eval) => inspect::ProbeStep::EvaluateGoals(eval.finalize()), + WipProbeStep::NestedProbe(probe) => inspect::ProbeStep::NestedProbe(probe.finalize()), + WipProbeStep::CommitIfOkStart => inspect::ProbeStep::CommitIfOkStart, + WipProbeStep::CommitIfOkSuccess => inspect::ProbeStep::CommitIfOkSuccess, + } + } +} + +impl<'tcx> ProofTreeBuilder<'tcx> { + fn new(state: impl Into<DebugSolver<'tcx>>) -> ProofTreeBuilder<'tcx> { + ProofTreeBuilder { state: Some(Box::new(state.into())) } + } + + fn nested<T: Into<DebugSolver<'tcx>>>(&self, state: impl FnOnce() -> T) -> Self { + ProofTreeBuilder { state: self.state.as_ref().map(|_| Box::new(state().into())) } + } + + fn as_mut(&mut self) -> Option<&mut DebugSolver<'tcx>> { + self.state.as_deref_mut() + } + + pub fn finalize(self) -> Option<inspect::GoalEvaluation<'tcx>> { + match *self.state? { + DebugSolver::GoalEvaluation(wip_goal_evaluation) => { + Some(wip_goal_evaluation.finalize()) + } + root => unreachable!("unexpected proof tree builder root node: {:?}", root), + } + } + + pub fn new_maybe_root( + tcx: TyCtxt<'tcx>, + generate_proof_tree: GenerateProofTree, + ) -> ProofTreeBuilder<'tcx> { + match generate_proof_tree { + GenerateProofTree::Never => ProofTreeBuilder::new_noop(), + GenerateProofTree::IfEnabled => { + let opts = &tcx.sess.opts.unstable_opts; + match opts.next_solver.map(|c| c.dump_tree).unwrap_or_default() { + DumpSolverProofTree::Always => ProofTreeBuilder::new_root(), + // `OnError` is handled by reevaluating goals in error + // reporting with `GenerateProofTree::Yes`. + DumpSolverProofTree::OnError | DumpSolverProofTree::Never => { + ProofTreeBuilder::new_noop() + } + } + } + GenerateProofTree::Yes => ProofTreeBuilder::new_root(), + } + } + + pub fn new_root() -> ProofTreeBuilder<'tcx> { + ProofTreeBuilder::new(DebugSolver::Root) + } + + pub fn new_noop() -> ProofTreeBuilder<'tcx> { + ProofTreeBuilder { state: None } + } + + pub fn is_noop(&self) -> bool { + self.state.is_none() + } + + pub(in crate::solve) fn new_goal_evaluation( + &mut self, + goal: Goal<'tcx, ty::Predicate<'tcx>>, + orig_values: &[ty::GenericArg<'tcx>], + kind: solve::GoalEvaluationKind, + ) -> ProofTreeBuilder<'tcx> { + self.nested(|| WipGoalEvaluation { + uncanonicalized_goal: goal, + kind: match kind { + solve::GoalEvaluationKind::Root => { + WipGoalEvaluationKind::Root { orig_values: orig_values.to_vec() } + } + solve::GoalEvaluationKind::Nested { is_normalizes_to_hack } => { + WipGoalEvaluationKind::Nested { is_normalizes_to_hack } + } + }, + evaluation: None, + returned_goals: vec![], + }) + } + + pub fn new_canonical_goal_evaluation( + &mut self, + goal: CanonicalInput<'tcx>, + ) -> ProofTreeBuilder<'tcx> { + self.nested(|| WipCanonicalGoalEvaluation { + goal, + kind: None, + revisions: vec![], + result: None, + }) + } + + pub fn finalize_evaluation( + &mut self, + tcx: TyCtxt<'tcx>, + ) -> Option<&'tcx [inspect::GoalEvaluationStep<'tcx>]> { + self.as_mut().map(|this| match this { + DebugSolver::CanonicalGoalEvaluation(evaluation) => { + let revisions = mem::take(&mut evaluation.revisions) + .into_iter() + .map(WipGoalEvaluationStep::finalize); + let revisions = &*tcx.arena.alloc_from_iter(revisions); + let kind = WipCanonicalGoalEvaluationKind::Interned { revisions }; + assert_eq!(evaluation.kind.replace(kind), None); + revisions + } + _ => unreachable!(), + }) + } + + pub fn canonical_goal_evaluation(&mut self, canonical_goal_evaluation: ProofTreeBuilder<'tcx>) { + if let Some(this) = self.as_mut() { + match (this, *canonical_goal_evaluation.state.unwrap()) { + ( + DebugSolver::GoalEvaluation(goal_evaluation), + DebugSolver::CanonicalGoalEvaluation(canonical_goal_evaluation), + ) => goal_evaluation.evaluation = Some(canonical_goal_evaluation), + _ => unreachable!(), + } + } + } + + pub fn goal_evaluation_kind(&mut self, kind: WipCanonicalGoalEvaluationKind<'tcx>) { + if let Some(this) = self.as_mut() { + match this { + DebugSolver::CanonicalGoalEvaluation(canonical_goal_evaluation) => { + assert_eq!(canonical_goal_evaluation.kind.replace(kind), 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()) { + ( + 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> { + self.nested(|| WipGoalEvaluationStep { + instantiated_goal, + evaluation: WipProbe { steps: vec![], kind: None }, + }) + } + pub fn goal_evaluation_step(&mut self, goal_evaluation_step: ProofTreeBuilder<'tcx>) { + if let Some(this) = self.as_mut() { + match (this, *goal_evaluation_step.state.unwrap()) { + ( + DebugSolver::CanonicalGoalEvaluation(canonical_goal_evaluations), + DebugSolver::GoalEvaluationStep(goal_evaluation_step), + ) => { + canonical_goal_evaluations.revisions.push(goal_evaluation_step); + } + _ => unreachable!(), + } + } + } + + pub fn new_probe(&mut self) -> ProofTreeBuilder<'tcx> { + self.nested(|| WipProbe { steps: vec![], kind: None }) + } + + pub fn probe_kind(&mut self, probe_kind: inspect::ProbeKind<'tcx>) { + if let Some(this) = self.as_mut() { + match this { + DebugSolver::Probe(this) => { + assert_eq!(this.kind.replace(probe_kind), None) + } + _ => unreachable!(), + } + } + } + + pub fn add_goal( + ecx: &mut EvalCtxt<'_, 'tcx>, + source: GoalSource, + goal: Goal<'tcx, ty::Predicate<'tcx>>, + ) { + // Can't use `if let Some(this) = ecx.inspect.as_mut()` here because + // we have to immutably use the `EvalCtxt` for `make_canonical_state`. + if ecx.inspect.is_noop() { + return; + } + + let goal = Self::make_canonical_state(ecx, goal); + + match ecx.inspect.as_mut().unwrap() { + DebugSolver::GoalEvaluationStep(WipGoalEvaluationStep { + evaluation: WipProbe { steps, .. }, + .. + }) + | DebugSolver::Probe(WipProbe { steps, .. }) => { + steps.push(WipProbeStep::AddGoal(source, goal)) + } + s => unreachable!("tried to add {goal:?} to {s:?}"), + } + } + + pub fn finish_probe(&mut self, probe: ProofTreeBuilder<'tcx>) { + if let Some(this) = self.as_mut() { + match (this, *probe.state.unwrap()) { + ( + DebugSolver::Probe(WipProbe { steps, .. }) + | DebugSolver::GoalEvaluationStep(WipGoalEvaluationStep { + evaluation: WipProbe { steps, .. }, + .. + }), + DebugSolver::Probe(probe), + ) => steps.push(WipProbeStep::NestedProbe(probe)), + _ => unreachable!(), + } + } + } + + /// Used by `EvalCtxt::commit_if_ok` to flatten the work done inside + /// of the probe into the parent. + pub fn integrate_snapshot(&mut self, probe: ProofTreeBuilder<'tcx>) { + if let Some(this) = self.as_mut() { + match (this, *probe.state.unwrap()) { + ( + DebugSolver::Probe(WipProbe { steps, .. }) + | DebugSolver::GoalEvaluationStep(WipGoalEvaluationStep { + evaluation: WipProbe { steps, .. }, + .. + }), + DebugSolver::Probe(probe), + ) => { + steps.push(WipProbeStep::CommitIfOkStart); + assert_eq!(probe.kind, None); + steps.extend(probe.steps); + steps.push(WipProbeStep::CommitIfOkSuccess); + } + _ => unreachable!(), + } + } + } + + pub fn new_evaluate_added_goals(&mut self) -> ProofTreeBuilder<'tcx> { + 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, added_goals_evaluation: ProofTreeBuilder<'tcx>) { + if let Some(this) = self.as_mut() { + match (this, *added_goals_evaluation.state.unwrap()) { + ( + DebugSolver::GoalEvaluationStep(WipGoalEvaluationStep { + evaluation: WipProbe { steps, .. }, + .. + }) + | DebugSolver::Probe(WipProbe { steps, .. }), + DebugSolver::AddedGoalsEvaluation(added_goals_evaluation), + ) => steps.push(WipProbeStep::EvaluateGoals(added_goals_evaluation)), + _ => unreachable!(), + } + } + } + + pub fn query_result(&mut self, result: QueryResult<'tcx>) { + if let Some(this) = self.as_mut() { + match this { + DebugSolver::CanonicalGoalEvaluation(canonical_goal_evaluation) => { + assert_eq!(canonical_goal_evaluation.result.replace(result), None); + } + DebugSolver::GoalEvaluationStep(evaluation_step) => { + assert_eq!( + evaluation_step + .evaluation + .kind + .replace(inspect::ProbeKind::Root { result }), + None + ); + } + _ => unreachable!(), + } + } + } +} diff --git a/compiler/rustc_trait_selection/src/solve/inspect/mod.rs b/compiler/rustc_trait_selection/src/solve/inspect/mod.rs new file mode 100644 index 00000000000..60d52305a6b --- /dev/null +++ b/compiler/rustc_trait_selection/src/solve/inspect/mod.rs @@ -0,0 +1,7 @@ +pub use rustc_middle::traits::solve::inspect::*; + +mod build; +pub(in crate::solve) use build::*; + +mod analyse; +pub use analyse::*; diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs new file mode 100644 index 00000000000..2f3111a2414 --- /dev/null +++ b/compiler/rustc_trait_selection/src/solve/mod.rs @@ -0,0 +1,370 @@ +//! The next-generation trait solver, currently still WIP. +//! +//! As a user of rust, you can use `-Znext-solver` to enable the new trait solver. +//! +//! As a developer of rustc, you shouldn't be using the new trait +//! solver without asking the trait-system-refactor-initiative, but it can +//! be enabled with `InferCtxtBuilder::with_next_trait_solver`. This will +//! ensure that trait solving using that inference context will be routed +//! to the new trait solver. +//! +//! For a high-level overview of how this solver works, check out the relevant +//! section of the rustc-dev-guide. +//! +//! FIXME(@lcnr): Write that section. If you read this before then ask me +//! about it on zulip. +use rustc_hir::def_id::DefId; +use rustc_infer::infer::canonical::{Canonical, CanonicalVarValues}; +use rustc_infer::infer::DefineOpaqueTypes; +use rustc_infer::traits::query::NoSolution; +use rustc_middle::infer::canonical::CanonicalVarInfos; +use rustc_middle::traits::solve::{ + CanonicalResponse, Certainty, ExternalConstraintsData, Goal, GoalSource, IsNormalizesToHack, + QueryResult, Response, +}; +use rustc_middle::ty::{self, OpaqueTypeKey, Ty, TyCtxt, UniverseIndex}; +use rustc_middle::ty::{ + CoercePredicate, RegionOutlivesPredicate, SubtypePredicate, TypeOutlivesPredicate, +}; + +mod alias_relate; +mod assembly; +mod eval_ctxt; +mod fulfill; +pub mod inspect; +mod normalize; +mod normalizes_to; +mod project_goals; +mod search_graph; +mod trait_goals; + +pub use eval_ctxt::{EvalCtxt, GenerateProofTree, InferCtxtEvalExt, InferCtxtSelectExt}; +pub use fulfill::FulfillmentCtxt; +pub(crate) use normalize::deeply_normalize_for_diagnostics; +pub use normalize::{deeply_normalize, deeply_normalize_with_skipped_universes}; + +#[derive(Debug, Clone, Copy)] +enum SolverMode { + /// Ordinary trait solving, using everywhere except for coherence. + Normal, + /// Trait solving during coherence. There are a few notable differences + /// between coherence and ordinary trait solving. + /// + /// Most importantly, trait solving during coherence must not be incomplete, + /// i.e. return `Err(NoSolution)` for goals for which a solution exists. + /// This means that we must not make any guesses or arbitrary choices. + Coherence, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +enum GoalEvaluationKind { + Root, + Nested { is_normalizes_to_hack: IsNormalizesToHack }, +} + +trait CanonicalResponseExt { + fn has_no_inference_or_external_constraints(&self) -> bool; +} + +impl<'tcx> CanonicalResponseExt for Canonical<'tcx, Response<'tcx>> { + fn has_no_inference_or_external_constraints(&self) -> bool { + self.value.external_constraints.region_constraints.is_empty() + && self.value.var_values.is_identity() + && self.value.external_constraints.opaque_types.is_empty() + } +} + +impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { + #[instrument(level = "debug", skip(self))] + fn compute_type_outlives_goal( + &mut self, + goal: Goal<'tcx, TypeOutlivesPredicate<'tcx>>, + ) -> QueryResult<'tcx> { + let ty::OutlivesPredicate(ty, lt) = goal.predicate; + self.register_ty_outlives(ty, lt); + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } + + #[instrument(level = "debug", skip(self))] + fn compute_region_outlives_goal( + &mut self, + goal: Goal<'tcx, RegionOutlivesPredicate<'tcx>>, + ) -> QueryResult<'tcx> { + let ty::OutlivesPredicate(a, b) = goal.predicate; + self.register_region_outlives(a, b); + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } + + #[instrument(level = "debug", skip(self))] + fn compute_coerce_goal( + &mut self, + goal: Goal<'tcx, CoercePredicate<'tcx>>, + ) -> QueryResult<'tcx> { + self.compute_subtype_goal(Goal { + param_env: goal.param_env, + predicate: SubtypePredicate { + a_is_expected: false, + a: goal.predicate.a, + b: goal.predicate.b, + }, + }) + } + + #[instrument(level = "debug", skip(self))] + fn compute_subtype_goal( + &mut self, + goal: Goal<'tcx, SubtypePredicate<'tcx>>, + ) -> QueryResult<'tcx> { + if goal.predicate.a.is_ty_var() && goal.predicate.b.is_ty_var() { + self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) + } else { + self.sub(goal.param_env, goal.predicate.a, goal.predicate.b)?; + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } + } + + #[instrument(level = "debug", skip(self))] + fn compute_closure_kind_goal( + &mut self, + goal: Goal<'tcx, (DefId, ty::GenericArgsRef<'tcx>, ty::ClosureKind)>, + ) -> QueryResult<'tcx> { + let (_, args, expected_kind) = goal.predicate; + let found_kind = args.as_closure().kind_ty().to_opt_closure_kind(); + + let Some(found_kind) = found_kind else { + return self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS); + }; + if found_kind.extends(expected_kind) { + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } else { + Err(NoSolution) + } + } + + #[instrument(level = "debug", skip(self))] + fn compute_object_safe_goal(&mut self, trait_def_id: DefId) -> QueryResult<'tcx> { + if self.tcx().check_is_object_safe(trait_def_id) { + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } else { + Err(NoSolution) + } + } + + #[instrument(level = "debug", skip(self))] + fn compute_well_formed_goal( + &mut self, + goal: Goal<'tcx, ty::GenericArg<'tcx>>, + ) -> QueryResult<'tcx> { + match self.well_formed_goals(goal.param_env, goal.predicate) { + Some(goals) => { + self.add_goals(GoalSource::Misc, goals); + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } + None => self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS), + } + } + + #[instrument(level = "debug", skip(self))] + fn compute_const_evaluatable_goal( + &mut self, + Goal { param_env, predicate: ct }: Goal<'tcx, ty::Const<'tcx>>, + ) -> QueryResult<'tcx> { + 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 { + 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) + } + } + } + + #[instrument(level = "debug", skip(self), ret)] + fn compute_const_arg_has_type_goal( + &mut self, + goal: Goal<'tcx, (ty::Const<'tcx>, Ty<'tcx>)>, + ) -> QueryResult<'tcx> { + let (ct, ty) = goal.predicate; + self.eq(goal.param_env, ct.ty(), ty)?; + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } +} + +impl<'tcx> EvalCtxt<'_, 'tcx> { + #[instrument(level = "debug", skip(self))] + fn set_normalizes_to_hack_goal(&mut self, goal: Goal<'tcx, ty::NormalizesTo<'tcx>>) { + assert!( + self.nested_goals.normalizes_to_hack_goal.is_none(), + "attempted to set the projection eq hack goal when one already exists" + ); + self.nested_goals.normalizes_to_hack_goal = Some(goal); + } + + #[instrument(level = "debug", skip(self))] + fn add_goal(&mut self, source: GoalSource, goal: Goal<'tcx, ty::Predicate<'tcx>>) { + inspect::ProofTreeBuilder::add_goal(self, source, goal); + self.nested_goals.goals.push((source, goal)); + } + + #[instrument(level = "debug", skip(self, goals))] + fn add_goals( + &mut self, + source: GoalSource, + goals: impl IntoIterator<Item = Goal<'tcx, ty::Predicate<'tcx>>>, + ) { + for goal in goals { + self.add_goal(source, goal); + } + } + + /// Try to merge multiple possible ways to prove a goal, if that is not possible returns `None`. + /// + /// In this case we tend to flounder and return ambiguity by calling `[EvalCtxt::flounder]`. + #[instrument(level = "debug", skip(self), ret)] + fn try_merge_responses( + &mut self, + responses: &[CanonicalResponse<'tcx>], + ) -> Option<CanonicalResponse<'tcx>> { + if responses.is_empty() { + return None; + } + + // FIXME(-Znext-solver): We should instead try to find a `Certainty::Yes` response with + // a subset of the constraints that all the other responses have. + let one = responses[0]; + if responses[1..].iter().all(|&resp| resp == one) { + return Some(one); + } + + responses + .iter() + .find(|response| { + response.value.certainty == Certainty::Yes + && response.has_no_inference_or_external_constraints() + }) + .copied() + } + + /// If we fail to merge responses we flounder and return overflow or ambiguity. + #[instrument(level = "debug", skip(self), ret)] + fn flounder(&mut self, responses: &[CanonicalResponse<'tcx>]) -> QueryResult<'tcx> { + if responses.is_empty() { + return Err(NoSolution); + } + + let Certainty::Maybe(maybe_cause) = + responses.iter().fold(Certainty::AMBIGUOUS, |certainty, response| { + certainty.unify_with(response.value.certainty) + }) + else { + bug!("expected flounder response to be ambiguous") + }; + + Ok(self.make_ambiguous_response_no_constraints(maybe_cause)) + } + + /// Normalize a type when it is structually matched on. + /// + /// For self types this is generally already handled through + /// `assemble_candidates_after_normalizing_self_ty`, so anything happening + /// in [`EvalCtxt::assemble_candidates_via_self_ty`] does not have to normalize + /// the self type. It is required when structurally matching on any other + /// arguments of a trait goal, e.g. when assembling builtin unsize candidates. + #[instrument(level = "debug", skip(self), ret)] + fn try_normalize_ty( + &mut self, + param_env: ty::ParamEnv<'tcx>, + ty: Ty<'tcx>, + ) -> Option<Ty<'tcx>> { + self.try_normalize_ty_recur(param_env, DefineOpaqueTypes::Yes, 0, ty) + } + + fn try_normalize_ty_recur( + &mut self, + param_env: ty::ParamEnv<'tcx>, + define_opaque_types: DefineOpaqueTypes, + depth: usize, + ty: Ty<'tcx>, + ) -> Option<Ty<'tcx>> { + if !self.tcx().recursion_limit().value_within_limit(depth) { + return None; + } + + let ty::Alias(kind, alias) = *ty.kind() else { + return Some(ty); + }; + + // We do no always define opaque types eagerly to allow non-defining uses in the defining scope. + if let (DefineOpaqueTypes::No, ty::AliasKind::Opaque) = (define_opaque_types, kind) { + if let Some(def_id) = alias.def_id.as_local() { + if self + .unify_existing_opaque_tys( + param_env, + OpaqueTypeKey { def_id, args: alias.args }, + self.next_ty_infer(), + ) + .is_empty() + { + return Some(ty); + } + } + } + + match self.commit_if_ok(|this| { + let normalized_ty = this.next_ty_infer(); + let normalizes_to_goal = Goal::new( + this.tcx(), + param_env, + ty::NormalizesTo { alias, term: normalized_ty.into() }, + ); + this.add_goal(GoalSource::Misc, normalizes_to_goal); + this.try_evaluate_added_goals()?; + let ty = this.resolve_vars_if_possible(normalized_ty); + Ok(this.try_normalize_ty_recur(param_env, define_opaque_types, depth + 1, ty)) + }) { + Ok(ty) => ty, + Err(NoSolution) => Some(ty), + } + } +} + +fn response_no_constraints_raw<'tcx>( + tcx: TyCtxt<'tcx>, + max_universe: UniverseIndex, + variables: CanonicalVarInfos<'tcx>, + certainty: Certainty, +) -> CanonicalResponse<'tcx> { + Canonical { + max_universe, + variables, + value: Response { + var_values: CanonicalVarValues::make_identity(tcx, variables), + // FIXME: maybe we should store the "no response" version in tcx, like + // we do for tcx.types and stuff. + external_constraints: tcx.mk_external_constraints(ExternalConstraintsData::default()), + certainty, + }, + } +} 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 00000000000..55b79e6fc39 --- /dev/null +++ b/compiler/rustc_trait_selection/src/solve/normalize.rs @@ -0,0 +1,272 @@ +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::infer::InferCtxt; +use rustc_infer::traits::TraitEngineExt; +use rustc_infer::traits::{FulfillmentError, Obligation, TraitEngine}; +use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; +use rustc_middle::traits::{ObligationCause, Reveal}; +use rustc_middle::ty::{self, AliasTy, Ty, TyCtxt, UniverseIndex}; +use rustc_middle::ty::{FallibleTypeFolder, TypeFolder, 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 fn deeply_normalize<'tcx, T: TypeFoldable<TyCtxt<'tcx>>>( + at: At<'_, 'tcx>, + value: T, +) -> Result<T, Vec<FulfillmentError<'tcx>>> { + assert!(!value.has_escaping_bound_vars()); + deeply_normalize_with_skipped_universes(at, value, vec![]) +} + +/// 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. +pub fn deeply_normalize_with_skipped_universes<'tcx, T: TypeFoldable<TyCtxt<'tcx>>>( + at: At<'_, 'tcx>, + value: T, + universes: Vec<Option<UniverseIndex>>, +) -> Result<T, Vec<FulfillmentError<'tcx>>> { + let fulfill_cx = FulfillmentCtxt::new(at.infcx); + let mut folder = NormalizationFolder { at, fulfill_cx, depth: 0, universes }; + + 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::NormalizesTo { 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::NormalizesTo { + alias: AliasTy::new(tcx, uv.def, uv.args), + 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, + &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, + &self.universes, + result, + )) + } else { + ensure_sufficient_stack(|| self.normalize_unevaluated_const(ct.ty(), uv)) + } + } +} + +// Deeply normalize a value and return it +pub(crate) fn deeply_normalize_for_diagnostics<'tcx, T: TypeFoldable<TyCtxt<'tcx>>>( + infcx: &InferCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + t: T, +) -> T { + t.fold_with(&mut DeeplyNormalizeForDiagnosticsFolder { + at: infcx.at(&ObligationCause::dummy(), param_env), + }) +} + +struct DeeplyNormalizeForDiagnosticsFolder<'a, 'tcx> { + at: At<'a, 'tcx>, +} + +impl<'tcx> TypeFolder<TyCtxt<'tcx>> for DeeplyNormalizeForDiagnosticsFolder<'_, 'tcx> { + fn interner(&self) -> TyCtxt<'tcx> { + self.at.infcx.tcx + } + + fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { + deeply_normalize_with_skipped_universes( + self.at, + ty, + vec![None; ty.outer_exclusive_binder().as_usize()], + ) + .unwrap_or_else(|_| ty.super_fold_with(self)) + } + + fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { + deeply_normalize_with_skipped_universes( + self.at, + ct, + vec![None; ct.outer_exclusive_binder().as_usize()], + ) + .unwrap_or_else(|_| ct.super_fold_with(self)) + } +} diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/inherent.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/inherent.rs new file mode 100644 index 00000000000..b2dff9b48ff --- /dev/null +++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/inherent.rs @@ -0,0 +1,56 @@ +//! Computes a normalizes-to (projection) goal for inherent associated types, +//! `#![feature(inherent_associated_type)]`. Since astconv already determines +//! which impl the IAT is being projected from, we just: +//! 1. instantiate substs, +//! 2. equate the self type, and +//! 3. instantiate and register where clauses. +use rustc_middle::traits::solve::{Certainty, Goal, GoalSource, QueryResult}; +use rustc_middle::ty; + +use crate::solve::EvalCtxt; + +impl<'tcx> EvalCtxt<'_, 'tcx> { + pub(super) fn normalize_inherent_associated_type( + &mut self, + goal: Goal<'tcx, ty::NormalizesTo<'tcx>>, + ) -> QueryResult<'tcx> { + let tcx = self.tcx(); + let inherent = goal.predicate.alias; + let expected = goal.predicate.term.ty().expect("inherent consts are treated separately"); + + let impl_def_id = tcx.parent(inherent.def_id); + let impl_substs = self.fresh_args_for_item(impl_def_id); + + // Equate impl header and add impl where clauses + self.eq( + goal.param_env, + inherent.self_ty(), + tcx.type_of(impl_def_id).instantiate(tcx, impl_substs), + )?; + + // Equate IAT with the RHS of the project goal + let inherent_substs = inherent.rebase_inherent_args_onto_impl(impl_substs, tcx); + self.eq( + goal.param_env, + expected, + tcx.type_of(inherent.def_id).instantiate(tcx, inherent_substs), + ) + .expect("expected goal term to be fully unconstrained"); + + // Check both where clauses on the impl and IAT + // + // FIXME(-Znext-solver=coinductive): I think this should be split + // and we tag the impl bounds with `GoalSource::ImplWhereBound`? + // Right not this includes both the impl and the assoc item where bounds, + // and I don't think the assoc item where-bounds are allowed to be coinductive. + self.add_goals( + GoalSource::Misc, + tcx.predicates_of(inherent.def_id) + .instantiate(tcx, inherent_substs) + .into_iter() + .map(|(pred, _)| goal.with(tcx, pred)), + ); + + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } +} diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs new file mode 100644 index 00000000000..ccee6f8eb29 --- /dev/null +++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs @@ -0,0 +1,719 @@ +use crate::traits::{check_args_compatible, specialization_graph}; + +use super::assembly::{self, structural_traits, Candidate}; +use super::{EvalCtxt, GoalSource}; +use rustc_hir::def::DefKind; +use rustc_hir::def_id::DefId; +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::{ + CandidateSource, CanonicalResponse, Certainty, Goal, QueryResult, +}; +use rustc_middle::traits::BuiltinImplSource; +use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; +use rustc_middle::ty::NormalizesTo; +use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{ToPredicate, TypeVisitableExt}; +use rustc_span::{sym, ErrorGuaranteed, DUMMY_SP}; + +mod inherent; +mod opaques; +mod weak_types; + +impl<'tcx> EvalCtxt<'_, 'tcx> { + #[instrument(level = "debug", skip(self), ret)] + pub(super) fn compute_normalizes_to_goal( + &mut self, + goal: Goal<'tcx, NormalizesTo<'tcx>>, + ) -> QueryResult<'tcx> { + 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 + // 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, since we're piggybacking off of regular + // goal caching. + if self.term_is_fully_unconstrained(goal) { + 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 => { + self.normalize_inherent_associated_type(goal) + } + } + } else { + self.set_normalizes_to_hack_goal(goal); + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } + } + 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::NormalizesTo<'tcx>>, + ) -> QueryResult<'tcx> { + if let Some(normalized_const) = self.try_const_eval_resolve( + goal.param_env, + ty::UnevaluatedConst::new(goal.predicate.alias.def_id, goal.predicate.alias.args), + self.tcx() + .type_of(goal.predicate.alias.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) + } + } +} + +impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { + fn self_ty(self) -> Ty<'tcx> { + self.self_ty() + } + + fn trait_ref(self, tcx: TyCtxt<'tcx>) -> ty::TraitRef<'tcx> { + self.alias.trait_ref(tcx) + } + + fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self { + self.with_self_ty(tcx, self_ty) + } + + fn trait_def_id(self, tcx: TyCtxt<'tcx>) -> DefId { + self.trait_def_id(tcx) + } + + fn probe_and_match_goal_against_assumption( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + assumption: ty::Clause<'tcx>, + then: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> QueryResult<'tcx>, + ) -> QueryResult<'tcx> { + if let Some(projection_pred) = assumption.as_projection_clause() { + if projection_pred.projection_def_id() == goal.predicate.def_id() { + let tcx = ecx.tcx(); + ecx.probe_misc_candidate("assumption").enter(|ecx| { + let assumption_projection_pred = + ecx.instantiate_binder_with_infer(projection_pred); + ecx.eq( + goal.param_env, + goal.predicate.alias, + 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"); + + // Add GAT where clauses from the trait's definition + ecx.add_goals( + GoalSource::Misc, + tcx.predicates_of(goal.predicate.def_id()) + .instantiate_own(tcx, goal.predicate.alias.args) + .map(|(pred, _)| goal.with(tcx, pred)), + ); + + then(ecx) + }) + } else { + Err(NoSolution) + } + } else { + Err(NoSolution) + } + } + + fn consider_impl_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, NormalizesTo<'tcx>>, + impl_def_id: DefId, + ) -> Result<Candidate<'tcx>, NoSolution> { + let tcx = ecx.tcx(); + + let goal_trait_ref = goal.predicate.alias.trait_ref(tcx); + let impl_trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); + let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::ForLookup }; + if !drcx.args_may_unify(goal_trait_ref.args, impl_trait_ref.skip_binder().args) { + return Err(NoSolution); + } + + ecx.probe_trait_candidate(CandidateSource::Impl(impl_def_id)).enter(|ecx| { + let impl_args = ecx.fresh_args_for_item(impl_def_id); + let impl_trait_ref = impl_trait_ref.instantiate(tcx, impl_args); + + ecx.eq(goal.param_env, goal_trait_ref, impl_trait_ref)?; + + let where_clause_bounds = tcx + .predicates_of(impl_def_id) + .instantiate(tcx, impl_args) + .predicates + .into_iter() + .map(|pred| goal.with(tcx, pred)); + ecx.add_goals(GoalSource::ImplWhereBound, where_clause_bounds); + + // Add GAT where clauses from the trait's definition + ecx.add_goals( + GoalSource::Misc, + tcx.predicates_of(goal.predicate.def_id()) + .instantiate_own(tcx, goal.predicate.alias.args) + .map(|(pred, _)| goal.with(tcx, pred)), + ); + + // 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); + }; + + let error_response = |ecx: &mut EvalCtxt<'_, 'tcx>, reason| { + let guar = tcx.dcx().span_delayed_bug(tcx.def_span(assoc_def.item.def_id), reason); + let error_term = match assoc_def.item.kind { + ty::AssocKind::Const => ty::Const::new_error( + tcx, + guar, + tcx.type_of(goal.predicate.def_id()) + .instantiate(tcx, goal.predicate.alias.args), + ) + .into(), + ty::AssocKind::Type => Ty::new_error(tcx, guar).into(), + // This makes no sense... + ty::AssocKind::Fn => span_bug!( + tcx.def_span(assoc_def.item.def_id), + "cannot project to an associated function" + ), + }; + ecx.eq(goal.param_env, goal.predicate.term, error_term) + .expect("expected goal term to be fully unconstrained"); + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }; + + if !assoc_def.item.defaultness(tcx).has_value() { + return error_response(ecx, "missing value for assoc item in impl"); + } + + // Getting the right args 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 args onto the impl, going from `[Vec<u32>, i32, u64]` + // to `[u32, u64]`. + // + // And then map these args to the args of the defining impl of `Assoc`, going + // from `[u32, u64]` to `[u32, i32, u64]`. + let impl_args_with_gat = + goal.predicate.alias.args.rebase_onto(tcx, goal_trait_ref.def_id, impl_args); + let args = ecx.translate_args( + goal.param_env, + impl_def_id, + impl_args_with_gat, + assoc_def.defining_node, + ); + + if !check_args_compatible(tcx, assoc_def.item, args) { + return error_response( + ecx, + "associated item has mismatched generic item arguments", + ); + } + + // 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 => { + if tcx.features().associated_const_equality { + bug!("associated const projection is not supported yet") + } else { + ty::EarlyBinder::bind( + ty::Const::new_error_with_message( + tcx, + tcx.type_of(assoc_def.item.def_id).instantiate_identity(), + DUMMY_SP, + "associated const projection is not supported yet", + ) + .into(), + ) + } + } + ty::AssocKind::Fn => unreachable!("we should never project to a fn"), + }; + + ecx.eq(goal.param_env, goal.predicate.term, term.instantiate(tcx, args)) + .expect("expected goal term to be fully unconstrained"); + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }) + } + + /// Fail to normalize if the predicate contains an error, alternatively, we could normalize to `ty::Error` + /// and succeed. Can experiment with this to figure out what results in better error messages. + fn consider_error_guaranteed_candidate( + _ecx: &mut EvalCtxt<'_, 'tcx>, + _guar: ErrorGuaranteed, + ) -> QueryResult<'tcx> { + Err(NoSolution) + } + + fn consider_auto_trait_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + ecx.tcx().dcx().span_delayed_bug( + ecx.tcx().def_span(goal.predicate.def_id()), + "associated types not allowed on auto traits", + ); + Err(NoSolution) + } + + fn consider_trait_alias_candidate( + _ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + bug!("trait aliases do not have associated types: {:?}", goal); + } + + fn consider_builtin_sized_candidate( + _ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + bug!("`Sized` does not have an associated type: {:?}", goal); + } + + fn consider_builtin_copy_clone_candidate( + _ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + bug!("`Copy`/`Clone` does not have an associated type: {:?}", goal); + } + + fn consider_builtin_pointer_like_candidate( + _ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + bug!("`PointerLike` does not have an associated type: {:?}", goal); + } + + fn consider_builtin_fn_ptr_trait_candidate( + _ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + bug!("`FnPtr` does not have an associated type: {:?}", goal); + } + + fn consider_builtin_fn_trait_candidates( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + goal_kind: ty::ClosureKind, + ) -> QueryResult<'tcx> { + let tcx = ecx.tcx(); + let tupled_inputs_and_output = + match structural_traits::extract_tupled_inputs_and_output_from_callable( + tcx, + goal.predicate.self_ty(), + goal_kind, + )? { + Some(tupled_inputs_and_output) => tupled_inputs_and_output, + None => { + return ecx + .evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS); + } + }; + let output_is_sized_pred = tupled_inputs_and_output.map_bound(|(_, output)| { + ty::TraitRef::from_lang_item(tcx, LangItem::Sized, DUMMY_SP, [output]) + }); + + let pred = tupled_inputs_and_output + .map_bound(|(inputs, output)| ty::ProjectionPredicate { + projection_ty: ty::AliasTy::new( + tcx, + goal.predicate.def_id(), + [goal.predicate.self_ty(), inputs], + ), + term: output.into(), + }) + .to_predicate(tcx); + + // A built-in `Fn` impl only holds if the output is sized. + // (FIXME: technically we only need to check this if the type is a fn ptr...) + Self::consider_implied_clause(ecx, goal, pred, [goal.with(tcx, output_is_sized_pred)]) + } + + fn consider_builtin_tuple_candidate( + _ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + bug!("`Tuple` does not have an associated type: {:?}", goal); + } + + fn consider_builtin_pointee_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + let tcx = ecx.tcx(); + ecx.probe_misc_candidate("builtin pointee").enter(|ecx| { + let metadata_ty = match goal.predicate.self_ty().kind() { + ty::Bool + | ty::Char + | ty::Int(..) + | ty::Uint(..) + | ty::Float(..) + | ty::Array(..) + | ty::RawPtr(..) + | ty::Ref(..) + | ty::FnDef(..) + | ty::FnPtr(..) + | ty::Closure(..) + | ty::Infer(ty::IntVar(..) | ty::FloatVar(..)) + | ty::Coroutine(..) + | ty::CoroutineWitness(..) + | ty::Never + | ty::Foreign(..) => tcx.types.unit, + + ty::Error(e) => Ty::new_error(tcx, *e), + + ty::Str | ty::Slice(_) => tcx.types.usize, + + ty::Dynamic(_, _, _) => { + let dyn_metadata = tcx.require_lang_item(LangItem::DynMetadata, None); + tcx.type_of(dyn_metadata) + .instantiate(tcx, &[ty::GenericArg::from(goal.predicate.self_ty())]) + } + + ty::Alias(_, _) | ty::Param(_) | ty::Placeholder(..) => { + // FIXME(ptr_metadata): It would also be possible to return a `Ok(Ambig)` with no constraints. + let sized_predicate = ty::TraitRef::from_lang_item( + tcx, + LangItem::Sized, + DUMMY_SP, + [ty::GenericArg::from(goal.predicate.self_ty())], + ); + // FIXME(-Znext-solver=coinductive): Should this be `GoalSource::ImplWhereBound`? + ecx.add_goal(GoalSource::Misc, goal.with(tcx, sized_predicate)); + tcx.types.unit + } + + ty::Adt(def, args) if def.is_struct() => match def.non_enum_variant().tail_opt() { + None => tcx.types.unit, + Some(field_def) => { + let self_ty = field_def.ty(tcx, args); + // FIXME(-Znext-solver=coinductive): Should this be `GoalSource::ImplWhereBound`? + ecx.add_goal( + GoalSource::Misc, + goal.with(tcx, goal.predicate.with_self_ty(tcx, self_ty)), + ); + return ecx + .evaluate_added_goals_and_make_canonical_response(Certainty::Yes); + } + }, + ty::Adt(_, _) => tcx.types.unit, + + ty::Tuple(elements) => match elements.last() { + None => tcx.types.unit, + Some(&self_ty) => { + // FIXME(-Znext-solver=coinductive): Should this be `GoalSource::ImplWhereBound`? + ecx.add_goal( + GoalSource::Misc, + goal.with(tcx, goal.predicate.with_self_ty(tcx, self_ty)), + ); + return ecx + .evaluate_added_goals_and_make_canonical_response(Certainty::Yes); + } + }, + + ty::Infer( + ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_), + ) + | ty::Bound(..) => bug!( + "unexpected self ty `{:?}` when normalizing `<T as Pointee>::Metadata`", + goal.predicate.self_ty() + ), + }; + + ecx.eq(goal.param_env, goal.predicate.term, metadata_ty.into()) + .expect("expected goal term to be fully unconstrained"); + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }) + } + + fn consider_builtin_future_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + let self_ty = goal.predicate.self_ty(); + let ty::Coroutine(def_id, args) = *self_ty.kind() else { + return Err(NoSolution); + }; + + // Coroutines are not futures unless they come from `async` desugaring + let tcx = ecx.tcx(); + if !tcx.coroutine_is_async(def_id) { + return Err(NoSolution); + } + + let term = args.as_coroutine().return_ty().into(); + + Self::consider_implied_clause( + ecx, + goal, + ty::ProjectionPredicate { + projection_ty: ty::AliasTy::new(ecx.tcx(), goal.predicate.def_id(), [self_ty]), + term, + } + .to_predicate(tcx), + // Technically, we need to check that the future type is Sized, + // but that's already proven by the coroutine being WF. + [], + ) + } + + fn consider_builtin_iterator_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + let self_ty = goal.predicate.self_ty(); + let ty::Coroutine(def_id, args) = *self_ty.kind() else { + return Err(NoSolution); + }; + + // Coroutines are not Iterators unless they come from `gen` desugaring + let tcx = ecx.tcx(); + if !tcx.coroutine_is_gen(def_id) { + return Err(NoSolution); + } + + let term = args.as_coroutine().yield_ty().into(); + + Self::consider_implied_clause( + ecx, + goal, + ty::ProjectionPredicate { + projection_ty: ty::AliasTy::new(ecx.tcx(), goal.predicate.def_id(), [self_ty]), + term, + } + .to_predicate(tcx), + // Technically, we need to check that the iterator type is Sized, + // but that's already proven by the generator being WF. + [], + ) + } + + fn consider_builtin_async_iterator_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + let self_ty = goal.predicate.self_ty(); + let ty::Coroutine(def_id, args) = *self_ty.kind() else { + return Err(NoSolution); + }; + + // Coroutines are not AsyncIterators unless they come from `gen` desugaring + let tcx = ecx.tcx(); + if !tcx.coroutine_is_async_gen(def_id) { + return Err(NoSolution); + } + + ecx.probe_misc_candidate("builtin AsyncIterator kind").enter(|ecx| { + // Take `AsyncIterator<Item = I>` and turn it into the corresponding + // coroutine yield ty `Poll<Option<I>>`. + let expected_ty = Ty::new_adt( + tcx, + tcx.adt_def(tcx.require_lang_item(LangItem::Poll, None)), + tcx.mk_args(&[Ty::new_adt( + tcx, + tcx.adt_def(tcx.require_lang_item(LangItem::Option, None)), + tcx.mk_args(&[goal.predicate.term.into()]), + ) + .into()]), + ); + let yield_ty = args.as_coroutine().yield_ty(); + ecx.eq(goal.param_env, expected_ty, yield_ty)?; + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }) + } + + fn consider_builtin_coroutine_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + let self_ty = goal.predicate.self_ty(); + let ty::Coroutine(def_id, args) = *self_ty.kind() else { + return Err(NoSolution); + }; + + // `async`-desugared coroutines do not implement the coroutine trait + let tcx = ecx.tcx(); + if !tcx.is_general_coroutine(def_id) { + return Err(NoSolution); + } + + let coroutine = args.as_coroutine(); + + let name = tcx.associated_item(goal.predicate.def_id()).name; + let term = if name == sym::Return { + coroutine.return_ty().into() + } else if name == sym::Yield { + coroutine.yield_ty().into() + } else { + bug!("unexpected associated item `<{self_ty} as Coroutine>::{name}`") + }; + + Self::consider_implied_clause( + ecx, + goal, + ty::ProjectionPredicate { + projection_ty: ty::AliasTy::new( + ecx.tcx(), + goal.predicate.def_id(), + [self_ty, coroutine.resume_ty()], + ), + term, + } + .to_predicate(tcx), + // Technically, we need to check that the coroutine type is Sized, + // but that's already proven by the coroutine being WF. + [], + ) + } + + fn consider_unsize_to_dyn_candidate( + _ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + bug!("`Unsize` does not have an associated type: {:?}", goal) + } + + fn consider_structural_builtin_unsize_candidates( + _ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)> { + bug!("`Unsize` does not have an associated type: {:?}", goal); + } + + fn consider_builtin_discriminant_kind_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + let self_ty = goal.predicate.self_ty(); + let discriminant_ty = match *self_ty.kind() { + ty::Bool + | ty::Char + | ty::Int(..) + | ty::Uint(..) + | ty::Float(..) + | ty::Array(..) + | ty::RawPtr(..) + | ty::Ref(..) + | ty::FnDef(..) + | ty::FnPtr(..) + | ty::Closure(..) + | ty::Infer(ty::IntVar(..) | ty::FloatVar(..)) + | ty::Coroutine(..) + | ty::CoroutineWitness(..) + | ty::Never + | ty::Foreign(..) + | ty::Adt(_, _) + | ty::Str + | ty::Slice(_) + | ty::Dynamic(_, _, _) + | ty::Tuple(_) + | ty::Error(_) => self_ty.discriminant_ty(ecx.tcx()), + + // We do not call `Ty::discriminant_ty` on alias, param, or placeholder + // types, which return `<self_ty as DiscriminantKind>::Discriminant` + // (or ICE in the case of placeholders). Projecting a type to itself + // is never really productive. + ty::Alias(_, _) | ty::Param(_) | ty::Placeholder(..) => { + return Err(NoSolution); + } + + ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) + | ty::Bound(..) => bug!( + "unexpected self ty `{:?}` when normalizing `<T as DiscriminantKind>::Discriminant`", + goal.predicate.self_ty() + ), + }; + + ecx.probe_misc_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) + }) + } + + fn consider_builtin_destruct_candidate( + _ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + bug!("`Destruct` does not have an associated type: {:?}", goal); + } + + fn consider_builtin_transmute_candidate( + _ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + bug!("`BikeshedIntrinsicFrom` does not have an associated type: {:?}", goal) + } +} + +/// This behavior is also implemented in `rustc_ty_utils` and in the old `project` code. +/// +/// FIXME: We should merge these 3 implementations as it's likely that they otherwise +/// diverge. +#[instrument(level = "debug", skip(ecx, param_env), ret)] +fn fetch_eligible_assoc_item_def<'tcx>( + ecx: &EvalCtxt<'_, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + goal_trait_ref: ty::TraitRef<'tcx>, + trait_assoc_def_id: DefId, + impl_def_id: DefId, +) -> Result<Option<LeafDef>, NoSolution> { + let node_item = specialization_graph::assoc_def(ecx.tcx(), impl_def_id, trait_assoc_def_id) + .map_err(|ErrorGuaranteed { .. }| NoSolution)?; + + let eligible = if node_item.is_final() { + // Non-specializable items are always projectable. + true + } else { + // Only reveal a specializable default if we're past type-checking + // and the obligation is monomorphic, otherwise passes such as + // transmute checking and polymorphic MIR optimizations could + // get a result which isn't correct for all monomorphizations. + if param_env.reveal() == Reveal::All { + let poly_trait_ref = ecx.resolve_vars_if_possible(goal_trait_ref); + !poly_trait_ref.still_further_specializable() + } else { + debug!(?node_item.item.def_id, "not eligible due to default"); + false + } + }; + + if eligible { Ok(Some(node_item)) } else { Ok(None) } +} diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/opaques.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/opaques.rs new file mode 100644 index 00000000000..b5d1aa06e4e --- /dev/null +++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/opaques.rs @@ -0,0 +1,106 @@ +//! Computes a normalizes-to (projection) goal for opaque types. This goal +//! behaves differently depending on the param-env's reveal mode and whether +//! the opaque is in a defining scope. +use rustc_middle::traits::query::NoSolution; +use rustc_middle::traits::solve::{Certainty, Goal, QueryResult}; +use rustc_middle::traits::Reveal; +use rustc_middle::ty; +use rustc_middle::ty::util::NotUniqueParam; + +use crate::solve::{EvalCtxt, SolverMode}; + +impl<'tcx> EvalCtxt<'_, 'tcx> { + pub(super) fn normalize_opaque_type( + &mut self, + goal: Goal<'tcx, ty::NormalizesTo<'tcx>>, + ) -> QueryResult<'tcx> { + let tcx = self.tcx(); + let opaque_ty = goal.predicate.alias; + let expected = goal.predicate.term.ty().expect("no such thing as an opaque const"); + + match (goal.param_env.reveal(), self.solver_mode()) { + (Reveal::UserFacing, SolverMode::Normal) => { + let Some(opaque_ty_def_id) = opaque_ty.def_id.as_local() else { + return Err(NoSolution); + }; + // 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". + if !self.can_define_opaque_ty(opaque_ty_def_id) { + return Err(NoSolution); + } + // FIXME: This may have issues when the args contain aliases... + match self.tcx().uses_unique_placeholders_ignoring_regions(opaque_ty.args) { + Err(NotUniqueParam::NotParam(param)) if param.is_non_region_infer() => { + return self.evaluate_added_goals_and_make_canonical_response( + Certainty::AMBIGUOUS, + ); + } + Err(_) => { + return Err(NoSolution); + } + Ok(()) => {} + } + // Prefer opaques registered already. + let opaque_type_key = + ty::OpaqueTypeKey { def_id: opaque_ty_def_id, args: opaque_ty.args }; + // FIXME: This also unifies the previous hidden type with the expected. + // + // If that fails, we insert `expected` as a new hidden type instead of + // eagerly emitting an error. + 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); + } else { + return self.flounder(&matches); + } + } + + let expected = match self.try_normalize_ty(goal.param_env, expected) { + Some(ty) => { + if ty.is_ty_var() { + return self.evaluate_added_goals_and_make_canonical_response( + Certainty::AMBIGUOUS, + ); + } else { + ty + } + } + None => { + return self + .evaluate_added_goals_and_make_canonical_response(Certainty::OVERFLOW); + } + }; + + // Otherwise, define a new opaque type + self.insert_hidden_type(opaque_type_key, goal.param_env, expected)?; + self.add_item_bounds_for_hidden_type( + opaque_ty.def_id, + opaque_ty.args, + 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.args, + goal.param_env, + expected, + ); + self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) + } + (Reveal::All, _) => { + // FIXME: Add an assertion that opaque type storage is empty. + let actual = tcx.type_of(opaque_ty.def_id).instantiate(tcx, opaque_ty.args); + 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/solve/normalizes_to/weak_types.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/weak_types.rs new file mode 100644 index 00000000000..6d5728797d1 --- /dev/null +++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/weak_types.rs @@ -0,0 +1,35 @@ +//! Computes a normalizes-to (projection) goal for inherent associated types, +//! `#![feature(lazy_type_alias)]` and `#![feature(type_alias_impl_trait)]`. +//! +//! Since a weak alias is not ambiguous, this just computes the `type_of` of +//! the alias and registers the where-clauses of the type alias. +use rustc_middle::traits::solve::{Certainty, Goal, GoalSource, QueryResult}; +use rustc_middle::ty; + +use crate::solve::EvalCtxt; + +impl<'tcx> EvalCtxt<'_, 'tcx> { + pub(super) fn normalize_weak_type( + &mut self, + goal: Goal<'tcx, ty::NormalizesTo<'tcx>>, + ) -> QueryResult<'tcx> { + let tcx = self.tcx(); + let weak_ty = goal.predicate.alias; + let expected = goal.predicate.term.ty().expect("no such thing as a const alias"); + + let actual = tcx.type_of(weak_ty.def_id).instantiate(tcx, weak_ty.args); + self.eq(goal.param_env, expected, actual)?; + + // Check where clauses + self.add_goals( + GoalSource::Misc, + tcx.predicates_of(weak_ty.def_id) + .instantiate(tcx, weak_ty.args) + .predicates + .into_iter() + .map(|pred| goal.with(tcx, pred)), + ); + + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } +} diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs new file mode 100644 index 00000000000..30ae385a8a0 --- /dev/null +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -0,0 +1,38 @@ +use crate::solve::GoalSource; + +use super::EvalCtxt; +use rustc_middle::traits::solve::{Certainty, Goal, QueryResult}; +use rustc_middle::ty::{self, ProjectionPredicate}; + +impl<'tcx> EvalCtxt<'_, 'tcx> { + #[instrument(level = "debug", skip(self), ret)] + pub(super) fn compute_projection_goal( + &mut self, + goal: Goal<'tcx, ProjectionPredicate<'tcx>>, + ) -> QueryResult<'tcx> { + let tcx = self.tcx(); + let projection_term = match goal.predicate.term.unpack() { + ty::TermKind::Ty(_) => goal.predicate.projection_ty.to_ty(tcx).into(), + ty::TermKind::Const(_) => ty::Const::new_unevaluated( + tcx, + ty::UnevaluatedConst::new( + goal.predicate.projection_ty.def_id, + goal.predicate.projection_ty.args, + ), + tcx.type_of(goal.predicate.projection_ty.def_id) + .instantiate(tcx, goal.predicate.projection_ty.args), + ) + .into(), + }; + let goal = goal.with( + tcx, + ty::PredicateKind::AliasRelate( + projection_term, + goal.predicate.term, + ty::AliasRelationDirection::Equate, + ), + ); + self.add_goal(GoalSource::Misc, goal); + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } +} diff --git a/compiler/rustc_trait_selection/src/solve/search_graph.rs b/compiler/rustc_trait_selection/src/solve/search_graph.rs new file mode 100644 index 00000000000..2a161c2d956 --- /dev/null +++ b/compiler/rustc_trait_selection/src/solve/search_graph.rs @@ -0,0 +1,360 @@ +use super::inspect; +use super::inspect::ProofTreeBuilder; +use super::SolverMode; +use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::FxHashSet; +use rustc_index::Idx; +use rustc_index::IndexVec; +use rustc_middle::dep_graph::dep_kinds; +use rustc_middle::traits::solve::CacheData; +use rustc_middle::traits::solve::{CanonicalInput, Certainty, EvaluationCache, QueryResult}; +use rustc_middle::ty; +use rustc_middle::ty::TyCtxt; +use rustc_session::Limit; +use std::collections::hash_map::Entry; + +rustc_index::newtype_index! { + #[orderable] + pub struct StackDepth {} +} + +#[derive(Debug)] +struct StackEntry<'tcx> { + input: CanonicalInput<'tcx>, + available_depth: Limit, + // The maximum depth reached by this stack entry, only up-to date + // for the top of the stack and lazily updated for the rest. + reached_depth: StackDepth, + // In case of a cycle, the depth of the root. + cycle_root_depth: StackDepth, + + encountered_overflow: bool, + has_been_used: bool, + /// Starts out as `None` and gets set when rerunning this + /// goal in case we encounter a cycle. + provisional_result: Option<QueryResult<'tcx>>, + + /// We put only the root goal of a coinductive cycle into the global cache. + /// + /// If we were to use that result when later trying to prove another cycle + /// participant, we can end up with unstable query results. + /// + /// See tests/ui/next-solver/coinduction/incompleteness-unstable-result.rs for + /// an example of where this is needed. + cycle_participants: FxHashSet<CanonicalInput<'tcx>>, +} + +pub(super) struct SearchGraph<'tcx> { + mode: SolverMode, + local_overflow_limit: usize, + /// The stack of goals currently being computed. + /// + /// An element is *deeper* in the stack if its index is *lower*. + stack: IndexVec<StackDepth, StackEntry<'tcx>>, + stack_entries: FxHashMap<CanonicalInput<'tcx>, StackDepth>, +} + +impl<'tcx> SearchGraph<'tcx> { + pub(super) fn new(tcx: TyCtxt<'tcx>, mode: SolverMode) -> SearchGraph<'tcx> { + Self { + mode, + local_overflow_limit: tcx.recursion_limit().0.checked_ilog2().unwrap_or(0) as usize, + stack: Default::default(), + stack_entries: Default::default(), + } + } + + pub(super) fn solver_mode(&self) -> SolverMode { + self.mode + } + + pub(super) fn local_overflow_limit(&self) -> usize { + self.local_overflow_limit + } + + /// Update the stack and reached depths on cache hits. + #[instrument(level = "debug", skip(self))] + fn on_cache_hit(&mut self, additional_depth: usize, encountered_overflow: bool) { + let reached_depth = self.stack.next_index().plus(additional_depth); + if let Some(last) = self.stack.raw.last_mut() { + last.reached_depth = last.reached_depth.max(reached_depth); + last.encountered_overflow |= encountered_overflow; + } + } + + /// Pops the highest goal from the stack, lazily updating the + /// the next goal in the stack. + /// + /// Directly popping from the stack instead of using this method + /// would cause us to not track overflow and recursion depth correctly. + fn pop_stack(&mut self) -> StackEntry<'tcx> { + let elem = self.stack.pop().unwrap(); + assert!(self.stack_entries.remove(&elem.input).is_some()); + if let Some(last) = self.stack.raw.last_mut() { + last.reached_depth = last.reached_depth.max(elem.reached_depth); + last.encountered_overflow |= elem.encountered_overflow; + } + elem + } + + /// The trait solver behavior is different for coherence + /// so we use a separate cache. Alternatively we could use + /// a single cache and share it between coherence and ordinary + /// trait solving. + pub(super) fn global_cache(&self, tcx: TyCtxt<'tcx>) -> &'tcx EvaluationCache<'tcx> { + match self.mode { + SolverMode::Normal => &tcx.new_solver_evaluation_cache, + SolverMode::Coherence => &tcx.new_solver_coherence_evaluation_cache, + } + } + + pub(super) fn is_empty(&self) -> bool { + self.stack.is_empty() + } + + pub(super) fn current_goal_is_normalizes_to(&self) -> bool { + self.stack.raw.last().map_or(false, |e| { + matches!( + e.input.value.goal.predicate.kind().skip_binder(), + ty::PredicateKind::NormalizesTo(..) + ) + }) + } + + /// Returns the remaining depth allowed for nested goals. + /// + /// This is generally simply one less than the current depth. + /// However, if we encountered overflow, we significantly reduce + /// the remaining depth of all nested goals to prevent hangs + /// in case there is exponential blowup. + fn allowed_depth_for_nested( + tcx: TyCtxt<'tcx>, + stack: &IndexVec<StackDepth, StackEntry<'tcx>>, + ) -> Option<Limit> { + if let Some(last) = stack.raw.last() { + if last.available_depth.0 == 0 { + return None; + } + + Some(if last.encountered_overflow { + Limit(last.available_depth.0 / 4) + } else { + Limit(last.available_depth.0 - 1) + }) + } else { + Some(tcx.recursion_limit()) + } + } + + /// Probably the most involved method of the whole solver. + /// + /// Given some goal which is proven via the `prove_goal` closure, this + /// handles caching, overflow, and coinductive cycles. + pub(super) fn with_new_goal( + &mut self, + tcx: TyCtxt<'tcx>, + input: CanonicalInput<'tcx>, + inspect: &mut ProofTreeBuilder<'tcx>, + mut prove_goal: impl FnMut(&mut Self, &mut ProofTreeBuilder<'tcx>) -> QueryResult<'tcx>, + ) -> QueryResult<'tcx> { + // Check for overflow. + let Some(available_depth) = Self::allowed_depth_for_nested(tcx, &self.stack) else { + if let Some(last) = self.stack.raw.last_mut() { + last.encountered_overflow = true; + } + + inspect.goal_evaluation_kind(inspect::WipCanonicalGoalEvaluationKind::Overflow); + return Self::response_no_constraints(tcx, input, Certainty::OVERFLOW); + }; + + // Try to fetch the goal from the global cache. + 'global: { + let Some(CacheData { result, proof_tree, reached_depth, encountered_overflow }) = + self.global_cache(tcx).get( + tcx, + input, + |cycle_participants| { + self.stack.iter().any(|entry| cycle_participants.contains(&entry.input)) + }, + available_depth, + ) + else { + break 'global; + }; + + // If we're building a proof tree and the current cache entry does not + // contain a proof tree, we do not use the entry but instead recompute + // the goal. We simply overwrite the existing entry once we're done, + // caching the proof tree. + if !inspect.is_noop() { + if let Some(revisions) = proof_tree { + inspect.goal_evaluation_kind( + inspect::WipCanonicalGoalEvaluationKind::Interned { revisions }, + ); + } else { + break 'global; + } + } + + self.on_cache_hit(reached_depth, encountered_overflow); + return result; + } + + // Check whether we're in a cycle. + match self.stack_entries.entry(input) { + // No entry, we push this goal on the stack and try to prove it. + Entry::Vacant(v) => { + let depth = self.stack.next_index(); + let entry = StackEntry { + input, + available_depth, + reached_depth: depth, + cycle_root_depth: depth, + encountered_overflow: false, + has_been_used: false, + provisional_result: None, + cycle_participants: Default::default(), + }; + assert_eq!(self.stack.push(entry), depth); + v.insert(depth); + } + // We have a nested goal which relies on a goal `root` deeper in the stack. + // + // We first store that we may have to reprove `root` in case the provisional + // response is not equal to the final response. We also update the depth of all + // goals which recursively depend on our current goal to depend on `root` + // instead. + // + // 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) => { + inspect.goal_evaluation_kind(inspect::WipCanonicalGoalEvaluationKind::CycleInStack); + + let stack_depth = *entry.get(); + debug!("encountered cycle with depth {stack_depth:?}"); + // We start by updating the root depth of all cycle participants, and + // add all cycle participants to the root. + let root_depth = self.stack[stack_depth].cycle_root_depth; + let (prev, participants) = self.stack.raw.split_at_mut(stack_depth.as_usize() + 1); + let root = &mut prev[root_depth.as_usize()]; + for entry in participants { + debug_assert!(entry.cycle_root_depth >= root_depth); + entry.cycle_root_depth = root_depth; + root.cycle_participants.insert(entry.input); + // FIXME(@lcnr): I believe that this line is needed as we could + // otherwise access a cache entry for the root of a cycle while + // computing the result for a cycle participant. This can result + // in unstable results due to incompleteness. + // + // However, a test for this would be an even more complex version of + // tests/ui/traits/next-solver/coinduction/incompleteness-unstable-result.rs. + // I did not bother to write such a test and we have no regression test + // for this. It would be good to have such a test :) + #[allow(rustc::potential_query_instability)] + root.cycle_participants.extend(entry.cycle_participants.drain()); + } + + // If we're in a cycle, we have to retry proving the cycle head + // until we reach a fixpoint. It is not enough to simply retry the + // `root` goal of this cycle. + // + // See tests/ui/traits/next-solver/cycles/fixpoint-rerun-all-cycle-heads.rs + // for an example. + self.stack[stack_depth].has_been_used = true; + return if let Some(result) = self.stack[stack_depth].provisional_result { + result + } else { + // If we don't have a provisional result yet we're in the first iteration, + // so we start with no constraints. + let is_coinductive = self.stack.raw[stack_depth.index()..] + .iter() + .all(|entry| entry.input.value.goal.predicate.is_coinductive(tcx)); + if is_coinductive { + Self::response_no_constraints(tcx, input, Certainty::Yes) + } else { + Self::response_no_constraints(tcx, input, Certainty::OVERFLOW) + } + }; + } + } + + // This is for global caching, so we properly track query dependencies. + // Everything that affects the `result` should be performed within this + // `with_anon_task` closure. + let ((final_entry, result), dep_node) = + tcx.dep_graph.with_anon_task(tcx, dep_kinds::TraitSelect, || { + // When we encounter a coinductive cycle, we have to fetch the + // result of that cycle while we are still computing it. Because + // of this we continuously recompute the cycle until the result + // of the previous iteration is equal to the final result, at which + // point we are done. + for _ in 0..self.local_overflow_limit() { + let result = prove_goal(self, inspect); + + // Check whether the current goal is the root of a cycle and whether + // we have to rerun because its provisional result differed from the + // final result. + let stack_entry = self.pop_stack(); + debug_assert_eq!(stack_entry.input, input); + if stack_entry.has_been_used + && stack_entry.provisional_result.map_or(true, |r| r != result) + { + // If so, update its provisional result and reevaluate it. + let depth = self.stack.push(StackEntry { + has_been_used: false, + provisional_result: Some(result), + ..stack_entry + }); + assert_eq!(self.stack_entries.insert(input, depth), None); + } else { + return (stack_entry, result); + } + } + + debug!("canonical cycle overflow"); + let current_entry = self.pop_stack(); + let result = Self::response_no_constraints(tcx, input, Certainty::OVERFLOW); + (current_entry, result) + }); + + let proof_tree = inspect.finalize_evaluation(tcx); + + // We're now done with this goal. In case this goal is involved in a larger cycle + // do not remove it from the provisional cache and update its provisional result. + // We only add the root of cycles to the global cache. + // + // It is not possible for any nested goal to depend on something deeper on the + // stack, as this would have also updated the depth of the current goal. + if final_entry.cycle_root_depth == self.stack.next_index() { + // When encountering a cycle, both inductive and coinductive, we only + // move the root into the global cache. We also store all other cycle + // participants involved. + // + // We disable the global cache entry of the root goal if a cycle + // participant is on the stack. This is necessary to prevent unstable + // results. See the comment of `StackEntry::cycle_participants` for + // more details. + let reached_depth = final_entry.reached_depth.as_usize() - self.stack.len(); + self.global_cache(tcx).insert( + tcx, + input, + proof_tree, + reached_depth, + final_entry.encountered_overflow, + final_entry.cycle_participants, + dep_node, + result, + ) + } + + result + } + + fn response_no_constraints( + tcx: TyCtxt<'tcx>, + goal: CanonicalInput<'tcx>, + certainty: Certainty, + ) -> QueryResult<'tcx> { + Ok(super::response_no_constraints_raw(tcx, goal.max_universe, goal.variables, certainty)) + } +} diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs new file mode 100644 index 00000000000..be079275684 --- /dev/null +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -0,0 +1,1021 @@ +//! Dealing with trait goals, i.e. `T: Trait<'a, U>`. + +use super::assembly::{self, structural_traits, Candidate}; +use super::{EvalCtxt, GoalSource, SolverMode}; +use rustc_hir::def_id::DefId; +use rustc_hir::{LangItem, Movability}; +use rustc_infer::traits::query::NoSolution; +use rustc_middle::traits::solve::inspect::ProbeKind; +use rustc_middle::traits::solve::{ + CandidateSource, CanonicalResponse, Certainty, Goal, QueryResult, +}; +use rustc_middle::traits::{BuiltinImplSource, Reveal}; +use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams, TreatProjections}; +use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt}; +use rustc_middle::ty::{TraitPredicate, TypeVisitableExt}; +use rustc_span::{ErrorGuaranteed, DUMMY_SP}; + +impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { + fn self_ty(self) -> Ty<'tcx> { + self.self_ty() + } + + fn trait_ref(self, _: TyCtxt<'tcx>) -> ty::TraitRef<'tcx> { + self.trait_ref + } + + fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self { + self.with_self_ty(tcx, self_ty) + } + + fn trait_def_id(self, _: TyCtxt<'tcx>) -> DefId { + self.def_id() + } + + fn consider_impl_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, TraitPredicate<'tcx>>, + impl_def_id: DefId, + ) -> Result<Candidate<'tcx>, NoSolution> { + let tcx = ecx.tcx(); + + let impl_trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); + let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::ForLookup }; + if !drcx.args_may_unify(goal.predicate.trait_ref.args, impl_trait_ref.skip_binder().args) { + return Err(NoSolution); + } + + let impl_polarity = tcx.impl_polarity(impl_def_id); + // An upper bound of the certainty of this goal, used to lower the certainty + // of reservation impl to ambiguous during coherence. + let maximal_certainty = match impl_polarity { + ty::ImplPolarity::Positive | ty::ImplPolarity::Negative => { + match impl_polarity == goal.predicate.polarity { + true => Certainty::Yes, + false => return Err(NoSolution), + } + } + ty::ImplPolarity::Reservation => match ecx.solver_mode() { + SolverMode::Normal => return Err(NoSolution), + SolverMode::Coherence => Certainty::AMBIGUOUS, + }, + }; + + ecx.probe_trait_candidate(CandidateSource::Impl(impl_def_id)).enter(|ecx| { + let impl_args = ecx.fresh_args_for_item(impl_def_id); + let impl_trait_ref = impl_trait_ref.instantiate(tcx, impl_args); + + ecx.eq(goal.param_env, goal.predicate.trait_ref, impl_trait_ref)?; + let where_clause_bounds = tcx + .predicates_of(impl_def_id) + .instantiate(tcx, impl_args) + .predicates + .into_iter() + .map(|pred| goal.with(tcx, pred)); + ecx.add_goals(GoalSource::ImplWhereBound, where_clause_bounds); + + ecx.evaluate_added_goals_and_make_canonical_response(maximal_certainty) + }) + } + + fn consider_error_guaranteed_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + _guar: ErrorGuaranteed, + ) -> QueryResult<'tcx> { + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } + + fn probe_and_match_goal_against_assumption( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + assumption: ty::Clause<'tcx>, + then: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> QueryResult<'tcx>, + ) -> QueryResult<'tcx> { + 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_misc_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) + } + } + + fn consider_auto_trait_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + if goal.predicate.polarity != ty::ImplPolarity::Positive { + return Err(NoSolution); + } + + if let Some(result) = ecx.disqualify_auto_trait_candidate_due_to_possible_impl(goal) { + 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. + // + // We always return `Err(NoSolution)` here in `SolverMode::Coherence` + // since we'll always register 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) + || matches!(ecx.solver_mode(), SolverMode::Coherence) + || 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, + ) + } + + fn consider_trait_alias_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + if goal.predicate.polarity != ty::ImplPolarity::Positive { + return Err(NoSolution); + } + + let tcx = ecx.tcx(); + + ecx.probe_misc_candidate("trait alias").enter(|ecx| { + let nested_obligations = tcx + .predicates_of(goal.predicate.def_id()) + .instantiate(tcx, goal.predicate.trait_ref.args); + // FIXME(-Znext-solver=coinductive): Should this be `GoalSource::ImplWhereBound`? + ecx.add_goals( + GoalSource::Misc, + nested_obligations.predicates.into_iter().map(|p| goal.with(tcx, p)), + ); + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }) + } + + fn consider_builtin_sized_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + if goal.predicate.polarity != ty::ImplPolarity::Positive { + return Err(NoSolution); + } + + ecx.probe_and_evaluate_goal_for_constituent_tys( + goal, + structural_traits::instantiate_constituent_tys_for_sized_trait, + ) + } + + fn consider_builtin_copy_clone_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + if goal.predicate.polarity != ty::ImplPolarity::Positive { + return Err(NoSolution); + } + + ecx.probe_and_evaluate_goal_for_constituent_tys( + goal, + structural_traits::instantiate_constituent_tys_for_copy_clone_trait, + ) + } + + fn consider_builtin_pointer_like_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + if goal.predicate.polarity != ty::ImplPolarity::Positive { + return Err(NoSolution); + } + + // The regions of a type don't affect the size of the type + let tcx = ecx.tcx(); + // We should erase regions from both the param-env and type, since both + // may have infer regions. Specifically, after canonicalizing and instantiating, + // early bound regions turn into region vars in both the new and old solver. + let key = tcx.erase_regions(goal.param_env.and(goal.predicate.self_ty())); + // But if there are inference variables, we have to wait until it's resolved. + if key.has_non_region_infer() { + return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS); + } + + if let Ok(layout) = tcx.layout_of(key) + && layout.layout.is_pointer_like(&tcx.data_layout) + { + // FIXME: We could make this faster by making a no-constraints response + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } else { + Err(NoSolution) + } + } + + fn consider_builtin_fn_ptr_trait_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + let self_ty = goal.predicate.self_ty(); + match goal.predicate.polarity { + ty::ImplPolarity::Positive => { + if self_ty.is_fn_ptr() { + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } else { + Err(NoSolution) + } + } + ty::ImplPolarity::Negative => { + // If a type is rigid and not a fn ptr, then we know for certain + // that it does *not* implement `FnPtr`. + if !self_ty.is_fn_ptr() && self_ty.is_known_rigid() { + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } else { + Err(NoSolution) + } + } + // FIXME: Goal polarity should be split from impl polarity + ty::ImplPolarity::Reservation => { + bug!("we never expect a `Reservation` polarity in a trait goal") + } + } + } + + fn consider_builtin_fn_trait_candidates( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + goal_kind: ty::ClosureKind, + ) -> QueryResult<'tcx> { + if goal.predicate.polarity != ty::ImplPolarity::Positive { + return Err(NoSolution); + } + + let tcx = ecx.tcx(); + let tupled_inputs_and_output = + match structural_traits::extract_tupled_inputs_and_output_from_callable( + tcx, + goal.predicate.self_ty(), + goal_kind, + )? { + Some(a) => a, + None => { + return ecx + .evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS); + } + }; + let output_is_sized_pred = tupled_inputs_and_output.map_bound(|(_, output)| { + ty::TraitRef::from_lang_item(tcx, LangItem::Sized, DUMMY_SP, [output]) + }); + + let pred = tupled_inputs_and_output + .map_bound(|(inputs, _)| { + ty::TraitRef::new(tcx, goal.predicate.def_id(), [goal.predicate.self_ty(), inputs]) + }) + .to_predicate(tcx); + // A built-in `Fn` impl only holds if the output is sized. + // (FIXME: technically we only need to check this if the type is a fn ptr...) + Self::consider_implied_clause(ecx, goal, pred, [goal.with(tcx, output_is_sized_pred)]) + } + + fn consider_builtin_tuple_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + if goal.predicate.polarity != ty::ImplPolarity::Positive { + return Err(NoSolution); + } + + if let ty::Tuple(..) = goal.predicate.self_ty().kind() { + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } else { + Err(NoSolution) + } + } + + fn consider_builtin_pointee_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + if goal.predicate.polarity != ty::ImplPolarity::Positive { + return Err(NoSolution); + } + + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } + + fn consider_builtin_future_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + if goal.predicate.polarity != ty::ImplPolarity::Positive { + return Err(NoSolution); + } + + let ty::Coroutine(def_id, _) = *goal.predicate.self_ty().kind() else { + return Err(NoSolution); + }; + + // Coroutines are not futures unless they come from `async` desugaring + let tcx = ecx.tcx(); + if !tcx.coroutine_is_async(def_id) { + return Err(NoSolution); + } + + // Async coroutine unconditionally implement `Future` + // Technically, we need to check that the future output type is Sized, + // but that's already proven by the coroutine being WF. + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } + + fn consider_builtin_iterator_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + if goal.predicate.polarity != ty::ImplPolarity::Positive { + return Err(NoSolution); + } + + let ty::Coroutine(def_id, _) = *goal.predicate.self_ty().kind() else { + return Err(NoSolution); + }; + + // Coroutines are not iterators unless they come from `gen` desugaring + let tcx = ecx.tcx(); + if !tcx.coroutine_is_gen(def_id) { + return Err(NoSolution); + } + + // Gen coroutines unconditionally implement `Iterator` + // Technically, we need to check that the iterator output type is Sized, + // but that's already proven by the coroutines being WF. + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } + + fn consider_builtin_async_iterator_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + if goal.predicate.polarity != ty::ImplPolarity::Positive { + return Err(NoSolution); + } + + let ty::Coroutine(def_id, _) = *goal.predicate.self_ty().kind() else { + return Err(NoSolution); + }; + + // Coroutines are not iterators unless they come from `gen` desugaring + let tcx = ecx.tcx(); + if !tcx.coroutine_is_async_gen(def_id) { + return Err(NoSolution); + } + + // Gen coroutines unconditionally implement `Iterator` + // Technically, we need to check that the iterator output type is Sized, + // but that's already proven by the coroutines being WF. + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } + + fn consider_builtin_coroutine_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + if goal.predicate.polarity != ty::ImplPolarity::Positive { + return Err(NoSolution); + } + + let self_ty = goal.predicate.self_ty(); + let ty::Coroutine(def_id, args) = *self_ty.kind() else { + return Err(NoSolution); + }; + + // `async`-desugared coroutines do not implement the coroutine trait + let tcx = ecx.tcx(); + if !tcx.is_general_coroutine(def_id) { + return Err(NoSolution); + } + + let coroutine = args.as_coroutine(); + Self::consider_implied_clause( + ecx, + goal, + ty::TraitRef::new(tcx, goal.predicate.def_id(), [self_ty, coroutine.resume_ty()]) + .to_predicate(tcx), + // Technically, we need to check that the coroutine types are Sized, + // but that's already proven by the coroutine being WF. + [], + ) + } + + fn consider_builtin_discriminant_kind_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + if goal.predicate.polarity != ty::ImplPolarity::Positive { + return Err(NoSolution); + } + + // `DiscriminantKind` is automatically implemented for every type. + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } + + fn consider_builtin_destruct_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + if goal.predicate.polarity != ty::ImplPolarity::Positive { + return Err(NoSolution); + } + + // FIXME(-Znext-solver): Implement this when we get const working in the new solver + + // `Destruct` is automatically implemented for every type in + // non-const environments. + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } + + fn consider_builtin_transmute_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + if goal.predicate.polarity != ty::ImplPolarity::Positive { + return Err(NoSolution); + } + + // `rustc_transmute` does not have support for type or const params + if goal.has_non_region_placeholders() { + return Err(NoSolution); + } + + // Erase regions because we compute layouts in `rustc_transmute`, + // which will ICE for region vars. + let args = ecx.tcx().erase_regions(goal.predicate.trait_ref.args); + + let Some(assume) = + rustc_transmute::Assume::from_const(ecx.tcx(), goal.param_env, args.const_at(3)) + else { + return Err(NoSolution); + }; + + let certainty = ecx.is_transmutable( + rustc_transmute::Types { dst: args.type_at(0), src: args.type_at(1) }, + args.type_at(2), + assume, + )?; + ecx.evaluate_added_goals_and_make_canonical_response(certainty) + } + + fn consider_unsize_to_dyn_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + ecx.probe(|_| ProbeKind::UnsizeAssembly).enter(|ecx| { + let a_ty = goal.predicate.self_ty(); + // We need to normalize the b_ty since it's destructured as a `dyn Trait`. + let Some(b_ty) = + ecx.try_normalize_ty(goal.param_env, goal.predicate.trait_ref.args.type_at(1)) + else { + return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::OVERFLOW); + }; + + let ty::Dynamic(b_data, b_region, ty::Dyn) = *b_ty.kind() else { + return Err(NoSolution); + }; + + let tcx = ecx.tcx(); + + // Can only unsize to an object-safe trait. + if b_data.principal_def_id().is_some_and(|def_id| !tcx.check_is_object_safe(def_id)) { + return Err(NoSolution); + } + + // Check that the type implements all of the predicates of the trait object. + // (i.e. the principal, all of the associated types match, and any auto traits) + ecx.add_goals( + GoalSource::ImplWhereBound, + b_data.iter().map(|pred| goal.with(tcx, pred.with_self_ty(tcx, a_ty))), + ); + + // The type must be `Sized` to be unsized. + if let Some(sized_def_id) = tcx.lang_items().sized_trait() { + ecx.add_goal( + GoalSource::ImplWhereBound, + goal.with(tcx, ty::TraitRef::new(tcx, sized_def_id, [a_ty])), + ); + } else { + return Err(NoSolution); + } + + // The type must outlive the lifetime of the `dyn` we're unsizing into. + ecx.add_goal(GoalSource::Misc, goal.with(tcx, ty::OutlivesPredicate(a_ty, b_region))); + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }) + } + + /// ```ignore (builtin impl example) + /// trait Trait { + /// fn foo(&self); + /// } + /// // results in the following builtin impl + /// impl<'a, T: Trait + 'a> Unsize<dyn Trait + 'a> for T {} + /// ``` + fn consider_structural_builtin_unsize_candidates( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)> { + if goal.predicate.polarity != ty::ImplPolarity::Positive { + return vec![]; + } + + let misc_candidate = |ecx: &mut EvalCtxt<'_, 'tcx>, certainty| { + ( + ecx.evaluate_added_goals_and_make_canonical_response(certainty).unwrap(), + BuiltinImplSource::Misc, + ) + }; + + let result_to_single = |result, source| match result { + Ok(resp) => vec![(resp, source)], + Err(NoSolution) => vec![], + }; + + ecx.probe(|_| ProbeKind::UnsizeAssembly).enter(|ecx| { + let a_ty = goal.predicate.self_ty(); + // We need to normalize the b_ty since it's matched structurally + // in the other functions below. + let b_ty = match ecx + .try_normalize_ty(goal.param_env, goal.predicate.trait_ref.args.type_at(1)) + { + Some(b_ty) => b_ty, + None => return vec![misc_candidate(ecx, Certainty::OVERFLOW)], + }; + + let goal = goal.with(ecx.tcx(), (a_ty, b_ty)); + match (a_ty.kind(), b_ty.kind()) { + (ty::Infer(ty::TyVar(..)), ..) => bug!("unexpected infer {a_ty:?} {b_ty:?}"), + (_, ty::Infer(ty::TyVar(..))) => vec![misc_candidate(ecx, Certainty::AMBIGUOUS)], + + // Trait upcasting, or `dyn Trait + Auto + 'a` -> `dyn Trait + 'b`. + ( + &ty::Dynamic(a_data, a_region, ty::Dyn), + &ty::Dynamic(b_data, b_region, ty::Dyn), + ) => ecx.consider_builtin_dyn_upcast_candidates( + goal, a_data, a_region, b_data, b_region, + ), + + // `T` -> `dyn Trait` unsizing is handled separately in `consider_unsize_to_dyn_candidate` + (_, &ty::Dynamic(..)) => vec![], + + // `[T; N]` -> `[T]` unsizing + (&ty::Array(a_elem_ty, ..), &ty::Slice(b_elem_ty)) => result_to_single( + ecx.consider_builtin_array_unsize(goal, a_elem_ty, b_elem_ty), + BuiltinImplSource::Misc, + ), + + // `Struct<T>` -> `Struct<U>` where `T: Unsize<U>` + (&ty::Adt(a_def, a_args), &ty::Adt(b_def, b_args)) + if a_def.is_struct() && a_def == b_def => + { + result_to_single( + ecx.consider_builtin_struct_unsize(goal, a_def, a_args, b_args), + BuiltinImplSource::Misc, + ) + } + + // `(A, B, T)` -> `(A, B, U)` where `T: Unsize<U>` + (&ty::Tuple(a_tys), &ty::Tuple(b_tys)) + if a_tys.len() == b_tys.len() && !a_tys.is_empty() => + { + result_to_single( + ecx.consider_builtin_tuple_unsize(goal, a_tys, b_tys), + BuiltinImplSource::TupleUnsizing, + ) + } + + _ => vec![], + } + }) + } +} + +impl<'tcx> EvalCtxt<'_, 'tcx> { + /// Trait upcasting allows for coercions between trait objects: + /// ```ignore (builtin impl example) + /// trait Super {} + /// trait Trait: Super {} + /// // results in builtin impls upcasting to a super trait + /// impl<'a, 'b: 'a> Unsize<dyn Super + 'a> for dyn Trait + 'b {} + /// // and impls removing auto trait bounds. + /// impl<'a, 'b: 'a> Unsize<dyn Trait + 'a> for dyn Trait + Send + 'b {} + /// ``` + fn consider_builtin_dyn_upcast_candidates( + &mut self, + goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>, + a_data: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>, + a_region: ty::Region<'tcx>, + b_data: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>, + b_region: ty::Region<'tcx>, + ) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)> { + let tcx = self.tcx(); + let Goal { predicate: (a_ty, _b_ty), .. } = goal; + + // All of a's auto traits need to be in b's auto traits. + let auto_traits_compatible = + b_data.auto_traits().all(|b| a_data.auto_traits().any(|a| a == b)); + if !auto_traits_compatible { + return vec![]; + } + + let mut responses = vec![]; + // If the principal def ids match (or are both none), then we're not doing + // trait upcasting. We're just removing auto traits (or shortening the lifetime). + if a_data.principal_def_id() == b_data.principal_def_id() { + if let Ok(resp) = self.consider_builtin_upcast_to_principal( + goal, + a_data, + a_region, + b_data, + b_region, + a_data.principal(), + ) { + responses.push((resp, BuiltinImplSource::Misc)); + } + } else if let Some(a_principal) = a_data.principal() { + self.walk_vtable( + a_principal.with_self_ty(tcx, a_ty), + |ecx, new_a_principal, _, vtable_vptr_slot| { + if let Ok(resp) = ecx.probe_misc_candidate("dyn upcast").enter(|ecx| { + ecx.consider_builtin_upcast_to_principal( + goal, + a_data, + a_region, + b_data, + b_region, + Some(new_a_principal.map_bound(|trait_ref| { + ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref) + })), + ) + }) { + responses + .push((resp, BuiltinImplSource::TraitUpcasting { vtable_vptr_slot })); + } + }, + ); + } + + responses + } + + fn consider_builtin_upcast_to_principal( + &mut self, + goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>, + a_data: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>, + a_region: ty::Region<'tcx>, + b_data: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>, + b_region: ty::Region<'tcx>, + upcast_principal: Option<ty::PolyExistentialTraitRef<'tcx>>, + ) -> QueryResult<'tcx> { + let param_env = goal.param_env; + + // More than one projection in a_ty's bounds may match the projection + // in b_ty's bound. Use this to first determine *which* apply without + // having any inference side-effects. We process obligations because + // unification may initially succeed due to deferred projection equality. + let projection_may_match = + |ecx: &mut Self, + source_projection: ty::PolyExistentialProjection<'tcx>, + target_projection: ty::PolyExistentialProjection<'tcx>| { + source_projection.item_def_id() == target_projection.item_def_id() + && ecx + .probe(|_| ProbeKind::UpcastProjectionCompatibility) + .enter(|ecx| -> Result<(), NoSolution> { + ecx.eq(param_env, source_projection, target_projection)?; + let _ = ecx.try_evaluate_added_goals()?; + Ok(()) + }) + .is_ok() + }; + + for bound in b_data { + match bound.skip_binder() { + // Check that a's supertrait (upcast_principal) is compatible + // with the target (b_ty). + ty::ExistentialPredicate::Trait(target_principal) => { + self.eq(param_env, upcast_principal.unwrap(), bound.rebind(target_principal))?; + } + // Check that b_ty's projection is satisfied by exactly one of + // a_ty's projections. First, we look through the list to see if + // any match. If not, error. Then, if *more* than one matches, we + // return ambiguity. Otherwise, if exactly one matches, equate + // it with b_ty's projection. + ty::ExistentialPredicate::Projection(target_projection) => { + let target_projection = bound.rebind(target_projection); + let mut matching_projections = + a_data.projection_bounds().filter(|source_projection| { + projection_may_match(self, *source_projection, target_projection) + }); + let Some(source_projection) = matching_projections.next() else { + return Err(NoSolution); + }; + if matching_projections.next().is_some() { + return self.evaluate_added_goals_and_make_canonical_response( + Certainty::AMBIGUOUS, + ); + } + self.eq(param_env, source_projection, target_projection)?; + } + // Check that b_ty's auto traits are present in a_ty's bounds. + ty::ExistentialPredicate::AutoTrait(def_id) => { + if !a_data.auto_traits().any(|source_def_id| source_def_id == def_id) { + return Err(NoSolution); + } + } + } + } + + // Also require that a_ty's lifetime outlives b_ty's lifetime. + self.add_goal( + GoalSource::ImplWhereBound, + Goal::new( + self.tcx(), + param_env, + ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region)), + ), + ); + + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } + + /// We have the following builtin impls for arrays: + /// ```ignore (builtin impl example) + /// impl<T: ?Sized, const N: usize> Unsize<[T]> for [T; N] {} + /// ``` + /// While the impl itself could theoretically not be builtin, + /// the actual unsizing behavior is builtin. Its also easier to + /// make all impls of `Unsize` builtin as we're able to use + /// `#[rustc_deny_explicit_impl]` in this case. + fn consider_builtin_array_unsize( + &mut self, + goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>, + a_elem_ty: Ty<'tcx>, + b_elem_ty: Ty<'tcx>, + ) -> QueryResult<'tcx> { + self.eq(goal.param_env, a_elem_ty, b_elem_ty)?; + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } + + /// We generate a builtin `Unsize` impls for structs with generic parameters only + /// mentioned by the last field. + /// ```ignore (builtin impl example) + /// struct Foo<T, U: ?Sized> { + /// sized_field: Vec<T>, + /// unsizable: Box<U>, + /// } + /// // results in the following builtin impl + /// impl<T: ?Sized, U: ?Sized, V: ?Sized> Unsize<Foo<T, V>> for Foo<T, U> + /// where + /// Box<U>: Unsize<Box<V>>, + /// {} + /// ``` + fn consider_builtin_struct_unsize( + &mut self, + goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>, + def: ty::AdtDef<'tcx>, + a_args: ty::GenericArgsRef<'tcx>, + b_args: ty::GenericArgsRef<'tcx>, + ) -> QueryResult<'tcx> { + let tcx = self.tcx(); + let Goal { predicate: (_a_ty, b_ty), .. } = goal; + + let unsizing_params = tcx.unsizing_params_for_adt(def.did()); + // We must be unsizing some type parameters. This also implies + // that the struct has a tail field. + if unsizing_params.is_empty() { + return Err(NoSolution); + } + + let tail_field = def.non_enum_variant().tail(); + let tail_field_ty = tcx.type_of(tail_field.did); + + let a_tail_ty = tail_field_ty.instantiate(tcx, a_args); + let b_tail_ty = tail_field_ty.instantiate(tcx, b_args); + + // Substitute just the unsizing params from B into A. The type after + // this substitution must be equal to B. This is so we don't unsize + // unrelated type parameters. + let new_a_args = tcx.mk_args_from_iter( + a_args + .iter() + .enumerate() + .map(|(i, a)| if unsizing_params.contains(i as u32) { b_args[i] } else { a }), + ); + let unsized_a_ty = Ty::new_adt(tcx, def, new_a_args); + + // Finally, we require that `TailA: Unsize<TailB>` for the tail field + // types. + self.eq(goal.param_env, unsized_a_ty, b_ty)?; + self.add_goal( + GoalSource::ImplWhereBound, + goal.with( + tcx, + ty::TraitRef::new( + tcx, + tcx.lang_items().unsize_trait().unwrap(), + [a_tail_ty, b_tail_ty], + ), + ), + ); + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } + + /// We generate the following builtin impl for tuples of all sizes. + /// + /// This impl is still unstable and we emit a feature error when it + /// when it is used by a coercion. + /// ```ignore (builtin impl example) + /// impl<T: ?Sized, U: ?Sized, V: ?Sized> Unsize<(T, V)> for (T, U) + /// where + /// U: Unsize<V>, + /// {} + /// ``` + fn consider_builtin_tuple_unsize( + &mut self, + goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>, + a_tys: &'tcx ty::List<Ty<'tcx>>, + b_tys: &'tcx ty::List<Ty<'tcx>>, + ) -> QueryResult<'tcx> { + let tcx = self.tcx(); + let Goal { predicate: (_a_ty, b_ty), .. } = goal; + + let (&a_last_ty, a_rest_tys) = a_tys.split_last().unwrap(); + let &b_last_ty = b_tys.last().unwrap(); + + // Substitute just the tail field of B., and require that they're equal. + let unsized_a_ty = + Ty::new_tup_from_iter(tcx, a_rest_tys.iter().copied().chain([b_last_ty])); + self.eq(goal.param_env, unsized_a_ty, b_ty)?; + + // Similar to ADTs, require that we can unsize the tail. + self.add_goal( + GoalSource::ImplWhereBound, + goal.with( + tcx, + ty::TraitRef::new( + tcx, + tcx.lang_items().unsize_trait().unwrap(), + [a_last_ty, b_last_ty], + ), + ), + ); + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } + + // Return `Some` if there is an impl (built-in or user provided) that may + // hold for the self type of the goal, which for coherence and soundness + // purposes must disqualify the built-in auto impl assembled by considering + // the type's constituent types. + fn disqualify_auto_trait_candidate_due_to_possible_impl( + &mut self, + goal: Goal<'tcx, TraitPredicate<'tcx>>, + ) -> Option<QueryResult<'tcx>> { + let self_ty = goal.predicate.self_ty(); + match *self_ty.kind() { + // Stall int and float vars until they are resolved to a concrete + // numerical type. That's because the check for impls below treats + // int vars as matching any impl. Even if we filtered such impls, + // we probably don't want to treat an `impl !AutoTrait for i32` as + // disqualifying the built-in auto impl for `i64: AutoTrait` either. + ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) => { + Some(self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)) + } + + // These types cannot be structurally decomposed into constituent + // types, and therefore have no built-in auto impl. + ty::Dynamic(..) + | ty::Param(..) + | ty::Foreign(..) + | ty::Alias(ty::Projection | ty::Weak | ty::Inherent, ..) + | ty::Placeholder(..) => Some(Err(NoSolution)), + + ty::Infer(_) | ty::Bound(_, _) => bug!("unexpected type `{self_ty}`"), + + // Coroutines have one special built-in candidate, `Unpin`, which + // takes precedence over the structural auto trait candidate being + // assembled. + ty::Coroutine(def_id, _) + if Some(goal.predicate.def_id()) == self.tcx().lang_items().unpin_trait() => + { + match self.tcx().coroutine_movability(def_id) { + Movability::Static => Some(Err(NoSolution)), + Movability::Movable => { + Some(self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)) + } + } + } + + // For rigid types, any possible implementation that could apply to + // the type (even if after unification and processing nested goals + // it does not hold) will disqualify the built-in auto impl. + // + // This differs from the current stable behavior and fixes #84857. + // Due to breakage found via crater, we currently instead lint + // patterns which can be used to exploit this unsoundness on stable, + // see #93367 for more details. + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Str + | ty::Array(_, _) + | ty::Slice(_) + | ty::RawPtr(_) + | ty::Ref(_, _, _) + | ty::FnDef(_, _) + | ty::FnPtr(_) + | ty::Closure(_, _) + | ty::Coroutine(_, _) + | ty::CoroutineWitness(..) + | ty::Never + | ty::Tuple(_) + | ty::Adt(_, _) + // FIXME: Handling opaques here is kinda sus. Especially because we + // simplify them to SimplifiedType::Placeholder. + | ty::Alias(ty::Opaque, _) => { + let mut disqualifying_impl = None; + self.tcx().for_each_relevant_impl_treating_projections( + goal.predicate.def_id(), + goal.predicate.self_ty(), + TreatProjections::NextSolverLookup, + |impl_def_id| { + disqualifying_impl = Some(impl_def_id); + }, + ); + if let Some(def_id) = disqualifying_impl { + debug!(?def_id, ?goal, "disqualified auto-trait implementation"); + // No need to actually consider the candidate here, + // since we do that in `consider_impl_candidate`. + return Some(Err(NoSolution)); + } else { + None + } + } + ty::Error(_) => None, + } + } + + /// Convenience function for traits that are structural, i.e. that only + /// have nested subgoals that only change the self type. Unlike other + /// evaluate-like helpers, this does a probe, so it doesn't need to be + /// wrapped in one. + fn probe_and_evaluate_goal_for_constituent_tys( + &mut self, + goal: Goal<'tcx, TraitPredicate<'tcx>>, + constituent_tys: impl Fn(&EvalCtxt<'_, 'tcx>, Ty<'tcx>) -> Result<Vec<Ty<'tcx>>, NoSolution>, + ) -> QueryResult<'tcx> { + self.probe_misc_candidate("constituent tys").enter(|ecx| { + ecx.add_goals( + GoalSource::ImplWhereBound, + constituent_tys(ecx, goal.predicate.self_ty())? + .into_iter() + .map(|ty| goal.with(ecx.tcx(), goal.predicate.with_self_ty(ecx.tcx(), ty))) + .collect::<Vec<_>>(), + ); + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + }) + } + + #[instrument(level = "debug", skip(self))] + pub(super) fn compute_trait_goal( + &mut self, + goal: Goal<'tcx, TraitPredicate<'tcx>>, + ) -> QueryResult<'tcx> { + let candidates = self.assemble_and_evaluate_candidates(goal); + self.merge_candidates(candidates) + } +} diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs new file mode 100644 index 00000000000..c4344758574 --- /dev/null +++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs @@ -0,0 +1,841 @@ +//! Support code for rustdoc and external tools. +//! You really don't want to be using this unless you need to. + +use super::*; + +use crate::errors::UnableToConstructConstantValue; +use crate::infer::region_constraints::{Constraint, RegionConstraintData}; +use crate::traits::project::ProjectAndUnifyResult; +use rustc_infer::infer::DefineOpaqueTypes; +use rustc_middle::mir::interpret::ErrorHandled; +use rustc_middle::ty::{ImplPolarity, Region, RegionVid}; + +use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; + +use std::collections::hash_map::Entry; +use std::collections::VecDeque; +use std::iter; + +// FIXME(twk): this is obviously not nice to duplicate like that +#[derive(Eq, PartialEq, Hash, Copy, Clone, Debug)] +pub enum RegionTarget<'tcx> { + Region(Region<'tcx>), + RegionVid(RegionVid), +} + +#[derive(Default, Debug, Clone)] +pub struct RegionDeps<'tcx> { + larger: FxIndexSet<RegionTarget<'tcx>>, + smaller: FxIndexSet<RegionTarget<'tcx>>, +} + +pub enum AutoTraitResult<A> { + ExplicitImpl, + PositiveImpl(A), + NegativeImpl, +} + +#[allow(dead_code)] +impl<A> AutoTraitResult<A> { + fn is_auto(&self) -> bool { + matches!(self, AutoTraitResult::PositiveImpl(_) | AutoTraitResult::NegativeImpl) + } +} + +pub struct AutoTraitInfo<'cx> { + pub full_user_env: ty::ParamEnv<'cx>, + pub region_data: RegionConstraintData<'cx>, + pub vid_to_region: FxHashMap<ty::RegionVid, ty::Region<'cx>>, +} + +pub struct AutoTraitFinder<'tcx> { + tcx: TyCtxt<'tcx>, +} + +impl<'tcx> AutoTraitFinder<'tcx> { + pub fn new(tcx: TyCtxt<'tcx>) -> Self { + AutoTraitFinder { tcx } + } + + /// Makes a best effort to determine whether and under which conditions an auto trait is + /// implemented for a type. For example, if you have + /// + /// ``` + /// struct Foo<T> { data: Box<T> } + /// ``` + /// + /// then this might return that `Foo<T>: Send` if `T: Send` (encoded in the AutoTraitResult + /// type). The analysis attempts to account for custom impls as well as other complex cases. + /// This result is intended for use by rustdoc and other such consumers. + /// + /// (Note that due to the coinductive nature of Send, the full and correct result is actually + /// quite simple to generate. That is, when a type has no custom impl, it is Send iff its field + /// types are all Send. So, in our example, we might have that `Foo<T>: Send` if `Box<T>: Send`. + /// But this is often not the best way to present to the user.) + /// + /// Warning: The API should be considered highly unstable, and it may be refactored or removed + /// in the future. + pub fn find_auto_trait_generics<A>( + &self, + ty: Ty<'tcx>, + orig_env: ty::ParamEnv<'tcx>, + trait_did: DefId, + mut auto_trait_callback: impl FnMut(AutoTraitInfo<'tcx>) -> A, + ) -> AutoTraitResult<A> { + let tcx = self.tcx; + + let trait_ref = ty::TraitRef::new(tcx, trait_did, [ty]); + + let infcx = tcx.infer_ctxt().build(); + let mut selcx = SelectionContext::new(&infcx); + for polarity in [true, false] { + let result = selcx.select(&Obligation::new( + tcx, + ObligationCause::dummy(), + orig_env, + ty::TraitPredicate { + trait_ref, + polarity: if polarity { + ImplPolarity::Positive + } else { + ImplPolarity::Negative + }, + }, + )); + if let Ok(Some(ImplSource::UserDefined(_))) = result { + debug!( + "find_auto_trait_generics({:?}): \ + manual impl found, bailing out", + trait_ref + ); + // If an explicit impl exists, it always takes priority over an auto impl + return AutoTraitResult::ExplicitImpl; + } + } + + let infcx = tcx.infer_ctxt().build(); + let mut fresh_preds = FxHashSet::default(); + + // Due to the way projections are handled by SelectionContext, we need to run + // evaluate_predicates twice: once on the original param env, and once on the result of + // the first evaluate_predicates call. + // + // The problem is this: most of rustc, including SelectionContext and traits::project, + // are designed to work with a concrete usage of a type (e.g., Vec<u8> + // fn<T>() { Vec<T> }. This information will generally never change - given + // the 'T' in fn<T>() { ... }, we'll never know anything else about 'T'. + // If we're unable to prove that 'T' implements a particular trait, we're done - + // there's nothing left to do but error out. + // + // However, synthesizing an auto trait impl works differently. Here, we start out with + // a set of initial conditions - the ParamEnv of the struct/enum/union we're dealing + // with - and progressively discover the conditions we need to fulfill for it to + // implement a certain auto trait. This ends up breaking two assumptions made by trait + // selection and projection: + // + // * We can always cache the result of a particular trait selection for the lifetime of + // an InfCtxt + // * Given a projection bound such as '<T as SomeTrait>::SomeItem = K', if 'T: + // SomeTrait' doesn't hold, then we don't need to care about the 'SomeItem = K' + // + // We fix the first assumption by manually clearing out all of the InferCtxt's caches + // in between calls to SelectionContext.select. This allows us to keep all of the + // intermediate types we create bound to the 'tcx lifetime, rather than needing to lift + // them between calls. + // + // We fix the second assumption by reprocessing the result of our first call to + // evaluate_predicates. Using the example of '<T as SomeTrait>::SomeItem = K', our first + // pass will pick up 'T: SomeTrait', but not 'SomeItem = K'. On our second pass, + // traits::project will see that 'T: SomeTrait' is in our ParamEnv, allowing + // SelectionContext to return it back to us. + + let Some((new_env, user_env)) = + self.evaluate_predicates(&infcx, trait_did, ty, orig_env, orig_env, &mut fresh_preds) + else { + return AutoTraitResult::NegativeImpl; + }; + + let (full_env, full_user_env) = self + .evaluate_predicates(&infcx, trait_did, ty, new_env, user_env, &mut fresh_preds) + .unwrap_or_else(|| { + panic!("Failed to fully process: {ty:?} {trait_did:?} {orig_env:?}") + }); + + debug!( + "find_auto_trait_generics({:?}): fulfilling \ + with {:?}", + trait_ref, full_env + ); + infcx.clear_caches(); + + // At this point, we already have all of the bounds we need. FulfillmentContext is used + // to store all of the necessary region/lifetime bounds in the InferContext, as well as + // an additional sanity check. + let ocx = ObligationCtxt::new(&infcx); + ocx.register_bound(ObligationCause::dummy(), full_env, ty, trait_did); + let errors = ocx.select_all_or_error(); + if !errors.is_empty() { + panic!("Unable to fulfill trait {trait_did:?} for '{ty:?}': {errors:?}"); + } + + let outlives_env = OutlivesEnvironment::new(full_env); + infcx.process_registered_region_obligations(&outlives_env); + + let region_data = + infcx.inner.borrow_mut().unwrap_region_constraints().region_constraint_data().clone(); + + let vid_to_region = self.map_vid_to_region(®ion_data); + + let info = AutoTraitInfo { full_user_env, region_data, vid_to_region }; + + AutoTraitResult::PositiveImpl(auto_trait_callback(info)) + } +} + +impl<'tcx> AutoTraitFinder<'tcx> { + /// The core logic responsible for computing the bounds for our synthesized impl. + /// + /// To calculate the bounds, we call `SelectionContext.select` in a loop. Like + /// `FulfillmentContext`, we recursively select the nested obligations of predicates we + /// encounter. However, whenever we encounter an `UnimplementedError` involving a type + /// parameter, we add it to our `ParamEnv`. Since our goal is to determine when a particular + /// type implements an auto trait, Unimplemented errors tell us what conditions need to be met. + /// + /// This method ends up working somewhat similarly to `FulfillmentContext`, but with a few key + /// differences. `FulfillmentContext` works under the assumption that it's dealing with concrete + /// user code. According, it considers all possible ways that a `Predicate` could be met, which + /// isn't always what we want for a synthesized impl. For example, given the predicate `T: + /// Iterator`, `FulfillmentContext` can end up reporting an Unimplemented error for `T: + /// IntoIterator` -- since there's an implementation of `Iterator` where `T: IntoIterator`, + /// `FulfillmentContext` will drive `SelectionContext` to consider that impl before giving up. + /// If we were to rely on `FulfillmentContext`s decision, we might end up synthesizing an impl + /// like this: + /// ```ignore (illustrative) + /// impl<T> Send for Foo<T> where T: IntoIterator + /// ``` + /// While it might be technically true that Foo implements Send where `T: IntoIterator`, + /// the bound is overly restrictive - it's really only necessary that `T: Iterator`. + /// + /// For this reason, `evaluate_predicates` handles predicates with type variables specially. + /// When we encounter an `Unimplemented` error for a bound such as `T: Iterator`, we immediately + /// add it to our `ParamEnv`, and add it to our stack for recursive evaluation. When we later + /// select it, we'll pick up any nested bounds, without ever inferring that `T: IntoIterator` + /// needs to hold. + /// + /// One additional consideration is supertrait bounds. Normally, a `ParamEnv` is only ever + /// constructed once for a given type. As part of the construction process, the `ParamEnv` will + /// have any supertrait bounds normalized -- e.g., if we have a type `struct Foo<T: Copy>`, the + /// `ParamEnv` will contain `T: Copy` and `T: Clone`, since `Copy: Clone`. When we construct our + /// own `ParamEnv`, we need to do this ourselves, through `traits::elaborate`, or + /// else `SelectionContext` will choke on the missing predicates. However, this should never + /// show up in the final synthesized generics: we don't want our generated docs page to contain + /// something like `T: Copy + Clone`, as that's redundant. Therefore, we keep track of a + /// separate `user_env`, which only holds the predicates that will actually be displayed to the + /// user. + fn evaluate_predicates( + &self, + infcx: &InferCtxt<'tcx>, + trait_did: DefId, + ty: Ty<'tcx>, + param_env: ty::ParamEnv<'tcx>, + user_env: ty::ParamEnv<'tcx>, + fresh_preds: &mut FxHashSet<ty::Predicate<'tcx>>, + ) -> Option<(ty::ParamEnv<'tcx>, ty::ParamEnv<'tcx>)> { + let tcx = infcx.tcx; + + // Don't try to process any nested obligations involving predicates + // 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.as_predicate())); + } + + let mut select = SelectionContext::new(infcx); + + let mut already_visited = FxHashSet::default(); + let mut predicates = VecDeque::new(); + predicates.push_back(ty::Binder::dummy(ty::TraitPredicate { + trait_ref: ty::TraitRef::new(infcx.tcx, trait_did, [ty]), + + // Auto traits are positive + polarity: ty::ImplPolarity::Positive, + })); + + 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(); + + while let Some(pred) = predicates.pop_front() { + infcx.clear_caches(); + + if !already_visited.insert(pred) { + continue; + } + + // Call `infcx.resolve_vars_if_possible` to see if we can + // get rid of any inference variables. + let obligation = infcx.resolve_vars_if_possible(Obligation::new( + tcx, + dummy_cause.clone(), + new_env, + pred, + )); + let result = select.poly_select(&obligation); + + match result { + Ok(Some(ref impl_source)) => { + // If we see an explicit negative impl (e.g., `impl !Send for MyStruct`), + // we immediately bail out, since it's impossible for us to continue. + + if let ImplSource::UserDefined(ImplSourceUserDefinedData { + impl_def_id, .. + }) = impl_source + { + // Blame 'tidy' for the weird bracket placement. + if infcx.tcx.impl_polarity(*impl_def_id) == ty::ImplPolarity::Negative { + debug!( + "evaluate_nested_obligations: found explicit negative impl\ + {:?}, bailing out", + impl_def_id + ); + return None; + } + } + + let obligations = impl_source.borrow_nested_obligations().iter().cloned(); + + if !self.evaluate_nested_obligations( + ty, + obligations, + &mut user_computed_preds, + fresh_preds, + &mut predicates, + &mut select, + ) { + return None; + } + } + Ok(None) => {} + Err(SelectionError::Unimplemented) => { + if self.is_param_no_infer(pred.skip_binder().trait_ref.args) { + already_visited.remove(&pred); + self.add_user_pred(&mut user_computed_preds, pred.to_predicate(self.tcx)); + predicates.push_back(pred); + } else { + debug!( + "evaluate_nested_obligations: `Unimplemented` found, bailing: \ + {:?} {:?} {:?}", + ty, + pred, + pred.skip_binder().trait_ref.args + ); + return None; + } + } + _ => panic!("Unexpected error for '{ty:?}': {result:?}"), + }; + + let normalized_preds = + elaborate(tcx, computed_preds.clone().chain(user_computed_preds.iter().cloned())); + new_env = ty::ParamEnv::new( + tcx.mk_clauses_from_iter(normalized_preds.filter_map(|p| p.as_clause())), + param_env.reveal(), + ); + } + + let final_user_env = ty::ParamEnv::new( + tcx.mk_clauses_from_iter(user_computed_preds.into_iter().filter_map(|p| p.as_clause())), + user_env.reveal(), + ); + debug!( + "evaluate_nested_obligations(ty={:?}, trait_did={:?}): succeeded with '{:?}' \ + '{:?}'", + ty, trait_did, new_env, final_user_env + ); + + Some((new_env, final_user_env)) + } + + /// This method is designed to work around the following issue: + /// When we compute auto trait bounds, we repeatedly call `SelectionContext.select`, + /// progressively building a `ParamEnv` based on the results we get. + /// However, our usage of `SelectionContext` differs from its normal use within the compiler, + /// in that we capture and re-reprocess predicates from `Unimplemented` errors. + /// + /// This can lead to a corner case when dealing with region parameters. + /// During our selection loop in `evaluate_predicates`, we might end up with + /// two trait predicates that differ only in their region parameters: + /// one containing a HRTB lifetime parameter, and one containing a 'normal' + /// lifetime parameter. For example: + /// ```ignore (illustrative) + /// T as MyTrait<'a> + /// T as MyTrait<'static> + /// ``` + /// If we put both of these predicates in our computed `ParamEnv`, we'll + /// confuse `SelectionContext`, since it will (correctly) view both as being applicable. + /// + /// To solve this, we pick the 'more strict' lifetime bound -- i.e., the HRTB + /// Our end goal is to generate a user-visible description of the conditions + /// under which a type implements an auto trait. A trait predicate involving + /// a HRTB means that the type needs to work with any choice of lifetime, + /// not just one specific lifetime (e.g., `'static`). + fn add_user_pred( + &self, + user_computed_preds: &mut FxIndexSet<ty::Predicate<'tcx>>, + new_pred: ty::Predicate<'tcx>, + ) { + let mut should_add_new = true; + user_computed_preds.retain(|&old_pred| { + if let ( + ty::PredicateKind::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() { + let new_args = new_trait.trait_ref.args; + let old_args = old_trait.trait_ref.args; + + if !new_args.types().eq(old_args.types()) { + // We can't compare lifetimes if the types are different, + // so skip checking `old_pred`. + return true; + } + + for (new_region, old_region) in + iter::zip(new_args.regions(), old_args.regions()) + { + match (*new_region, *old_region) { + // If both predicates have an `ReBound` (a HRTB) in the + // same spot, we do nothing. + (ty::ReBound(_, _), ty::ReBound(_, _)) => {} + + (ty::ReBound(_, _), _) | (_, ty::ReVar(_)) => { + // One of these is true: + // The new predicate has a HRTB in a spot where the old + // predicate does not (if they both had a HRTB, the previous + // match arm would have executed). A HRBT is a 'stricter' + // bound than anything else, so we want to keep the newer + // predicate (with the HRBT) in place of the old predicate. + // + // OR + // + // The old predicate has a region variable where the new + // predicate has some other kind of region. An region + // variable isn't something we can actually display to a user, + // so we choose their new predicate (which doesn't have a region + // variable). + // + // In both cases, we want to remove the old predicate, + // from `user_computed_preds`, and replace it with the new + // one. Having both the old and the new + // predicate in a `ParamEnv` would confuse `SelectionContext`. + // + // We're currently in the predicate passed to 'retain', + // so we return `false` to remove the old predicate from + // `user_computed_preds`. + return false; + } + (_, ty::ReBound(_, _)) | (ty::ReVar(_), _) => { + // This is the opposite situation as the previous arm. + // One of these is true: + // + // The old predicate has a HRTB lifetime in a place where the + // new predicate does not. + // + // OR + // + // The new predicate has a region variable where the old + // predicate has some other type of region. + // + // We want to leave the old + // predicate in `user_computed_preds`, and skip adding + // new_pred to `user_computed_params`. + should_add_new = false + } + _ => {} + } + } + } + } + true + }); + + if should_add_new { + user_computed_preds.insert(new_pred); + } + } + + /// This is very similar to `handle_lifetimes`. However, instead of matching `ty::Region`s + /// to each other, we match `ty::RegionVid`s to `ty::Region`s. + fn map_vid_to_region<'cx>( + &self, + regions: &RegionConstraintData<'cx>, + ) -> FxHashMap<ty::RegionVid, ty::Region<'cx>> { + let mut vid_map: FxHashMap<RegionTarget<'cx>, RegionDeps<'cx>> = FxHashMap::default(); + let mut finished_map = FxHashMap::default(); + + for (constraint, _) in ®ions.constraints { + match constraint { + &Constraint::VarSubVar(r1, r2) => { + { + let deps1 = vid_map.entry(RegionTarget::RegionVid(r1)).or_default(); + deps1.larger.insert(RegionTarget::RegionVid(r2)); + } + + let deps2 = vid_map.entry(RegionTarget::RegionVid(r2)).or_default(); + deps2.smaller.insert(RegionTarget::RegionVid(r1)); + } + &Constraint::RegSubVar(region, vid) => { + { + let deps1 = vid_map.entry(RegionTarget::Region(region)).or_default(); + deps1.larger.insert(RegionTarget::RegionVid(vid)); + } + + let deps2 = vid_map.entry(RegionTarget::RegionVid(vid)).or_default(); + deps2.smaller.insert(RegionTarget::Region(region)); + } + &Constraint::VarSubReg(vid, region) => { + finished_map.insert(vid, region); + } + &Constraint::RegSubReg(r1, r2) => { + { + let deps1 = vid_map.entry(RegionTarget::Region(r1)).or_default(); + deps1.larger.insert(RegionTarget::Region(r2)); + } + + let deps2 = vid_map.entry(RegionTarget::Region(r2)).or_default(); + deps2.smaller.insert(RegionTarget::Region(r1)); + } + } + } + + while !vid_map.is_empty() { + let target = *vid_map.keys().next().expect("Keys somehow empty"); + let deps = vid_map.remove(&target).expect("Entry somehow missing"); + + for smaller in deps.smaller.iter() { + for larger in deps.larger.iter() { + match (smaller, larger) { + (&RegionTarget::Region(_), &RegionTarget::Region(_)) => { + if let Entry::Occupied(v) = vid_map.entry(*smaller) { + let smaller_deps = v.into_mut(); + smaller_deps.larger.insert(*larger); + smaller_deps.larger.remove(&target); + } + + if let Entry::Occupied(v) = vid_map.entry(*larger) { + let larger_deps = v.into_mut(); + larger_deps.smaller.insert(*smaller); + larger_deps.smaller.remove(&target); + } + } + (&RegionTarget::RegionVid(v1), &RegionTarget::Region(r1)) => { + finished_map.insert(v1, r1); + } + (&RegionTarget::Region(_), &RegionTarget::RegionVid(_)) => { + // Do nothing; we don't care about regions that are smaller than vids. + } + (&RegionTarget::RegionVid(_), &RegionTarget::RegionVid(_)) => { + if let Entry::Occupied(v) = vid_map.entry(*smaller) { + let smaller_deps = v.into_mut(); + smaller_deps.larger.insert(*larger); + smaller_deps.larger.remove(&target); + } + + if let Entry::Occupied(v) = vid_map.entry(*larger) { + let larger_deps = v.into_mut(); + larger_deps.smaller.insert(*smaller); + larger_deps.smaller.remove(&target); + } + } + } + } + } + } + finished_map + } + + fn is_param_no_infer(&self, args: GenericArgsRef<'_>) -> bool { + self.is_of_param(args.type_at(0)) && !args.types().any(|t| t.has_infer_types()) + } + + pub fn is_of_param(&self, ty: Ty<'_>) -> bool { + match ty.kind() { + ty::Param(_) => true, + ty::Alias(ty::Projection, p) => self.is_of_param(p.self_ty()), + _ => false, + } + } + + fn is_self_referential_projection(&self, p: ty::PolyProjectionPredicate<'_>) -> bool { + if let Some(ty) = p.term().skip_binder().ty() { + matches!(ty.kind(), ty::Alias(ty::Projection, proj) if proj == &p.skip_binder().projection_ty) + } else { + false + } + } + + fn evaluate_nested_obligations( + &self, + ty: Ty<'_>, + nested: impl Iterator<Item = PredicateObligation<'tcx>>, + computed_preds: &mut FxIndexSet<ty::Predicate<'tcx>>, + fresh_preds: &mut FxHashSet<ty::Predicate<'tcx>>, + predicates: &mut VecDeque<ty::PolyTraitPredicate<'tcx>>, + selcx: &mut SelectionContext<'_, 'tcx>, + ) -> bool { + let dummy_cause = ObligationCause::dummy(); + + for obligation in nested { + let is_new_pred = + fresh_preds.insert(self.clean_pred(selcx.infcx, obligation.predicate)); + + // Resolve any inference variables that we can, to help selection succeed + let predicate = selcx.infcx.resolve_vars_if_possible(obligation.predicate); + + // We only add a predicate as a user-displayable bound if + // it involves a generic parameter, and doesn't contain + // any inference variables. + // + // Displaying a bound involving a concrete type (instead of a generic + // parameter) would be pointless, since it's always true + // (e.g. u8: Copy) + // Displaying an inference variable is impossible, since they're + // an internal compiler detail without a defined visual representation + // + // We check this by calling is_of_param on the relevant types + // from the various possible predicates + + let bound_predicate = predicate.kind(); + match bound_predicate.skip_binder() { + 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::ClauseKind::Projection(p)) => { + let p = bound_predicate.rebind(p); + debug!( + "evaluate_nested_obligations: examining projection predicate {:?}", + predicate + ); + + // As described above, we only want to display + // bounds which include a generic parameter but don't include + // an inference variable. + // Additionally, we check if we've seen this predicate before, + // to avoid rendering duplicate bounds to the user. + if self.is_param_no_infer(p.skip_binder().projection_ty.args) + && !p.term().skip_binder().has_infer_types() + && is_new_pred + { + debug!( + "evaluate_nested_obligations: adding projection predicate \ + to computed_preds: {:?}", + predicate + ); + + // Under unusual circumstances, we can end up with a self-referential + // projection predicate. For example: + // <T as MyType>::Value == <T as MyType>::Value + // Not only is displaying this to the user pointless, + // having it in the ParamEnv will cause an issue if we try to call + // poly_project_and_unify_type on the predicate, since this kind of + // predicate will normally never end up in a ParamEnv. + // + // For these reasons, we ignore these weird predicates, + // ensuring that we're able to properly synthesize an auto trait impl + if self.is_self_referential_projection(p) { + debug!( + "evaluate_nested_obligations: encountered a projection + predicate equating a type with itself! Skipping" + ); + } else { + self.add_user_pred(computed_preds, predicate); + } + } + + // There are three possible cases when we project a predicate: + // + // 1. We encounter an error. This means that it's impossible for + // our current type to implement the auto trait - there's bound + // that we could add to our ParamEnv that would 'fix' this kind + // of error, as it's not caused by an unimplemented type. + // + // 2. We successfully project the predicate (Ok(Some(_))), generating + // some subobligations. We then process these subobligations + // like any other generated sub-obligations. + // + // 3. We receive an 'ambiguous' result (Ok(None)) + // If we were actually trying to compile a crate, + // we would need to re-process this obligation later. + // However, all we care about is finding out what bounds + // are needed for our type to implement a particular auto trait. + // We've already added this obligation to our computed ParamEnv + // above (if it was necessary). Therefore, we don't need + // to do any further processing of the obligation. + // + // Note that we *must* try to project *all* projection predicates + // we encounter, even ones without inference variable. + // This ensures that we detect any projection errors, + // which indicate that our type can *never* implement the given + // auto trait. In that case, we will generate an explicit negative + // impl (e.g. 'impl !Send for MyType'). However, we don't + // try to process any of the generated subobligations - + // they contain no new information, since we already know + // that our type implements the projected-through trait, + // and can lead to weird region issues. + // + // Normally, we'll generate a negative impl as a result of encountering + // a type with an explicit negative impl of an auto trait + // (for example, raw pointers have !Send and !Sync impls) + // However, through some **interesting** manipulations of the type + // system, it's actually possible to write a type that never + // implements an auto trait due to a projection error, not a normal + // negative impl error. To properly handle this case, we need + // to ensure that we catch any potential projection errors, + // and turn them into an explicit negative impl for our type. + debug!("Projecting and unifying projection predicate {:?}", predicate); + + match project::poly_project_and_unify_type(selcx, &obligation.with(self.tcx, p)) + { + ProjectAndUnifyResult::MismatchedProjectionTypes(e) => { + debug!( + "evaluate_nested_obligations: Unable to unify predicate \ + '{:?}' '{:?}', bailing out", + ty, e + ); + return false; + } + ProjectAndUnifyResult::Recursive => { + debug!("evaluate_nested_obligations: recursive projection predicate"); + return false; + } + ProjectAndUnifyResult::Holds(v) => { + // We only care about sub-obligations + // when we started out trying to unify + // some inference variables. See the comment above + // for more information + if p.term().skip_binder().has_infer_types() { + if !self.evaluate_nested_obligations( + ty, + v.into_iter(), + computed_preds, + fresh_preds, + predicates, + selcx, + ) { + return false; + } + } + } + ProjectAndUnifyResult::FailedNormalization => { + // It's ok not to make progress when have no inference variables - + // in that case, we were only performing unification to check if an + // error occurred (which would indicate that it's impossible for our + // type to implement the auto trait). + // However, we should always make progress (either by generating + // subobligations or getting an error) when we started off with + // inference variables + if p.term().skip_binder().has_infer_types() { + panic!("Unexpected result when selecting {ty:?} {obligation:?}") + } + } + } + } + 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::ClauseKind::TypeOutlives(binder)) => { + let binder = bound_predicate.rebind(binder); + match ( + binder.no_bound_vars(), + binder.map_bound_ref(|pred| pred.0).no_bound_vars(), + ) { + (None, Some(t_a)) => { + selcx.infcx.register_region_obligation_with_cause( + t_a, + selcx.infcx.tcx.lifetimes.re_static, + &dummy_cause, + ); + } + (Some(ty::OutlivesPredicate(t_a, r_b)), _) => { + selcx.infcx.register_region_obligation_with_cause( + t_a, + r_b, + &dummy_cause, + ); + } + _ => {} + }; + } + ty::PredicateKind::ConstEquate(c1, c2) => { + let evaluate = |c: ty::Const<'tcx>| { + if let ty::ConstKind::Unevaluated(unevaluated) = c.kind() { + match selcx.infcx.const_eval_resolve( + obligation.param_env, + unevaluated, + Some(obligation.cause.span), + ) { + Ok(Some(valtree)) => Ok(ty::Const::new_value(selcx.tcx(),valtree, c.ty())), + Ok(None) => { + let tcx = self.tcx; + let reported = + tcx.dcx().emit_err(UnableToConstructConstantValue { + span: tcx.def_span(unevaluated.def), + unevaluated: unevaluated, + }); + Err(ErrorHandled::Reported(reported.into(), tcx.def_span(unevaluated.def))) + } + Err(err) => Err(err), + } + } else { + Ok(c) + } + }; + + match (evaluate(c1), evaluate(c2)) { + (Ok(c1), Ok(c2)) => { + match selcx.infcx.at(&obligation.cause, obligation.param_env).eq(DefineOpaqueTypes::No,c1, c2) + { + Ok(_) => (), + Err(_) => return false, + } + } + _ => return false, + } + } + + // There's not really much we can do with these predicates - + // 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::Clause(ty::ClauseKind::WellFormed(..)) + | ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(..)) + | ty::PredicateKind::NormalizesTo(..) + | ty::PredicateKind::AliasRelate(..) + | ty::PredicateKind::ObjectSafe(..) + | ty::PredicateKind::Subtype(..) + // FIXME(generic_const_exprs): you can absolutely add this as a where clauses + | ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..)) + | ty::PredicateKind::Coerce(..) => {} + ty::PredicateKind::Ambiguous => return false, + }; + } + true + } + + pub fn clean_pred( + &self, + infcx: &InferCtxt<'tcx>, + p: ty::Predicate<'tcx>, + ) -> ty::Predicate<'tcx> { + infcx.freshen(p) + } +} diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs new file mode 100644 index 00000000000..533fe32f70d --- /dev/null +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -0,0 +1,1148 @@ +//! See Rustc Dev Guide chapters on [trait-resolution] and [trait-specialization] for more info on +//! how this works. +//! +//! [trait-resolution]: https://rustc-dev-guide.rust-lang.org/traits/resolution.html +//! [trait-specialization]: https://rustc-dev-guide.rust-lang.org/traits/specialization.html + +use crate::infer::outlives::env::OutlivesEnvironment; +use crate::infer::InferOk; +use crate::solve::inspect::{InspectGoal, ProofTreeInferCtxtExt, ProofTreeVisitor}; +use crate::solve::{deeply_normalize_for_diagnostics, inspect}; +use crate::traits::engine::TraitEngineExt; +use crate::traits::query::evaluate_obligation::InferCtxtExt; +use crate::traits::select::{IntercrateAmbiguityCause, TreatInductiveCycleAs}; +use crate::traits::structural_normalize::StructurallyNormalizeExt; +use crate::traits::NormalizeExt; +use crate::traits::SkipLeakCheck; +use crate::traits::{ + Obligation, ObligationCause, ObligationCtxt, PredicateObligation, PredicateObligations, + SelectionContext, +}; +use rustc_data_structures::fx::FxIndexSet; +use rustc_errors::Diagnostic; +use rustc_hir::def::DefKind; +use rustc_hir::def_id::{DefId, LOCAL_CRATE}; +use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, TyCtxtInferExt}; +use rustc_infer::traits::{util, TraitEngine}; +use rustc_middle::traits::query::NoSolution; +use rustc_middle::traits::solve::{CandidateSource, Certainty, Goal}; +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, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor}; +use rustc_session::lint::builtin::COINDUCTIVE_OVERLAP_IN_COHERENCE; +use rustc_span::symbol::sym; +use rustc_span::DUMMY_SP; +use std::fmt::Debug; +use std::ops::ControlFlow; + +/// Whether we do the orphan check relative to this crate or +/// to some remote crate. +#[derive(Copy, Clone, Debug)] +enum InCrate { + Local, + Remote, +} + +#[derive(Debug, Copy, Clone)] +pub enum Conflict { + Upstream, + Downstream, +} + +pub struct OverlapResult<'tcx> { + pub impl_header: ty::ImplHeader<'tcx>, + pub intercrate_ambiguity_causes: FxIndexSet<IntercrateAmbiguityCause<'tcx>>, + + /// `true` if the overlap might've been permitted before the shift + /// to universes. + pub involves_placeholder: bool, +} + +pub fn add_placeholder_note(err: &mut Diagnostic) { + err.note( + "this behavior recently changed as a result of a bug fix; \ + see rust-lang/rust#56105 for details", + ); +} + +#[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`. +#[instrument(skip(tcx, skip_leak_check), level = "debug")] +pub fn overlapping_impls( + tcx: TyCtxt<'_>, + impl1_def_id: DefId, + impl2_def_id: DefId, + skip_leak_check: SkipLeakCheck, + overlap_mode: OverlapMode, +) -> Option<OverlapResult<'_>> { + // Before doing expensive operations like entering an inference context, do + // a quick check via fast_reject to tell if the impl headers could possibly + // unify. + let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::AsCandidateKey }; + let impl1_ref = tcx.impl_trait_ref(impl1_def_id); + let impl2_ref = tcx.impl_trait_ref(impl2_def_id); + let may_overlap = match (impl1_ref, impl2_ref) { + (Some(a), Some(b)) => drcx.args_may_unify(a.skip_binder().args, b.skip_binder().args), + (None, None) => { + let self_ty1 = tcx.type_of(impl1_def_id).skip_binder(); + let self_ty2 = tcx.type_of(impl2_def_id).skip_binder(); + drcx.types_may_unify(self_ty1, self_ty2) + } + _ => bug!("unexpected impls: {impl1_def_id:?} {impl2_def_id:?}"), + }; + + if !may_overlap { + // Some types involved are definitely different, so the impls couldn't possibly overlap. + debug!("overlapping_impls: fast_reject early-exit"); + 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 overlap = overlap( + tcx, + TrackAmbiguityCauses::Yes, + skip_leak_check, + impl1_def_id, + impl2_def_id, + overlap_mode, + ) + .unwrap(); + Some(overlap) +} + +fn fresh_impl_header<'tcx>(infcx: &InferCtxt<'tcx>, impl_def_id: DefId) -> ty::ImplHeader<'tcx> { + let tcx = infcx.tcx; + let impl_args = infcx.fresh_args_for_item(DUMMY_SP, impl_def_id); + + ty::ImplHeader { + impl_def_id, + impl_args, + self_ty: tcx.type_of(impl_def_id).instantiate(tcx, impl_args), + trait_ref: tcx.impl_trait_ref(impl_def_id).map(|i| i.instantiate(tcx, impl_args)), + predicates: tcx + .predicates_of(impl_def_id) + .instantiate(tcx, impl_args) + .iter() + .map(|(c, _)| c.as_predicate()) + .collect(), + } +} + +fn fresh_impl_header_normalized<'tcx>( + infcx: &InferCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + impl_def_id: DefId, +) -> ty::ImplHeader<'tcx> { + let header = fresh_impl_header(infcx, impl_def_id); + + let InferOk { value: mut header, obligations } = + infcx.at(&ObligationCause::dummy(), param_env).normalize(header); + + header.predicates.extend(obligations.into_iter().map(|o| o.predicate)); + header +} + +/// 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. +#[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>> { + if overlap_mode.use_negative_impl() { + 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 + // empty environment. + let param_env = ty::ParamEnv::empty(); + + let impl1_header = fresh_impl_header_normalized(selcx.infcx, param_env, impl1_def_id); + let impl2_header = fresh_impl_header_normalized(selcx.infcx, param_env, impl2_def_id); + + // Equate the headers to find their intersection (the general type, with infer vars, + // that may apply both impls). + let mut obligations = + equate_impl_headers(selcx.infcx, param_env, &impl1_header, &impl2_header)?; + debug!("overlap: unification check succeeded"); + + obligations.extend( + [&impl1_header.predicates, &impl2_header.predicates].into_iter().flatten().map( + |&predicate| Obligation::new(infcx.tcx, ObligationCause::dummy(), param_env, predicate), + ), + ); + + if overlap_mode.use_implicit_negative() { + for mode in [TreatInductiveCycleAs::Ambig, TreatInductiveCycleAs::Recur] { + if let Some(failing_obligation) = selcx.with_treat_inductive_cycle_as(mode, |selcx| { + impl_intersection_has_impossible_obligation(selcx, &obligations) + }) { + if matches!(mode, TreatInductiveCycleAs::Recur) { + let first_local_impl = impl1_header + .impl_def_id + .as_local() + .or(impl2_header.impl_def_id.as_local()) + .expect("expected one of the impls to be local"); + infcx.tcx.struct_span_lint_hir( + COINDUCTIVE_OVERLAP_IN_COHERENCE, + infcx.tcx.local_def_id_to_hir_id(first_local_impl), + infcx.tcx.def_span(first_local_impl), + format!( + "implementations {} will conflict in the future", + match impl1_header.trait_ref { + Some(trait_ref) => { + let trait_ref = infcx.resolve_vars_if_possible(trait_ref); + format!( + "of `{}` for `{}`", + trait_ref.print_trait_sugared(), + trait_ref.self_ty() + ) + } + None => format!( + "for `{}`", + infcx.resolve_vars_if_possible(impl1_header.self_ty) + ), + }, + ), + |lint| { + lint.note( + "impls that are not considered to overlap may be considered to \ + overlap in the future", + ) + .span_label( + infcx.tcx.def_span(impl1_header.impl_def_id), + "the first impl is here", + ) + .span_label( + infcx.tcx.def_span(impl2_header.impl_def_id), + "the second impl is here", + ); + lint.note(format!( + "`{}` may be considered to hold in future releases, \ + causing the impls to overlap", + infcx.resolve_vars_if_possible(failing_obligation.predicate) + )); + }, + ); + } + + return None; + } + } + } + + // 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 = if !overlap_mode.use_implicit_negative() { + Default::default() + } else if infcx.next_trait_solver() { + compute_intercrate_ambiguity_causes(&infcx, &obligations) + } else { + selcx.take_intercrate_ambiguity_causes() + }; + + debug!("overlap: intercrate_ambiguity_causes={:#?}", intercrate_ambiguity_causes); + let involves_placeholder = infcx + .inner + .borrow_mut() + .unwrap_region_constraints() + .data() + .constraints + .iter() + .any(|c| c.0.involves_placeholders()); + + let mut impl_header = infcx.resolve_vars_if_possible(impl1_header); + + // Deeply normalize the impl header for diagnostics, ignoring any errors if this fails. + if infcx.next_trait_solver() { + impl_header = deeply_normalize_for_diagnostics(&infcx, param_env, impl_header); + } + + Some(OverlapResult { impl_header, intercrate_ambiguity_causes, involves_placeholder }) +} + +#[instrument(level = "debug", skip(infcx), ret)] +fn equate_impl_headers<'tcx>( + infcx: &InferCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + impl1: &ty::ImplHeader<'tcx>, + impl2: &ty::ImplHeader<'tcx>, +) -> Option<PredicateObligations<'tcx>> { + let result = + match (impl1.trait_ref, impl2.trait_ref) { + (Some(impl1_ref), Some(impl2_ref)) => infcx + .at(&ObligationCause::dummy(), param_env) + .eq(DefineOpaqueTypes::Yes, impl1_ref, impl2_ref), + (None, None) => infcx.at(&ObligationCause::dummy(), param_env).eq( + DefineOpaqueTypes::Yes, + impl1.self_ty, + impl2.self_ty, + ), + _ => bug!("equate_impl_headers given mismatched impl kinds"), + }; + + result.map(|infer_ok| infer_ok.obligations).ok() +} + +/// 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<'a, 'cx, 'tcx>( + selcx: &mut SelectionContext<'cx, 'tcx>, + obligations: &'a [PredicateObligation<'tcx>], +) -> Option<&'a PredicateObligation<'tcx>> { + let infcx = selcx.infcx; + + obligations.iter().find(|obligation| { + let evaluation_result = if infcx.next_trait_solver() { + infcx.evaluate_obligation(obligation) + } else { + // We use `evaluate_root_obligation` to correctly track intercrate + // ambiguity clauses. We cannot use this in the new solver. + selcx.evaluate_root_obligation(obligation) + }; + + match evaluation_result { + Ok(result) => !result.may_apply(), + // If overflow occurs, we need to conservatively treat the goal as possibly holding, + // since there can be instantiations of this goal that don't overflow and result in + // success. This isn't much of a problem in the old solver, since we treat overflow + // fatally (this still can be encountered: <https://github.com/rust-lang/rust/issues/105231>), + // but in the new solver, this is very important for correctness, since overflow + // *must* be treated as ambiguity for completeness. + Err(_overflow) => false, + } + }) +} + +/// 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); + + // N.B. We need to unify impl headers *with* intercrate mode, even if proving negative predicates + // do not need intercrate mode enabled. + let ref infcx = tcx.infer_ctxt().intercrate(true).with_next_trait_solver(true).build(); + let root_universe = infcx.universe(); + assert_eq!(root_universe, ty::UniverseIndex::ROOT); + + let impl1_header = fresh_impl_header(infcx, impl1_def_id); + let param_env = + ty::EarlyBinder::bind(tcx.param_env(impl1_def_id)).instantiate(tcx, impl1_header.impl_args); + + let impl2_header = fresh_impl_header(infcx, impl2_def_id); + + // Equate the headers to find their intersection (the general type, with infer vars, + // that may apply both impls). + let Some(equate_obligations) = + equate_impl_headers(infcx, param_env, &impl1_header, &impl2_header) + else { + return false; + }; + + // FIXME(with_negative_coherence): the infcx has constraints from equating + // the impl headers. We should use these constraints as assumptions, not as + // requirements, when proving the negated where clauses below. + drop(equate_obligations); + drop(infcx.take_registered_region_obligations()); + drop(infcx.take_and_reset_region_constraints()); + + plug_infer_with_placeholders( + infcx, + root_universe, + (impl1_header.impl_args, impl2_header.impl_args), + ); + let param_env = infcx.resolve_vars_if_possible(param_env); + + util::elaborate(tcx, tcx.predicates_of(impl2_def_id).instantiate(tcx, impl2_header.impl_args)) + .any(|(clause, _)| try_prove_negated_where_clause(infcx, clause, param_env)) +} + +fn plug_infer_with_placeholders<'tcx>( + infcx: &InferCtxt<'tcx>, + universe: ty::UniverseIndex, + value: impl TypeVisitable<TyCtxt<'tcx>>, +) { + struct PlugInferWithPlaceholder<'a, 'tcx> { + infcx: &'a InferCtxt<'tcx>, + universe: ty::UniverseIndex, + var: ty::BoundVar, + } + + impl<'tcx> PlugInferWithPlaceholder<'_, 'tcx> { + fn next_var(&mut self) -> ty::BoundVar { + let var = self.var; + self.var = self.var + 1; + var + } + } + + impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for PlugInferWithPlaceholder<'_, 'tcx> { + fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { + let ty = self.infcx.shallow_resolve(ty); + if ty.is_ty_var() { + let Ok(InferOk { value: (), obligations }) = + self.infcx.at(&ObligationCause::dummy(), ty::ParamEnv::empty()).eq( + DefineOpaqueTypes::No, + ty, + Ty::new_placeholder( + self.infcx.tcx, + ty::Placeholder { + universe: self.universe, + bound: ty::BoundTy { + var: self.next_var(), + kind: ty::BoundTyKind::Anon, + }, + }, + ), + ) + else { + bug!("we always expect to be able to plug an infer var with placeholder") + }; + assert_eq!(obligations, &[]); + ControlFlow::Continue(()) + } else { + ty.super_visit_with(self) + } + } + + fn visit_const(&mut self, ct: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> { + let ct = self.infcx.shallow_resolve(ct); + if ct.is_ct_infer() { + let Ok(InferOk { value: (), obligations }) = + self.infcx.at(&ObligationCause::dummy(), ty::ParamEnv::empty()).eq( + DefineOpaqueTypes::No, + ct, + ty::Const::new_placeholder( + self.infcx.tcx, + ty::Placeholder { universe: self.universe, bound: self.next_var() }, + ct.ty(), + ), + ) + else { + bug!("we always expect to be able to plug an infer var with placeholder") + }; + assert_eq!(obligations, &[]); + ControlFlow::Continue(()) + } else { + ct.super_visit_with(self) + } + } + + fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> { + if let ty::ReVar(vid) = *r { + let r = self + .infcx + .inner + .borrow_mut() + .unwrap_region_constraints() + .opportunistic_resolve_var(self.infcx.tcx, vid); + if r.is_var() { + let Ok(InferOk { value: (), obligations }) = + self.infcx.at(&ObligationCause::dummy(), ty::ParamEnv::empty()).eq( + DefineOpaqueTypes::No, + r, + ty::Region::new_placeholder( + self.infcx.tcx, + ty::Placeholder { + universe: self.universe, + bound: ty::BoundRegion { + var: self.next_var(), + kind: ty::BoundRegionKind::BrAnon, + }, + }, + ), + ) + else { + bug!("we always expect to be able to plug an infer var with placeholder") + }; + assert_eq!(obligations, &[]); + } + } + ControlFlow::Continue(()) + } + } + + value.visit_with(&mut PlugInferWithPlaceholder { + infcx, + universe, + var: ty::BoundVar::from_u32(0), + }); +} + +fn try_prove_negated_where_clause<'tcx>( + root_infcx: &InferCtxt<'tcx>, + clause: ty::Clause<'tcx>, + param_env: ty::ParamEnv<'tcx>, +) -> bool { + let Some(negative_predicate) = clause.as_predicate().flip_polarity(root_infcx.tcx) else { + return false; + }; + + // N.B. We don't need to use intercrate mode here because we're trying to prove + // the *existence* of a negative goal, not the non-existence of a positive goal. + // Without this, we over-eagerly register coherence ambiguity candidates when + // impl candidates do exist. + let ref infcx = root_infcx.fork_with_intercrate(false); + let ocx = ObligationCtxt::new(infcx); + + ocx.register_obligation(Obligation::new( + infcx.tcx, + ObligationCause::dummy(), + param_env, + negative_predicate, + )); + if !ocx.select_all_or_error().is_empty() { + return false; + } + + // FIXME: We could use the assumed_wf_types from both impls, I think, + // if that wasn't implemented just for LocalDefId, and we'd need to do + // the normalization ourselves since this is totally fallible... + let outlives_env = OutlivesEnvironment::new(param_env); + + let errors = infcx.resolve_regions(&outlives_env); + if !errors.is_empty() { + return false; + } + + true +} + +/// Returns whether all impls which would apply to the `trait_ref` +/// e.g. `Ty: Trait<Arg>` are already known in the local crate. +/// +/// This both checks whether any downstream or sibling crates could +/// implement it and whether an upstream crate can add this impl +/// without breaking backwards compatibility. +#[instrument(level = "debug", skip(tcx, lazily_normalize_ty), ret)] +pub fn trait_ref_is_knowable<'tcx, E: Debug>( + tcx: TyCtxt<'tcx>, + trait_ref: ty::TraitRef<'tcx>, + mut lazily_normalize_ty: impl FnMut(Ty<'tcx>) -> Result<Ty<'tcx>, E>, +) -> Result<Result<(), Conflict>, E> { + if orphan_check_trait_ref(trait_ref, InCrate::Remote, &mut lazily_normalize_ty)?.is_ok() { + // A downstream or cousin crate is allowed to implement some + // substitution of this trait-ref. + return Ok(Err(Conflict::Downstream)); + } + + if trait_ref_is_local_or_fundamental(tcx, trait_ref) { + // This is a local or fundamental trait, so future-compatibility + // is no concern. We know that downstream/cousin crates are not + // allowed to implement a substitution of this trait ref, which + // means impls could only come from dependencies of this crate, + // which we already know about. + return Ok(Ok(())); + } + + // This is a remote non-fundamental trait, so if another crate + // can be the "final owner" of a substitution of this trait-ref, + // they are allowed to implement it future-compatibly. + // + // However, if we are a final owner, then nobody else can be, + // and if we are an intermediate owner, then we don't care + // about future-compatibility, which means that we're OK if + // we are an owner. + if orphan_check_trait_ref(trait_ref, InCrate::Local, &mut lazily_normalize_ty)?.is_ok() { + Ok(Ok(())) + } else { + Ok(Err(Conflict::Upstream)) + } +} + +pub fn trait_ref_is_local_or_fundamental<'tcx>( + tcx: TyCtxt<'tcx>, + trait_ref: ty::TraitRef<'tcx>, +) -> bool { + trait_ref.def_id.krate == LOCAL_CRATE || tcx.has_attr(trait_ref.def_id, sym::fundamental) +} + +#[derive(Debug)] +pub enum OrphanCheckErr<'tcx> { + NonLocalInputType(Vec<(Ty<'tcx>, bool /* Is this the first input type? */)>), + UncoveredTy(Ty<'tcx>, Option<Ty<'tcx>>), +} + +/// Checks the coherence orphan rules. `impl_def_id` should be the +/// `DefId` of a trait impl. To pass, either the trait must be local, or else +/// two conditions must be satisfied: +/// +/// 1. All type parameters in `Self` must be "covered" by some local type constructor. +/// 2. Some local type must appear in `Self`. +#[instrument(level = "debug", skip(tcx), ret)] +pub fn orphan_check(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Result<(), OrphanCheckErr<'_>> { + // We only except this routine to be invoked on implementations + // of a trait, not inherent implementations. + let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap().instantiate_identity(); + debug!(?trait_ref); + + // If the *trait* is local to the crate, ok. + if trait_ref.def_id.is_local() { + debug!("trait {:?} is local to current crate", trait_ref.def_id); + return Ok(()); + } + + orphan_check_trait_ref::<!>(trait_ref, InCrate::Local, |ty| Ok(ty)).unwrap() +} + +/// Checks whether a trait-ref is potentially implementable by a crate. +/// +/// The current rule is that a trait-ref orphan checks in a crate C: +/// +/// 1. Order the parameters in the trait-ref in subst order - Self first, +/// others linearly (e.g., `<U as Foo<V, W>>` is U < V < W). +/// 2. Of these type parameters, there is at least one type parameter +/// in which, walking the type as a tree, you can reach a type local +/// to C where all types in-between are fundamental types. Call the +/// first such parameter the "local key parameter". +/// - e.g., `Box<LocalType>` is OK, because you can visit LocalType +/// going through `Box`, which is fundamental. +/// - similarly, `FundamentalPair<Vec<()>, Box<LocalType>>` is OK for +/// the same reason. +/// - but (knowing that `Vec<T>` is non-fundamental, and assuming it's +/// not local), `Vec<LocalType>` is bad, because `Vec<->` is between +/// the local type and the type parameter. +/// 3. Before this local type, no generic type parameter of the impl must +/// be reachable through fundamental types. +/// - e.g. `impl<T> Trait<LocalType> for Vec<T>` is fine, as `Vec` is not fundamental. +/// - while `impl<T> Trait<LocalType> for Box<T>` results in an error, as `T` is +/// reachable through the fundamental type `Box`. +/// 4. Every type in the local key parameter not known in C, going +/// through the parameter's type tree, must appear only as a subtree of +/// a type local to C, with only fundamental types between the type +/// local to C and the local key parameter. +/// - e.g., `Vec<LocalType<T>>>` (or equivalently `Box<Vec<LocalType<T>>>`) +/// is bad, because the only local type with `T` as a subtree is +/// `LocalType<T>`, and `Vec<->` is between it and the type parameter. +/// - similarly, `FundamentalPair<LocalType<T>, T>` is bad, because +/// the second occurrence of `T` is not a subtree of *any* local type. +/// - however, `LocalType<Vec<T>>` is OK, because `T` is a subtree of +/// `LocalType<Vec<T>>`, which is local and has no types between it and +/// the type parameter. +/// +/// The orphan rules actually serve several different purposes: +/// +/// 1. They enable link-safety - i.e., 2 mutually-unknowing crates (where +/// every type local to one crate is unknown in the other) can't implement +/// the same trait-ref. This follows because it can be seen that no such +/// type can orphan-check in 2 such crates. +/// +/// To check that a local impl follows the orphan rules, we check it in +/// InCrate::Local mode, using type parameters for the "generic" types. +/// +/// 2. They ground negative reasoning for coherence. If a user wants to +/// write both a conditional blanket impl and a specific impl, we need to +/// make sure they do not overlap. For example, if we write +/// ```ignore (illustrative) +/// impl<T> IntoIterator for Vec<T> +/// impl<T: Iterator> IntoIterator for T +/// ``` +/// We need to be able to prove that `Vec<$0>: !Iterator` for every type $0. +/// We can observe that this holds in the current crate, but we need to make +/// sure this will also hold in all unknown crates (both "independent" crates, +/// which we need for link-safety, and also child crates, because we don't want +/// child crates to get error for impl conflicts in a *dependency*). +/// +/// For that, we only allow negative reasoning if, for every assignment to the +/// inference variables, every unknown crate would get an orphan error if they +/// try to implement this trait-ref. To check for this, we use InCrate::Remote +/// mode. That is sound because we already know all the impls from known crates. +/// +/// 3. For non-`#[fundamental]` traits, they guarantee that parent crates can +/// add "non-blanket" impls without breaking negative reasoning in dependent +/// crates. This is the "rebalancing coherence" (RFC 1023) restriction. +/// +/// For that, we only a allow crate to perform negative reasoning on +/// non-local-non-`#[fundamental]` only if there's a local key parameter as per (2). +/// +/// Because we never perform negative reasoning generically (coherence does +/// not involve type parameters), this can be interpreted as doing the full +/// orphan check (using InCrate::Local mode), substituting non-local known +/// types for all inference variables. +/// +/// This allows for crates to future-compatibly add impls as long as they +/// can't apply to types with a key parameter in a child crate - applying +/// the rules, this basically means that every type parameter in the impl +/// must appear behind a non-fundamental type (because this is not a +/// type-system requirement, crate owners might also go for "semantic +/// future-compatibility" involving things such as sealed traits, but +/// the above requirement is sufficient, and is necessary in "open world" +/// cases). +/// +/// Note that this function is never called for types that have both type +/// parameters and inference variables. +#[instrument(level = "trace", skip(lazily_normalize_ty), ret)] +fn orphan_check_trait_ref<'tcx, E: Debug>( + trait_ref: ty::TraitRef<'tcx>, + in_crate: InCrate, + lazily_normalize_ty: impl FnMut(Ty<'tcx>) -> Result<Ty<'tcx>, E>, +) -> Result<Result<(), OrphanCheckErr<'tcx>>, E> { + if trait_ref.has_infer() && trait_ref.has_param() { + bug!( + "can't orphan check a trait ref with both params and inference variables {:?}", + trait_ref + ); + } + + let mut checker = OrphanChecker::new(in_crate, lazily_normalize_ty); + Ok(match trait_ref.visit_with(&mut checker) { + ControlFlow::Continue(()) => Err(OrphanCheckErr::NonLocalInputType(checker.non_local_tys)), + ControlFlow::Break(OrphanCheckEarlyExit::NormalizationFailure(err)) => return Err(err), + ControlFlow::Break(OrphanCheckEarlyExit::ParamTy(ty)) => { + // Does there exist some local type after the `ParamTy`. + checker.search_first_local_ty = true; + if let Some(OrphanCheckEarlyExit::LocalTy(local_ty)) = + trait_ref.visit_with(&mut checker).break_value() + { + Err(OrphanCheckErr::UncoveredTy(ty, Some(local_ty))) + } else { + Err(OrphanCheckErr::UncoveredTy(ty, None)) + } + } + ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(_)) => Ok(()), + }) +} + +struct OrphanChecker<'tcx, F> { + in_crate: InCrate, + in_self_ty: bool, + lazily_normalize_ty: F, + /// Ignore orphan check failures and exclusively search for the first + /// local type. + search_first_local_ty: bool, + non_local_tys: Vec<(Ty<'tcx>, bool)>, +} + +impl<'tcx, F, E> OrphanChecker<'tcx, F> +where + F: FnOnce(Ty<'tcx>) -> Result<Ty<'tcx>, E>, +{ + fn new(in_crate: InCrate, lazily_normalize_ty: F) -> Self { + OrphanChecker { + in_crate, + in_self_ty: true, + lazily_normalize_ty, + search_first_local_ty: false, + non_local_tys: Vec::new(), + } + } + + fn found_non_local_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<OrphanCheckEarlyExit<'tcx, E>> { + self.non_local_tys.push((t, self.in_self_ty)); + ControlFlow::Continue(()) + } + + fn found_param_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<OrphanCheckEarlyExit<'tcx, E>> { + if self.search_first_local_ty { + ControlFlow::Continue(()) + } else { + ControlFlow::Break(OrphanCheckEarlyExit::ParamTy(t)) + } + } + + fn def_id_is_local(&mut self, def_id: DefId) -> bool { + match self.in_crate { + InCrate::Local => def_id.is_local(), + InCrate::Remote => false, + } + } +} + +enum OrphanCheckEarlyExit<'tcx, E> { + NormalizationFailure(E), + ParamTy(Ty<'tcx>), + LocalTy(Ty<'tcx>), +} + +impl<'tcx, F, E> TypeVisitor<TyCtxt<'tcx>> for OrphanChecker<'tcx, F> +where + F: FnMut(Ty<'tcx>) -> Result<Ty<'tcx>, E>, +{ + type BreakTy = OrphanCheckEarlyExit<'tcx, E>; + fn visit_region(&mut self, _r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> { + ControlFlow::Continue(()) + } + + fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { + // Need to lazily normalize here in with `-Znext-solver=coherence`. + let ty = match (self.lazily_normalize_ty)(ty) { + Ok(ty) => ty, + Err(err) => return ControlFlow::Break(OrphanCheckEarlyExit::NormalizationFailure(err)), + }; + + let result = match *ty.kind() { + ty::Bool + | ty::Char + | ty::Int(..) + | ty::Uint(..) + | ty::Float(..) + | ty::Str + | ty::FnDef(..) + | ty::FnPtr(_) + | ty::Array(..) + | ty::Slice(..) + | ty::RawPtr(..) + | ty::Never + | ty::Tuple(..) + | ty::Alias(ty::Projection | ty::Inherent | ty::Weak, ..) => { + self.found_non_local_ty(ty) + } + + ty::Param(..) => self.found_param_ty(ty), + + ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) => match self.in_crate { + InCrate::Local => self.found_non_local_ty(ty), + // The inference variable might be unified with a local + // type in that remote crate. + InCrate::Remote => ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)), + }, + + // For fundamental types, we just look inside of them. + ty::Ref(_, ty, _) => ty.visit_with(self), + ty::Adt(def, args) => { + if self.def_id_is_local(def.did()) { + ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)) + } else if def.is_fundamental() { + args.visit_with(self) + } else { + self.found_non_local_ty(ty) + } + } + ty::Foreign(def_id) => { + if self.def_id_is_local(def_id) { + ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)) + } else { + self.found_non_local_ty(ty) + } + } + ty::Dynamic(tt, ..) => { + let principal = tt.principal().map(|p| p.def_id()); + if principal.is_some_and(|p| self.def_id_is_local(p)) { + ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)) + } else { + self.found_non_local_ty(ty) + } + } + ty::Error(_) => ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)), + ty::Closure(did, ..) | ty::Coroutine(did, ..) => { + if self.def_id_is_local(did) { + ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)) + } else { + self.found_non_local_ty(ty) + } + } + // This should only be created when checking whether we have to check whether some + // auto trait impl applies. There will never be multiple impls, so we can just + // act as if it were a local type here. + ty::CoroutineWitness(..) => ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)), + ty::Alias(ty::Opaque, ..) => { + // This merits some explanation. + // Normally, opaque types are not involved when performing + // coherence checking, since it is illegal to directly + // implement a trait on an opaque type. However, we might + // end up looking at an opaque type during coherence checking + // if an opaque type gets used within another type (e.g. as + // the type of a field) when checking for auto trait or `Sized` + // impls. This requires us to decide whether or not an opaque + // type should be considered 'local' or not. + // + // We choose to treat all opaque types as non-local, even + // those that appear within the same crate. This seems + // somewhat surprising at first, but makes sense when + // you consider that opaque types are supposed to hide + // the underlying type *within the same crate*. When an + // opaque type is used from outside the module + // where it is declared, it should be impossible to observe + // anything about it other than the traits that it implements. + // + // The alternative would be to look at the underlying type + // to determine whether or not the opaque type itself should + // be considered local. However, this could make it a breaking change + // to switch the underlying ('defining') type from a local type + // to a remote type. This would violate the rule that opaque + // types should be completely opaque apart from the traits + // that they implement, so we don't use this behavior. + self.found_non_local_ty(ty) + } + }; + // A bit of a hack, the `OrphanChecker` is only used to visit a `TraitRef`, so + // the first type we visit is always the self type. + self.in_self_ty = false; + result + } + + /// All possible values for a constant parameter already exist + /// in the crate defining the trait, so they are always non-local[^1]. + /// + /// Because there's no way to have an impl where the first local + /// generic argument is a constant, we also don't have to fail + /// the orphan check when encountering a parameter or a generic constant. + /// + /// This means that we can completely ignore constants during the orphan check. + /// + /// See `tests/ui/coherence/const-generics-orphan-check-ok.rs` for examples. + /// + /// [^1]: This might not hold for function pointers or trait objects in the future. + /// As these should be quite rare as const arguments and especially rare as impl + /// parameters, allowing uncovered const parameters in impls seems more useful + /// than allowing `impl<T> Trait<local_fn_ptr, T> for i32` to compile. + fn visit_const(&mut self, _c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> { + ControlFlow::Continue(()) + } +} + +/// Compute the `intercrate_ambiguity_causes` for the new solver using +/// "proof trees". +/// +/// This is a bit scuffed but seems to be good enough, at least +/// when looking at UI tests. Given that it is only used to improve +/// diagnostics this is good enough. We can always improve it once there +/// are test cases where it is currently not enough. +fn compute_intercrate_ambiguity_causes<'tcx>( + infcx: &InferCtxt<'tcx>, + obligations: &[PredicateObligation<'tcx>], +) -> FxIndexSet<IntercrateAmbiguityCause<'tcx>> { + let mut causes: FxIndexSet<IntercrateAmbiguityCause<'tcx>> = Default::default(); + + for obligation in obligations { + search_ambiguity_causes(infcx, obligation.clone().into(), &mut causes); + } + + causes +} + +struct AmbiguityCausesVisitor<'a, 'tcx> { + causes: &'a mut FxIndexSet<IntercrateAmbiguityCause<'tcx>>, +} + +impl<'a, 'tcx> ProofTreeVisitor<'tcx> for AmbiguityCausesVisitor<'a, 'tcx> { + type BreakTy = !; + fn visit_goal(&mut self, goal: &InspectGoal<'_, 'tcx>) -> ControlFlow<Self::BreakTy> { + let infcx = goal.infcx(); + for cand in goal.candidates() { + cand.visit_nested(self)?; + } + // When searching for intercrate ambiguity causes, we only need to look + // at ambiguous goals, as for others the coherence unknowable candidate + // was irrelevant. + match goal.result() { + Ok(Certainty::Maybe(_)) => {} + Ok(Certainty::Yes) | Err(NoSolution) => return ControlFlow::Continue(()), + } + + let Goal { param_env, predicate } = goal.goal(); + + // For bound predicates we simply call `infcx.instantiate_binder_with_placeholders` + // and then prove the resulting predicate as a nested goal. + let trait_ref = match predicate.kind().no_bound_vars() { + Some(ty::PredicateKind::Clause(ty::ClauseKind::Trait(tr))) => tr.trait_ref, + Some(ty::PredicateKind::Clause(ty::ClauseKind::Projection(proj))) + if matches!( + infcx.tcx.def_kind(proj.projection_ty.def_id), + DefKind::AssocTy | DefKind::AssocConst + ) => + { + proj.projection_ty.trait_ref(infcx.tcx) + } + _ => return ControlFlow::Continue(()), + }; + + // Add ambiguity causes for reservation impls. + for cand in goal.candidates() { + if let inspect::ProbeKind::TraitCandidate { + source: CandidateSource::Impl(def_id), + result: Ok(_), + } = cand.kind() + { + if let ty::ImplPolarity::Reservation = infcx.tcx.impl_polarity(def_id) { + let message = infcx + .tcx + .get_attr(def_id, sym::rustc_reservation_impl) + .and_then(|a| a.value_str()); + if let Some(message) = message { + self.causes.insert(IntercrateAmbiguityCause::ReservationImpl { message }); + } + } + } + } + + // Add ambiguity causes for unknowable goals. + let mut ambiguity_cause = None; + for cand in goal.candidates() { + // FIXME: boiiii, using string comparisions here sure is scuffed. + if let inspect::ProbeKind::MiscCandidate { + name: "coherence unknowable", + result: Ok(_), + } = cand.kind() + { + let lazily_normalize_ty = |ty: Ty<'tcx>| { + let mut fulfill_cx = <dyn TraitEngine<'tcx>>::new(infcx); + if matches!(ty.kind(), ty::Alias(..)) { + // FIXME(-Znext-solver=coherence): we currently don't + // normalize opaque types here, resulting in diverging behavior + // for TAITs. + match infcx + .at(&ObligationCause::dummy(), param_env) + .structurally_normalize(ty, &mut *fulfill_cx) + { + Ok(ty) => Ok(ty), + Err(_errs) => Err(()), + } + } else { + Ok(ty) + } + }; + + infcx.probe(|_| { + match trait_ref_is_knowable(infcx.tcx, trait_ref, lazily_normalize_ty) { + Err(()) => {} + Ok(Ok(())) => warn!("expected an unknowable trait ref: {trait_ref:?}"), + Ok(Err(conflict)) => { + if !trait_ref.references_error() { + // Normalize the trait ref for diagnostics, ignoring any errors if this fails. + let trait_ref = + deeply_normalize_for_diagnostics(infcx, param_env, trait_ref); + + let self_ty = trait_ref.self_ty(); + let self_ty = self_ty.has_concrete_skeleton().then(|| self_ty); + ambiguity_cause = Some(match conflict { + Conflict::Upstream => { + IntercrateAmbiguityCause::UpstreamCrateUpdate { + trait_ref, + self_ty, + } + } + Conflict::Downstream => { + IntercrateAmbiguityCause::DownstreamCrate { + trait_ref, + self_ty, + } + } + }); + } + } + } + }) + } else { + match cand.result() { + // We only add an ambiguity cause if the goal would otherwise + // result in an error. + // + // FIXME: While this matches the behavior of the + // old solver, it is not the only way in which the unknowable + // candidates *weaken* coherence, they can also force otherwise + // sucessful normalization to be ambiguous. + Ok(Certainty::Maybe(_) | Certainty::Yes) => { + ambiguity_cause = None; + break; + } + Err(NoSolution) => continue, + } + } + } + + if let Some(ambiguity_cause) = ambiguity_cause { + self.causes.insert(ambiguity_cause); + } + + ControlFlow::Continue(()) + } +} + +fn search_ambiguity_causes<'tcx>( + infcx: &InferCtxt<'tcx>, + goal: Goal<'tcx, ty::Predicate<'tcx>>, + causes: &mut FxIndexSet<IntercrateAmbiguityCause<'tcx>>, +) { + infcx.visit_proof_tree(goal, &mut AmbiguityCausesVisitor { causes }); +} diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs new file mode 100644 index 00000000000..451e0823c25 --- /dev/null +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -0,0 +1,231 @@ +//! Checking that constant values used in types can be successfully evaluated. +//! +//! For concrete constants, this is fairly simple as we can just try and evaluate it. +//! +//! When dealing with polymorphic constants, for example `std::mem::size_of::<T>() - 1`, +//! this is not as easy. +//! +//! In this case we try to build an abstract representation of this constant using +//! `thir_abstract_const` which can then be checked for structural equality with other +//! generic constants mentioned in the `caller_bounds` of the current environment. +use rustc_hir::def::DefKind; +use rustc_infer::infer::InferCtxt; +use rustc_middle::mir::interpret::ErrorHandled; + +use rustc_middle::traits::ObligationCause; +use rustc_middle::ty::abstract_const::NotConstEvaluatable; +use rustc_middle::ty::{self, TyCtxt, TypeVisitable, TypeVisitableExt, TypeVisitor}; + +use rustc_span::Span; +use std::ops::ControlFlow; + +use crate::traits::ObligationCtxt; + +/// Check if a given constant can be evaluated. +#[instrument(skip(infcx), level = "debug")] +pub fn is_const_evaluatable<'tcx>( + infcx: &InferCtxt<'tcx>, + unexpanded_ct: ty::Const<'tcx>, + param_env: ty::ParamEnv<'tcx>, + span: Span, +) -> Result<(), NotConstEvaluatable> { + let tcx = infcx.tcx; + match tcx.expand_abstract_consts(unexpanded_ct).kind() { + ty::ConstKind::Unevaluated(_) | ty::ConstKind::Expr(_) => (), + ty::ConstKind::Param(_) + | ty::ConstKind::Bound(_, _) + | ty::ConstKind::Placeholder(_) + | ty::ConstKind::Value(_) + | ty::ConstKind::Error(_) => return Ok(()), + ty::ConstKind::Infer(_) => return Err(NotConstEvaluatable::MentionsInfer), + }; + + if tcx.features().generic_const_exprs { + let ct = tcx.expand_abstract_consts(unexpanded_ct); + + let is_anon_ct = if let ty::ConstKind::Unevaluated(uv) = ct.kind() { + tcx.def_kind(uv.def) == DefKind::AnonConst + } else { + false + }; + + if !is_anon_ct { + if satisfied_from_param_env(tcx, infcx, ct, param_env) { + return Ok(()); + } + if ct.has_non_region_infer() { + return Err(NotConstEvaluatable::MentionsInfer); + } else if ct.has_non_region_param() { + return Err(NotConstEvaluatable::MentionsParam); + } + } + + match unexpanded_ct.kind() { + ty::ConstKind::Expr(_) => { + // FIXME(generic_const_exprs): we have a `ConstKind::Expr` which is fully concrete, but + // currently it is not possible to evaluate `ConstKind::Expr` so we are unable to tell if it + // is evaluatable or not. For now we just ICE until this is implemented. + Err(NotConstEvaluatable::Error(tcx.dcx().span_delayed_bug( + span, + "evaluating `ConstKind::Expr` is not currently supported", + ))) + } + ty::ConstKind::Unevaluated(uv) => { + let concrete = infcx.const_eval_resolve(param_env, uv, Some(span)); + match concrete { + Err(ErrorHandled::TooGeneric(_)) => { + Err(NotConstEvaluatable::Error(infcx.dcx().span_delayed_bug( + span, + "Missing value for constant, but no error reported?", + ))) + } + Err(ErrorHandled::Reported(e, _)) => Err(NotConstEvaluatable::Error(e.into())), + Ok(_) => Ok(()), + } + } + _ => bug!("unexpected constkind in `is_const_evalautable: {unexpanded_ct:?}`"), + } + } else { + let uv = match unexpanded_ct.kind() { + ty::ConstKind::Unevaluated(uv) => uv, + ty::ConstKind::Expr(_) => { + bug!("`ConstKind::Expr` without `feature(generic_const_exprs)` enabled") + } + _ => bug!("unexpected constkind in `is_const_evalautable: {unexpanded_ct:?}`"), + }; + + // FIXME: We should only try to evaluate a given constant here if it is fully concrete + // as we don't want to allow things like `[u8; std::mem::size_of::<*mut T>()]`. + // + // We previously did not check this, so we only emit a future compat warning if + // const evaluation succeeds and the given constant is still polymorphic for now + // and hopefully soon change this to an error. + // + // See #74595 for more details about this. + let concrete = infcx.const_eval_resolve(param_env, uv, Some(span)); + match concrete { + // If we're evaluating a generic foreign constant, under a nightly compiler while + // the current crate does not enable `feature(generic_const_exprs)`, abort + // compilation with a useful error. + Err(_) + if tcx.sess.is_nightly_build() + && satisfied_from_param_env( + tcx, + infcx, + tcx.expand_abstract_consts(unexpanded_ct), + param_env, + ) => + { + tcx.dcx() + .struct_span_fatal( + // Slightly better span than just using `span` alone + if span == rustc_span::DUMMY_SP { tcx.def_span(uv.def) } else { span }, + "failed to evaluate generic const expression", + ) + .note("the crate this constant originates from uses `#![feature(generic_const_exprs)]`") + .span_suggestion_verbose( + rustc_span::DUMMY_SP, + "consider enabling this feature", + "#![feature(generic_const_exprs)]\n", + rustc_errors::Applicability::MaybeIncorrect, + ) + .emit() + } + + Err(ErrorHandled::TooGeneric(_)) => { + let err = if uv.has_non_region_infer() { + NotConstEvaluatable::MentionsInfer + } else if uv.has_non_region_param() { + NotConstEvaluatable::MentionsParam + } else { + let guar = infcx.dcx().span_delayed_bug( + span, + "Missing value for constant, but no error reported?", + ); + NotConstEvaluatable::Error(guar) + }; + + Err(err) + } + Err(ErrorHandled::Reported(e, _)) => Err(NotConstEvaluatable::Error(e.into())), + Ok(_) => Ok(()), + } + } +} + +#[instrument(skip(infcx, tcx), level = "debug")] +fn satisfied_from_param_env<'tcx>( + tcx: TyCtxt<'tcx>, + infcx: &InferCtxt<'tcx>, + ct: ty::Const<'tcx>, + param_env: ty::ParamEnv<'tcx>, +) -> bool { + // Try to unify with each subtree in the AbstractConst to allow for + // `N + 1` being const evaluatable even if theres only a `ConstEvaluatable` + // predicate for `(N + 1) * 2` + struct Visitor<'a, 'tcx> { + ct: ty::Const<'tcx>, + param_env: ty::ParamEnv<'tcx>, + + infcx: &'a InferCtxt<'tcx>, + single_match: Option<Result<ty::Const<'tcx>, ()>>, + } + + impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for Visitor<'a, 'tcx> { + type BreakTy = (); + fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> { + debug!("is_const_evaluatable: candidate={:?}", c); + if self.infcx.probe(|_| { + 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() + }) { + self.single_match = match self.single_match { + None => Some(Ok(c)), + Some(Ok(o)) if o == c => Some(Ok(c)), + Some(_) => Some(Err(())), + }; + } + + if let ty::ConstKind::Expr(e) = c.kind() { + e.visit_with(self) + } else { + // FIXME(generic_const_exprs): This doesn't recurse into `<T as Trait<U>>::ASSOC`'s args. + // This is currently unobservable as `<T as Trait<{ U + 1 }>>::ASSOC` creates an anon const + // with its own `ConstEvaluatable` bound in the param env which we will visit separately. + // + // If we start allowing directly writing `ConstKind::Expr` without an intermediate anon const + // this will be incorrect. It might be worth investigating making `predicates_of` elaborate + // all of the `ConstEvaluatable` bounds rather than having a visitor here. + ControlFlow::Continue(()) + } + } + } + + let mut single_match: Option<Result<ty::Const<'tcx>, ()>> = None; + + for pred in param_env.caller_bounds() { + match pred.kind().skip_binder() { + 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); + + single_match = v.single_match; + } + _ => {} // don't care + } + } + + if let Some(Ok(c)) = single_match { + 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()); + return true; + } + + debug!("is_const_evaluatable: no"); + false +} diff --git a/compiler/rustc_trait_selection/src/traits/engine.rs b/compiler/rustc_trait_selection/src/traits/engine.rs new file mode 100644 index 00000000000..013a50f9fa1 --- /dev/null +++ b/compiler/rustc_trait_selection/src/traits/engine.rs @@ -0,0 +1,272 @@ +use std::cell::RefCell; +use std::fmt::Debug; + +use super::FulfillmentContext; +use super::TraitEngine; +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; +use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_infer::infer::at::ToTrace; +use rustc_infer::infer::canonical::{ + Canonical, CanonicalQueryResponse, CanonicalVarValues, QueryResponse, +}; +use rustc_infer::infer::outlives::env::OutlivesEnvironment; +use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk}; +use rustc_infer::traits::{ + FulfillmentError, Obligation, ObligationCause, PredicateObligation, TraitEngineExt as _, +}; +use rustc_middle::arena::ArenaAllocatable; +use rustc_middle::traits::query::NoSolution; +use rustc_middle::ty::error::TypeError; +use rustc_middle::ty::ToPredicate; +use rustc_middle::ty::TypeFoldable; +use rustc_middle::ty::Variance; +use rustc_middle::ty::{self, Ty, TyCtxt}; + +pub trait TraitEngineExt<'tcx> { + fn new(infcx: &InferCtxt<'tcx>) -> Box<Self>; +} + +impl<'tcx> TraitEngineExt<'tcx> for dyn TraitEngine<'tcx> { + fn new(infcx: &InferCtxt<'tcx>) -> Box<Self> { + if infcx.next_trait_solver() { + Box::new(NextFulfillmentCtxt::new(infcx)) + } else { + let new_solver_globally = + infcx.tcx.sess.opts.unstable_opts.next_solver.map_or(false, |c| c.globally); + assert!( + !new_solver_globally, + "using old solver even though new solver is enabled globally" + ); + Box::new(FulfillmentContext::new(infcx)) + } + } +} + +/// Used if you want to have pleasant experience when dealing +/// with obligations outside of hir or mir typeck. +pub struct ObligationCtxt<'a, 'tcx> { + pub infcx: &'a InferCtxt<'tcx>, + engine: RefCell<Box<dyn TraitEngine<'tcx>>>, +} + +impl<'a, 'tcx> ObligationCtxt<'a, 'tcx> { + pub fn new(infcx: &'a InferCtxt<'tcx>) -> Self { + Self { infcx, engine: RefCell::new(<dyn TraitEngine<'_>>::new(infcx)) } + } + + pub fn register_obligation(&self, obligation: PredicateObligation<'tcx>) { + self.engine.borrow_mut().register_predicate_obligation(self.infcx, obligation); + } + + pub fn register_obligations( + &self, + obligations: impl IntoIterator<Item = PredicateObligation<'tcx>>, + ) { + // Can't use `register_predicate_obligations` because the iterator + // may also use this `ObligationCtxt`. + for obligation in obligations { + self.engine.borrow_mut().register_predicate_obligation(self.infcx, obligation) + } + } + + pub fn register_infer_ok_obligations<T>(&self, infer_ok: InferOk<'tcx, T>) -> T { + let InferOk { value, obligations } = infer_ok; + self.engine.borrow_mut().register_predicate_obligations(self.infcx, obligations); + value + } + + /// Requires that `ty` must implement the trait with `def_id` in + /// the given environment. This trait must not have any type + /// parameters (except for `Self`). + pub fn register_bound( + &self, + cause: ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ty: Ty<'tcx>, + def_id: DefId, + ) { + let tcx = self.infcx.tcx; + let trait_ref = ty::TraitRef::new(tcx, def_id, [ty]); + self.register_obligation(Obligation { + cause, + recursion_depth: 0, + param_env, + predicate: ty::Binder::dummy(trait_ref).to_predicate(tcx), + }); + } + + pub fn normalize<T: TypeFoldable<TyCtxt<'tcx>>>( + &self, + cause: &ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + value: T, + ) -> T { + let infer_ok = self.infcx.at(cause, param_env).normalize(value); + self.register_infer_ok_obligations(infer_ok) + } + + /// Makes `expected <: actual`. + pub fn eq_exp<T>( + &self, + cause: &ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + a_is_expected: bool, + a: T, + b: T, + ) -> Result<(), TypeError<'tcx>> + where + T: ToTrace<'tcx>, + { + self.infcx + .at(cause, param_env) + .eq_exp(DefineOpaqueTypes::Yes, a_is_expected, a, b) + .map(|infer_ok| self.register_infer_ok_obligations(infer_ok)) + } + + pub fn eq<T: ToTrace<'tcx>>( + &self, + cause: &ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + expected: T, + actual: T, + ) -> Result<(), TypeError<'tcx>> { + self.infcx + .at(cause, param_env) + .eq(DefineOpaqueTypes::Yes, expected, actual) + .map(|infer_ok| self.register_infer_ok_obligations(infer_ok)) + } + + /// Checks whether `expected` is a subtype of `actual`: `expected <: actual`. + pub fn sub<T: ToTrace<'tcx>>( + &self, + cause: &ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + expected: T, + actual: T, + ) -> Result<(), TypeError<'tcx>> { + self.infcx + .at(cause, param_env) + .sub(DefineOpaqueTypes::Yes, expected, actual) + .map(|infer_ok| self.register_infer_ok_obligations(infer_ok)) + } + + pub fn relate<T: ToTrace<'tcx>>( + &self, + cause: &ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + variance: Variance, + expected: T, + actual: T, + ) -> Result<(), TypeError<'tcx>> { + self.infcx + .at(cause, param_env) + .relate(DefineOpaqueTypes::Yes, expected, variance, actual) + .map(|infer_ok| self.register_infer_ok_obligations(infer_ok)) + } + + /// Checks whether `expected` is a supertype of `actual`: `expected :> actual`. + pub fn sup<T: ToTrace<'tcx>>( + &self, + cause: &ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + expected: T, + actual: T, + ) -> Result<(), TypeError<'tcx>> { + self.infcx + .at(cause, param_env) + .sup(DefineOpaqueTypes::Yes, expected, actual) + .map(|infer_ok| self.register_infer_ok_obligations(infer_ok)) + } + + #[must_use] + pub fn select_where_possible(&self) -> Vec<FulfillmentError<'tcx>> { + self.engine.borrow_mut().select_where_possible(self.infcx) + } + + #[must_use] + pub fn select_all_or_error(&self) -> Vec<FulfillmentError<'tcx>> { + self.engine.borrow_mut().select_all_or_error(self.infcx) + } + + /// Resolves regions and reports errors. + /// + /// Takes ownership of the context as doing trait solving afterwards + /// will result in region constraints getting ignored. + pub fn resolve_regions_and_report_errors( + self, + generic_param_scope: LocalDefId, + outlives_env: &OutlivesEnvironment<'tcx>, + ) -> Result<(), ErrorGuaranteed> { + let errors = self.infcx.resolve_regions(outlives_env); + if errors.is_empty() { + Ok(()) + } else { + Err(self.infcx.err_ctxt().report_region_errors(generic_param_scope, &errors)) + } + } + + 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>, + def_id: LocalDefId, + ) -> Result<FxIndexSet<Ty<'tcx>>, Vec<FulfillmentError<'tcx>>> { + let tcx = self.infcx.tcx; + let mut implied_bounds = FxIndexSet::default(); + 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. + // + // Not adding the unnormalized types here mostly fixes that, except + // that there are projections which are still ambiguous in the item definition + // but do normalize successfully when using the item, see #98543. + // + // Anyways, I will hopefully soon change implied bounds to make all of this + // sound and then uncomment this line again. + + // implied_bounds.insert(ty); + let 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), + }; + } + + if errors.is_empty() { Ok(implied_bounds) } else { Err(errors) } + } + + pub fn make_canonicalized_query_response<T>( + &self, + inference_vars: CanonicalVarValues<'tcx>, + answer: T, + ) -> Result<CanonicalQueryResponse<'tcx, T>, NoSolution> + where + T: Debug + TypeFoldable<TyCtxt<'tcx>>, + Canonical<'tcx, QueryResponse<'tcx, T>>: ArenaAllocatable<'tcx>, + { + self.infcx.make_canonicalized_query_response( + inference_vars, + answer, + &mut **self.engine.borrow_mut(), + ) + } +} diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs new file mode 100644 index 00000000000..b246e476bed --- /dev/null +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs @@ -0,0 +1,101 @@ +use rustc_hir::def_id::DefId; +use rustc_infer::infer::{BoundRegionConversionTime, InferCtxt}; +use rustc_infer::traits::util::elaborate; +use rustc_infer::traits::{Obligation, ObligationCause, PolyTraitObligation}; +use rustc_middle::ty; +use rustc_span::{Span, DUMMY_SP}; + +use crate::traits::ObligationCtxt; + +pub enum Ambiguity { + DefId(DefId), + ParamEnv(Span), +} + +pub fn recompute_applicable_impls<'tcx>( + infcx: &InferCtxt<'tcx>, + obligation: &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(infcx); + let placeholder_obligation = + infcx.instantiate_binder_with_placeholders(obligation.predicate); + let obligation_trait_ref = + ocx.normalize(&ObligationCause::dummy(), param_env, placeholder_obligation.trait_ref); + + let impl_args = infcx.fresh_args_for_item(DUMMY_SP, impl_def_id); + let impl_trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap().instantiate(tcx, impl_args); + let impl_trait_ref = ocx.normalize(&ObligationCause::dummy(), param_env, impl_trait_ref); + + if let Err(_) = + ocx.eq(&ObligationCause::dummy(), param_env, obligation_trait_ref, impl_trait_ref) + { + return false; + } + + let impl_predicates = tcx.predicates_of(impl_def_id).instantiate(tcx, impl_args); + ocx.register_obligations(impl_predicates.predicates.iter().map(|&predicate| { + Obligation::new(tcx, ObligationCause::dummy(), param_env, predicate) + })); + + ocx.select_where_possible().is_empty() + }; + + let param_env_candidate_may_apply = |poly_trait_predicate: ty::PolyTraitPredicate<'tcx>| { + let ocx = ObligationCtxt::new(infcx); + let placeholder_obligation = + infcx.instantiate_binder_with_placeholders(obligation.predicate); + let obligation_trait_ref = + ocx.normalize(&ObligationCause::dummy(), param_env, placeholder_obligation.trait_ref); + + let param_env_predicate = infcx.instantiate_binder_with_fresh_vars( + DUMMY_SP, + BoundRegionConversionTime::HigherRankedType, + poly_trait_predicate, + ); + let param_env_trait_ref = + ocx.normalize(&ObligationCause::dummy(), param_env, param_env_predicate.trait_ref); + + if let Err(_) = + ocx.eq(&ObligationCause::dummy(), param_env, obligation_trait_ref, param_env_trait_ref) + { + return false; + } + + ocx.select_where_possible().is_empty() + }; + + let mut ambiguities = Vec::new(); + + tcx.for_each_relevant_impl( + obligation.predicate.def_id(), + obligation.predicate.skip_binder().trait_ref.self_ty(), + |impl_def_id| { + if infcx.probe(|_| impl_may_apply(impl_def_id)) { + ambiguities.push(Ambiguity::DefId(impl_def_id)) + } + }, + ); + + let predicates = + 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::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())) + { + ambiguities.push(Ambiguity::ParamEnv(tcx.def_span(trait_pred.def_id()))) + } else { + ambiguities.push(Ambiguity::ParamEnv(span)) + } + } + } + + ambiguities +} diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/infer_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/infer_ctxt_ext.rs new file mode 100644 index 00000000000..90e2c1531b4 --- /dev/null +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/infer_ctxt_ext.rs @@ -0,0 +1,274 @@ +use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use crate::infer::InferCtxt; +use crate::traits::{Obligation, ObligationCause, ObligationCtxt}; +use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder}; +use rustc_hir as hir; +use rustc_hir::Node; +use rustc_middle::ty::{self, Ty}; +use rustc_span::{Span, DUMMY_SP}; + +use super::ArgKind; + +pub use rustc_infer::traits::error_reporting::*; + +pub trait InferCtxtExt<'tcx> { + /// Given some node representing a fn-like thing in the HIR map, + /// returns a span and `ArgKind` information that describes the + /// arguments it expects. This can be supplied to + /// `report_arg_count_mismatch`. + fn get_fn_like_arguments(&self, node: Node<'_>) -> Option<(Span, Option<Span>, Vec<ArgKind>)>; + + /// Reports an error when the number of arguments needed by a + /// trait match doesn't match the number that the expression + /// provides. + fn report_arg_count_mismatch( + &self, + span: Span, + found_span: Option<Span>, + expected_args: Vec<ArgKind>, + found_args: Vec<ArgKind>, + is_closure: bool, + closure_pipe_span: Option<Span>, + ) -> DiagnosticBuilder<'tcx>; + + /// Checks if the type implements one of `Fn`, `FnMut`, or `FnOnce` + /// in that order, and returns the generic type corresponding to the + /// argument of that trait (corresponding to the closure arguments). + fn type_implements_fn_trait( + &self, + param_env: ty::ParamEnv<'tcx>, + ty: ty::Binder<'tcx, Ty<'tcx>>, + polarity: ty::ImplPolarity, + ) -> Result<(ty::ClosureKind, ty::Binder<'tcx, Ty<'tcx>>), ()>; +} + +impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { + /// Given some node representing a fn-like thing in the HIR map, + /// returns a span and `ArgKind` information that describes the + /// arguments it expects. This can be supplied to + /// `report_arg_count_mismatch`. + fn get_fn_like_arguments(&self, node: Node<'_>) -> Option<(Span, Option<Span>, Vec<ArgKind>)> { + let sm = self.tcx.sess.source_map(); + let hir = self.tcx.hir(); + Some(match node { + Node::Expr(&hir::Expr { + kind: hir::ExprKind::Closure(&hir::Closure { body, fn_decl_span, fn_arg_span, .. }), + .. + }) => ( + fn_decl_span, + fn_arg_span, + hir.body(body) + .params + .iter() + .map(|arg| { + if let hir::Pat { kind: hir::PatKind::Tuple(args, _), span, .. } = *arg.pat + { + Some(ArgKind::Tuple( + Some(span), + args.iter() + .map(|pat| { + sm.span_to_snippet(pat.span) + .ok() + .map(|snippet| (snippet, "_".to_owned())) + }) + .collect::<Option<Vec<_>>>()?, + )) + } else { + let name = sm.span_to_snippet(arg.pat.span).ok()?; + Some(ArgKind::Arg(name, "_".to_owned())) + } + }) + .collect::<Option<Vec<ArgKind>>>()?, + ), + Node::Item(&hir::Item { kind: hir::ItemKind::Fn(ref sig, ..), .. }) + | Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(ref sig, _), .. }) + | Node::TraitItem(&hir::TraitItem { + kind: hir::TraitItemKind::Fn(ref sig, _), .. + }) => ( + sig.span, + None, + sig.decl + .inputs + .iter() + .map(|arg| match arg.kind { + hir::TyKind::Tup(tys) => ArgKind::Tuple( + Some(arg.span), + vec![("_".to_owned(), "_".to_owned()); tys.len()], + ), + _ => ArgKind::empty(), + }) + .collect::<Vec<ArgKind>>(), + ), + Node::Ctor(variant_data) => { + let span = variant_data.ctor_hir_id().map_or(DUMMY_SP, |id| hir.span(id)); + (span, None, vec![ArgKind::empty(); variant_data.fields().len()]) + } + _ => panic!("non-FnLike node found: {node:?}"), + }) + } + + /// Reports an error when the number of arguments needed by a + /// trait match doesn't match the number that the expression + /// provides. + fn report_arg_count_mismatch( + &self, + span: Span, + found_span: Option<Span>, + expected_args: Vec<ArgKind>, + found_args: Vec<ArgKind>, + is_closure: bool, + closure_arg_span: Option<Span>, + ) -> DiagnosticBuilder<'tcx> { + let kind = if is_closure { "closure" } else { "function" }; + + let args_str = |arguments: &[ArgKind], other: &[ArgKind]| { + let arg_length = arguments.len(); + let distinct = matches!(other, &[ArgKind::Tuple(..)]); + match (arg_length, arguments.get(0)) { + (1, Some(ArgKind::Tuple(_, fields))) => { + format!("a single {}-tuple as argument", fields.len()) + } + _ => format!( + "{} {}argument{}", + arg_length, + if distinct && arg_length > 1 { "distinct " } else { "" }, + pluralize!(arg_length) + ), + } + }; + + let expected_str = args_str(&expected_args, &found_args); + let found_str = args_str(&found_args, &expected_args); + + let mut err = struct_span_err!( + self.dcx(), + span, + E0593, + "{} is expected to take {}, but it takes {}", + kind, + expected_str, + found_str, + ); + + err.span_label(span, format!("expected {kind} that takes {expected_str}")); + + if let Some(found_span) = found_span { + err.span_label(found_span, format!("takes {found_str}")); + + // Suggest to take and ignore the arguments with expected_args_length `_`s if + // found arguments is empty (assume the user just wants to ignore args in this case). + // For example, if `expected_args_length` is 2, suggest `|_, _|`. + if found_args.is_empty() && is_closure { + let underscores = vec!["_"; expected_args.len()].join(", "); + err.span_suggestion_verbose( + closure_arg_span.unwrap_or(found_span), + format!( + "consider changing the closure to take and ignore the expected argument{}", + pluralize!(expected_args.len()) + ), + format!("|{underscores}|"), + Applicability::MachineApplicable, + ); + } + + if let &[ArgKind::Tuple(_, ref fields)] = &found_args[..] { + if fields.len() == expected_args.len() { + let sugg = fields + .iter() + .map(|(name, _)| name.to_owned()) + .collect::<Vec<String>>() + .join(", "); + err.span_suggestion_verbose( + found_span, + "change the closure to take multiple arguments instead of a single tuple", + format!("|{sugg}|"), + Applicability::MachineApplicable, + ); + } + } + if let &[ArgKind::Tuple(_, ref fields)] = &expected_args[..] + && fields.len() == found_args.len() + && is_closure + { + let sugg = format!( + "|({}){}|", + found_args + .iter() + .map(|arg| match arg { + ArgKind::Arg(name, _) => name.to_owned(), + _ => "_".to_owned(), + }) + .collect::<Vec<String>>() + .join(", "), + // add type annotations if available + if found_args.iter().any(|arg| match arg { + ArgKind::Arg(_, ty) => ty != "_", + _ => false, + }) { + format!( + ": ({})", + fields + .iter() + .map(|(_, ty)| ty.to_owned()) + .collect::<Vec<String>>() + .join(", ") + ) + } else { + String::new() + }, + ); + err.span_suggestion_verbose( + found_span, + "change the closure to accept a tuple instead of individual arguments", + sugg, + Applicability::MachineApplicable, + ); + } + } + + err + } + + fn type_implements_fn_trait( + &self, + param_env: ty::ParamEnv<'tcx>, + ty: ty::Binder<'tcx, Ty<'tcx>>, + polarity: ty::ImplPolarity, + ) -> Result<(ty::ClosureKind, ty::Binder<'tcx, Ty<'tcx>>), ()> { + self.commit_if_ok(|_| { + for trait_def_id in [ + self.tcx.lang_items().fn_trait(), + self.tcx.lang_items().fn_mut_trait(), + self.tcx.lang_items().fn_once_trait(), + ] { + let Some(trait_def_id) = trait_def_id else { continue }; + // Make a fresh inference variable so we can determine what the substitutions + // of the trait are. + let var = self.next_ty_var(TypeVariableOrigin { + span: DUMMY_SP, + kind: TypeVariableOriginKind::MiscVariable, + }); + // FIXME(effects) + let trait_ref = ty::TraitRef::new(self.tcx, trait_def_id, [ty.skip_binder(), var]); + let obligation = Obligation::new( + self.tcx, + ObligationCause::dummy(), + param_env, + ty.rebind(ty::TraitPredicate { trait_ref, polarity }), + ); + let ocx = ObligationCtxt::new(self); + ocx.register_obligation(obligation); + if ocx.select_all_or_error().is_empty() { + return Ok(( + self.tcx + .fn_trait_kind_from_def_id(trait_def_id) + .expect("expected to map DefId to ClosureKind"), + ty.rebind(self.resolve_vars_if_possible(var)), + )); + } + } + + Err(()) + }) + } +} diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs new file mode 100644 index 00000000000..0796cb57d97 --- /dev/null +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -0,0 +1,183 @@ +// ignore-tidy-filelength :( + +mod ambiguity; +mod infer_ctxt_ext; +pub mod on_unimplemented; +pub mod suggestions; +mod type_err_ctxt_ext; + +use super::{Obligation, ObligationCause, ObligationCauseCode, PredicateObligation}; +use crate::infer::InferCtxt; +use crate::solve::{GenerateProofTree, InferCtxtEvalExt}; +use rustc_hir as hir; +use rustc_hir::def_id::DefId; +use rustc_hir::intravisit::Visitor; +use rustc_middle::traits::solve::Goal; +use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_span::Span; +use std::io::Write; +use std::ops::ControlFlow; + +pub use self::infer_ctxt_ext::*; +pub use self::type_err_ctxt_ext::*; + +// When outputting impl candidates, prefer showing those that are more similar. +// +// We also compare candidates after skipping lifetimes, which has a lower +// priority than exact matches. +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub enum CandidateSimilarity { + Exact { ignoring_lifetimes: bool }, + Fuzzy { ignoring_lifetimes: bool }, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct ImplCandidate<'tcx> { + pub trait_ref: ty::TraitRef<'tcx>, + pub similarity: CandidateSimilarity, + impl_def_id: DefId, +} + +enum GetSafeTransmuteErrorAndReason { + Silent, + Error { err_msg: String, safe_transmute_explanation: String }, +} + +struct UnsatisfiedConst(pub bool); + +/// Crude way of getting back an `Expr` from a `Span`. +pub struct FindExprBySpan<'hir> { + pub span: Span, + pub result: Option<&'hir hir::Expr<'hir>>, + pub ty_result: Option<&'hir hir::Ty<'hir>>, +} + +impl<'hir> FindExprBySpan<'hir> { + pub fn new(span: Span) -> Self { + Self { span, result: None, ty_result: None } + } +} + +impl<'v> Visitor<'v> for FindExprBySpan<'v> { + fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) { + if self.span == ex.span { + self.result = Some(ex); + } else { + hir::intravisit::walk_expr(self, ex); + } + } + fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) { + if self.span == ty.span { + self.ty_result = Some(ty); + } else { + hir::intravisit::walk_ty(self, ty); + } + } +} + +/// Look for type `param` in an ADT being used only through a reference to confirm that suggesting +/// `param: ?Sized` would be a valid constraint. +struct FindTypeParam { + param: rustc_span::Symbol, + invalid_spans: Vec<Span>, + nested: bool, +} + +impl<'v> Visitor<'v> for FindTypeParam { + fn visit_where_predicate(&mut self, _: &'v hir::WherePredicate<'v>) { + // Skip where-clauses, to avoid suggesting indirection for type parameters found there. + } + + fn visit_ty(&mut self, ty: &hir::Ty<'_>) { + // We collect the spans of all uses of the "bare" type param, like in `field: T` or + // `field: (T, T)` where we could make `T: ?Sized` while skipping cases that are known to be + // valid like `field: &'a T` or `field: *mut T` and cases that *might* have further `Sized` + // obligations like `Box<T>` and `Vec<T>`, but we perform no extra analysis for those cases + // and suggest `T: ?Sized` regardless of their obligations. This is fine because the errors + // in that case should make what happened clear enough. + match ty.kind { + hir::TyKind::Ptr(_) | hir::TyKind::Ref(..) | hir::TyKind::TraitObject(..) => {} + hir::TyKind::Path(hir::QPath::Resolved(None, path)) + if path.segments.len() == 1 && path.segments[0].ident.name == self.param => + { + if !self.nested { + debug!(?ty, "FindTypeParam::visit_ty"); + self.invalid_spans.push(ty.span); + } + } + hir::TyKind::Path(_) => { + let prev = self.nested; + self.nested = true; + hir::intravisit::walk_ty(self, ty); + self.nested = prev; + } + _ => { + hir::intravisit::walk_ty(self, ty); + } + } + } +} + +/// Summarizes information +#[derive(Clone)] +pub enum ArgKind { + /// An argument of non-tuple type. Parameters are (name, ty) + Arg(String, String), + + /// An argument of tuple type. For a "found" argument, the span is + /// the location in the source of the pattern. For an "expected" + /// argument, it will be None. The vector is a list of (name, ty) + /// strings for the components of the tuple. + Tuple(Option<Span>, Vec<(String, String)>), +} + +impl ArgKind { + fn empty() -> ArgKind { + ArgKind::Arg("_".to_owned(), "_".to_owned()) + } + + /// Creates an `ArgKind` from the expected type of an + /// argument. It has no name (`_`) and an optional source span. + pub fn from_expected_ty(t: Ty<'_>, span: Option<Span>) -> ArgKind { + match t.kind() { + ty::Tuple(tys) => ArgKind::Tuple( + span, + tys.iter().map(|ty| ("_".to_owned(), ty.to_string())).collect::<Vec<_>>(), + ), + _ => ArgKind::Arg("_".to_owned(), t.to_string()), + } + } +} + +struct HasNumericInferVisitor; + +impl<'tcx> ty::TypeVisitor<TyCtxt<'tcx>> for HasNumericInferVisitor { + type BreakTy = (); + + fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { + if matches!(ty.kind(), ty::Infer(ty::FloatVar(_) | ty::IntVar(_))) { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) + } + } +} + +#[derive(Copy, Clone)] +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) + .1 + .expect("proof tree should have been generated"); + let mut lock = std::io::stdout().lock(); + let _ = lock.write_fmt(format_args!("{tree:?}\n")); + 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 new file mode 100644 index 00000000000..52f91d282f0 --- /dev/null +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs @@ -0,0 +1,910 @@ +use super::{ObligationCauseCode, PredicateObligation}; +use crate::infer::error_reporting::TypeErrCtxt; +use rustc_ast::AttrArgs; +use rustc_ast::AttrArgsEq; +use rustc_ast::AttrKind; +use rustc_ast::{Attribute, MetaItem, NestedMetaItem}; +use rustc_attr as attr; +use rustc_data_structures::fx::FxHashMap; +use rustc_errors::{struct_span_err, ErrorGuaranteed}; +use rustc_hir as hir; +use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_middle::ty::GenericArgsRef; +use rustc_middle::ty::{self, GenericParamDefKind, TyCtxt}; +use rustc_parse_format::{ParseMode, Parser, Piece, Position}; +use rustc_session::lint::builtin::UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES; +use rustc_span::symbol::{kw, sym, Symbol}; +use rustc_span::{Span, DUMMY_SP}; +use std::iter; + +use crate::errors::{ + EmptyOnClauseInOnUnimplemented, InvalidOnClauseInOnUnimplemented, NoValueInOnUnimplemented, +}; + +use crate::traits::error_reporting::type_err_ctxt_ext::InferCtxtPrivExt; + +pub trait TypeErrCtxtExt<'tcx> { + /*private*/ + fn impl_similar_to( + &self, + trait_ref: ty::PolyTraitRef<'tcx>, + obligation: &PredicateObligation<'tcx>, + ) -> Option<(DefId, GenericArgsRef<'tcx>)>; + + /*private*/ + fn describe_enclosure(&self, def_id: LocalDefId) -> Option<&'static str>; + + fn on_unimplemented_note( + &self, + trait_ref: ty::PolyTraitRef<'tcx>, + obligation: &PredicateObligation<'tcx>, + ) -> OnUnimplementedNote; +} + +/// The symbols which are always allowed in a format string +static ALLOWED_FORMAT_SYMBOLS: &[Symbol] = &[ + kw::SelfUpper, + sym::ItemContext, + sym::from_desugaring, + sym::direct, + sym::cause, + sym::integral, + sym::integer_, + sym::float, + sym::_Self, + sym::crate_local, + sym::Trait, +]; + +impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { + fn impl_similar_to( + &self, + trait_ref: ty::PolyTraitRef<'tcx>, + obligation: &PredicateObligation<'tcx>, + ) -> Option<(DefId, GenericArgsRef<'tcx>)> { + let tcx = self.tcx; + let param_env = obligation.param_env; + let trait_ref = self.instantiate_binder_with_placeholders(trait_ref); + let trait_self_ty = trait_ref.self_ty(); + + let mut self_match_impls = vec![]; + let mut fuzzy_match_impls = vec![]; + + self.tcx.for_each_relevant_impl(trait_ref.def_id, trait_self_ty, |def_id| { + let impl_args = self.fresh_args_for_item(obligation.cause.span, def_id); + let impl_trait_ref = tcx.impl_trait_ref(def_id).unwrap().instantiate(tcx, impl_args); + + let impl_self_ty = impl_trait_ref.self_ty(); + + if self.can_eq(param_env, trait_self_ty, impl_self_ty) { + self_match_impls.push((def_id, impl_args)); + + if iter::zip(trait_ref.args.types().skip(1), impl_trait_ref.args.types().skip(1)) + .all(|(u, v)| self.fuzzy_match_tys(u, v, false).is_some()) + { + fuzzy_match_impls.push((def_id, impl_args)); + } + } + }); + + let impl_def_id_and_args = if self_match_impls.len() == 1 { + self_match_impls[0] + } else if fuzzy_match_impls.len() == 1 { + fuzzy_match_impls[0] + } else { + return None; + }; + + tcx.has_attr(impl_def_id_and_args.0, sym::rustc_on_unimplemented) + .then_some(impl_def_id_and_args) + } + + /// Used to set on_unimplemented's `ItemContext` + /// to be the enclosing (async) block/function/closure + fn describe_enclosure(&self, def_id: LocalDefId) -> Option<&'static str> { + match self.tcx.opt_hir_node_by_def_id(def_id)? { + hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(..), .. }) => Some("a function"), + hir::Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Fn(..), .. }) => { + Some("a trait method") + } + hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(..), .. }) => { + Some("a method") + } + hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::Closure(hir::Closure { kind, .. }), + .. + }) => Some(self.describe_closure(*kind)), + _ => None, + } + } + + fn on_unimplemented_note( + &self, + trait_ref: ty::PolyTraitRef<'tcx>, + obligation: &PredicateObligation<'tcx>, + ) -> OnUnimplementedNote { + let (def_id, args) = self + .impl_similar_to(trait_ref, obligation) + .unwrap_or_else(|| (trait_ref.def_id(), trait_ref.skip_binder().args)); + let trait_ref = trait_ref.skip_binder(); + + let mut flags = vec![]; + // FIXME(-Zlower-impl-trait-in-trait-to-assoc-ty): HIR is not present for RPITITs, + // but I guess we could synthesize one here. We don't see any errors that rely on + // that yet, though. + let enclosure = self.describe_enclosure(obligation.cause.body_id).map(|t| t.to_owned()); + flags.push((sym::ItemContext, enclosure)); + + match obligation.cause.code() { + ObligationCauseCode::BuiltinDerivedObligation(..) + | ObligationCauseCode::ImplDerivedObligation(..) + | ObligationCauseCode::DerivedObligation(..) => {} + _ => { + // this is a "direct", user-specified, rather than derived, + // obligation. + flags.push((sym::direct, None)); + } + } + + if let Some(k) = obligation.cause.span.desugaring_kind() { + flags.push((sym::from_desugaring, None)); + flags.push((sym::from_desugaring, Some(format!("{k:?}")))); + } + + if let ObligationCauseCode::MainFunctionType = obligation.cause.code() { + flags.push((sym::cause, Some("MainFunctionType".to_string()))); + } + + flags.push((sym::Trait, Some(trait_ref.print_trait_sugared().to_string()))); + + // Add all types without trimmed paths or visible paths, ensuring they end up with + // their "canonical" def path. + ty::print::with_no_trimmed_paths!(ty::print::with_no_visible_paths!({ + let generics = self.tcx.generics_of(def_id); + let self_ty = trait_ref.self_ty(); + // This is also included through the generics list as `Self`, + // but the parser won't allow you to use it + flags.push((sym::_Self, Some(self_ty.to_string()))); + if let Some(def) = self_ty.ty_adt_def() { + // We also want to be able to select self's original + // signature with no type arguments resolved + flags.push(( + sym::_Self, + Some(self.tcx.type_of(def.did()).instantiate_identity().to_string()), + )); + } + + for param in generics.params.iter() { + let value = match param.kind { + GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => { + args[param.index as usize].to_string() + } + GenericParamDefKind::Lifetime => continue, + }; + let name = param.name; + flags.push((name, Some(value))); + + if let GenericParamDefKind::Type { .. } = param.kind { + let param_ty = args[param.index as usize].expect_ty(); + if let Some(def) = param_ty.ty_adt_def() { + // We also want to be able to select the parameter's + // original signature with no type arguments resolved + flags.push(( + name, + Some(self.tcx.type_of(def.did()).instantiate_identity().to_string()), + )); + } + } + } + + if let Some(true) = self_ty.ty_adt_def().map(|def| def.did().is_local()) { + flags.push((sym::crate_local, None)); + } + + // Allow targeting all integers using `{integral}`, even if the exact type was resolved + if self_ty.is_integral() { + flags.push((sym::_Self, Some("{integral}".to_owned()))); + } + + if self_ty.is_array_slice() { + flags.push((sym::_Self, Some("&[]".to_owned()))); + } + + if self_ty.is_fn() { + let fn_sig = self_ty.fn_sig(self.tcx); + let shortname = match fn_sig.unsafety() { + hir::Unsafety::Normal => "fn", + hir::Unsafety::Unsafe => "unsafe fn", + }; + flags.push((sym::_Self, Some(shortname.to_owned()))); + } + + // Slices give us `[]`, `[{ty}]` + if let ty::Slice(aty) = self_ty.kind() { + flags.push((sym::_Self, Some("[]".to_string()))); + if let Some(def) = aty.ty_adt_def() { + // We also want to be able to select the slice's type's original + // signature with no type arguments resolved + flags.push(( + sym::_Self, + Some(format!("[{}]", self.tcx.type_of(def.did()).instantiate_identity())), + )); + } + if aty.is_integral() { + flags.push((sym::_Self, Some("[{integral}]".to_string()))); + } + } + + // 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.try_to_valtree().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}]")))); + } + if let Some(def) = aty.ty_adt_def() { + // We also want to be able to select the array's type's original + // signature with no type arguments resolved + let def_ty = self.tcx.type_of(def.did()).instantiate_identity(); + flags.push((sym::_Self, Some(format!("[{def_ty}; _]")))); + if let Some(n) = len { + flags.push((sym::_Self, Some(format!("[{def_ty}; {n}]")))); + } + } + if aty.is_integral() { + flags.push((sym::_Self, Some("[{integral}; _]".to_string()))); + if let Some(n) = len { + flags.push((sym::_Self, Some(format!("[{{integral}}; {n}]")))); + } + } + } + if let ty::Dynamic(traits, _, _) = self_ty.kind() { + for t in traits.iter() { + if let ty::ExistentialPredicate::Trait(trait_ref) = t.skip_binder() { + flags.push((sym::_Self, Some(self.tcx.def_path_str(trait_ref.def_id)))) + } + } + } + + // `&[{integral}]` - `FromIterator` needs that. + if let ty::Ref(_, ref_ty, rustc_ast::Mutability::Not) = self_ty.kind() + && let ty::Slice(sty) = ref_ty.kind() + && sty.is_integral() + { + flags.push((sym::_Self, Some("&[{integral}]".to_owned()))); + } + })); + + if let Ok(Some(command)) = OnUnimplementedDirective::of_item(self.tcx, def_id) { + command.evaluate(self.tcx, trait_ref, &flags) + } else { + OnUnimplementedNote::default() + } + } +} + +#[derive(Clone, Debug)] +pub struct OnUnimplementedFormatString { + symbol: Symbol, + span: Span, + is_diagnostic_namespace_variant: bool, +} + +#[derive(Debug)] +pub struct OnUnimplementedDirective { + pub condition: Option<MetaItem>, + pub subcommands: Vec<OnUnimplementedDirective>, + pub message: Option<OnUnimplementedFormatString>, + pub label: Option<OnUnimplementedFormatString>, + pub notes: Vec<OnUnimplementedFormatString>, + pub parent_label: Option<OnUnimplementedFormatString>, + pub append_const_msg: Option<AppendConstMessage>, +} + +/// For the `#[rustc_on_unimplemented]` attribute +#[derive(Default)] +pub struct OnUnimplementedNote { + pub message: Option<String>, + pub label: Option<String>, + pub notes: Vec<String>, + pub parent_label: Option<String>, + // If none, should fall back to a generic message + pub append_const_msg: Option<AppendConstMessage>, +} + +/// Append a message for `~const Trait` errors. +#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)] +pub enum AppendConstMessage { + #[default] + Default, + Custom(Symbol, Span), +} + +#[derive(LintDiagnostic)] +#[diag(trait_selection_malformed_on_unimplemented_attr)] +#[help] +pub struct MalformedOnUnimplementedAttrLint { + #[label] + pub span: Span, +} + +impl MalformedOnUnimplementedAttrLint { + fn new(span: Span) -> Self { + Self { span } + } +} + +#[derive(LintDiagnostic)] +#[diag(trait_selection_missing_options_for_on_unimplemented_attr)] +#[help] +pub struct MissingOptionsForOnUnimplementedAttr; + +#[derive(LintDiagnostic)] +#[diag(trait_selection_ignored_diagnostic_option)] +pub struct IgnoredDiagnosticOption { + pub option_name: &'static str, + #[label] + pub span: Span, + #[label(trait_selection_other_label)] + pub prev_span: Span, +} + +impl IgnoredDiagnosticOption { + fn maybe_emit_warning<'tcx>( + tcx: TyCtxt<'tcx>, + item_def_id: DefId, + new: Option<Span>, + old: Option<Span>, + option_name: &'static str, + ) { + if let (Some(new_item), Some(old_item)) = (new, old) { + tcx.emit_spanned_lint( + UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, + tcx.local_def_id_to_hir_id(item_def_id.expect_local()), + new_item, + IgnoredDiagnosticOption { span: new_item, prev_span: old_item, option_name }, + ); + } + } +} + +#[derive(LintDiagnostic)] +#[diag(trait_selection_unknown_format_parameter_for_on_unimplemented_attr)] +#[help] +pub struct UnknownFormatParameterForOnUnimplementedAttr { + argument_name: Symbol, + trait_name: Symbol, +} + +impl<'tcx> OnUnimplementedDirective { + fn parse( + tcx: TyCtxt<'tcx>, + item_def_id: DefId, + items: &[NestedMetaItem], + span: Span, + is_root: bool, + is_diagnostic_namespace_variant: bool, + ) -> Result<Option<Self>, ErrorGuaranteed> { + let mut errored = None; + let mut item_iter = items.iter(); + + let parse_value = |value_str, value_span| { + OnUnimplementedFormatString::try_parse( + tcx, + item_def_id, + value_str, + value_span, + is_diagnostic_namespace_variant, + ) + .map(Some) + }; + + let condition = if is_root { + None + } else { + let cond = item_iter + .next() + .ok_or_else(|| tcx.dcx().emit_err(EmptyOnClauseInOnUnimplemented { span }))? + .meta_item() + .ok_or_else(|| tcx.dcx().emit_err(InvalidOnClauseInOnUnimplemented { span }))?; + attr::eval_condition(cond, &tcx.sess.parse_sess, Some(tcx.features()), &mut |cfg| { + if let Some(value) = cfg.value + && let Err(guar) = parse_value(value, cfg.span) + { + errored = Some(guar); + } + true + }); + Some(cond.clone()) + }; + + let mut message = None; + let mut label = None; + let mut notes = Vec::new(); + let mut parent_label = None; + let mut subcommands = vec![]; + let mut append_const_msg = None; + + for item in item_iter { + if item.has_name(sym::message) && message.is_none() { + if let Some(message_) = item.value_str() { + message = parse_value(message_, item.span())?; + continue; + } + } else if item.has_name(sym::label) && label.is_none() { + if let Some(label_) = item.value_str() { + label = parse_value(label_, item.span())?; + continue; + } + } else if item.has_name(sym::note) { + if let Some(note_) = item.value_str() { + if let Some(note) = parse_value(note_, item.span())? { + notes.push(note); + continue; + } + } + } else if item.has_name(sym::parent_label) + && parent_label.is_none() + && !is_diagnostic_namespace_variant + { + if let Some(parent_label_) = item.value_str() { + parent_label = parse_value(parent_label_, item.span())?; + continue; + } + } else if item.has_name(sym::on) + && is_root + && message.is_none() + && label.is_none() + && notes.is_empty() + && !is_diagnostic_namespace_variant + // FIXME(diagnostic_namespace): disallow filters for now + { + if let Some(items) = item.meta_item_list() { + match Self::parse( + tcx, + item_def_id, + items, + item.span(), + false, + is_diagnostic_namespace_variant, + ) { + Ok(Some(subcommand)) => subcommands.push(subcommand), + Ok(None) => bug!( + "This cannot happen for now as we only reach that if `is_diagnostic_namespace_variant` is false" + ), + Err(reported) => errored = Some(reported), + }; + continue; + } + } else if item.has_name(sym::append_const_msg) + && append_const_msg.is_none() + && !is_diagnostic_namespace_variant + { + if let Some(msg) = item.value_str() { + append_const_msg = Some(AppendConstMessage::Custom(msg, item.span())); + continue; + } else if item.is_word() { + append_const_msg = Some(AppendConstMessage::Default); + continue; + } + } + + if is_diagnostic_namespace_variant { + tcx.emit_spanned_lint( + UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, + tcx.local_def_id_to_hir_id(item_def_id.expect_local()), + vec![item.span()], + MalformedOnUnimplementedAttrLint::new(item.span()), + ); + } else { + // nothing found + tcx.dcx().emit_err(NoValueInOnUnimplemented { span: item.span() }); + } + } + + if let Some(reported) = errored { + if is_diagnostic_namespace_variant { Ok(None) } else { Err(reported) } + } else { + Ok(Some(OnUnimplementedDirective { + condition, + subcommands, + message, + label, + notes, + parent_label, + append_const_msg, + })) + } + } + + pub fn of_item(tcx: TyCtxt<'tcx>, item_def_id: DefId) -> Result<Option<Self>, ErrorGuaranteed> { + if let Some(attr) = tcx.get_attr(item_def_id, sym::rustc_on_unimplemented) { + return Self::parse_attribute(attr, false, tcx, item_def_id); + } else if tcx.features().diagnostic_namespace { + tcx.get_attrs_by_path(item_def_id, &[sym::diagnostic, sym::on_unimplemented]) + .filter_map(|attr| Self::parse_attribute(attr, true, tcx, item_def_id).transpose()) + .try_fold(None, |aggr: Option<Self>, directive| { + let directive = directive?; + if let Some(aggr) = aggr { + let mut subcommands = aggr.subcommands; + subcommands.extend(directive.subcommands); + let mut notes = aggr.notes; + notes.extend(directive.notes); + IgnoredDiagnosticOption::maybe_emit_warning( + tcx, + item_def_id, + directive.message.as_ref().map(|f| f.span), + aggr.message.as_ref().map(|f| f.span), + "message", + ); + IgnoredDiagnosticOption::maybe_emit_warning( + tcx, + item_def_id, + directive.label.as_ref().map(|f| f.span), + aggr.label.as_ref().map(|f| f.span), + "label", + ); + IgnoredDiagnosticOption::maybe_emit_warning( + tcx, + item_def_id, + directive.condition.as_ref().map(|i| i.span), + aggr.condition.as_ref().map(|i| i.span), + "condition", + ); + IgnoredDiagnosticOption::maybe_emit_warning( + tcx, + item_def_id, + directive.parent_label.as_ref().map(|f| f.span), + aggr.parent_label.as_ref().map(|f| f.span), + "parent_label", + ); + IgnoredDiagnosticOption::maybe_emit_warning( + tcx, + item_def_id, + directive.append_const_msg.as_ref().and_then(|c| { + if let AppendConstMessage::Custom(_, s) = c { + Some(*s) + } else { + None + } + }), + aggr.append_const_msg.as_ref().and_then(|c| { + if let AppendConstMessage::Custom(_, s) = c { + Some(*s) + } else { + None + } + }), + "append_const_msg", + ); + + Ok(Some(Self { + condition: aggr.condition.or(directive.condition), + subcommands, + message: aggr.message.or(directive.message), + label: aggr.label.or(directive.label), + notes, + parent_label: aggr.parent_label.or(directive.parent_label), + append_const_msg: aggr.append_const_msg.or(directive.append_const_msg), + })) + } else { + Ok(Some(directive)) + } + }) + } else { + Ok(None) + } + } + + fn parse_attribute( + attr: &Attribute, + is_diagnostic_namespace_variant: bool, + tcx: TyCtxt<'tcx>, + item_def_id: DefId, + ) -> Result<Option<Self>, ErrorGuaranteed> { + let result = if let Some(items) = attr.meta_item_list() { + Self::parse(tcx, item_def_id, &items, attr.span, true, is_diagnostic_namespace_variant) + } else if let Some(value) = attr.value_str() { + if !is_diagnostic_namespace_variant { + Ok(Some(OnUnimplementedDirective { + condition: None, + message: None, + subcommands: vec![], + label: Some(OnUnimplementedFormatString::try_parse( + tcx, + item_def_id, + value, + attr.span, + is_diagnostic_namespace_variant, + )?), + notes: Vec::new(), + parent_label: None, + append_const_msg: None, + })) + } else { + let item = attr.get_normal_item(); + let report_span = match &item.args { + AttrArgs::Empty => item.path.span, + AttrArgs::Delimited(args) => args.dspan.entire(), + AttrArgs::Eq(eq_span, AttrArgsEq::Ast(expr)) => eq_span.to(expr.span), + AttrArgs::Eq(span, AttrArgsEq::Hir(expr)) => span.to(expr.span), + }; + + tcx.emit_spanned_lint( + UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, + tcx.local_def_id_to_hir_id(item_def_id.expect_local()), + report_span, + MalformedOnUnimplementedAttrLint::new(report_span), + ); + Ok(None) + } + } else if is_diagnostic_namespace_variant { + match &attr.kind { + AttrKind::Normal(p) if !matches!(p.item.args, AttrArgs::Empty) => { + tcx.emit_spanned_lint( + UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, + tcx.local_def_id_to_hir_id(item_def_id.expect_local()), + attr.span, + MalformedOnUnimplementedAttrLint::new(attr.span), + ); + } + _ => tcx.emit_spanned_lint( + UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, + tcx.local_def_id_to_hir_id(item_def_id.expect_local()), + attr.span, + MissingOptionsForOnUnimplementedAttr, + ), + }; + + Ok(None) + } else { + let reported = tcx + .dcx() + .span_delayed_bug(DUMMY_SP, "of_item: neither meta_item_list nor value_str"); + return Err(reported); + }; + debug!("of_item({:?}) = {:?}", item_def_id, result); + result + } + + pub fn evaluate( + &self, + tcx: TyCtxt<'tcx>, + trait_ref: ty::TraitRef<'tcx>, + options: &[(Symbol, Option<String>)], + ) -> OnUnimplementedNote { + let mut message = None; + let mut label = None; + let mut notes = Vec::new(); + let mut parent_label = None; + let mut append_const_msg = None; + info!("evaluate({:?}, trait_ref={:?}, options={:?})", self, trait_ref, options); + + let options_map: FxHashMap<Symbol, String> = + options.iter().filter_map(|(k, v)| v.clone().map(|v| (*k, v))).collect(); + + for command in self.subcommands.iter().chain(Some(self)).rev() { + if let Some(ref condition) = command.condition + && !attr::eval_condition( + condition, + &tcx.sess.parse_sess, + Some(tcx.features()), + &mut |cfg| { + let value = cfg.value.map(|v| { + // `with_no_visible_paths` is also used when generating the options, + // so we need to match it here. + ty::print::with_no_visible_paths!( + OnUnimplementedFormatString { + symbol: v, + span: cfg.span, + is_diagnostic_namespace_variant: false + } + .format( + tcx, + trait_ref, + &options_map + ) + ) + }); + + options.contains(&(cfg.name, value)) + }, + ) + { + debug!("evaluate: skipping {:?} due to condition", command); + continue; + } + debug!("evaluate: {:?} succeeded", command); + if let Some(ref message_) = command.message { + message = Some(message_.clone()); + } + + if let Some(ref label_) = command.label { + label = Some(label_.clone()); + } + + notes.extend(command.notes.clone()); + + if let Some(ref parent_label_) = command.parent_label { + parent_label = Some(parent_label_.clone()); + } + + append_const_msg = command.append_const_msg; + } + + OnUnimplementedNote { + label: label.map(|l| l.format(tcx, trait_ref, &options_map)), + message: message.map(|m| m.format(tcx, trait_ref, &options_map)), + notes: notes.into_iter().map(|n| n.format(tcx, trait_ref, &options_map)).collect(), + parent_label: parent_label.map(|e_s| e_s.format(tcx, trait_ref, &options_map)), + append_const_msg, + } + } +} + +impl<'tcx> OnUnimplementedFormatString { + fn try_parse( + tcx: TyCtxt<'tcx>, + item_def_id: DefId, + from: Symbol, + value_span: Span, + is_diagnostic_namespace_variant: bool, + ) -> Result<Self, ErrorGuaranteed> { + let result = OnUnimplementedFormatString { + symbol: from, + span: value_span, + is_diagnostic_namespace_variant, + }; + result.verify(tcx, item_def_id)?; + Ok(result) + } + + fn verify(&self, tcx: TyCtxt<'tcx>, item_def_id: DefId) -> Result<(), ErrorGuaranteed> { + let trait_def_id = if tcx.is_trait(item_def_id) { + item_def_id + } else { + tcx.trait_id_of_impl(item_def_id) + .expect("expected `on_unimplemented` to correspond to a trait") + }; + let trait_name = tcx.item_name(trait_def_id); + let generics = tcx.generics_of(item_def_id); + let s = self.symbol.as_str(); + let parser = Parser::new(s, None, None, false, ParseMode::Format); + let mut result = Ok(()); + for token in parser { + match token { + Piece::String(_) => (), // Normal string, no need to check it + Piece::NextArgument(a) => match a.position { + Position::ArgumentNamed(s) => { + match Symbol::intern(s) { + // `{ThisTraitsName}` is allowed + s if s == trait_name && !self.is_diagnostic_namespace_variant => (), + s if ALLOWED_FORMAT_SYMBOLS.contains(&s) + && !self.is_diagnostic_namespace_variant => + { + () + } + // So is `{A}` if A is a type parameter + s if generics.params.iter().any(|param| param.name == s) => (), + s => { + if self.is_diagnostic_namespace_variant { + tcx.emit_spanned_lint( + UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, + tcx.local_def_id_to_hir_id(item_def_id.expect_local()), + self.span, + UnknownFormatParameterForOnUnimplementedAttr { + argument_name: s, + trait_name, + }, + ); + } else { + result = Err(struct_span_err!( + tcx.dcx(), + self.span, + E0230, + "there is no parameter `{}` on {}", + s, + if trait_def_id == item_def_id { + format!("trait `{trait_name}`") + } else { + "impl".to_string() + } + ) + .emit()); + } + } + } + } + // `{:1}` and `{}` are not to be used + Position::ArgumentIs(..) | Position::ArgumentImplicitlyIs(_) => { + let reported = struct_span_err!( + tcx.dcx(), + self.span, + E0231, + "only named substitution parameters are allowed" + ) + .emit(); + result = Err(reported); + } + }, + } + } + + result + } + + pub fn format( + &self, + tcx: TyCtxt<'tcx>, + trait_ref: ty::TraitRef<'tcx>, + options: &FxHashMap<Symbol, String>, + ) -> String { + let name = tcx.item_name(trait_ref.def_id); + let trait_str = tcx.def_path_str(trait_ref.def_id); + let generics = tcx.generics_of(trait_ref.def_id); + let generic_map = generics + .params + .iter() + .filter_map(|param| { + let value = match param.kind { + GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => { + trait_ref.args[param.index as usize].to_string() + } + GenericParamDefKind::Lifetime => return None, + }; + let name = param.name; + Some((name, value)) + }) + .collect::<FxHashMap<Symbol, String>>(); + let empty_string = String::new(); + + let s = self.symbol.as_str(); + let parser = Parser::new(s, None, None, false, ParseMode::Format); + let item_context = (options.get(&sym::ItemContext)).unwrap_or(&empty_string); + parser + .map(|p| match p { + Piece::String(s) => s.to_owned(), + Piece::NextArgument(a) => match a.position { + Position::ArgumentNamed(arg) => { + let s = Symbol::intern(arg); + match generic_map.get(&s) { + Some(val) => val.to_string(), + None if self.is_diagnostic_namespace_variant => { + format!("{{{arg}}}") + } + None if s == name => trait_str.clone(), + None => { + if let Some(val) = options.get(&s) { + val.clone() + } else if s == sym::from_desugaring { + // don't break messages using these two arguments incorrectly + String::new() + } else if s == sym::ItemContext + && !self.is_diagnostic_namespace_variant + { + item_context.clone() + } else if s == sym::integral { + String::from("{integral}") + } else if s == sym::integer_ { + String::from("{integer}") + } else if s == sym::float { + String::from("{float}") + } else { + bug!( + "broken on_unimplemented {:?} for {:?}: \ + no argument matching {:?}", + self.symbol, + trait_ref, + s + ) + } + } + } + } + _ => bug!("broken on_unimplemented {:?} - bad format arg", self.symbol), + }, + }) + .collect() + } +} diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs new file mode 100644 index 00000000000..f63314081d6 --- /dev/null +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -0,0 +1,5020 @@ +// ignore-tidy-filelength + +use super::{ + DefIdOrName, FindExprBySpan, ImplCandidate, Obligation, ObligationCause, ObligationCauseCode, + PredicateObligation, +}; + +use crate::errors; +use crate::infer::InferCtxt; +use crate::traits::{NormalizeExt, ObligationCtxt}; + +use hir::def::CtorOf; +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, + MultiSpan, Style, SuggestionStyle, +}; +use rustc_hir as hir; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def_id::DefId; +use rustc_hir::intravisit::Visitor; +use rustc_hir::is_range_literal; +use rustc_hir::lang_items::LangItem; +use rustc_hir::{CoroutineDesugaring, CoroutineKind, CoroutineSource, Node}; +use rustc_hir::{Expr, HirId}; +use rustc_infer::infer::error_reporting::TypeErrCtxt; +use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferOk}; +use rustc_middle::hir::map; +use rustc_middle::traits::IsConstable; +use rustc_middle::ty::error::TypeError::{self, Sorts}; +use rustc_middle::ty::{ + self, suggest_arbitrary_trait_bound, suggest_constraining_type_param, AdtKind, GenericArgs, + InferTy, IsSuggestable, ToPredicate, Ty, TyCtxt, TypeAndMut, TypeFoldable, TypeFolder, + TypeSuperFoldable, TypeVisitableExt, TypeckResults, +}; +use rustc_span::def_id::LocalDefId; +use rustc_span::symbol::{kw, 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 crate::infer::InferCtxtExt as _; +use crate::traits::error_reporting::type_err_ctxt_ext::InferCtxtPrivExt; +use crate::traits::query::evaluate_obligation::InferCtxtExt as _; +use rustc_middle::ty::print::{with_forced_trimmed_paths, with_no_trimmed_paths}; + +use itertools::EitherOrBoth; +use itertools::Itertools; + +#[derive(Debug)] +pub enum CoroutineInteriorOrUpvar { + // span of interior type + Interior(Span, Option<(Span, Option<Span>)>), + // span of upvar + Upvar(Span), +} + +// This type provides a uniform interface to retrieve data on coroutines, whether it originated from +// the local crate being compiled or from a foreign crate. +#[derive(Debug)] +struct CoroutineData<'tcx, 'a>(&'a TypeckResults<'tcx>); + +impl<'tcx, 'a> CoroutineData<'tcx, 'a> { + /// Try to get information about variables captured by the coroutine that matches a type we are + /// looking for with `ty_matches` function. We uses it to find upvar which causes a failure to + /// meet an obligation + fn try_get_upvar_span<F>( + &self, + infer_context: &InferCtxt<'tcx>, + coroutine_did: DefId, + ty_matches: F, + ) -> Option<CoroutineInteriorOrUpvar> + where + F: Fn(ty::Binder<'tcx, Ty<'tcx>>) -> bool, + { + infer_context.tcx.upvars_mentioned(coroutine_did).and_then(|upvars| { + upvars.iter().find_map(|(upvar_id, upvar)| { + let upvar_ty = self.0.node_type(*upvar_id); + let upvar_ty = infer_context.resolve_vars_if_possible(upvar_ty); + ty_matches(ty::Binder::dummy(upvar_ty)) + .then(|| CoroutineInteriorOrUpvar::Upvar(upvar.span)) + }) + }) + } + + /// Try to get the span of a type being awaited on that matches the type we are looking with the + /// `ty_matches` function. We uses it to find awaited type which causes a failure to meet an + /// obligation + fn get_from_await_ty<F>( + &self, + visitor: AwaitsVisitor, + hir: map::Map<'tcx>, + ty_matches: F, + ) -> Option<Span> + where + F: Fn(ty::Binder<'tcx, Ty<'tcx>>) -> bool, + { + visitor + .awaits + .into_iter() + .map(|id| hir.expect_expr(id)) + .find(|await_expr| ty_matches(ty::Binder::dummy(self.0.expr_ty_adjusted(await_expr)))) + .map(|expr| expr.span) + } +} + +// This trait is public to expose the diagnostics methods to clippy. +pub trait TypeErrCtxtExt<'tcx> { + fn suggest_restricting_param_bound( + &self, + err: &mut Diagnostic, + trait_pred: ty::PolyTraitPredicate<'tcx>, + associated_item: Option<(&'static str, Ty<'tcx>)>, + body_id: LocalDefId, + ); + + fn suggest_dereferences( + &self, + obligation: &PredicateObligation<'tcx>, + err: &mut Diagnostic, + trait_pred: ty::PolyTraitPredicate<'tcx>, + ) -> bool; + + fn get_closure_name( + &self, + def_id: DefId, + err: &mut Diagnostic, + msg: Cow<'static, str>, + ) -> Option<Symbol>; + + fn suggest_fn_call( + &self, + obligation: &PredicateObligation<'tcx>, + err: &mut Diagnostic, + trait_pred: ty::PolyTraitPredicate<'tcx>, + ) -> bool; + + fn check_for_binding_assigned_block_without_tail_expression( + &self, + obligation: &PredicateObligation<'tcx>, + err: &mut Diagnostic, + trait_pred: ty::PolyTraitPredicate<'tcx>, + ); + + fn suggest_add_clone_to_arg( + &self, + obligation: &PredicateObligation<'tcx>, + err: &mut Diagnostic, + trait_pred: ty::PolyTraitPredicate<'tcx>, + ) -> bool; + + fn extract_callable_info( + &self, + body_id: LocalDefId, + param_env: ty::ParamEnv<'tcx>, + found: Ty<'tcx>, + ) -> Option<(DefIdOrName, Ty<'tcx>, Vec<Ty<'tcx>>)>; + + fn suggest_add_reference_to_arg( + &self, + obligation: &PredicateObligation<'tcx>, + err: &mut Diagnostic, + trait_pred: ty::PolyTraitPredicate<'tcx>, + has_custom_message: bool, + ) -> bool; + + fn suggest_borrowing_for_object_cast( + &self, + err: &mut Diagnostic, + obligation: &PredicateObligation<'tcx>, + self_ty: Ty<'tcx>, + object_ty: Ty<'tcx>, + ); + + fn suggest_remove_reference( + &self, + obligation: &PredicateObligation<'tcx>, + err: &mut Diagnostic, + trait_pred: ty::PolyTraitPredicate<'tcx>, + ) -> bool; + + fn suggest_remove_await(&self, obligation: &PredicateObligation<'tcx>, err: &mut Diagnostic); + + fn suggest_change_mut( + &self, + obligation: &PredicateObligation<'tcx>, + err: &mut Diagnostic, + trait_pred: ty::PolyTraitPredicate<'tcx>, + ); + + fn suggest_semicolon_removal( + &self, + obligation: &PredicateObligation<'tcx>, + err: &mut Diagnostic, + span: Span, + trait_pred: ty::PolyTraitPredicate<'tcx>, + ) -> bool; + + fn return_type_span(&self, obligation: &PredicateObligation<'tcx>) -> Option<Span>; + + fn suggest_impl_trait( + &self, + err: &mut Diagnostic, + obligation: &PredicateObligation<'tcx>, + trait_pred: ty::PolyTraitPredicate<'tcx>, + ) -> bool; + + fn point_at_returns_when_relevant( + &self, + err: &mut DiagnosticBuilder<'tcx>, + obligation: &PredicateObligation<'tcx>, + ); + + fn report_closure_arg_mismatch( + &self, + span: Span, + found_span: Option<Span>, + found: ty::PolyTraitRef<'tcx>, + expected: ty::PolyTraitRef<'tcx>, + cause: &ObligationCauseCode<'tcx>, + found_node: Option<Node<'_>>, + param_env: ty::ParamEnv<'tcx>, + ) -> DiagnosticBuilder<'tcx>; + + fn note_conflicting_fn_args( + &self, + err: &mut Diagnostic, + cause: &ObligationCauseCode<'tcx>, + expected: Ty<'tcx>, + found: Ty<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ); + + fn note_conflicting_closure_bounds( + &self, + cause: &ObligationCauseCode<'tcx>, + err: &mut DiagnosticBuilder<'tcx>, + ); + + fn suggest_fully_qualified_path( + &self, + err: &mut Diagnostic, + item_def_id: DefId, + span: Span, + trait_ref: DefId, + ); + + fn maybe_note_obligation_cause_for_async_await( + &self, + err: &mut Diagnostic, + obligation: &PredicateObligation<'tcx>, + ) -> bool; + + fn note_obligation_cause_for_async_await( + &self, + err: &mut Diagnostic, + interior_or_upvar_span: CoroutineInteriorOrUpvar, + is_async: bool, + outer_coroutine: Option<DefId>, + trait_pred: ty::TraitPredicate<'tcx>, + target_ty: Ty<'tcx>, + obligation: &PredicateObligation<'tcx>, + next_code: Option<&ObligationCauseCode<'tcx>>, + ); + + fn note_obligation_cause_code<T>( + &self, + body_id: LocalDefId, + err: &mut Diagnostic, + predicate: T, + param_env: ty::ParamEnv<'tcx>, + cause_code: &ObligationCauseCode<'tcx>, + obligated_types: &mut Vec<Ty<'tcx>>, + seen_requirements: &mut FxHashSet<DefId>, + ) where + T: ToPredicate<'tcx>; + + /// Suggest to await before try: future? => future.await? + fn suggest_await_before_try( + &self, + err: &mut Diagnostic, + obligation: &PredicateObligation<'tcx>, + trait_pred: ty::PolyTraitPredicate<'tcx>, + span: Span, + ); + + fn suggest_floating_point_literal( + &self, + obligation: &PredicateObligation<'tcx>, + err: &mut Diagnostic, + trait_ref: &ty::PolyTraitRef<'tcx>, + ); + + fn suggest_derive( + &self, + obligation: &PredicateObligation<'tcx>, + err: &mut Diagnostic, + trait_pred: ty::PolyTraitPredicate<'tcx>, + ); + + fn suggest_dereferencing_index( + &self, + obligation: &PredicateObligation<'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, + err: &mut Diagnostic, + arg_hir_id: HirId, + parent_code: &ObligationCauseCode<'tcx>, + param_env: ty::ParamEnv<'tcx>, + predicate: ty::Predicate<'tcx>, + call_hir_id: HirId, + ); + + fn look_for_iterator_item_mistakes( + &self, + assocs_in_this_method: &[Option<(Span, (DefId, Ty<'tcx>))>], + typeck_results: &TypeckResults<'tcx>, + type_diffs: &[TypeError<'tcx>], + param_env: ty::ParamEnv<'tcx>, + path_segment: &hir::PathSegment<'_>, + args: &[hir::Expr<'_>], + err: &mut Diagnostic, + ); + + fn point_at_chain( + &self, + expr: &hir::Expr<'_>, + typeck_results: &TypeckResults<'tcx>, + type_diffs: Vec<TypeError<'tcx>>, + param_env: ty::ParamEnv<'tcx>, + err: &mut Diagnostic, + ); + + fn probe_assoc_types_at_expr( + &self, + type_diffs: &[TypeError<'tcx>], + span: Span, + prev_ty: Ty<'tcx>, + body_id: hir::HirId, + param_env: ty::ParamEnv<'tcx>, + ) -> Vec<Option<(Span, (DefId, Ty<'tcx>))>>; + + fn suggest_convert_to_slice( + &self, + err: &mut Diagnostic, + obligation: &PredicateObligation<'tcx>, + trait_ref: ty::PolyTraitRef<'tcx>, + candidate_impls: &[ImplCandidate<'tcx>], + span: Span, + ); + + fn explain_hrtb_projection( + &self, + diag: &mut Diagnostic, + pred: ty::PolyTraitPredicate<'tcx>, + param_env: ty::ParamEnv<'tcx>, + cause: &ObligationCause<'tcx>, + ); + + fn suggest_desugaring_async_fn_in_trait( + &self, + err: &mut Diagnostic, + trait_ref: ty::PolyTraitRef<'tcx>, + ); +} + +fn predicate_constraint(generics: &hir::Generics<'_>, pred: ty::Predicate<'_>) -> (Span, String) { + ( + generics.tail_span_for_predicate_suggestion(), + format!("{} {}", generics.add_where_or_trailing_comma(), pred), + ) +} + +/// Type parameter needs more bounds. The trivial case is `T` `where T: Bound`, but +/// it can also be an `impl Trait` param that needs to be decomposed to a type +/// param for cleaner code. +pub fn suggest_restriction<'tcx>( + tcx: TyCtxt<'tcx>, + item_id: LocalDefId, + hir_generics: &hir::Generics<'tcx>, + msg: &str, + err: &mut Diagnostic, + fn_sig: Option<&hir::FnSig<'_>>, + projection: Option<&ty::AliasTy<'_>>, + trait_pred: ty::PolyTraitPredicate<'tcx>, + // When we are dealing with a trait, `super_traits` will be `Some`: + // Given `trait T: A + B + C {}` + // - ^^^^^^^^^ GenericBounds + // | + // &Ident + super_traits: Option<(&Ident, &hir::GenericBounds<'_>)>, +) { + if hir_generics.where_clause_span.from_expansion() + || hir_generics.where_clause_span.desugaring_kind().is_some() + || projection.is_some_and(|projection| tcx.is_impl_trait_in_trait(projection.def_id)) + { + return; + } + let generics = tcx.generics_of(item_id); + // Given `fn foo(t: impl Trait)` where `Trait` requires assoc type `A`... + if let Some((param, bound_str, fn_sig)) = + fn_sig.zip(projection).and_then(|(sig, p)| match p.self_ty().kind() { + // Shenanigans to get the `Trait` from the `impl Trait`. + ty::Param(param) => { + let param_def = generics.type_param(param, tcx); + if param_def.kind.is_synthetic() { + let bound_str = + param_def.name.as_str().strip_prefix("impl ")?.trim_start().to_string(); + return Some((param_def, bound_str, sig)); + } + None + } + _ => None, + }) + { + let type_param_name = hir_generics.params.next_type_param_name(Some(&bound_str)); + let trait_pred = trait_pred.fold_with(&mut ReplaceImplTraitFolder { + tcx, + param, + replace_ty: ty::ParamTy::new(generics.count() as u32, Symbol::intern(&type_param_name)) + .to_ty(tcx), + }); + if !trait_pred.is_suggestable(tcx, false) { + return; + } + // We know we have an `impl Trait` that doesn't satisfy a required projection. + + // Find all of the occurrences of `impl Trait` for `Trait` in the function arguments' + // types. There should be at least one, but there might be *more* than one. In that + // case we could just ignore it and try to identify which one needs the restriction, + // but instead we choose to suggest replacing all instances of `impl Trait` with `T` + // where `T: Trait`. + let mut ty_spans = vec![]; + for input in fn_sig.decl.inputs { + ReplaceImplTraitVisitor { ty_spans: &mut ty_spans, param_did: param.def_id } + .visit_ty(input); + } + // The type param `T: Trait` we will suggest to introduce. + let type_param = format!("{type_param_name}: {bound_str}"); + + let mut sugg = vec![ + if let Some(span) = hir_generics.span_for_param_suggestion() { + (span, format!(", {type_param}")) + } else { + (hir_generics.span, format!("<{type_param}>")) + }, + // `fn foo(t: impl Trait)` + // ^ suggest `where <T as Trait>::A: Bound` + predicate_constraint(hir_generics, trait_pred.to_predicate(tcx)), + ]; + sugg.extend(ty_spans.into_iter().map(|s| (s, type_param_name.to_string()))); + + // Suggest `fn foo<T: Trait>(t: T) where <T as Trait>::A: Bound`. + // FIXME: once `#![feature(associated_type_bounds)]` is stabilized, we should suggest + // `fn foo(t: impl Trait<A: Bound>)` instead. + err.multipart_suggestion( + "introduce a type parameter with a trait bound instead of using `impl Trait`", + sugg, + Applicability::MaybeIncorrect, + ); + } else { + if !trait_pred.is_suggestable(tcx, false) { + return; + } + // Trivial case: `T` needs an extra bound: `T: Bound`. + let (sp, suggestion) = match ( + hir_generics + .params + .iter() + .find(|p| !matches!(p.kind, hir::GenericParamKind::Type { synthetic: true, .. })), + super_traits, + ) { + (_, None) => predicate_constraint(hir_generics, trait_pred.to_predicate(tcx)), + (None, Some((ident, []))) => ( + ident.span.shrink_to_hi(), + format!(": {}", trait_pred.print_modifiers_and_trait_path()), + ), + (_, Some((_, [.., bounds]))) => ( + bounds.span().shrink_to_hi(), + format!(" + {}", trait_pred.print_modifiers_and_trait_path()), + ), + (Some(_), Some((_, []))) => ( + hir_generics.span.shrink_to_hi(), + format!(": {}", trait_pred.print_modifiers_and_trait_path()), + ), + }; + + err.span_suggestion_verbose( + sp, + format!("consider further restricting {msg}"), + suggestion, + Applicability::MachineApplicable, + ); + } +} + +impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { + fn suggest_restricting_param_bound( + &self, + err: &mut Diagnostic, + trait_pred: ty::PolyTraitPredicate<'tcx>, + associated_ty: Option<(&'static str, Ty<'tcx>)>, + mut body_id: LocalDefId, + ) { + if trait_pred.skip_binder().polarity == ty::ImplPolarity::Negative { + return; + } + + let trait_pred = self.resolve_numeric_literals_with_default(trait_pred); + + let self_ty = trait_pred.skip_binder().self_ty(); + let (param_ty, projection) = match self_ty.kind() { + ty::Param(_) => (true, None), + ty::Alias(ty::Projection, projection) => (false, Some(projection)), + _ => (false, None), + }; + + // FIXME: Add check for trait bound that is already present, particularly `?Sized` so we + // don't suggest `T: Sized + ?Sized`. + while let Some(node) = self.tcx.opt_hir_node_by_def_id(body_id) { + match node { + hir::Node::Item(hir::Item { + ident, + kind: hir::ItemKind::Trait(_, _, generics, bounds, _), + .. + }) if self_ty == self.tcx.types.self_param => { + assert!(param_ty); + // Restricting `Self` for a single method. + suggest_restriction( + self.tcx, + body_id, + generics, + "`Self`", + err, + None, + projection, + trait_pred, + Some((ident, bounds)), + ); + return; + } + + hir::Node::TraitItem(hir::TraitItem { + generics, + kind: hir::TraitItemKind::Fn(..), + .. + }) if self_ty == self.tcx.types.self_param => { + assert!(param_ty); + // Restricting `Self` for a single method. + suggest_restriction( + self.tcx, body_id, generics, "`Self`", err, None, projection, trait_pred, + None, + ); + return; + } + + hir::Node::TraitItem(hir::TraitItem { + generics, + kind: hir::TraitItemKind::Fn(fn_sig, ..), + .. + }) + | hir::Node::ImplItem(hir::ImplItem { + generics, + kind: hir::ImplItemKind::Fn(fn_sig, ..), + .. + }) + | hir::Node::Item(hir::Item { + kind: hir::ItemKind::Fn(fn_sig, generics, _), .. + }) if projection.is_some() => { + // Missing restriction on associated type of type parameter (unmet projection). + suggest_restriction( + self.tcx, + body_id, + generics, + "the associated type", + err, + Some(fn_sig), + projection, + trait_pred, + None, + ); + return; + } + hir::Node::Item(hir::Item { + kind: + hir::ItemKind::Trait(_, _, generics, ..) + | hir::ItemKind::Impl(hir::Impl { generics, .. }), + .. + }) if projection.is_some() => { + // Missing restriction on associated type of type parameter (unmet projection). + suggest_restriction( + self.tcx, + body_id, + generics, + "the associated type", + err, + None, + projection, + trait_pred, + None, + ); + return; + } + + hir::Node::Item(hir::Item { + kind: + hir::ItemKind::Struct(_, generics) + | hir::ItemKind::Enum(_, generics) + | hir::ItemKind::Union(_, generics) + | hir::ItemKind::Trait(_, _, generics, ..) + | hir::ItemKind::Impl(hir::Impl { generics, .. }) + | hir::ItemKind::Fn(_, generics, _) + | hir::ItemKind::TyAlias(_, generics) + | hir::ItemKind::Const(_, generics, _) + | hir::ItemKind::TraitAlias(generics, _) + | hir::ItemKind::OpaqueTy(hir::OpaqueTy { generics, .. }), + .. + }) + | hir::Node::TraitItem(hir::TraitItem { generics, .. }) + | hir::Node::ImplItem(hir::ImplItem { generics, .. }) + if param_ty => + { + // We skip the 0'th subst (self) because we do not want + // to consider the predicate as not suggestible if the + // self type is an arg position `impl Trait` -- instead, + // we handle that by adding ` + Bound` below. + // FIXME(compiler-errors): It would be nice to do the same + // this that we do in `suggest_restriction` and pull the + // `impl Trait` into a new generic if it shows up somewhere + // else in the predicate. + if !trait_pred.skip_binder().trait_ref.args[1..] + .iter() + .all(|g| g.is_suggestable(self.tcx, false)) + { + return; + } + // Missing generic type parameter bound. + let param_name = self_ty.to_string(); + let mut constraint = with_no_trimmed_paths!( + trait_pred.print_modifiers_and_trait_path().to_string() + ); + + if let Some((name, term)) = associated_ty { + // FIXME: this case overlaps with code in TyCtxt::note_and_explain_type_err. + // That should be extracted into a helper function. + if constraint.ends_with('>') { + constraint = format!( + "{}, {} = {}>", + &constraint[..constraint.len() - 1], + name, + term + ); + } else { + constraint.push_str(&format!("<{name} = {term}>")); + } + } + + if suggest_constraining_type_param( + self.tcx, + generics, + err, + ¶m_name, + &constraint, + Some(trait_pred.def_id()), + None, + ) { + return; + } + } + + hir::Node::Item(hir::Item { + kind: + hir::ItemKind::Struct(_, generics) + | hir::ItemKind::Enum(_, generics) + | hir::ItemKind::Union(_, generics) + | hir::ItemKind::Trait(_, _, generics, ..) + | hir::ItemKind::Impl(hir::Impl { generics, .. }) + | hir::ItemKind::Fn(_, generics, _) + | hir::ItemKind::TyAlias(_, generics) + | hir::ItemKind::Const(_, generics, _) + | hir::ItemKind::TraitAlias(generics, _) + | hir::ItemKind::OpaqueTy(hir::OpaqueTy { generics, .. }), + .. + }) if !param_ty => { + // Missing generic type parameter bound. + if suggest_arbitrary_trait_bound( + self.tcx, + generics, + err, + trait_pred, + associated_ty, + ) { + return; + } + } + hir::Node::Crate(..) => return, + + _ => {} + } + body_id = self.tcx.local_parent(body_id); + } + } + + /// When after several dereferencing, the reference satisfies the trait + /// binding. This function provides dereference suggestion for this + /// specific situation. + fn suggest_dereferences( + &self, + obligation: &PredicateObligation<'tcx>, + err: &mut Diagnostic, + trait_pred: ty::PolyTraitPredicate<'tcx>, + ) -> bool { + let mut code = obligation.cause.code(); + if let ObligationCauseCode::FunctionArgumentObligation { arg_hir_id, call_hir_id, .. } = + code + && let Some(typeck_results) = &self.typeck_results + && let hir::Node::Expr(expr) = self.tcx.hir_node(*arg_hir_id) + && let Some(arg_ty) = typeck_results.expr_ty_adjusted_opt(expr) + { + // Suggest dereferencing the argument to a function/method call if possible + + let mut real_trait_pred = trait_pred; + while let Some((parent_code, parent_trait_pred)) = code.parent() { + code = parent_code; + if let Some(parent_trait_pred) = parent_trait_pred { + real_trait_pred = parent_trait_pred; + } + + // We `instantiate_bound_regions_with_erased` here because `make_subregion` does not handle + // `ReBound`, and we don't particularly care about the regions. + let real_ty = + self.tcx.instantiate_bound_regions_with_erased(real_trait_pred.self_ty()); + + if self.can_eq(obligation.param_env, real_ty, arg_ty) + && let ty::Ref(region, base_ty, mutbl) = *real_ty.kind() + { + let autoderef = (self.autoderef_steps)(base_ty); + if let Some(steps) = + autoderef.into_iter().enumerate().find_map(|(steps, (ty, obligations))| { + // Re-add the `&` + let ty = Ty::new_ref(self.tcx, region, TypeAndMut { ty, mutbl }); + + // Remapping bound vars here + let real_trait_pred_and_ty = real_trait_pred + .map_bound(|inner_trait_pred| (inner_trait_pred, ty)); + let obligation = self.mk_trait_obligation_with_new_self_ty( + obligation.param_env, + real_trait_pred_and_ty, + ); + let may_hold = obligations + .iter() + .chain([&obligation]) + .all(|obligation| self.predicate_may_hold(obligation)) + .then_some(steps); + + may_hold + }) + { + if steps > 0 { + // Don't care about `&mut` because `DerefMut` is used less + // often and user will not expect that an autoderef happens. + if let Some(hir::Node::Expr(hir::Expr { + kind: + hir::ExprKind::AddrOf( + hir::BorrowKind::Ref, + hir::Mutability::Not, + expr, + ), + .. + })) = self.tcx.opt_hir_node(*arg_hir_id) + { + let derefs = "*".repeat(steps); + err.span_suggestion_verbose( + expr.span.shrink_to_lo(), + "consider dereferencing here", + derefs, + Applicability::MachineApplicable, + ); + return true; + } + } + } else if real_trait_pred != trait_pred { + // This branch addresses #87437. + + let span = obligation.cause.span; + // Remapping bound vars here + let real_trait_pred_and_base_ty = real_trait_pred + .map_bound(|inner_trait_pred| (inner_trait_pred, base_ty)); + let obligation = self.mk_trait_obligation_with_new_self_ty( + obligation.param_env, + real_trait_pred_and_base_ty, + ); + let sized_obligation = Obligation::new( + self.tcx, + obligation.cause.clone(), + obligation.param_env, + ty::TraitRef::from_lang_item( + self.tcx, + hir::LangItem::Sized, + obligation.cause.span, + [base_ty], + ), + ); + if self.predicate_may_hold(&obligation) + && self.predicate_must_hold_modulo_regions(&sized_obligation) + { + let call_node = self.tcx.hir_node(*call_hir_id); + let msg = "consider dereferencing here"; + let is_receiver = matches!( + call_node, + Node::Expr(hir::Expr { + kind: hir::ExprKind::MethodCall(_, receiver_expr, ..), + .. + }) + if receiver_expr.hir_id == *arg_hir_id + ); + if is_receiver { + err.multipart_suggestion_verbose( + msg, + vec![ + (span.shrink_to_lo(), "(*".to_string()), + (span.shrink_to_hi(), ")".to_string()), + ], + Applicability::MachineApplicable, + ) + } else { + err.span_suggestion_verbose( + span.shrink_to_lo(), + msg, + '*', + Applicability::MachineApplicable, + ) + }; + return true; + } + } + } + } + } else if let ( + ObligationCauseCode::BinOp { lhs_hir_id, rhs_hir_id: Some(rhs_hir_id), .. }, + predicate, + ) = code.peel_derives_with_predicate() + && let Some(typeck_results) = &self.typeck_results + && let hir::Node::Expr(lhs) = self.tcx.hir_node(*lhs_hir_id) + && let hir::Node::Expr(rhs) = self.tcx.hir_node(*rhs_hir_id) + && let Some(rhs_ty) = typeck_results.expr_ty_opt(rhs) + { + // Suggest dereferencing the LHS, RHS, or both terms of a binop if possible + + let trait_pred = predicate.unwrap_or(trait_pred); + let lhs_ty = self.tcx.instantiate_bound_regions_with_erased(trait_pred.self_ty()); + let lhs_autoderef = (self.autoderef_steps)(lhs_ty); + let rhs_autoderef = (self.autoderef_steps)(rhs_ty); + let first_lhs = lhs_autoderef.first().unwrap().clone(); + let first_rhs = rhs_autoderef.first().unwrap().clone(); + let mut autoderefs = lhs_autoderef + .into_iter() + .enumerate() + .rev() + .zip_longest(rhs_autoderef.into_iter().enumerate().rev()) + .map(|t| match t { + EitherOrBoth::Both(a, b) => (a, b), + EitherOrBoth::Left(a) => (a, (0, first_rhs.clone())), + EitherOrBoth::Right(b) => ((0, first_lhs.clone()), b), + }) + .rev(); + if let Some((lsteps, rsteps)) = + autoderefs.find_map(|((lsteps, (l_ty, _)), (rsteps, (r_ty, _)))| { + // Create a new predicate with the dereferenced LHS and RHS + // We simultaneously dereference both sides rather than doing them + // one at a time to account for cases such as &Box<T> == &&T + let trait_pred_and_ty = trait_pred.map_bound(|inner| { + ( + ty::TraitPredicate { + trait_ref: ty::TraitRef::new( + self.tcx, + inner.trait_ref.def_id, + self.tcx.mk_args( + &[&[l_ty.into(), r_ty.into()], &inner.trait_ref.args[2..]] + .concat(), + ), + ), + ..inner + }, + l_ty, + ) + }); + let obligation = self.mk_trait_obligation_with_new_self_ty( + obligation.param_env, + trait_pred_and_ty, + ); + self.predicate_may_hold(&obligation).then_some(match (lsteps, rsteps) { + (_, 0) => (Some(lsteps), None), + (0, _) => (None, Some(rsteps)), + _ => (Some(lsteps), Some(rsteps)), + }) + }) + { + let make_sugg = |mut expr: &Expr<'_>, mut steps| { + let mut prefix_span = expr.span.shrink_to_lo(); + let mut msg = "consider dereferencing here"; + if let hir::ExprKind::AddrOf(_, _, inner) = expr.kind { + msg = "consider removing the borrow and dereferencing instead"; + if let hir::ExprKind::AddrOf(..) = inner.kind { + msg = "consider removing the borrows and dereferencing instead"; + } + } + while let hir::ExprKind::AddrOf(_, _, inner) = expr.kind + && steps > 0 + { + prefix_span = prefix_span.with_hi(inner.span.lo()); + expr = inner; + steps -= 1; + } + // Empty suggestions with empty spans ICE with debug assertions + if steps == 0 { + return ( + msg.trim_end_matches(" and dereferencing instead"), + vec![(prefix_span, String::new())], + ); + } + let derefs = "*".repeat(steps); + let needs_parens = steps > 0 + && match expr.kind { + hir::ExprKind::Cast(_, _) | hir::ExprKind::Binary(_, _, _) => true, + _ if is_range_literal(expr) => true, + _ => false, + }; + let mut suggestion = if needs_parens { + vec![ + ( + expr.span.with_lo(prefix_span.hi()).shrink_to_lo(), + format!("{derefs}("), + ), + (expr.span.shrink_to_hi(), ")".to_string()), + ] + } else { + vec![( + expr.span.with_lo(prefix_span.hi()).shrink_to_lo(), + format!("{derefs}"), + )] + }; + // Empty suggestions with empty spans ICE with debug assertions + if !prefix_span.is_empty() { + suggestion.push((prefix_span, String::new())); + } + (msg, suggestion) + }; + + if let Some(lsteps) = lsteps + && let Some(rsteps) = rsteps + && lsteps > 0 + && rsteps > 0 + { + let mut suggestion = make_sugg(lhs, lsteps).1; + suggestion.append(&mut make_sugg(rhs, rsteps).1); + err.multipart_suggestion_verbose( + "consider dereferencing both sides of the expression", + suggestion, + Applicability::MachineApplicable, + ); + return true; + } else if let Some(lsteps) = lsteps + && lsteps > 0 + { + let (msg, suggestion) = make_sugg(lhs, lsteps); + err.multipart_suggestion_verbose( + msg, + suggestion, + Applicability::MachineApplicable, + ); + return true; + } else if let Some(rsteps) = rsteps + && rsteps > 0 + { + let (msg, suggestion) = make_sugg(rhs, rsteps); + err.multipart_suggestion_verbose( + msg, + suggestion, + Applicability::MachineApplicable, + ); + return true; + } + } + } + false + } + + /// 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: 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. + match &kind { + hir::PatKind::Binding(hir::BindingAnnotation::NONE, _, ident, None) => { + Some(ident.name) + } + _ => { + err.note(msg); + None + } + } + }; + + let hir = self.tcx.hir(); + let hir_id = self.tcx.local_def_id_to_hir_id(def_id.as_local()?); + match hir.find_parent(hir_id) { + Some(hir::Node::Stmt(hir::Stmt { kind: hir::StmtKind::Local(local), .. })) => { + get_name(err, &local.pat.kind) + } + // Different to previous arm because one is `&hir::Local` and the other + // is `P<hir::Local>`. + Some(hir::Node::Local(local)) => get_name(err, &local.pat.kind), + _ => None, + } + } + + /// We tried to apply the bound to an `fn` or closure. Check whether calling it would + /// evaluate to a type that *would* satisfy the trait binding. If it would, suggest calling + /// it: `bar(foo)` → `bar(foo())`. This case is *very* likely to be hit if `foo` is `async`. + fn suggest_fn_call( + &self, + obligation: &PredicateObligation<'tcx>, + err: &mut Diagnostic, + trait_pred: ty::PolyTraitPredicate<'tcx>, + ) -> bool { + // It doesn't make sense to make this suggestion outside of typeck... + // (also autoderef will ICE...) + if self.typeck_results.is_none() { + return false; + } + + 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 + return false; + } + + let self_ty = self.instantiate_binder_with_fresh_vars( + DUMMY_SP, + BoundRegionConversionTime::FnCall, + trait_pred.self_ty(), + ); + + let Some((def_id_or_name, output, inputs)) = + self.extract_callable_info(obligation.cause.body_id, obligation.param_env, self_ty) + else { + return false; + }; + + // Remapping bound vars here + let trait_pred_and_self = trait_pred.map_bound(|trait_pred| (trait_pred, output)); + + let new_obligation = + self.mk_trait_obligation_with_new_self_ty(obligation.param_env, trait_pred_and_self); + if !self.predicate_must_hold_modulo_regions(&new_obligation) { + return false; + } + + // Get the name of the callable and the arguments to be used in the suggestion. + let hir = self.tcx.hir(); + + let msg = match def_id_or_name { + DefIdOrName::DefId(def_id) => match self.tcx.def_kind(def_id) { + DefKind::Ctor(CtorOf::Struct, _) => { + Cow::from("use parentheses to construct this tuple struct") + } + DefKind::Ctor(CtorOf::Variant, _) => { + Cow::from("use parentheses to construct this tuple variant") + } + kind => Cow::from(format!( + "use parentheses to call this {}", + self.tcx.def_kind_descr(kind, def_id) + )), + }, + DefIdOrName::Name(name) => Cow::from(format!("use parentheses to call this {name}")), + }; + + let args = inputs + .into_iter() + .map(|ty| { + if ty.is_suggestable(self.tcx, false) { + format!("/* {ty} */") + } else { + "/* value */".to_string() + } + }) + .collect::<Vec<_>>() + .join(", "); + + if matches!(obligation.cause.code(), ObligationCauseCode::FunctionArgumentObligation { .. }) + && obligation.cause.span.can_be_used_for_suggestions() + { + // When the obligation error has been ensured to have been caused by + // an argument, the `obligation.cause.span` points at the expression + // of the argument, so we can provide a suggestion. Otherwise, we give + // a more general note. + err.span_suggestion_verbose( + obligation.cause.span.shrink_to_hi(), + msg, + format!("({args})"), + Applicability::HasPlaceholders, + ); + } else if let DefIdOrName::DefId(def_id) = def_id_or_name { + let name = match hir.get_if_local(def_id) { + Some(hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::Closure(hir::Closure { fn_decl_span, .. }), + .. + })) => { + err.span_label(*fn_decl_span, "consider calling this closure"); + let Some(name) = self.get_closure_name(def_id, err, msg.clone()) else { + return false; + }; + name.to_string() + } + Some(hir::Node::Item(hir::Item { ident, kind: hir::ItemKind::Fn(..), .. })) => { + err.span_label(ident.span, "consider calling this function"); + ident.to_string() + } + Some(hir::Node::Ctor(..)) => { + let name = self.tcx.def_path_str(def_id); + err.span_label( + self.tcx.def_span(def_id), + format!("consider calling the constructor for `{name}`"), + ); + name + } + _ => return false, + }; + err.help(format!("{msg}: `{name}({args})`")); + } + true + } + + fn check_for_binding_assigned_block_without_tail_expression( + &self, + obligation: &PredicateObligation<'tcx>, + err: &mut Diagnostic, + trait_pred: ty::PolyTraitPredicate<'tcx>, + ) { + let mut span = obligation.cause.span; + while span.from_expansion() { + // Remove all the desugaring and macro contexts. + span.remove_mark(); + } + let mut expr_finder = FindExprBySpan::new(span); + let Some(body_id) = self.tcx.hir().maybe_body_owned_by(obligation.cause.body_id) else { + return; + }; + let body = self.tcx.hir().body(body_id); + expr_finder.visit_expr(body.value); + let Some(expr) = expr_finder.result else { + return; + }; + let Some(typeck) = &self.typeck_results else { + return; + }; + let Some(ty) = typeck.expr_ty_adjusted_opt(expr) else { + return; + }; + if !ty.is_unit() { + return; + }; + let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind else { + return; + }; + let Res::Local(hir_id) = path.res else { + return; + }; + let Some(hir::Node::Pat(pat)) = self.tcx.opt_hir_node(hir_id) else { + return; + }; + let Some(hir::Node::Local(hir::Local { ty: None, init: Some(init), .. })) = + self.tcx.hir().find_parent(pat.hir_id) + else { + return; + }; + let hir::ExprKind::Block(block, None) = init.kind else { + return; + }; + if block.expr.is_some() { + return; + } + let [.., stmt] = block.stmts else { + err.span_label(block.span, "this empty block is missing a tail expression"); + return; + }; + let hir::StmtKind::Semi(tail_expr) = stmt.kind else { + return; + }; + let Some(ty) = typeck.expr_ty_opt(tail_expr) else { + err.span_label(block.span, "this block is missing a tail expression"); + return; + }; + let ty = self.resolve_numeric_literals_with_default(self.resolve_vars_if_possible(ty)); + let trait_pred_and_self = trait_pred.map_bound(|trait_pred| (trait_pred, ty)); + + let new_obligation = + self.mk_trait_obligation_with_new_self_ty(obligation.param_env, trait_pred_and_self); + if self.predicate_must_hold_modulo_regions(&new_obligation) { + err.span_suggestion_short( + stmt.span.with_lo(tail_expr.span.hi()), + "remove this semicolon", + "", + Applicability::MachineApplicable, + ); + } else { + err.span_label(block.span, "this block is missing a tail expression"); + } + } + + fn suggest_add_clone_to_arg( + &self, + obligation: &PredicateObligation<'tcx>, + err: &mut Diagnostic, + trait_pred: ty::PolyTraitPredicate<'tcx>, + ) -> bool { + let self_ty = self.resolve_vars_if_possible(trait_pred.self_ty()); + let ty = self.instantiate_binder_with_placeholders(self_ty); + let Some(generics) = self.tcx.hir().get_generics(obligation.cause.body_id) else { + return false; + }; + let ty::Ref(_, inner_ty, hir::Mutability::Not) = ty.kind() else { return false }; + let ty::Param(param) = inner_ty.kind() else { return false }; + let ObligationCauseCode::FunctionArgumentObligation { arg_hir_id, .. } = + obligation.cause.code() + else { + return false; + }; + let arg_node = self.tcx.hir_node(*arg_hir_id); + let Node::Expr(Expr { kind: hir::ExprKind::Path(_), .. }) = arg_node else { return false }; + + let clone_trait = self.tcx.require_lang_item(LangItem::Clone, None); + let has_clone = |ty| { + self.type_implements_trait(clone_trait, [ty], obligation.param_env) + .must_apply_modulo_regions() + }; + + let new_obligation = self.mk_trait_obligation_with_new_self_ty( + obligation.param_env, + trait_pred.map_bound(|trait_pred| (trait_pred, *inner_ty)), + ); + + if self.predicate_may_hold(&new_obligation) && has_clone(ty) { + if !has_clone(param.to_ty(self.tcx)) { + suggest_constraining_type_param( + self.tcx, + generics, + err, + param.name.as_str(), + "Clone", + Some(clone_trait), + None, + ); + } + err.span_suggestion_verbose( + obligation.cause.span.shrink_to_hi(), + "consider using clone here", + ".clone()".to_string(), + Applicability::MaybeIncorrect, + ); + return true; + } + false + } + + /// Extracts information about a callable type for diagnostics. This is a + /// heuristic -- it doesn't necessarily mean that a type is always callable, + /// because the callable type must also be well-formed to be called. + fn extract_callable_info( + &self, + body_id: LocalDefId, + param_env: ty::ParamEnv<'tcx>, + found: Ty<'tcx>, + ) -> Option<(DefIdOrName, Ty<'tcx>, Vec<Ty<'tcx>>)> { + // Autoderef is useful here because sometimes we box callables, etc. + let Some((def_id_or_name, output, inputs)) = + (self.autoderef_steps)(found).into_iter().find_map(|(found, _)| { + match *found.kind() { + ty::FnPtr(fn_sig) => Some(( + DefIdOrName::Name("function pointer"), + fn_sig.output(), + fn_sig.inputs(), + )), + ty::FnDef(def_id, _) => { + let fn_sig = found.fn_sig(self.tcx); + Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs())) + } + ty::Closure(def_id, args) => { + let fn_sig = args.as_closure().sig(); + Some(( + DefIdOrName::DefId(def_id), + fn_sig.output(), + fn_sig.inputs().map_bound(|inputs| &inputs[1..]), + )) + } + ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => { + self.tcx.item_bounds(def_id).instantiate(self.tcx, args).iter().find_map( + |pred| { + 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 args[1] + && let ty::Tuple(args) = proj.projection_ty.args.type_at(1).kind() + { + Some(( + DefIdOrName::DefId(def_id), + pred.kind().rebind(proj.term.ty().unwrap()), + pred.kind().rebind(args.as_slice()), + )) + } else { + None + } + }, + ) + } + ty::Dynamic(data, _, ty::Dyn) => { + data.iter().find_map(|pred| { + if let ty::ExistentialPredicate::Projection(proj) = pred.skip_binder() + && Some(proj.def_id) == self.tcx.lang_items().fn_once_output() + // for existential projection, args are shifted over by 1 + && let ty::Tuple(args) = proj.args.type_at(0).kind() + { + Some(( + DefIdOrName::Name("trait object"), + pred.rebind(proj.term.ty().unwrap()), + pred.rebind(args.as_slice()), + )) + } else { + None + } + }) + } + ty::Param(param) => { + let generics = self.tcx.generics_of(body_id); + let name = if generics.count() > param.index as usize + && let def = generics.param_at(param.index as usize, self.tcx) + && matches!(def.kind, ty::GenericParamDefKind::Type { .. }) + && def.name == param.name + { + DefIdOrName::DefId(def.def_id) + } else { + DefIdOrName::Name("type parameter") + }; + param_env.caller_bounds().iter().find_map(|pred| { + 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 args[1] + && let ty::Tuple(args) = proj.projection_ty.args.type_at(1).kind() + { + Some(( + name, + pred.kind().rebind(proj.term.ty().unwrap()), + pred.kind().rebind(args.as_slice()), + )) + } else { + None + } + }) + } + _ => None, + } + }) + else { + return None; + }; + + let output = self.instantiate_binder_with_fresh_vars( + DUMMY_SP, + BoundRegionConversionTime::FnCall, + output, + ); + let inputs = inputs + .skip_binder() + .iter() + .map(|ty| { + self.instantiate_binder_with_fresh_vars( + DUMMY_SP, + BoundRegionConversionTime::FnCall, + inputs.rebind(*ty), + ) + }) + .collect(); + + // We don't want to register any extra obligations, which should be + // implied by wf, but also because that would possibly result in + // erroneous errors later on. + let InferOk { value: output, obligations: _ } = + self.at(&ObligationCause::dummy(), param_env).normalize(output); + + if output.is_ty_var() { None } else { Some((def_id_or_name, output, inputs)) } + } + + fn suggest_add_reference_to_arg( + &self, + obligation: &PredicateObligation<'tcx>, + err: &mut Diagnostic, + poly_trait_pred: ty::PolyTraitPredicate<'tcx>, + has_custom_message: bool, + ) -> bool { + let span = obligation.cause.span; + + let code = if let ObligationCauseCode::FunctionArgumentObligation { parent_code, .. } = + obligation.cause.code() + { + parent_code + } else if let ObligationCauseCode::ItemObligation(_) + | ObligationCauseCode::ExprItemObligation(..) = obligation.cause.code() + { + obligation.cause.code() + } else if let ExpnKind::Desugaring(DesugaringKind::ForLoop) = + span.ctxt().outer_expn_data().kind + { + obligation.cause.code() + } else { + return false; + }; + + // List of traits for which it would be nonsensical to suggest borrowing. + // For instance, immutable references are always Copy, so suggesting to + // borrow would always succeed, but it's probably not what the user wanted. + let mut never_suggest_borrow: Vec<_> = + [LangItem::Copy, LangItem::Clone, LangItem::Unpin, LangItem::Sized] + .iter() + .filter_map(|lang_item| self.tcx.lang_items().get(*lang_item)) + .collect(); + + if let Some(def_id) = self.tcx.get_diagnostic_item(sym::Send) { + never_suggest_borrow.push(def_id); + } + + let param_env = obligation.param_env; + + // Try to apply the original trait binding obligation by borrowing. + let mut try_borrowing = |old_pred: ty::PolyTraitPredicate<'tcx>, + blacklist: &[DefId]| + -> bool { + if blacklist.contains(&old_pred.def_id()) { + return false; + } + // We map bounds to `&T` and `&mut T` + let trait_pred_and_imm_ref = old_pred.map_bound(|trait_pred| { + ( + trait_pred, + 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, + Ty::new_mut_ref(self.tcx, self.tcx.lifetimes.re_static, trait_pred.self_ty()), + ) + }); + + let mk_result = |trait_pred_and_new_ty| { + let obligation = + self.mk_trait_obligation_with_new_self_ty(param_env, trait_pred_and_new_ty); + self.predicate_must_hold_modulo_regions(&obligation) + }; + let imm_ref_self_ty_satisfies_pred = mk_result(trait_pred_and_imm_ref); + let mut_ref_self_ty_satisfies_pred = mk_result(trait_pred_and_mut_ref); + + let (ref_inner_ty_satisfies_pred, ref_inner_ty_mut) = + if let ObligationCauseCode::ItemObligation(_) + | ObligationCauseCode::ExprItemObligation(..) = obligation.cause.code() + && let ty::Ref(_, ty, mutability) = old_pred.self_ty().skip_binder().kind() + { + ( + mk_result(old_pred.map_bound(|trait_pred| (trait_pred, *ty))), + mutability.is_mut(), + ) + } else { + (false, false) + }; + + if imm_ref_self_ty_satisfies_pred + || mut_ref_self_ty_satisfies_pred + || ref_inner_ty_satisfies_pred + { + if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { + // We don't want a borrowing suggestion on the fields in structs, + // ``` + // struct Foo { + // the_foos: Vec<Foo> + // } + // ``` + if !matches!( + span.ctxt().outer_expn_data().kind, + ExpnKind::Root | ExpnKind::Desugaring(DesugaringKind::ForLoop) + ) { + return false; + } + if snippet.starts_with('&') { + // This is already a literal borrow and the obligation is failing + // somewhere else in the obligation chain. Do not suggest non-sense. + return false; + } + // We have a very specific type of error, where just borrowing this argument + // might solve the problem. In cases like this, the important part is the + // original type obligation, not the last one that failed, which is arbitrary. + // Because of this, we modify the error to refer to the original obligation and + // return early in the caller. + + let msg = format!("the trait bound `{old_pred}` is not satisfied"); + if has_custom_message { + err.note(msg); + } else { + err.messages = + vec![(rustc_errors::DiagnosticMessage::from(msg), Style::NoStyle)]; + } + let mut file = None; + err.span_label( + span, + format!( + "the trait `{}` is not implemented for `{}`", + old_pred.print_modifiers_and_trait_path(), + self.tcx.short_ty_string(old_pred.self_ty().skip_binder(), &mut file), + ), + ); + if let Some(file) = file { + err.note(format!( + "the full type name has been written to '{}'", + file.display() + )); + } + + if imm_ref_self_ty_satisfies_pred && mut_ref_self_ty_satisfies_pred { + err.span_suggestions( + span.shrink_to_lo(), + "consider borrowing here", + ["&".to_string(), "&mut ".to_string()], + Applicability::MaybeIncorrect, + ); + } else { + let is_mut = mut_ref_self_ty_satisfies_pred || ref_inner_ty_mut; + let sugg_prefix = format!("&{}", if is_mut { "mut " } else { "" }); + let sugg_msg = format!( + "consider{} borrowing here", + if is_mut { " mutably" } else { "" } + ); + + // Issue #109436, we need to add parentheses properly for method calls + // for example, `foo.into()` should be `(&foo).into()` + if let Some(_) = + self.tcx.sess.source_map().span_look_ahead(span, ".", Some(50)) + { + err.multipart_suggestion_verbose( + sugg_msg, + vec![ + (span.shrink_to_lo(), format!("({sugg_prefix}")), + (span.shrink_to_hi(), ")".to_string()), + ], + Applicability::MaybeIncorrect, + ); + return true; + } + + // Issue #104961, we need to add parentheses properly for compound expressions + // for example, `x.starts_with("hi".to_string() + "you")` + // should be `x.starts_with(&("hi".to_string() + "you"))` + let Some(body_id) = + self.tcx.hir().maybe_body_owned_by(obligation.cause.body_id) + else { + return false; + }; + let body = self.tcx.hir().body(body_id); + let mut expr_finder = FindExprBySpan::new(span); + expr_finder.visit_expr(body.value); + let Some(expr) = expr_finder.result else { + return false; + }; + let needs_parens = match expr.kind { + // parenthesize if needed (Issue #46756) + hir::ExprKind::Cast(_, _) | hir::ExprKind::Binary(_, _, _) => true, + // parenthesize borrows of range literals (Issue #54505) + _ if is_range_literal(expr) => true, + _ => false, + }; + + let span = if needs_parens { span } else { span.shrink_to_lo() }; + let suggestions = if !needs_parens { + vec![(span.shrink_to_lo(), sugg_prefix)] + } else { + vec![ + (span.shrink_to_lo(), format!("{sugg_prefix}(")), + (span.shrink_to_hi(), ")".to_string()), + ] + }; + err.multipart_suggestion_verbose( + sugg_msg, + suggestions, + Applicability::MaybeIncorrect, + ); + } + return true; + } + } + return false; + }; + + if let ObligationCauseCode::ImplDerivedObligation(cause) = &*code { + try_borrowing(cause.derived.parent_trait_pred, &[]) + } else if let ObligationCauseCode::BindingObligation(_, _) + | ObligationCauseCode::ItemObligation(_) + | ObligationCauseCode::ExprItemObligation(..) + | ObligationCauseCode::ExprBindingObligation(..) = code + { + try_borrowing(poly_trait_pred, &never_suggest_borrow) + } else { + false + } + } + + // Suggest borrowing the type + fn suggest_borrowing_for_object_cast( + &self, + err: &mut Diagnostic, + obligation: &PredicateObligation<'tcx>, + self_ty: Ty<'tcx>, + target_ty: Ty<'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 = 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( + &obligation.with(self.tcx, predicate.with_self_ty(self.tcx, self_ref_ty)), + ) { + return; + } + } + + err.span_suggestion( + obligation.cause.span.shrink_to_lo(), + format!( + "consider borrowing the value, since `&{self_ty}` can be coerced into `{target_ty}`" + ), + "&", + Applicability::MaybeIncorrect, + ); + } + + /// Whenever references are used by mistake, like `for (i, e) in &vec.iter().enumerate()`, + /// suggest removing these references until we reach a type that implements the trait. + fn suggest_remove_reference( + &self, + obligation: &PredicateObligation<'tcx>, + err: &mut Diagnostic, + trait_pred: ty::PolyTraitPredicate<'tcx>, + ) -> bool { + let mut span = obligation.cause.span; + let mut trait_pred = trait_pred; + let mut code = obligation.cause.code(); + while let Some((c, Some(parent_trait_pred))) = code.parent() { + // We want the root obligation, in order to detect properly handle + // `for _ in &mut &mut vec![] {}`. + code = c; + trait_pred = parent_trait_pred; + } + while span.desugaring_kind().is_some() { + // Remove all the hir desugaring contexts while maintaining the macro contexts. + span.remove_mark(); + } + let mut expr_finder = super::FindExprBySpan::new(span); + let Some(body_id) = self.tcx.hir().maybe_body_owned_by(obligation.cause.body_id) else { + return false; + }; + let body = self.tcx.hir().body(body_id); + expr_finder.visit_expr(body.value); + let mut maybe_suggest = |suggested_ty, count, suggestions| { + // Remapping bound vars here + let trait_pred_and_suggested_ty = + trait_pred.map_bound(|trait_pred| (trait_pred, suggested_ty)); + + let new_obligation = self.mk_trait_obligation_with_new_self_ty( + obligation.param_env, + trait_pred_and_suggested_ty, + ); + + if self.predicate_may_hold(&new_obligation) { + let msg = if count == 1 { + "consider removing the leading `&`-reference".to_string() + } else { + format!("consider removing {count} leading `&`-references") + }; + + err.multipart_suggestion_verbose( + msg, + suggestions, + Applicability::MachineApplicable, + ); + true + } else { + false + } + }; + + // Maybe suggest removal of borrows from types in type parameters, like in + // `src/test/ui/not-panic/not-panic-safe.rs`. + let mut count = 0; + let mut suggestions = vec![]; + // Skipping binder here, remapping below + let mut suggested_ty = trait_pred.self_ty().skip_binder(); + if let Some(mut hir_ty) = expr_finder.ty_result { + while let hir::TyKind::Ref(_, mut_ty) = &hir_ty.kind { + count += 1; + let span = hir_ty.span.until(mut_ty.ty.span); + suggestions.push((span, String::new())); + + let ty::Ref(_, inner_ty, _) = suggested_ty.kind() else { + break; + }; + suggested_ty = *inner_ty; + + hir_ty = mut_ty.ty; + + if maybe_suggest(suggested_ty, count, suggestions.clone()) { + return true; + } + } + } + + // Maybe suggest removal of borrows from expressions, like in `for i in &&&foo {}`. + let Some(mut expr) = expr_finder.result else { + return false; + }; + let mut count = 0; + let mut suggestions = vec![]; + // Skipping binder here, remapping below + let mut suggested_ty = trait_pred.self_ty().skip_binder(); + 'outer: loop { + while let hir::ExprKind::AddrOf(_, _, borrowed) = expr.kind { + count += 1; + let span = if expr.span.eq_ctxt(borrowed.span) { + expr.span.until(borrowed.span) + } else { + expr.span.with_hi(expr.span.lo() + BytePos(1)) + }; + suggestions.push((span, String::new())); + + let ty::Ref(_, inner_ty, _) = suggested_ty.kind() else { + break 'outer; + }; + suggested_ty = *inner_ty; + + expr = borrowed; + + if maybe_suggest(suggested_ty, count, suggestions.clone()) { + return true; + } + } + if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind + && let Res::Local(hir_id) = path.res + && let Some(hir::Node::Pat(binding)) = self.tcx.opt_hir_node(hir_id) + && let Some(hir::Node::Local(local)) = self.tcx.hir().find_parent(binding.hir_id) + && let None = local.ty + && let Some(binding_expr) = local.init + { + expr = binding_expr; + } else { + break 'outer; + } + } + false + } + + fn suggest_remove_await(&self, obligation: &PredicateObligation<'tcx>, err: &mut Diagnostic) { + let hir = self.tcx.hir(); + if let ObligationCauseCode::AwaitableExpr(hir_id) = obligation.cause.code().peel_derives() + && let hir::Node::Expr(expr) = self.tcx.hir_node(*hir_id) + { + // FIXME: use `obligation.predicate.kind()...trait_ref.self_ty()` to see if we have `()` + // and if not maybe suggest doing something else? If we kept the expression around we + // could also check if it is an fn call (very likely) and suggest changing *that*, if + // it is from the local crate. + + // use nth(1) to skip one layer of desugaring from `IntoIter::into_iter` + if let Some((_, hir::Node::Expr(await_expr))) = hir.parent_iter(*hir_id).nth(1) + && let Some(expr_span) = expr.span.find_ancestor_inside_same_ctxt(await_expr.span) + { + let removal_span = self + .tcx + .sess + .source_map() + .span_extend_while(expr_span, char::is_whitespace) + .unwrap_or(expr_span) + .shrink_to_hi() + .to(await_expr.span.shrink_to_hi()); + err.span_suggestion( + removal_span, + "remove the `.await`", + "", + Applicability::MachineApplicable, + ); + } else { + err.span_label(obligation.cause.span, "remove the `.await`"); + } + // FIXME: account for associated `async fn`s. + if let hir::Expr { span, kind: hir::ExprKind::Call(base, _), .. } = expr { + 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())); + } + if let Some(typeck_results) = &self.typeck_results + && let ty = typeck_results.expr_ty_adjusted(base) + && let ty::FnDef(def_id, _args) = ty.kind() + && let Some(hir::Node::Item(hir::Item { ident, span, vis_span, .. })) = + hir.get_if_local(*def_id) + { + let msg = format!("alternatively, consider making `fn {ident}` asynchronous"); + if vis_span.is_empty() { + err.span_suggestion_verbose( + span.shrink_to_lo(), + msg, + "async ", + Applicability::MaybeIncorrect, + ); + } else { + err.span_suggestion_verbose( + vis_span.shrink_to_hi(), + msg, + " async", + Applicability::MaybeIncorrect, + ); + } + } + } + } + } + + /// Check if the trait bound is implemented for a different mutability and note it in the + /// final error. + fn suggest_change_mut( + &self, + obligation: &PredicateObligation<'tcx>, + err: &mut Diagnostic, + trait_pred: ty::PolyTraitPredicate<'tcx>, + ) { + let points_at_arg = matches!( + obligation.cause.code(), + ObligationCauseCode::FunctionArgumentObligation { .. }, + ); + + let span = obligation.cause.span; + if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { + let refs_number = + snippet.chars().filter(|c| !c.is_whitespace()).take_while(|c| *c == '&').count(); + if let Some('\'') = snippet.chars().filter(|c| !c.is_whitespace()).nth(refs_number) { + // Do not suggest removal of borrow from type arguments. + return; + } + let trait_pred = self.resolve_vars_if_possible(trait_pred); + if trait_pred.has_non_region_infer() { + // Do not ICE while trying to find if a reborrow would succeed on a trait with + // unresolved bindings. + return; + } + + // Skipping binder here, remapping below + if let ty::Ref(region, t_type, mutability) = *trait_pred.skip_binder().self_ty().kind() + { + let suggested_ty = match mutability { + 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 + let trait_pred_and_suggested_ty = + trait_pred.map_bound(|trait_pred| (trait_pred, suggested_ty)); + + let new_obligation = self.mk_trait_obligation_with_new_self_ty( + obligation.param_env, + trait_pred_and_suggested_ty, + ); + let suggested_ty_would_satisfy_obligation = self + .evaluate_obligation_no_overflow(&new_obligation) + .must_apply_modulo_regions(); + if suggested_ty_would_satisfy_obligation { + let sp = self + .tcx + .sess + .source_map() + .span_take_while(span, |c| c.is_whitespace() || *c == '&'); + if points_at_arg && mutability.is_not() && refs_number > 0 { + // If we have a call like foo(&mut buf), then don't suggest foo(&mut mut buf) + if snippet + .trim_start_matches(|c: char| c.is_whitespace() || c == '&') + .starts_with("mut") + { + return; + } + err.span_suggestion_verbose( + sp, + "consider changing this borrow's mutability", + "&mut ", + Applicability::MachineApplicable, + ); + } else { + err.note(format!( + "`{}` is implemented for `{}`, but not for `{}`", + trait_pred.print_modifiers_and_trait_path(), + suggested_ty, + trait_pred.skip_binder().self_ty(), + )); + } + } + } + } + } + + fn suggest_semicolon_removal( + &self, + obligation: &PredicateObligation<'tcx>, + err: &mut Diagnostic, + span: Span, + trait_pred: ty::PolyTraitPredicate<'tcx>, + ) -> bool { + let hir = self.tcx.hir(); + let node = self.tcx.opt_hir_node_by_def_id(obligation.cause.body_id); + if let Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, _, body_id), .. })) = node + && let hir::ExprKind::Block(blk, _) = &hir.body(*body_id).value.kind + && sig.decl.output.span().overlaps(span) + && blk.expr.is_none() + && trait_pred.self_ty().skip_binder().is_unit() + && let Some(stmt) = blk.stmts.last() + && let hir::StmtKind::Semi(expr) = stmt.kind + // Only suggest this if the expression behind the semicolon implements the predicate + && let Some(typeck_results) = &self.typeck_results + && let Some(ty) = typeck_results.expr_ty_opt(expr) + && self.predicate_may_hold(&self.mk_trait_obligation_with_new_self_ty( + obligation.param_env, trait_pred.map_bound(|trait_pred| (trait_pred, ty)) + )) + { + err.span_label( + expr.span, + format!( + "this expression has type `{}`, which implements `{}`", + ty, + trait_pred.print_modifiers_and_trait_path() + ), + ); + err.span_suggestion( + self.tcx.sess.source_map().end_point(stmt.span), + "remove this semicolon", + "", + Applicability::MachineApplicable, + ); + return true; + } + false + } + + fn return_type_span(&self, obligation: &PredicateObligation<'tcx>) -> Option<Span> { + let Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, ..), .. })) = + self.tcx.opt_hir_node_by_def_id(obligation.cause.body_id) + else { + return None; + }; + + if let hir::FnRetTy::Return(ret_ty) = sig.decl.output { Some(ret_ty.span) } else { None } + } + + /// If all conditions are met to identify a returned `dyn Trait`, suggest using `impl Trait` if + /// applicable and signal that the error has been expanded appropriately and needs to be + /// emitted. + fn suggest_impl_trait( + &self, + err: &mut Diagnostic, + obligation: &PredicateObligation<'tcx>, + trait_pred: ty::PolyTraitPredicate<'tcx>, + ) -> bool { + let ObligationCauseCode::SizedReturnType = obligation.cause.code() else { + return false; + }; + let ty::Dynamic(_, _, ty::Dyn) = trait_pred.self_ty().skip_binder().kind() else { + return false; + }; + + err.code(error_code!(E0746)); + err.set_primary_message("return type cannot have an unboxed trait object"); + err.children.clear(); + + let span = obligation.cause.span; + if let Ok(snip) = self.tcx.sess.source_map().span_to_snippet(span) + && snip.starts_with("dyn ") + { + err.span_suggestion( + span.with_hi(span.lo() + BytePos(4)), + "return an `impl Trait` instead of a `dyn Trait`, \ + if all returned values are the same type", + "impl ", + Applicability::MaybeIncorrect, + ); + } + + let body = self.tcx.hir().body(self.tcx.hir().body_owned_by(obligation.cause.body_id)); + + let mut visitor = ReturnsVisitor::default(); + visitor.visit_body(body); + + let mut sugg = + vec![(span.shrink_to_lo(), "Box<".to_string()), (span.shrink_to_hi(), ">".to_string())]; + sugg.extend(visitor.returns.into_iter().flat_map(|expr| { + let span = + expr.span.find_ancestor_in_same_ctxt(obligation.cause.span).unwrap_or(expr.span); + if !span.can_be_used_for_suggestions() { + vec![] + } else if let hir::ExprKind::Call(path, ..) = expr.kind + && let hir::ExprKind::Path(hir::QPath::TypeRelative(ty, method)) = path.kind + && method.ident.name == sym::new + && let hir::TyKind::Path(hir::QPath::Resolved(.., box_path)) = ty.kind + && box_path + .res + .opt_def_id() + .is_some_and(|def_id| Some(def_id) == self.tcx.lang_items().owned_box()) + { + // Don't box `Box::new` + vec![] + } else { + vec![ + (span.shrink_to_lo(), "Box::new(".to_string()), + (span.shrink_to_hi(), ")".to_string()), + ] + } + })); + + err.multipart_suggestion( + "box the return type, and wrap all of the returned values in `Box::new`", + sugg, + Applicability::MaybeIncorrect, + ); + + true + } + + fn point_at_returns_when_relevant( + &self, + err: &mut DiagnosticBuilder<'tcx>, + obligation: &PredicateObligation<'tcx>, + ) { + match obligation.cause.code().peel_derives() { + ObligationCauseCode::SizedReturnType => {} + _ => return, + } + + let hir = self.tcx.hir(); + let node = self.tcx.opt_hir_node_by_def_id(obligation.cause.body_id); + if let Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, _, body_id), .. })) = + node + { + let body = hir.body(*body_id); + // Point at all the `return`s in the function as they have failed trait bounds. + let mut visitor = ReturnsVisitor::default(); + visitor.visit_body(body); + let typeck_results = self.typeck_results.as_ref().unwrap(); + for expr in &visitor.returns { + if let Some(returned_ty) = typeck_results.node_type_opt(expr.hir_id) { + let ty = self.resolve_vars_if_possible(returned_ty); + if ty.references_error() { + // don't print out the [type error] here + err.delay_as_bug(); + } else { + err.span_label(expr.span, format!("this returned value is of type `{ty}`")); + } + } + } + } + } + + fn report_closure_arg_mismatch( + &self, + span: Span, + found_span: Option<Span>, + found: ty::PolyTraitRef<'tcx>, + expected: ty::PolyTraitRef<'tcx>, + cause: &ObligationCauseCode<'tcx>, + found_node: Option<Node<'_>>, + param_env: ty::ParamEnv<'tcx>, + ) -> DiagnosticBuilder<'tcx> { + pub(crate) fn build_fn_sig_ty<'tcx>( + infcx: &InferCtxt<'tcx>, + trait_ref: ty::PolyTraitRef<'tcx>, + ) -> Ty<'tcx> { + let inputs = trait_ref.skip_binder().args.type_at(1); + let sig = match inputs.kind() { + ty::Tuple(inputs) if infcx.tcx.is_fn_trait(trait_ref.def_id()) => { + infcx.tcx.mk_fn_sig( + *inputs, + infcx.next_ty_var(TypeVariableOrigin { + span: DUMMY_SP, + kind: TypeVariableOriginKind::MiscVariable, + }), + false, + hir::Unsafety::Normal, + abi::Abi::Rust, + ) + } + _ => infcx.tcx.mk_fn_sig( + [inputs], + infcx.next_ty_var(TypeVariableOrigin { + span: DUMMY_SP, + kind: TypeVariableOriginKind::MiscVariable, + }), + false, + hir::Unsafety::Normal, + abi::Abi::Rust, + ), + }; + + Ty::new_fn_ptr(infcx.tcx, trait_ref.rebind(sig)) + } + + let argument_kind = match expected.skip_binder().self_ty().kind() { + ty::Closure(..) => "closure", + ty::Coroutine(..) => "coroutine", + _ => "function", + }; + let mut err = struct_span_err!( + self.dcx(), + span, + E0631, + "type mismatch in {argument_kind} arguments", + ); + + err.span_label(span, "expected due to this"); + + let found_span = found_span.unwrap_or(span); + err.span_label(found_span, "found signature defined here"); + + let expected = build_fn_sig_ty(self, expected); + let found = build_fn_sig_ty(self, found); + + let (expected_str, found_str) = self.cmp(expected, found); + + let signature_kind = format!("{argument_kind} signature"); + err.note_expected_found(&signature_kind, expected_str, &signature_kind, found_str); + + self.note_conflicting_fn_args(&mut err, cause, expected, found, param_env); + self.note_conflicting_closure_bounds(cause, &mut err); + + if let Some(found_node) = found_node { + hint_missing_borrow(self, param_env, span, found, expected, found_node, &mut err); + } + + err + } + + fn note_conflicting_fn_args( + &self, + err: &mut Diagnostic, + cause: &ObligationCauseCode<'tcx>, + expected: Ty<'tcx>, + found: Ty<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ) { + let ObligationCauseCode::FunctionArgumentObligation { arg_hir_id, .. } = cause else { + return; + }; + let ty::FnPtr(expected) = expected.kind() else { + return; + }; + let ty::FnPtr(found) = found.kind() else { + return; + }; + let Some(Node::Expr(arg)) = self.tcx.opt_hir_node(*arg_hir_id) else { + return; + }; + let hir::ExprKind::Path(path) = arg.kind else { + return; + }; + let expected_inputs = self.tcx.instantiate_bound_regions_with_erased(*expected).inputs(); + let found_inputs = self.tcx.instantiate_bound_regions_with_erased(*found).inputs(); + let both_tys = expected_inputs.iter().copied().zip(found_inputs.iter().copied()); + + let arg_expr = |infcx: &InferCtxt<'tcx>, name, expected: Ty<'tcx>, found: Ty<'tcx>| { + let (expected_ty, expected_refs) = get_deref_type_and_refs(expected); + let (found_ty, found_refs) = get_deref_type_and_refs(found); + + if infcx.can_eq(param_env, found_ty, expected_ty) { + if found_refs.len() == expected_refs.len() + && found_refs.iter().eq(expected_refs.iter()) + { + name + } else if found_refs.len() > expected_refs.len() { + let refs = &found_refs[..found_refs.len() - expected_refs.len()]; + if found_refs[..expected_refs.len()].iter().eq(expected_refs.iter()) { + format!( + "{}{name}", + refs.iter() + .map(|mutbl| format!("&{}", mutbl.prefix_str())) + .collect::<Vec<_>>() + .join(""), + ) + } else { + // The refs have different mutability. + format!( + "{}*{name}", + refs.iter() + .map(|mutbl| format!("&{}", mutbl.prefix_str())) + .collect::<Vec<_>>() + .join(""), + ) + } + } else if expected_refs.len() > found_refs.len() { + format!( + "{}{name}", + (0..(expected_refs.len() - found_refs.len())) + .map(|_| "*") + .collect::<Vec<_>>() + .join(""), + ) + } else { + format!( + "{}{name}", + found_refs + .iter() + .map(|mutbl| format!("&{}", mutbl.prefix_str())) + .chain(found_refs.iter().map(|_| "*".to_string())) + .collect::<Vec<_>>() + .join(""), + ) + } + } else { + format!("/* {found} */") + } + }; + let args_have_same_underlying_type = both_tys.clone().all(|(expected, found)| { + let (expected_ty, _) = get_deref_type_and_refs(expected); + let (found_ty, _) = get_deref_type_and_refs(found); + self.can_eq(param_env, found_ty, expected_ty) + }); + let (closure_names, call_names): (Vec<_>, Vec<_>) = if args_have_same_underlying_type + && !expected_inputs.is_empty() + && expected_inputs.len() == found_inputs.len() + && let Some(typeck) = &self.typeck_results + && let Res::Def(res_kind, fn_def_id) = typeck.qpath_res(&path, *arg_hir_id) + && res_kind.is_fn_like() + { + let closure: Vec<_> = self + .tcx + .fn_arg_names(fn_def_id) + .iter() + .enumerate() + .map(|(i, ident)| { + if ident.name.is_empty() || ident.name == kw::SelfLower { + format!("arg{i}") + } else { + format!("{ident}") + } + }) + .collect(); + let args = closure + .iter() + .zip(both_tys) + .map(|(name, (expected, found))| { + arg_expr(self.infcx, name.to_owned(), expected, found) + }) + .collect(); + (closure, args) + } else { + let closure_args = expected_inputs + .iter() + .enumerate() + .map(|(i, _)| format!("arg{i}")) + .collect::<Vec<_>>(); + let call_args = both_tys + .enumerate() + .map(|(i, (expected, found))| { + arg_expr(self.infcx, format!("arg{i}"), expected, found) + }) + .collect::<Vec<_>>(); + (closure_args, call_args) + }; + let closure_names: Vec<_> = closure_names + .into_iter() + .zip(expected_inputs.iter()) + .map(|(name, ty)| { + format!( + "{name}{}", + if ty.has_infer_types() { + String::new() + } else if ty.references_error() { + ": /* type */".to_string() + } else { + format!(": {ty}") + } + ) + }) + .collect(); + err.multipart_suggestion( + "consider wrapping the function in a closure", + vec![ + (arg.span.shrink_to_lo(), format!("|{}| ", closure_names.join(", "))), + (arg.span.shrink_to_hi(), format!("({})", call_names.join(", "))), + ], + Applicability::MaybeIncorrect, + ); + } + + // Add a note if there are two `Fn`-family bounds that have conflicting argument + // requirements, which will always cause a closure to have a type error. + fn note_conflicting_closure_bounds( + &self, + cause: &ObligationCauseCode<'tcx>, + err: &mut DiagnosticBuilder<'tcx>, + ) { + // First, look for an `ExprBindingObligation`, which means we can get + // the unsubstituted predicate list of the called function. And check + // that the predicate that we failed to satisfy is a `Fn`-like trait. + if let ObligationCauseCode::ExprBindingObligation(def_id, _, _, idx) = cause + && let predicates = self.tcx.predicates_of(def_id).instantiate_identity(self.tcx) + && let Some(pred) = predicates.predicates.get(*idx) + && let ty::ClauseKind::Trait(trait_pred) = pred.kind().skip_binder() + && self.tcx.is_fn_trait(trait_pred.def_id()) + { + let expected_self = + self.tcx.anonymize_bound_vars(pred.kind().rebind(trait_pred.self_ty())); + let expected_args = + self.tcx.anonymize_bound_vars(pred.kind().rebind(trait_pred.trait_ref.args)); + + // Find another predicate whose self-type is equal to the expected self type, + // but whose args don't match. + let other_pred = predicates.into_iter().enumerate().find(|(other_idx, (pred, _))| { + match pred.kind().skip_binder() { + 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 + // (i.e. constraining this closure) + && expected_self + == self.tcx.anonymize_bound_vars( + pred.kind().rebind(trait_pred.self_ty()), + ) + // But the args don't match (i.e. incompatible args) + && expected_args + != self.tcx.anonymize_bound_vars( + pred.kind().rebind(trait_pred.trait_ref.args), + ) => + { + true + } + _ => false, + } + }); + // If we found one, then it's very likely the cause of the error. + if let Some((_, (_, other_pred_span))) = other_pred { + err.span_note( + other_pred_span, + "closure inferred to have a different signature due to this bound", + ); + } + } + } + + fn suggest_fully_qualified_path( + &self, + err: &mut Diagnostic, + item_def_id: DefId, + span: Span, + trait_ref: DefId, + ) { + if let Some(assoc_item) = self.tcx.opt_associated_item(item_def_id) { + if let ty::AssocKind::Const | ty::AssocKind::Type = assoc_item.kind { + err.note(format!( + "{}s cannot be accessed directly on a `trait`, they can only be \ + accessed through a specific `impl`", + self.tcx.def_kind_descr(assoc_item.kind.as_def_kind(), item_def_id) + )); + err.span_suggestion( + span, + "use the fully qualified path to an implementation", + format!("<Type as {}>::{}", self.tcx.def_path_str(trait_ref), assoc_item.name), + Applicability::HasPlaceholders, + ); + } + } + } + + /// Adds an async-await specific note to the diagnostic when the future does not implement + /// an auto trait because of a captured type. + /// + /// ```text + /// note: future does not implement `Qux` as this value is used across an await + /// --> $DIR/issue-64130-3-other.rs:17:5 + /// | + /// LL | let x = Foo; + /// | - has type `Foo` + /// LL | baz().await; + /// | ^^^^^^^^^^^ await occurs here, with `x` maybe used later + /// LL | } + /// | - `x` is later dropped here + /// ``` + /// + /// When the diagnostic does not implement `Send` or `Sync` specifically, then the diagnostic + /// is "replaced" with a different message and a more specific error. + /// + /// ```text + /// error: future cannot be sent between threads safely + /// --> $DIR/issue-64130-2-send.rs:21:5 + /// | + /// LL | fn is_send<T: Send>(t: T) { } + /// | ---- required by this bound in `is_send` + /// ... + /// LL | is_send(bar()); + /// | ^^^^^^^ future returned by `bar` is not send + /// | + /// = help: within `impl std::future::Future`, the trait `std::marker::Send` is not + /// implemented for `Foo` + /// note: future is not send as this value is used across an await + /// --> $DIR/issue-64130-2-send.rs:15:5 + /// | + /// LL | let x = Foo; + /// | - has type `Foo` + /// LL | baz().await; + /// | ^^^^^^^^^^^ await occurs here, with `x` maybe used later + /// LL | } + /// | - `x` is later dropped here + /// ``` + /// + /// Returns `true` if an async-await specific note was added to the diagnostic. + #[instrument(level = "debug", skip_all, fields(?obligation.predicate, ?obligation.cause.span))] + fn maybe_note_obligation_cause_for_async_await( + &self, + err: &mut Diagnostic, + obligation: &PredicateObligation<'tcx>, + ) -> bool { + let hir = self.tcx.hir(); + + // Attempt to detect an async-await error by looking at the obligation causes, looking + // for a coroutine to be present. + // + // When a future does not implement a trait because of a captured type in one of the + // coroutines somewhere in the call stack, then the result is a chain of obligations. + // + // Given an `async fn` A that calls an `async fn` B which captures a non-send type and that + // future is passed as an argument to a function C which requires a `Send` type, then the + // chain looks something like this: + // + // - `BuiltinDerivedObligation` with a coroutine witness (B) + // - `BuiltinDerivedObligation` with a coroutine (B) + // - `BuiltinDerivedObligation` with `impl std::future::Future` (B) + // - `BuiltinDerivedObligation` with a coroutine witness (A) + // - `BuiltinDerivedObligation` with a coroutine (A) + // - `BuiltinDerivedObligation` with `impl std::future::Future` (A) + // - `BindingObligation` with `impl_send` (Send requirement) + // + // The first obligation in the chain is the most useful and has the coroutine that captured + // the type. The last coroutine (`outer_coroutine` below) has information about where the + // bound was introduced. At least one coroutine 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::ClauseKind::Trait(p)) => (Some(p), Some(p.self_ty())), + _ => (None, None), + }; + let mut coroutine = None; + let mut outer_coroutine = None; + let mut next_code = Some(obligation.cause.code()); + + let mut seen_upvar_tys_infer_tuple = false; + + while let Some(code) = next_code { + debug!(?code); + match code { + ObligationCauseCode::FunctionArgumentObligation { parent_code, .. } => { + next_code = Some(parent_code); + } + ObligationCauseCode::ImplDerivedObligation(cause) => { + let ty = cause.derived.parent_trait_pred.skip_binder().self_ty(); + debug!( + parent_trait_ref = ?cause.derived.parent_trait_pred, + self_ty.kind = ?ty.kind(), + "ImplDerived", + ); + + match *ty.kind() { + ty::Coroutine(did, ..) | ty::CoroutineWitness(did, _) => { + coroutine = coroutine.or(Some(did)); + outer_coroutine = Some(did); + } + ty::Tuple(_) if !seen_upvar_tys_infer_tuple => { + // By introducing a tuple of upvar types into the chain of obligations + // of a coroutine, the first non-coroutine item is now the tuple itself, + // we shall ignore this. + + seen_upvar_tys_infer_tuple = true; + } + _ if coroutine.is_none() => { + trait_ref = Some(cause.derived.parent_trait_pred.skip_binder()); + target_ty = Some(ty); + } + _ => {} + } + + next_code = Some(&cause.derived.parent_code); + } + ObligationCauseCode::DerivedObligation(derived_obligation) + | ObligationCauseCode::BuiltinDerivedObligation(derived_obligation) => { + let ty = derived_obligation.parent_trait_pred.skip_binder().self_ty(); + debug!( + parent_trait_ref = ?derived_obligation.parent_trait_pred, + self_ty.kind = ?ty.kind(), + ); + + match *ty.kind() { + ty::Coroutine(did, ..) | ty::CoroutineWitness(did, ..) => { + coroutine = coroutine.or(Some(did)); + outer_coroutine = Some(did); + } + ty::Tuple(_) if !seen_upvar_tys_infer_tuple => { + // By introducing a tuple of upvar types into the chain of obligations + // of a coroutine, the first non-coroutine item is now the tuple itself, + // we shall ignore this. + + seen_upvar_tys_infer_tuple = true; + } + _ if coroutine.is_none() => { + trait_ref = Some(derived_obligation.parent_trait_pred.skip_binder()); + target_ty = Some(ty); + } + _ => {} + } + + next_code = Some(&derived_obligation.parent_code); + } + _ => break, + } + } + + // Only continue if a coroutine was found. + debug!(?coroutine, ?trait_ref, ?target_ty); + let (Some(coroutine_did), Some(trait_ref), Some(target_ty)) = + (coroutine, trait_ref, target_ty) + else { + return false; + }; + + let span = self.tcx.def_span(coroutine_did); + + let coroutine_did_root = self.tcx.typeck_root_def_id(coroutine_did); + debug!( + ?coroutine_did, + ?coroutine_did_root, + typeck_results.hir_owner = ?self.typeck_results.as_ref().map(|t| t.hir_owner), + ?span, + ); + + let coroutine_body = coroutine_did + .as_local() + .and_then(|def_id| hir.maybe_body_owned_by(def_id)) + .map(|body_id| hir.body(body_id)); + let mut visitor = AwaitsVisitor::default(); + if let Some(body) = coroutine_body { + visitor.visit_body(body); + } + debug!(awaits = ?visitor.awaits); + + // Look for a type inside the coroutine interior that matches the target type to get + // a span. + let target_ty_erased = self.tcx.erase_regions(target_ty); + let ty_matches = |ty| -> bool { + // Careful: the regions for types that appear in the + // coroutine interior are not generally known, so we + // want to erase them when comparing (and anyway, + // `Send` and other bounds are generally unaffected by + // the choice of region). When erasing regions, we + // also have to erase late-bound regions. This is + // because the types that appear in the coroutine + // interior generally contain "bound regions" to + // represent regions that are part of the suspended + // coroutine frame. Bound regions are preserved by + // `erase_regions` and so we must also call + // `instantiate_bound_regions_with_erased`. + let ty_erased = self.tcx.instantiate_bound_regions_with_erased(ty); + let ty_erased = self.tcx.erase_regions(ty_erased); + let eq = ty_erased == target_ty_erased; + debug!(?ty_erased, ?target_ty_erased, ?eq); + eq + }; + + // Get the typeck results from the infcx if the coroutine is the function we are currently + // type-checking; otherwise, get them by performing a query. This is needed to avoid + // cycles. If we can't use resolved types because the coroutine comes from another crate, + // we still provide a targeted error but without all the relevant spans. + let coroutine_data = match &self.typeck_results { + Some(t) if t.hir_owner.to_def_id() == coroutine_did_root => CoroutineData(t), + _ if coroutine_did.is_local() => { + CoroutineData(self.tcx.typeck(coroutine_did.expect_local())) + } + _ => return false, + }; + + let coroutine_within_in_progress_typeck = match &self.typeck_results { + Some(t) => t.hir_owner.to_def_id() == coroutine_did_root, + _ => false, + }; + + let mut interior_or_upvar_span = None; + + let from_awaited_ty = coroutine_data.get_from_await_ty(visitor, hir, ty_matches); + debug!(?from_awaited_ty); + + // Avoid disclosing internal information to downstream crates. + if coroutine_did.is_local() + // Try to avoid cycles. + && !coroutine_within_in_progress_typeck + && let Some(coroutine_info) = self.tcx.mir_coroutine_witnesses(coroutine_did) + { + debug!(?coroutine_info); + 'find_source: for (variant, source_info) in + coroutine_info.variant_fields.iter().zip(&coroutine_info.variant_source_info) + { + debug!(?variant); + for &local in variant { + let decl = &coroutine_info.field_tys[local]; + debug!(?decl); + if ty_matches(ty::Binder::dummy(decl.ty)) && !decl.ignore_for_traits { + interior_or_upvar_span = Some(CoroutineInteriorOrUpvar::Interior( + decl.source_info.span, + Some((source_info.span, from_awaited_ty)), + )); + break 'find_source; + } + } + } + } + + if interior_or_upvar_span.is_none() { + interior_or_upvar_span = + coroutine_data.try_get_upvar_span(self, coroutine_did, ty_matches); + } + + if interior_or_upvar_span.is_none() && !coroutine_did.is_local() { + interior_or_upvar_span = Some(CoroutineInteriorOrUpvar::Interior(span, None)); + } + + debug!(?interior_or_upvar_span); + if let Some(interior_or_upvar_span) = interior_or_upvar_span { + let is_async = self.tcx.coroutine_is_async(coroutine_did); + self.note_obligation_cause_for_async_await( + err, + interior_or_upvar_span, + is_async, + outer_coroutine, + trait_ref, + target_ty, + obligation, + next_code, + ); + true + } else { + false + } + } + + /// Unconditionally adds the diagnostic note described in + /// `maybe_note_obligation_cause_for_async_await`'s documentation comment. + #[instrument(level = "debug", skip_all)] + fn note_obligation_cause_for_async_await( + &self, + err: &mut Diagnostic, + interior_or_upvar_span: CoroutineInteriorOrUpvar, + is_async: bool, + outer_coroutine: Option<DefId>, + trait_pred: ty::TraitPredicate<'tcx>, + target_ty: Ty<'tcx>, + obligation: &PredicateObligation<'tcx>, + next_code: Option<&ObligationCauseCode<'tcx>>, + ) { + let source_map = self.tcx.sess.source_map(); + + let (await_or_yield, an_await_or_yield) = + if is_async { ("await", "an await") } else { ("yield", "a yield") }; + let future_or_coroutine = if is_async { "future" } else { "coroutine" }; + + // Special case the primary error message when send or sync is the trait that was + // not implemented. + let hir = self.tcx.hir(); + let trait_explanation = if let Some(name @ (sym::Send | sym::Sync)) = + self.tcx.get_diagnostic_name(trait_pred.def_id()) + { + let (trait_name, trait_verb) = + if name == sym::Send { ("`Send`", "sent") } else { ("`Sync`", "shared") }; + + err.clear_code(); + err.set_primary_message(format!( + "{future_or_coroutine} cannot be {trait_verb} between threads safely" + )); + + let original_span = err.span.primary_span().unwrap(); + let mut span = MultiSpan::from_span(original_span); + + let message = outer_coroutine + .and_then(|coroutine_did| { + Some(match self.tcx.coroutine_kind(coroutine_did).unwrap() { + CoroutineKind::Coroutine(_) => format!("coroutine is not {trait_name}"), + CoroutineKind::Desugared( + CoroutineDesugaring::Async, + CoroutineSource::Fn, + ) => self + .tcx + .parent(coroutine_did) + .as_local() + .map(|parent_did| self.tcx.local_def_id_to_hir_id(parent_did)) + .and_then(|parent_hir_id| hir.opt_name(parent_hir_id)) + .map(|name| { + format!("future returned by `{name}` is not {trait_name}") + })?, + CoroutineKind::Desugared( + CoroutineDesugaring::Async, + CoroutineSource::Block, + ) => { + format!("future created by async block is not {trait_name}") + } + CoroutineKind::Desugared( + CoroutineDesugaring::Async, + CoroutineSource::Closure, + ) => { + format!("future created by async closure is not {trait_name}") + } + CoroutineKind::Desugared( + CoroutineDesugaring::AsyncGen, + CoroutineSource::Fn, + ) => self + .tcx + .parent(coroutine_did) + .as_local() + .map(|parent_did| self.tcx.local_def_id_to_hir_id(parent_did)) + .and_then(|parent_hir_id| hir.opt_name(parent_hir_id)) + .map(|name| { + format!("async iterator returned by `{name}` is not {trait_name}") + })?, + CoroutineKind::Desugared( + CoroutineDesugaring::AsyncGen, + CoroutineSource::Block, + ) => { + format!("async iterator created by async gen block is not {trait_name}") + } + CoroutineKind::Desugared( + CoroutineDesugaring::AsyncGen, + CoroutineSource::Closure, + ) => { + format!( + "async iterator created by async gen closure is not {trait_name}" + ) + } + CoroutineKind::Desugared(CoroutineDesugaring::Gen, CoroutineSource::Fn) => { + self.tcx + .parent(coroutine_did) + .as_local() + .map(|parent_did| self.tcx.local_def_id_to_hir_id(parent_did)) + .and_then(|parent_hir_id| hir.opt_name(parent_hir_id)) + .map(|name| { + format!("iterator returned by `{name}` is not {trait_name}") + })? + } + CoroutineKind::Desugared( + CoroutineDesugaring::Gen, + CoroutineSource::Block, + ) => { + format!("iterator created by gen block is not {trait_name}") + } + CoroutineKind::Desugared( + CoroutineDesugaring::Gen, + CoroutineSource::Closure, + ) => { + format!("iterator created by gen closure is not {trait_name}") + } + }) + }) + .unwrap_or_else(|| format!("{future_or_coroutine} is not {trait_name}")); + + span.push_span_label(original_span, message); + err.set_span(span); + + format!("is not {trait_name}") + } else { + format!("does not implement `{}`", trait_pred.print_modifiers_and_trait_path()) + }; + + let mut explain_yield = |interior_span: Span, yield_span: Span| { + let mut span = MultiSpan::from_span(yield_span); + let snippet = match source_map.span_to_snippet(interior_span) { + // #70935: If snippet contains newlines, display "the value" instead + // so that we do not emit complex diagnostics. + Ok(snippet) if !snippet.contains('\n') => format!("`{snippet}`"), + _ => "the value".to_string(), + }; + // note: future is not `Send` as this value is used across an await + // --> $DIR/issue-70935-complex-spans.rs:13:9 + // | + // LL | baz(|| async { + // | ______________- + // | | + // | | + // LL | | foo(tx.clone()); + // LL | | }).await; + // | | - ^^^^^^ await occurs here, with value maybe used later + // | |__________| + // | has type `closure` which is not `Send` + // note: value is later dropped here + // LL | | }).await; + // | | ^ + // + span.push_span_label( + yield_span, + format!("{await_or_yield} occurs here, with {snippet} maybe used later"), + ); + span.push_span_label( + interior_span, + format!("has type `{target_ty}` which {trait_explanation}"), + ); + err.span_note( + span, + format!("{future_or_coroutine} {trait_explanation} as this value is used across {an_await_or_yield}"), + ); + }; + match interior_or_upvar_span { + CoroutineInteriorOrUpvar::Interior(interior_span, interior_extra_info) => { + if let Some((yield_span, from_awaited_ty)) = interior_extra_info { + if let Some(await_span) = from_awaited_ty { + // The type causing this obligation is one being awaited at await_span. + let mut span = MultiSpan::from_span(await_span); + span.push_span_label( + await_span, + format!( + "await occurs here on type `{target_ty}`, which {trait_explanation}" + ), + ); + err.span_note( + span, + format!( + "future {trait_explanation} as it awaits another future which {trait_explanation}" + ), + ); + } else { + // Look at the last interior type to get a span for the `.await`. + explain_yield(interior_span, yield_span); + } + } + } + CoroutineInteriorOrUpvar::Upvar(upvar_span) => { + // `Some((ref_ty, is_mut))` if `target_ty` is `&T` or `&mut T` and fails to impl `Send` + let non_send = match target_ty.kind() { + ty::Ref(_, ref_ty, mutability) => match self.evaluate_obligation(obligation) { + Ok(eval) if !eval.may_apply() => Some((ref_ty, mutability.is_mut())), + _ => None, + }, + _ => None, + }; + + let (span_label, span_note) = match non_send { + // if `target_ty` is `&T` or `&mut T` and fails to impl `Send`, + // include suggestions to make `T: Sync` so that `&T: Send`, + // or to make `T: Send` so that `&mut T: Send` + Some((ref_ty, is_mut)) => { + let ref_ty_trait = if is_mut { "Send" } else { "Sync" }; + let ref_kind = if is_mut { "&mut" } else { "&" }; + ( + format!( + "has type `{target_ty}` which {trait_explanation}, because `{ref_ty}` is not `{ref_ty_trait}`" + ), + format!( + "captured value {trait_explanation} because `{ref_kind}` references cannot be sent unless their referent is `{ref_ty_trait}`" + ), + ) + } + None => ( + format!("has type `{target_ty}` which {trait_explanation}"), + format!("captured value {trait_explanation}"), + ), + }; + + let mut span = MultiSpan::from_span(upvar_span); + span.push_span_label(upvar_span, span_label); + err.span_note(span, span_note); + } + } + + // Add a note for the item obligation that remains - normally a note pointing to the + // bound that introduced the obligation (e.g. `T: Send`). + debug!(?next_code); + self.note_obligation_cause_code( + obligation.cause.body_id, + err, + obligation.predicate, + obligation.param_env, + next_code.unwrap(), + &mut Vec::new(), + &mut Default::default(), + ); + } + + fn note_obligation_cause_code<T>( + &self, + body_id: LocalDefId, + err: &mut Diagnostic, + predicate: T, + param_env: ty::ParamEnv<'tcx>, + cause_code: &ObligationCauseCode<'tcx>, + obligated_types: &mut Vec<Ty<'tcx>>, + seen_requirements: &mut FxHashSet<DefId>, + ) where + T: ToPredicate<'tcx>, + { + let tcx = self.tcx; + let predicate = predicate.to_predicate(tcx); + match *cause_code { + ObligationCauseCode::ExprAssignable + | ObligationCauseCode::MatchExpressionArm { .. } + | ObligationCauseCode::Pattern { .. } + | ObligationCauseCode::IfExpression { .. } + | ObligationCauseCode::IfExpressionWithNoElse + | ObligationCauseCode::MainFunctionType + | ObligationCauseCode::StartFunctionType + | ObligationCauseCode::LangFunctionType(_) + | ObligationCauseCode::IntrinsicType + | ObligationCauseCode::MethodReceiver + | ObligationCauseCode::ReturnNoExpression + | ObligationCauseCode::UnifyReceiver(..) + | ObligationCauseCode::MiscObligation + | ObligationCauseCode::WellFormed(..) + | ObligationCauseCode::MatchImpl(..) + | ObligationCauseCode::ReturnValue(_) + | ObligationCauseCode::BlockTailExpression(..) + | ObligationCauseCode::AwaitableExpr(_) + | ObligationCauseCode::ForLoopIterator + | ObligationCauseCode::QuestionMark + | ObligationCauseCode::CheckAssociatedTypeBounds { .. } + | ObligationCauseCode::LetElse + | ObligationCauseCode::BinOp { .. } + | ObligationCauseCode::AscribeUserTypeProvePredicate(..) + | ObligationCauseCode::DropImpl + | ObligationCauseCode::ConstParam(_) + | ObligationCauseCode::ReferenceOutlivesReferent(..) + | ObligationCauseCode::ObjectTypeBound(..) => {} + 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"); + } + ObligationCauseCode::TupleElem => { + err.note("only the last element of a tuple may have a dynamically sized type"); + } + ObligationCauseCode::ItemObligation(_) + | ObligationCauseCode::ExprItemObligation(..) => { + // We hold the `DefId` of the item introducing the obligation, but displaying it + // doesn't add user usable information. It always point at an associated item. + } + ObligationCauseCode::BindingObligation(item_def_id, span) + | ObligationCauseCode::ExprBindingObligation(item_def_id, span, ..) => { + let item_name = tcx.def_path_str(item_def_id); + let short_item_name = with_forced_trimmed_paths!(tcx.def_path_str(item_def_id)); + let mut multispan = MultiSpan::from(span); + let sm = tcx.sess.source_map(); + if let Some(ident) = tcx.opt_item_ident(item_def_id) { + let same_line = + match (sm.lookup_line(ident.span.hi()), sm.lookup_line(span.lo())) { + (Ok(l), Ok(r)) => l.line == r.line, + _ => true, + }; + if ident.span.is_visible(sm) && !ident.span.overlaps(span) && !same_line { + multispan.push_span_label( + ident.span, + format!( + "required by a bound in this {}", + tcx.def_kind(item_def_id).descr(item_def_id) + ), + ); + } + } + let descr = format!("required by a bound in `{item_name}`"); + if span.is_visible(sm) { + 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 Some(def_id) == self.tcx.lang_items().sized_trait() + && let Some(hir::Node::TraitItem(hir::TraitItem { + ident, + kind: hir::TraitItemKind::Type(bounds, None), + .. + })) = tcx.hir().get_if_local(item_def_id) + // Do not suggest relaxing if there is an explicit `Sized` obligation. + && !bounds.iter() + .filter_map(|bound| bound.trait_ref()) + .any(|tr| tr.trait_def_id() == self.tcx.lang_items().sized_trait()) + { + let (span, separator) = if let [.., last] = bounds { + (last.span().shrink_to_hi(), " +") + } else { + (ident.span.shrink_to_hi(), ":") + }; + err.span_suggestion_verbose( + span, + "consider relaxing the implicit `Sized` restriction", + format!("{separator} ?Sized"), + Applicability::MachineApplicable, + ); + } + if let DefKind::Trait = tcx.def_kind(item_def_id) + && !visible_item + { + err.note(format!( + "`{short_item_name}` is a \"sealed trait\", because to implement \ + it you also need to implement `{}`, 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)), + )); + let impls_of = tcx.trait_impls_of(def_id); + let impls = impls_of + .non_blanket_impls() + .values() + .flatten() + .chain(impls_of.blanket_impls().iter()) + .collect::<Vec<_>>(); + if !impls.is_empty() { + let len = impls.len(); + let mut types = impls + .iter() + .map(|t| { + with_no_trimmed_paths!(format!( + " {}", + tcx.type_of(*t).instantiate_identity(), + )) + }) + .collect::<Vec<_>>(); + let post = if types.len() > 9 { + types.truncate(8); + format!("\nand {} others", len - 8) + } else { + String::new() + }; + err.help(format!( + "the following type{} implement{} the trait:\n{}{post}", + pluralize!(len), + if len == 1 { "s" } else { "" }, + types.join("\n"), + )); + } + } + } + } else { + err.span_note(tcx.def_span(item_def_id), descr); + } + } + ObligationCauseCode::Coercion { source, target } => { + let mut file = None; + let source = + self.tcx.short_ty_string(self.resolve_vars_if_possible(source), &mut file); + let target = + self.tcx.short_ty_string(self.resolve_vars_if_possible(target), &mut file); + err.note(with_forced_trimmed_paths!(format!( + "required for the cast from `{source}` to `{target}`", + ))); + if let Some(file) = file { + err.note(format!( + "the full name for the type has been written to '{}'", + file.display(), + )); + } + } + ObligationCauseCode::RepeatElementCopy { + is_constable, + elt_type, + elt_span, + elt_stmt_span, + } => { + err.note( + "the `Copy` trait is required because this value will be copied for each element of the array", + ); + let value_kind = match is_constable { + IsConstable::Fn => Some("the result of the function call"), + IsConstable::Ctor => Some("the result of the constructor"), + _ => None, + }; + let sm = tcx.sess.source_map(); + if let Some(value_kind) = value_kind + && let Ok(snip) = sm.span_to_snippet(elt_span) + { + let help_msg = format!( + "consider creating a new `const` item and initializing it with {value_kind} \ + to be used in the repeat position" + ); + let indentation = sm.indentation_before(elt_stmt_span).unwrap_or_default(); + err.multipart_suggestion( + help_msg, + vec![ + ( + elt_stmt_span.shrink_to_lo(), + format!( + "const ARRAY_REPEAT_VALUE: {elt_type} = {snip};\n{indentation}" + ), + ), + (elt_span, "ARRAY_REPEAT_VALUE".to_string()), + ], + Applicability::MachineApplicable, + ); + } + + if self.tcx.sess.is_nightly_build() + && matches!(is_constable, IsConstable::Fn | IsConstable::Ctor) + { + err.help( + "create an inline `const` block, see RFC #2920 \ + <https://github.com/rust-lang/rfcs/pull/2920> for more information", + ); + } + } + ObligationCauseCode::VariableType(hir_id) => { + let parent_node = self.tcx.hir().parent_id(hir_id); + match self.tcx.opt_hir_node(parent_node) { + Some(Node::Local(hir::Local { ty: Some(ty), .. })) => { + err.span_suggestion_verbose( + ty.span.shrink_to_lo(), + "consider borrowing here", + "&", + Applicability::MachineApplicable, + ); + err.note("all local variables must have a statically known size"); + } + Some(Node::Local(hir::Local { + init: Some(hir::Expr { kind: hir::ExprKind::Index(..), span, .. }), + .. + })) => { + // When encountering an assignment of an unsized trait, like + // `let x = ""[..];`, provide a suggestion to borrow the initializer in + // order to use have a slice instead. + err.span_suggestion_verbose( + span.shrink_to_lo(), + "consider borrowing here", + "&", + Applicability::MachineApplicable, + ); + err.note("all local variables must have a statically known size"); + } + Some(Node::Param(param)) => { + err.span_suggestion_verbose( + param.ty_span.shrink_to_lo(), + "function arguments must have a statically known size, borrowed types \ + always have a known size", + "&", + Applicability::MachineApplicable, + ); + } + _ => { + err.note("all local variables must have a statically known size"); + } + } + if !self.tcx.features().unsized_locals { + err.help("unsized locals are gated as an unstable feature"); + } + } + ObligationCauseCode::SizedArgumentType(ty_span) => { + if let Some(span) = ty_span { + if let ty::PredicateKind::Clause(clause) = predicate.kind().skip_binder() + && 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) + && snippet.starts_with("dyn ") + { + let pos = snippet.len() - snippet[3..].trim_start().len(); + span.with_hi(span.lo() + BytePos(pos as u32)) + } else { + span.shrink_to_lo() + }; + err.span_suggestion_verbose( + span, + "you can use `impl Trait` as the argument type", + "impl ".to_string(), + Applicability::MaybeIncorrect, + ); + } + err.span_suggestion_verbose( + span.shrink_to_lo(), + "function arguments must have a statically known size, borrowed types \ + always have a known size", + "&", + Applicability::MachineApplicable, + ); + } else { + err.note("all function arguments must have a statically known size"); + } + if tcx.sess.opts.unstable_features.is_nightly_build() + && !self.tcx.features().unsized_fn_params + { + err.help("unsized fn params are gated as an unstable feature"); + } + } + ObligationCauseCode::SizedReturnType => { + err.note("the return type of a function must have a statically known size"); + } + ObligationCauseCode::SizedYieldType => { + err.note("the yield type of a coroutine must have a statically known size"); + } + ObligationCauseCode::AssignmentLhsSized => { + err.note("the left-hand-side of an assignment must have a statically known size"); + } + ObligationCauseCode::TupleInitializerSized => { + err.note("tuples must have a statically known size to be initialized"); + } + ObligationCauseCode::StructInitializerSized => { + err.note("structs must have a statically known size to be initialized"); + } + ObligationCauseCode::FieldSized { adt_kind: ref item, last, span } => { + match *item { + AdtKind::Struct => { + if last { + err.note( + "the last field of a packed struct may only have a \ + dynamically sized type if it does not need drop to be run", + ); + } else { + err.note( + "only the last field of a struct may have a dynamically sized type", + ); + } + } + AdtKind::Union => { + err.note("no field of a union may have a dynamically sized type"); + } + AdtKind::Enum => { + err.note("no field of an enum variant may have a dynamically sized type"); + } + } + err.help("change the field's type to have a statically known size"); + err.span_suggestion( + span.shrink_to_lo(), + "borrowed types always have a statically known size", + "&", + Applicability::MachineApplicable, + ); + err.multipart_suggestion( + "the `Box` type always has a statically known size and allocates its contents \ + in the heap", + vec![ + (span.shrink_to_lo(), "Box<".to_string()), + (span.shrink_to_hi(), ">".to_string()), + ], + Applicability::MachineApplicable, + ); + } + ObligationCauseCode::ConstSized => { + err.note("constant expressions must have a statically known size"); + } + ObligationCauseCode::InlineAsmSized => { + err.note("all inline asm arguments must have a statically known size"); + } + ObligationCauseCode::SizedClosureCapture(closure_def_id) => { + err.note( + "all values captured by value by a closure must have a statically known size", + ); + let hir::ExprKind::Closure(closure) = + self.tcx.hir_node_by_def_id(closure_def_id).expect_expr().kind + else { + bug!("expected closure in SizedClosureCapture obligation"); + }; + if let hir::CaptureBy::Value { .. } = closure.capture_clause + && let Some(span) = closure.fn_arg_span + { + err.span_label(span, "this closure captures all values by move"); + } + } + ObligationCauseCode::SizedCoroutineInterior(coroutine_def_id) => { + let what = match self.tcx.coroutine_kind(coroutine_def_id) { + None + | Some(hir::CoroutineKind::Coroutine(_)) + | Some(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Gen, _)) => { + "yield" + } + Some(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _)) => { + "await" + } + Some(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::AsyncGen, _)) => { + "yield`/`await" + } + }; + err.note(format!( + "all values live across `{what}` must have a statically known size" + )); + } + ObligationCauseCode::SharedStatic => { + err.note("shared static variables must have a type that implements `Sync`"); + } + ObligationCauseCode::BuiltinDerivedObligation(ref data) => { + let parent_trait_ref = self.resolve_vars_if_possible(data.parent_trait_pred); + let ty = parent_trait_ref.skip_binder().self_ty(); + if parent_trait_ref.references_error() { + // NOTE(eddyb) this was `.cancel()`, but `err` + // is borrowed, so we can't fully defuse it. + err.downgrade_to_delayed_bug(); + return; + } + + // If the obligation for a tuple is set directly by a Coroutine or Closure, + // then the tuple must be the one containing capture types. + let is_upvar_tys_infer_tuple = if !matches!(ty.kind(), ty::Tuple(..)) { + false + } else { + if let ObligationCauseCode::BuiltinDerivedObligation(data) = &*data.parent_code + { + let parent_trait_ref = + self.resolve_vars_if_possible(data.parent_trait_pred); + let nested_ty = parent_trait_ref.skip_binder().self_ty(); + matches!(nested_ty.kind(), ty::Coroutine(..)) + || matches!(nested_ty.kind(), ty::Closure(..)) + } else { + false + } + }; + + // Don't print the tuple of capture types + 'print: { + if !is_upvar_tys_infer_tuple { + let mut file = None; + let ty_str = self.tcx.short_ty_string(ty, &mut file); + let msg = format!("required because it appears within the type `{ty_str}`"); + match ty.kind() { + ty::Adt(def, _) => match self.tcx.opt_item_ident(def.did()) { + Some(ident) => err.span_note(ident.span, msg), + None => err.note(msg), + }, + ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) => { + // If the previous type is async fn, this is the future generated by the body of an async function. + // Avoid printing it twice (it was already printed in the `ty::Coroutine` arm below). + let is_future = tcx.ty_is_opaque_future(ty); + debug!( + ?obligated_types, + ?is_future, + "note_obligation_cause_code: check for async fn" + ); + if is_future + && obligated_types.last().is_some_and(|ty| match ty.kind() { + ty::Coroutine(last_def_id, ..) => { + tcx.coroutine_is_async(*last_def_id) + } + _ => false, + }) + { + break 'print; + } + err.span_note(self.tcx.def_span(def_id), msg) + } + ty::CoroutineWitness(def_id, args) => { + use std::fmt::Write; + + // FIXME: this is kind of an unusual format for rustc, can we make it more clear? + // Maybe we should just remove this note altogether? + // FIXME: only print types which don't meet the trait requirement + let mut msg = + "required because it captures the following types: ".to_owned(); + for bty in tcx.coroutine_hidden_types(*def_id) { + let ty = bty.instantiate(tcx, args); + write!(msg, "`{ty}`, ").unwrap(); + } + err.note(msg.trim_end_matches(", ").to_string()) + } + ty::Coroutine(def_id, _) => { + let sp = self.tcx.def_span(def_id); + + // Special-case this to say "async block" instead of `[static coroutine]`. + let kind = tcx.coroutine_kind(def_id).unwrap(); + err.span_note( + sp, + with_forced_trimmed_paths!(format!( + "required because it's used within this {kind:#}", + )), + ) + } + ty::Closure(def_id, _) => err.span_note( + self.tcx.def_span(def_id), + "required because it's used within this closure", + ), + ty::Str => err.note("`str` is considered to contain a `[u8]` slice for auto trait purposes"), + _ => err.note(msg), + }; + } + } + + obligated_types.push(ty); + + let parent_predicate = parent_trait_ref; + if !self.is_recursive_obligation(obligated_types, &data.parent_code) { + // #74711: avoid a stack overflow + ensure_sufficient_stack(|| { + self.note_obligation_cause_code( + body_id, + err, + parent_predicate, + param_env, + &data.parent_code, + obligated_types, + seen_requirements, + ) + }); + } else { + ensure_sufficient_stack(|| { + self.note_obligation_cause_code( + body_id, + err, + parent_predicate, + param_env, + cause_code.peel_derives(), + obligated_types, + seen_requirements, + ) + }); + } + } + ObligationCauseCode::ImplDerivedObligation(ref data) => { + let mut parent_trait_pred = + self.resolve_vars_if_possible(data.derived.parent_trait_pred); + let parent_def_id = parent_trait_pred.def_id(); + let mut file = None; + let self_ty = + self.tcx.short_ty_string(parent_trait_pred.skip_binder().self_ty(), &mut file); + let msg = format!( + "required for `{self_ty}` to implement `{}`", + parent_trait_pred.print_modifiers_and_trait_path() + ); + let mut is_auto_trait = false; + match self.tcx.hir().get_if_local(data.impl_or_alias_def_id) { + Some(Node::Item(hir::Item { + kind: hir::ItemKind::Trait(is_auto, ..), + ident, + .. + })) => { + // FIXME: we should do something else so that it works even on crate foreign + // auto traits. + is_auto_trait = matches!(is_auto, hir::IsAuto::Yes); + err.span_note(ident.span, msg); + } + Some(Node::Item(hir::Item { + kind: hir::ItemKind::Impl(hir::Impl { of_trait, self_ty, .. }), + .. + })) => { + let mut spans = Vec::with_capacity(2); + if let Some(trait_ref) = of_trait { + spans.push(trait_ref.path.span); + } + spans.push(self_ty.span); + let mut spans: MultiSpan = spans.into(); + if matches!( + self_ty.span.ctxt().outer_expn_data().kind, + ExpnKind::Macro(MacroKind::Derive, _) + ) || matches!( + of_trait.as_ref().map(|t| t.path.span.ctxt().outer_expn_data().kind), + Some(ExpnKind::Macro(MacroKind::Derive, _)) + ) { + spans.push_span_label( + data.span, + "unsatisfied trait bound introduced in this `derive` macro", + ); + } else if !data.span.is_dummy() && !data.span.overlaps(self_ty.span) { + spans.push_span_label( + data.span, + "unsatisfied trait bound introduced here", + ); + } + err.span_note(spans, msg); + } + _ => { + err.note(msg); + } + }; + + if let Some(file) = file { + err.note(format!( + "the full type name has been written to '{}'", + file.display(), + )); + } + let mut parent_predicate = parent_trait_pred; + let mut data = &data.derived; + let mut count = 0; + seen_requirements.insert(parent_def_id); + if is_auto_trait { + // We don't want to point at the ADT saying "required because it appears within + // the type `X`", like we would otherwise do in test `supertrait-auto-trait.rs`. + while let ObligationCauseCode::BuiltinDerivedObligation(derived) = + &*data.parent_code + { + let child_trait_ref = + self.resolve_vars_if_possible(derived.parent_trait_pred); + let child_def_id = child_trait_ref.def_id(); + if seen_requirements.insert(child_def_id) { + break; + } + data = derived; + parent_predicate = child_trait_ref.to_predicate(tcx); + parent_trait_pred = child_trait_ref; + } + } + while let ObligationCauseCode::ImplDerivedObligation(child) = &*data.parent_code { + // Skip redundant recursive obligation notes. See `ui/issue-20413.rs`. + let child_trait_pred = + self.resolve_vars_if_possible(child.derived.parent_trait_pred); + let child_def_id = child_trait_pred.def_id(); + if seen_requirements.insert(child_def_id) { + break; + } + count += 1; + data = &child.derived; + parent_predicate = child_trait_pred.to_predicate(tcx); + parent_trait_pred = child_trait_pred; + } + if count > 0 { + err.note(format!( + "{} redundant requirement{} hidden", + count, + pluralize!(count) + )); + let mut file = None; + let self_ty = self + .tcx + .short_ty_string(parent_trait_pred.skip_binder().self_ty(), &mut file); + err.note(format!( + "required for `{self_ty}` to implement `{}`", + parent_trait_pred.print_modifiers_and_trait_path() + )); + if let Some(file) = file { + err.note(format!( + "the full type name has been written to '{}'", + file.display(), + )); + } + } + // #74711: avoid a stack overflow + ensure_sufficient_stack(|| { + self.note_obligation_cause_code( + body_id, + err, + parent_predicate, + param_env, + &data.parent_code, + obligated_types, + seen_requirements, + ) + }); + } + ObligationCauseCode::DerivedObligation(ref data) => { + let parent_trait_ref = self.resolve_vars_if_possible(data.parent_trait_pred); + let parent_predicate = parent_trait_ref; + // #74711: avoid a stack overflow + ensure_sufficient_stack(|| { + self.note_obligation_cause_code( + body_id, + err, + parent_predicate, + param_env, + &data.parent_code, + obligated_types, + seen_requirements, + ) + }); + } + 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, + ref parent_code, + .. + } => { + self.note_function_argument_obligation( + body_id, + err, + arg_hir_id, + parent_code, + param_env, + predicate, + call_hir_id, + ); + ensure_sufficient_stack(|| { + self.note_obligation_cause_code( + body_id, + err, + predicate, + param_env, + parent_code, + obligated_types, + seen_requirements, + ) + }); + } + ObligationCauseCode::CompareImplItemObligation { trait_item_def_id, kind, .. } => { + let item_name = self.tcx.item_name(trait_item_def_id); + let msg = format!( + "the requirement `{predicate}` appears on the `impl`'s {kind} \ + `{item_name}` but not on the corresponding trait's {kind}", + ); + let sp = self + .tcx + .opt_item_ident(trait_item_def_id) + .map(|i| i.span) + .unwrap_or_else(|| self.tcx.def_span(trait_item_def_id)); + let mut assoc_span: MultiSpan = sp.into(); + assoc_span.push_span_label( + sp, + format!("this trait's {kind} doesn't have the requirement `{predicate}`"), + ); + if let Some(ident) = self + .tcx + .opt_associated_item(trait_item_def_id) + .and_then(|i| self.tcx.opt_item_ident(i.container_id(self.tcx))) + { + assoc_span.push_span_label(ident.span, "in this trait"); + } + err.span_note(assoc_span, msg); + } + ObligationCauseCode::TrivialBound => { + err.help("see issue #48214"); + if tcx.sess.opts.unstable_features.is_nightly_build() { + err.help("add `#![feature(trivial_bounds)]` to the crate attributes to enable"); + } + } + ObligationCauseCode::OpaqueReturnType(expr_info) => { + if let Some((expr_ty, expr_span)) = expr_info { + let expr_ty = with_forced_trimmed_paths!(self.ty_to_string(expr_ty)); + err.span_label( + expr_span, + with_forced_trimmed_paths!(format!( + "return type was inferred to be `{expr_ty}` here", + )), + ); + } + } + } + } + + #[instrument( + level = "debug", skip(self, err), fields(trait_pred.self_ty = ?trait_pred.self_ty()) + )] + fn suggest_await_before_try( + &self, + err: &mut Diagnostic, + obligation: &PredicateObligation<'tcx>, + trait_pred: ty::PolyTraitPredicate<'tcx>, + span: Span, + ) { + if let Some(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _)) = + self.tcx.coroutine_kind(obligation.cause.body_id) + { + let future_trait = self.tcx.require_lang_item(LangItem::Future, None); + + let self_ty = self.resolve_vars_if_possible(trait_pred.self_ty()); + let impls_future = self.type_implements_trait( + future_trait, + [self.tcx.instantiate_bound_regions_with_erased(self_ty)], + obligation.param_env, + ); + if !impls_future.must_apply_modulo_regions() { + return; + } + + 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| { + Ty::new_projection( + self.tcx, + item_def_id, + // Future::Output has no args + [trait_pred.self_ty()], + ) + }); + let InferOk { value: projection_ty, .. } = + self.at(&obligation.cause, obligation.param_env).normalize(projection_ty); + + debug!( + normalized_projection_type = ?self.resolve_vars_if_possible(projection_ty) + ); + let try_obligation = self.mk_trait_obligation_with_new_self_ty( + obligation.param_env, + trait_pred.map_bound(|trait_pred| (trait_pred, projection_ty.skip_binder())), + ); + debug!(try_trait_obligation = ?try_obligation); + if self.predicate_may_hold(&try_obligation) + && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) + && snippet.ends_with('?') + { + err.span_suggestion_verbose( + span.with_hi(span.hi() - BytePos(1)).shrink_to_hi(), + "consider `await`ing on the `Future`", + ".await", + Applicability::MaybeIncorrect, + ); + } + } + } + + fn suggest_floating_point_literal( + &self, + obligation: &PredicateObligation<'tcx>, + err: &mut Diagnostic, + trait_ref: &ty::PolyTraitRef<'tcx>, + ) { + let rhs_span = match obligation.cause.code() { + ObligationCauseCode::BinOp { rhs_span: Some(span), rhs_is_lit, .. } if *rhs_is_lit => { + span + } + _ => return, + }; + if let ty::Float(_) = trait_ref.skip_binder().self_ty().kind() + && let ty::Infer(InferTy::IntVar(_)) = trait_ref.skip_binder().args.type_at(1).kind() + { + err.span_suggestion_verbose( + rhs_span.shrink_to_hi(), + "consider using a floating-point literal by writing it with `.0`", + ".0", + Applicability::MaybeIncorrect, + ); + } + } + + fn suggest_derive( + &self, + obligation: &PredicateObligation<'tcx>, + err: &mut Diagnostic, + trait_pred: ty::PolyTraitPredicate<'tcx>, + ) { + let Some(diagnostic_name) = self.tcx.get_diagnostic_name(trait_pred.def_id()) else { + return; + }; + let (adt, args) = match trait_pred.skip_binder().self_ty().kind() { + ty::Adt(adt, args) if adt.did().is_local() => (adt, args), + _ => return, + }; + let can_derive = { + let is_derivable_trait = match diagnostic_name { + sym::Default => !adt.is_enum(), + sym::PartialEq | sym::PartialOrd => { + let rhs_ty = trait_pred.skip_binder().trait_ref.args.type_at(1); + trait_pred.skip_binder().self_ty() == rhs_ty + } + sym::Eq | sym::Ord | sym::Clone | sym::Copy | sym::Hash | sym::Debug => true, + _ => false, + }; + is_derivable_trait && + // Ensure all fields impl the trait. + adt.all_fields().all(|field| { + let field_ty = ty::GenericArg::from(field.ty(self.tcx, args)); + let trait_args = match diagnostic_name { + sym::PartialEq | sym::PartialOrd => { + Some(field_ty) + } + _ => None, + }; + // Also add host param, if present + let host = self.tcx.generics_of(trait_pred.def_id()).host_effect_index.map(|idx| trait_pred.skip_binder().trait_ref.args[idx]); + let trait_pred = trait_pred.map_bound_ref(|tr| ty::TraitPredicate { + trait_ref: ty::TraitRef::new(self.tcx, + trait_pred.def_id(), + [field_ty].into_iter().chain(trait_args).chain(host), + ), + ..*tr + }); + let field_obl = Obligation::new( + self.tcx, + obligation.cause.clone(), + obligation.param_env, + trait_pred, + ); + self.predicate_must_hold_modulo_regions(&field_obl) + }) + }; + if can_derive { + err.span_suggestion_verbose( + self.tcx.def_span(adt.did()).shrink_to_lo(), + format!( + "consider annotating `{}` with `#[derive({})]`", + trait_pred.skip_binder().self_ty(), + diagnostic_name, + ), + // FIXME(effects, const_trait_impl) derive_const as suggestion? + format!("#[derive({diagnostic_name})]\n"), + Applicability::MaybeIncorrect, + ); + } + } + + fn suggest_dereferencing_index( + &self, + obligation: &PredicateObligation<'tcx>, + err: &mut Diagnostic, + trait_pred: ty::PolyTraitPredicate<'tcx>, + ) { + if let ObligationCauseCode::ImplDerivedObligation(_) = obligation.cause.code() + && self + .tcx + .is_diagnostic_item(sym::SliceIndex, trait_pred.skip_binder().trait_ref.def_id) + && let ty::Slice(_) = trait_pred.skip_binder().trait_ref.args.type_at(1).kind() + && let ty::Ref(_, inner_ty, _) = trait_pred.skip_binder().self_ty().kind() + && let ty::Uint(ty::UintTy::Usize) = inner_ty.kind() + { + err.span_suggestion_verbose( + obligation.cause.span.shrink_to_lo(), + "dereference this index", + '*', + Applicability::MachineApplicable, + ); + } + } + + fn note_function_argument_obligation( + &self, + body_id: LocalDefId, + err: &mut Diagnostic, + arg_hir_id: HirId, + parent_code: &ObligationCauseCode<'tcx>, + param_env: ty::ParamEnv<'tcx>, + failed_pred: ty::Predicate<'tcx>, + call_hir_id: HirId, + ) { + let tcx = self.tcx; + if let Some(Node::Expr(expr)) = tcx.opt_hir_node(arg_hir_id) + && let Some(typeck_results) = &self.typeck_results + { + if let hir::Expr { kind: hir::ExprKind::Block(block, _), .. } = expr { + let inner_expr = expr.peel_blocks(); + let ty = typeck_results + .expr_ty_adjusted_opt(inner_expr) + .unwrap_or(Ty::new_misc_error(tcx)); + let span = inner_expr.span; + if Some(span) != err.span.primary_span() { + err.span_label( + span, + if ty.references_error() { + String::new() + } else { + let ty = with_forced_trimmed_paths!(self.ty_to_string(ty)); + format!("this tail expression is of type `{ty}`") + }, + ); + 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())) + { + if let [stmt, ..] = block.stmts + && let hir::StmtKind::Semi(value) = stmt.kind + && let hir::ExprKind::Closure(hir::Closure { + body, fn_decl_span, .. + }) = value.kind + && let body = tcx.hir().body(*body) + && !matches!(body.value.kind, hir::ExprKind::Block(..)) + { + // Check if the failed predicate was an expectation of a closure type + // and if there might have been a `{ |args|` typo instead of `|args| {`. + err.multipart_suggestion( + "you might have meant to open the closure body instead of placing \ + a closure within a block", + vec![ + (expr.span.with_hi(value.span.lo()), String::new()), + (fn_decl_span.shrink_to_hi(), " {".to_string()), + ], + Applicability::MaybeIncorrect, + ); + } else { + // Maybe the bare block was meant to be a closure. + err.span_suggestion_verbose( + expr.span.shrink_to_lo(), + "you might have meant to create the closure instead of a block", + format!( + "|{}| ", + (0..pred.trait_ref.args.len() - 1) + .map(|_| "_") + .collect::<Vec<_>>() + .join(", ") + ), + Applicability::MaybeIncorrect, + ); + } + } + } + } + + // FIXME: visit the ty to see if there's any closure involved, and if there is, + // check whether its evaluated return type is the same as the one corresponding + // to an associated type (as seen from `trait_pred`) in the predicate. Like in + // trait_pred `S: Sum<<Self as Iterator>::Item>` and predicate `i32: Sum<&()>` + let mut type_diffs = vec![]; + if let ObligationCauseCode::ExprBindingObligation(def_id, _, _, idx) = parent_code + && let Some(node_args) = typeck_results.node_args_opt(call_hir_id) + && let where_clauses = + self.tcx.predicates_of(def_id).instantiate(self.tcx, node_args) + && let Some(where_pred) = where_clauses.predicates.get(*idx) + { + 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); + let failed_pred = self.instantiate_binder_with_fresh_vars( + expr.span, + BoundRegionConversionTime::FnCall, + failed_pred, + ); + + let zipped = iter::zip(where_pred.trait_ref.args, failed_pred.trait_ref.args); + for (expected, actual) in zipped { + self.probe(|_| { + match self.at(&ObligationCause::misc(expr.span, body_id), param_env).eq( + DefineOpaqueTypes::No, + expected, + actual, + ) { + Ok(_) => (), // We ignore nested obligations here for now. + Err(err) => type_diffs.push(err), + } + }) + } + } 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: Ty::new_alias( + self.tcx, + ty::Projection, + where_pred.skip_binder().projection_ty, + ), + found, + })]; + } + } + if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind + && let hir::Path { res: Res::Local(hir_id), .. } = path + && let Some(hir::Node::Pat(binding)) = self.tcx.opt_hir_node(*hir_id) + && let parent_hir_id = self.tcx.hir().parent_id(binding.hir_id) + && let Some(hir::Node::Local(local)) = self.tcx.opt_hir_node(parent_hir_id) + && let Some(binding_expr) = local.init + { + // If the expression we're calling on is a binding, we want to point at the + // `let` when talking about the type. Otherwise we'll point at every part + // of the method chain with the type. + self.point_at_chain(binding_expr, typeck_results, type_diffs, param_env, err); + } else { + self.point_at_chain(expr, typeck_results, type_diffs, param_env, err); + } + } + let call_node = tcx.opt_hir_node(call_hir_id); + if let Some(Node::Expr(hir::Expr { + kind: hir::ExprKind::MethodCall(path, rcvr, ..), .. + })) = call_node + { + if Some(rcvr.span) == err.span.primary_span() { + err.replace_span_with(path.ident.span, true); + } + } + + if let Some(Node::Expr(expr)) = tcx.opt_hir_node(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.args.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_args(&[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) + { + 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, + ); + } + } + } + + fn look_for_iterator_item_mistakes( + &self, + assocs_in_this_method: &[Option<(Span, (DefId, Ty<'tcx>))>], + typeck_results: &TypeckResults<'tcx>, + type_diffs: &[TypeError<'tcx>], + param_env: ty::ParamEnv<'tcx>, + path_segment: &hir::PathSegment<'_>, + args: &[hir::Expr<'_>], + err: &mut Diagnostic, + ) { + let tcx = self.tcx; + // Special case for iterator chains, we look at potential failures of `Iterator::Item` + // not being `: Clone` and `Iterator::map` calls with spurious trailing `;`. + for entry in assocs_in_this_method { + let Some((_span, (def_id, ty))) = entry else { + continue; + }; + for diff in type_diffs { + let Sorts(expected_found) = diff else { + continue; + }; + if tcx.is_diagnostic_item(sym::IteratorItem, *def_id) + && path_segment.ident.name == sym::map + && self.can_eq(param_env, expected_found.found, *ty) + && let [arg] = args + && let hir::ExprKind::Closure(closure) = arg.kind + { + let body = tcx.hir().body(closure.body); + if let hir::ExprKind::Block(block, None) = body.value.kind + && let None = block.expr + && let [.., stmt] = block.stmts + && let hir::StmtKind::Semi(expr) = stmt.kind + // FIXME: actually check the expected vs found types, but right now + // the expected is a projection that we need to resolve. + // && let Some(tail_ty) = typeck_results.expr_ty_opt(expr) + && expected_found.found.is_unit() + { + err.span_suggestion_verbose( + expr.span.shrink_to_hi().with_hi(stmt.span.hi()), + "consider removing this semicolon", + String::new(), + Applicability::MachineApplicable, + ); + } + let expr = if let hir::ExprKind::Block(block, None) = body.value.kind + && let Some(expr) = block.expr + { + expr + } else { + body.value + }; + if let hir::ExprKind::MethodCall(path_segment, rcvr, [], span) = expr.kind + && path_segment.ident.name == sym::clone + && let Some(expr_ty) = typeck_results.expr_ty_opt(expr) + && let Some(rcvr_ty) = typeck_results.expr_ty_opt(rcvr) + && self.can_eq(param_env, expr_ty, rcvr_ty) + && let ty::Ref(_, ty, _) = expr_ty.kind() + { + err.span_label( + span, + format!( + "this method call is cloning the reference `{expr_ty}`, not \ + `{ty}` which doesn't implement `Clone`", + ), + ); + let ty::Param(..) = ty.kind() else { + continue; + }; + let hir = tcx.hir(); + let node = tcx.hir_node_by_def_id(hir.get_parent_item(expr.hir_id).def_id); + + let pred = ty::Binder::dummy(ty::TraitPredicate { + trait_ref: ty::TraitRef::from_lang_item( + tcx, + LangItem::Clone, + span, + [*ty], + ), + polarity: ty::ImplPolarity::Positive, + }); + let Some(generics) = node.generics() else { + continue; + }; + let Some(body_id) = node.body_id() else { + continue; + }; + suggest_restriction( + tcx, + hir.body_owner_def_id(body_id), + generics, + &format!("type parameter `{ty}`"), + err, + node.fn_sig(), + None, + pred, + None, + ); + } + } + } + } + } + + fn point_at_chain( + &self, + expr: &hir::Expr<'_>, + typeck_results: &TypeckResults<'tcx>, + type_diffs: Vec<TypeError<'tcx>>, + param_env: ty::ParamEnv<'tcx>, + err: &mut Diagnostic, + ) { + let mut primary_spans = vec![]; + let mut span_labels = vec![]; + + let tcx = self.tcx; + + let mut print_root_expr = true; + 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(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. + // vec![1, 2, 3].iter().map(mapper).sum<i32>() + // ^^^^^^ ^^^^^^^^^^^ + expr = rcvr_expr; + let assocs_in_this_method = + self.probe_assoc_types_at_expr(&type_diffs, span, prev_ty, expr.hir_id, param_env); + self.look_for_iterator_item_mistakes( + &assocs_in_this_method, + typeck_results, + &type_diffs, + param_env, + path_segment, + args, + err, + ); + assocs.push(assocs_in_this_method); + prev_ty = self.resolve_vars_if_possible( + 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 + && let hir::Path { res: Res::Local(hir_id), .. } = path + && let Some(hir::Node::Pat(binding)) = self.tcx.opt_hir_node(*hir_id) + && let Some(parent) = self.tcx.hir().find_parent(binding.hir_id) + { + // We've reached the root of the method call chain... + if let hir::Node::Local(local) = parent + && let Some(binding_expr) = local.init + { + // ...and it is a binding. Get the binding creation and continue the chain. + expr = binding_expr; + } + if let hir::Node::Param(param) = parent { + // ...and it is a an fn argument. + let prev_ty = self.resolve_vars_if_possible( + typeck_results + .node_type_opt(param.hir_id) + .unwrap_or(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()) { + assocs.push(assocs_in_this_method); + print_root_expr = false; + } + break; + } + } + } + // We want the type before deref coercions, otherwise we talk about `&[_]` + // instead of `Vec<_>`. + if let Some(ty) = typeck_results.expr_ty_opt(expr) + && print_root_expr + { + let ty = with_forced_trimmed_paths!(self.ty_to_string(ty)); + // Point at the root expression + // vec![1, 2, 3].iter().map(mapper).sum<i32>() + // ^^^^^^^^^^^^^ + span_labels.push((expr.span, format!("this expression has type `{ty}`"))); + }; + // Only show this if it is not a "trivial" expression (not a method + // chain) and there are associated types to talk about. + let mut assocs = assocs.into_iter().peekable(); + while let Some(assocs_in_method) = assocs.next() { + let Some(prev_assoc_in_method) = assocs.peek() else { + for entry in assocs_in_method { + let Some((span, (assoc, ty))) = entry else { + continue; + }; + if primary_spans.is_empty() + || type_diffs.iter().any(|diff| { + let Sorts(expected_found) = diff else { + return false; + }; + self.can_eq(param_env, expected_found.found, ty) + }) + { + // FIXME: this doesn't quite work for `Iterator::collect` + // because we have `Vec<i32>` and `()`, but we'd want `i32` + // to point at the `.into_iter()` call, but as long as we + // still point at the other method calls that might have + // introduced the issue, this is fine for now. + primary_spans.push(span); + } + span_labels.push(( + span, + with_forced_trimmed_paths!(format!( + "`{}` is `{ty}` here", + self.tcx.def_path_str(assoc), + )), + )); + } + break; + }; + for (entry, prev_entry) in + assocs_in_method.into_iter().zip(prev_assoc_in_method.into_iter()) + { + match (entry, prev_entry) { + (Some((span, (assoc, ty))), Some((_, (_, prev_ty)))) => { + let ty_str = with_forced_trimmed_paths!(self.ty_to_string(ty)); + + let assoc = with_forced_trimmed_paths!(self.tcx.def_path_str(assoc)); + if !self.can_eq(param_env, ty, *prev_ty) { + if type_diffs.iter().any(|diff| { + let Sorts(expected_found) = diff else { + return false; + }; + self.can_eq(param_env, expected_found.found, ty) + }) { + primary_spans.push(span); + } + span_labels + .push((span, format!("`{assoc}` changed to `{ty_str}` here"))); + } else { + span_labels.push((span, format!("`{assoc}` remains `{ty_str}` here"))); + } + } + (Some((span, (assoc, ty))), None) => { + span_labels.push(( + span, + with_forced_trimmed_paths!(format!( + "`{}` is `{}` here", + self.tcx.def_path_str(assoc), + self.ty_to_string(ty), + )), + )); + } + (None, Some(_)) | (None, None) => {} + } + } + } + if !primary_spans.is_empty() { + let mut multi_span: MultiSpan = primary_spans.into(); + for (span, label) in span_labels { + multi_span.push_span_label(span, label); + } + err.span_note( + multi_span, + "the method call chain might not have had the expected associated types", + ); + } + } + + fn probe_assoc_types_at_expr( + &self, + type_diffs: &[TypeError<'tcx>], + span: Span, + prev_ty: Ty<'tcx>, + body_id: hir::HirId, + param_env: ty::ParamEnv<'tcx>, + ) -> Vec<Option<(Span, (DefId, Ty<'tcx>))>> { + 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; + }; + let ty::Alias(ty::Projection, proj) = expected_found.expected.kind() else { + continue; + }; + + let origin = TypeVariableOrigin { kind: TypeVariableOriginKind::TypeInference, span }; + let trait_def_id = proj.trait_def_id(self.tcx); + // Make `Self` be equivalent to the type of the call chain + // expression we're looking at now, so that we can tell what + // for example `Iterator::Item` is at this point in the chain. + let args = GenericArgs::for_item(self.tcx, trait_def_id, |param, _| { + match param.kind { + ty::GenericParamDefKind::Type { .. } => { + if param.index == 0 { + return prev_ty.into(); + } + } + ty::GenericParamDefKind::Lifetime | ty::GenericParamDefKind::Const { .. } => {} + } + self.var_for_def(span, param) + }); + // This will hold the resolved type of the associated type, if the + // current expression implements the trait that associated type is + // in. For example, this would be what `Iterator::Item` is here. + let ty_var = self.infcx.next_ty_var(origin); + // This corresponds to `<ExprTy as Iterator>::Item = _`. + let projection = ty::Binder::dummy(ty::PredicateKind::Clause( + ty::ClauseKind::Projection(ty::ProjectionPredicate { + projection_ty: ty::AliasTy::new(self.tcx, proj.def_id, args), + 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( + self.tcx, + span, + body_def_id, + param_env, + projection, + )); + if ocx.select_where_possible().is_empty() { + // `ty_var` now holds the type that `Item` is for `ExprTy`. + let ty_var = self.resolve_vars_if_possible(ty_var); + assocs_in_this_method.push(Some((span, (proj.def_id, ty_var)))); + } else { + // `<ExprTy as Iterator>` didn't select, so likely we've + // reached the end of the iterator chain, like the originating + // `Vec<_>`. + // Keep the space consistent for later zipping. + assocs_in_this_method.push(None); + } + } + assocs_in_this_method + } + + /// If the type that failed selection is an array or a reference to an array, + /// but the trait is implemented for slices, suggest that the user converts + /// the array into a slice. + fn suggest_convert_to_slice( + &self, + err: &mut Diagnostic, + obligation: &PredicateObligation<'tcx>, + trait_ref: ty::PolyTraitRef<'tcx>, + candidate_impls: &[ImplCandidate<'tcx>], + span: Span, + ) { + // We can only suggest the slice coersion for function and binary operation arguments, + // since the suggestion would make no sense in turbofish or call + let (ObligationCauseCode::BinOp { .. } + | ObligationCauseCode::FunctionArgumentObligation { .. }) = obligation.cause.code() + else { + return; + }; + + // Three cases where we can make a suggestion: + // 1. `[T; _]` (array of T) + // 2. `&[T; _]` (reference to array of T) + // 3. `&mut [T; _]` (mutable reference to array of T) + let (element_ty, mut mutability) = match *trait_ref.skip_binder().self_ty().kind() { + ty::Array(element_ty, _) => (element_ty, None), + + ty::Ref(_, pointee_ty, mutability) => match *pointee_ty.kind() { + ty::Array(element_ty, _) => (element_ty, Some(mutability)), + _ => return, + }, + + _ => return, + }; + + // Go through all the candidate impls to see if any of them is for + // slices of `element_ty` with `mutability`. + let mut is_slice = |candidate: Ty<'tcx>| match *candidate.kind() { + ty::RawPtr(ty::TypeAndMut { ty: t, mutbl: m }) | ty::Ref(_, t, m) => { + if matches!(*t.kind(), ty::Slice(e) if e == element_ty) + && m == mutability.unwrap_or(m) + { + // Use the candidate's mutability going forward. + mutability = Some(m); + true + } else { + false + } + } + _ => false, + }; + + // Grab the first candidate that matches, if any, and make a suggestion. + if let Some(slice_ty) = candidate_impls + .iter() + .map(|trait_ref| trait_ref.trait_ref.self_ty()) + .find(|t| is_slice(*t)) + { + let msg = format!("convert the array to a `{slice_ty}` slice instead"); + + if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { + let mut suggestions = vec![]; + if snippet.starts_with('&') { + } else if let Some(hir::Mutability::Mut) = mutability { + suggestions.push((span.shrink_to_lo(), "&mut ".into())); + } else { + suggestions.push((span.shrink_to_lo(), "&".into())); + } + suggestions.push((span.shrink_to_hi(), "[..]".into())); + err.multipart_suggestion_verbose(msg, suggestions, Applicability::MaybeIncorrect); + } else { + err.span_help(span, msg); + } + } + } + + fn explain_hrtb_projection( + &self, + diag: &mut Diagnostic, + pred: ty::PolyTraitPredicate<'tcx>, + param_env: ty::ParamEnv<'tcx>, + cause: &ObligationCause<'tcx>, + ) { + if pred.skip_binder().has_escaping_bound_vars() && pred.skip_binder().has_non_region_infer() + { + self.probe(|_| { + let ocx = ObligationCtxt::new(self); + let pred = self.instantiate_binder_with_placeholders(pred); + let pred = ocx.normalize(&ObligationCause::dummy(), param_env, pred); + ocx.register_obligation(Obligation::new( + self.tcx, + ObligationCause::dummy(), + param_env, + pred, + )); + if !ocx.select_where_possible().is_empty() { + // encountered errors. + return; + } + + if let ObligationCauseCode::FunctionArgumentObligation { + call_hir_id, + arg_hir_id, + parent_code: _, + } = cause.code() + { + let arg_span = self.tcx.hir().span(*arg_hir_id); + let mut sp: MultiSpan = arg_span.into(); + + sp.push_span_label( + arg_span, + "the trait solver is unable to infer the \ + generic types that should be inferred from this argument", + ); + sp.push_span_label( + self.tcx.hir().span(*call_hir_id), + "add turbofish arguments to this call to \ + specify the types manually, even if it's redundant", + ); + diag.span_note( + sp, + "this is a known limitation of the trait solver that \ + will be lifted in the future", + ); + } else { + let mut sp: MultiSpan = cause.span.into(); + sp.push_span_label( + cause.span, + "try adding turbofish arguments to this expression to \ + specify the types manually, even if it's redundant", + ); + diag.span_note( + sp, + "this is a known limitation of the trait solver that \ + will be lifted in the future", + ); + } + }); + } + } + + fn suggest_desugaring_async_fn_in_trait( + &self, + err: &mut Diagnostic, + trait_ref: ty::PolyTraitRef<'tcx>, + ) { + // Don't suggest if RTN is active -- we should prefer a where-clause bound instead. + if self.tcx.features().return_type_notation { + return; + } + + let trait_def_id = trait_ref.def_id(); + + // Only suggest specifying auto traits + if !self.tcx.trait_is_auto(trait_def_id) { + return; + } + + // Look for an RPITIT + let ty::Alias(ty::Projection, alias_ty) = trait_ref.self_ty().skip_binder().kind() else { + return; + }; + let Some(ty::ImplTraitInTraitData::Trait { fn_def_id, opaque_def_id }) = + self.tcx.opt_rpitit_info(alias_ty.def_id) + else { + return; + }; + + let auto_trait = self.tcx.def_path_str(trait_def_id); + // ... which is a local function + let Some(fn_def_id) = fn_def_id.as_local() else { + // If it's not local, we can at least mention that the method is async, if it is. + if self.tcx.asyncness(fn_def_id).is_async() { + err.span_note( + self.tcx.def_span(fn_def_id), + format!( + "`{}::{}` is an `async fn` in trait, which does not \ + automatically imply that its future is `{auto_trait}`", + alias_ty.trait_ref(self.tcx), + self.tcx.item_name(fn_def_id) + ), + ); + } + return; + }; + let Some(hir::Node::TraitItem(item)) = self.tcx.opt_hir_node_by_def_id(fn_def_id) else { + return; + }; + + // ... whose signature is `async` (i.e. this is an AFIT) + let (sig, body) = item.expect_fn(); + let hir::FnRetTy::Return(hir::Ty { kind: hir::TyKind::OpaqueDef(def, ..), .. }) = + sig.decl.output + else { + // This should never happen, but let's not ICE. + return; + }; + + // Check that this is *not* a nested `impl Future` RPIT in an async fn + // (i.e. `async fn foo() -> impl Future`) + if def.owner_id.to_def_id() != opaque_def_id { + return; + } + + let Some(sugg) = suggest_desugaring_async_fn_to_impl_future_in_trait( + self.tcx, + *sig, + *body, + opaque_def_id.expect_local(), + &format!(" + {auto_trait}"), + ) else { + return; + }; + + let function_name = self.tcx.def_path_str(fn_def_id); + err.multipart_suggestion( + format!( + "`{auto_trait}` can be made part of the associated future's \ + guarantees for all implementations of `{function_name}`" + ), + sugg, + Applicability::MachineApplicable, + ); + } +} + +/// Add a hint to add a missing borrow or remove an unnecessary one. +fn hint_missing_borrow<'tcx>( + infcx: &InferCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + span: Span, + found: Ty<'tcx>, + expected: Ty<'tcx>, + found_node: Node<'_>, + err: &mut Diagnostic, +) { + if matches!(found_node, Node::TraitItem(..)) { + return; + } + + let found_args = match found.kind() { + ty::FnPtr(f) => infcx.instantiate_binder_with_placeholders(*f).inputs().iter(), + kind => { + span_bug!(span, "found was converted to a FnPtr above but is now {:?}", kind) + } + }; + let expected_args = match expected.kind() { + ty::FnPtr(f) => infcx.instantiate_binder_with_placeholders(*f).inputs().iter(), + kind => { + span_bug!(span, "expected was converted to a FnPtr above but is now {:?}", kind) + } + }; + + // This could be a variant constructor, for example. + let Some(fn_decl) = found_node.fn_decl() else { + return; + }; + + let args = fn_decl.inputs.iter(); + + let mut to_borrow = Vec::new(); + let mut remove_borrow = Vec::new(); + + for ((found_arg, expected_arg), arg) in found_args.zip(expected_args).zip(args) { + let (found_ty, found_refs) = get_deref_type_and_refs(*found_arg); + let (expected_ty, expected_refs) = get_deref_type_and_refs(*expected_arg); + + if infcx.can_eq(param_env, found_ty, expected_ty) { + // FIXME: This could handle more exotic cases like mutability mismatches too! + if found_refs.len() < expected_refs.len() + && found_refs[..] == expected_refs[expected_refs.len() - found_refs.len()..] + { + to_borrow.push(( + arg.span.shrink_to_lo(), + expected_refs[..expected_refs.len() - found_refs.len()] + .iter() + .map(|mutbl| format!("&{}", mutbl.prefix_str())) + .collect::<Vec<_>>() + .join(""), + )); + } else if found_refs.len() > expected_refs.len() { + let mut span = arg.span.shrink_to_lo(); + let mut left = found_refs.len() - expected_refs.len(); + let mut ty = arg; + while let hir::TyKind::Ref(_, mut_ty) = &ty.kind + && left > 0 + { + span = span.with_hi(mut_ty.ty.span.lo()); + ty = mut_ty.ty; + left -= 1; + } + let sugg = if left == 0 { + (span, String::new()) + } else { + (arg.span, expected_arg.to_string()) + }; + remove_borrow.push(sugg); + } + } + } + + if !to_borrow.is_empty() { + err.subdiagnostic(errors::AdjustSignatureBorrow::Borrow { to_borrow }); + } + + if !remove_borrow.is_empty() { + err.subdiagnostic(errors::AdjustSignatureBorrow::RemoveBorrow { remove_borrow }); + } +} + +/// Collect all the returned expressions within the input expression. +/// Used to point at the return spans when we want to suggest some change to them. +#[derive(Default)] +pub struct ReturnsVisitor<'v> { + pub returns: Vec<&'v hir::Expr<'v>>, + in_block_tail: bool, +} + +impl<'v> Visitor<'v> for ReturnsVisitor<'v> { + fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) { + // Visit every expression to detect `return` paths, either through the function's tail + // expression or `return` statements. We walk all nodes to find `return` statements, but + // we only care about tail expressions when `in_block_tail` is `true`, which means that + // they're in the return path of the function body. + match ex.kind { + hir::ExprKind::Ret(Some(ex)) => { + self.returns.push(ex); + } + hir::ExprKind::Block(block, _) if self.in_block_tail => { + self.in_block_tail = false; + for stmt in block.stmts { + hir::intravisit::walk_stmt(self, stmt); + } + self.in_block_tail = true; + if let Some(expr) = block.expr { + self.visit_expr(expr); + } + } + hir::ExprKind::If(_, then, else_opt) if self.in_block_tail => { + self.visit_expr(then); + if let Some(el) = else_opt { + self.visit_expr(el); + } + } + hir::ExprKind::Match(_, arms, _) if self.in_block_tail => { + for arm in arms { + self.visit_expr(arm.body); + } + } + // We need to walk to find `return`s in the entire body. + _ if !self.in_block_tail => hir::intravisit::walk_expr(self, ex), + _ => self.returns.push(ex), + } + } + + fn visit_body(&mut self, body: &'v hir::Body<'v>) { + assert!(!self.in_block_tail); + self.in_block_tail = true; + hir::intravisit::walk_body(self, body); + } +} + +/// Collect all the awaited expressions within the input expression. +#[derive(Default)] +struct AwaitsVisitor { + awaits: Vec<hir::HirId>, +} + +impl<'v> Visitor<'v> for AwaitsVisitor { + fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) { + if let hir::ExprKind::Yield(_, hir::YieldSource::Await { expr: Some(id) }) = ex.kind { + self.awaits.push(id) + } + hir::intravisit::walk_expr(self, ex) + } +} + +pub trait NextTypeParamName { + fn next_type_param_name(&self, name: Option<&str>) -> String; +} + +impl NextTypeParamName for &[hir::GenericParam<'_>] { + fn next_type_param_name(&self, name: Option<&str>) -> String { + // This is the list of possible parameter names that we might suggest. + let name = name.and_then(|n| n.chars().next()).map(|c| c.to_uppercase().to_string()); + let name = name.as_deref(); + let possible_names = [name.unwrap_or("T"), "T", "U", "V", "X", "Y", "Z", "A", "B", "C"]; + let used_names = self + .iter() + .filter_map(|p| match p.name { + hir::ParamName::Plain(ident) => Some(ident.name), + _ => None, + }) + .collect::<Vec<_>>(); + + possible_names + .iter() + .find(|n| !used_names.contains(&Symbol::intern(n))) + .unwrap_or(&"ParamName") + .to_string() + } +} + +/// Collect the spans that we see the generic param `param_did` +struct ReplaceImplTraitVisitor<'a> { + ty_spans: &'a mut Vec<Span>, + param_did: DefId, +} + +impl<'a, 'hir> hir::intravisit::Visitor<'hir> for ReplaceImplTraitVisitor<'a> { + fn visit_ty(&mut self, t: &'hir hir::Ty<'hir>) { + if let hir::TyKind::Path(hir::QPath::Resolved( + None, + hir::Path { res: Res::Def(_, segment_did), .. }, + )) = t.kind + { + if self.param_did == *segment_did { + // `fn foo(t: impl Trait)` + // ^^^^^^^^^^ get this to suggest `T` instead + + // There might be more than one `impl Trait`. + self.ty_spans.push(t.span); + return; + } + } + + hir::intravisit::walk_ty(self, t); + } +} + +pub(super) fn get_explanation_based_on_obligation<'tcx>( + tcx: TyCtxt<'tcx>, + obligation: &PredicateObligation<'tcx>, + trait_ref: ty::PolyTraitRef<'tcx>, + trait_predicate: &ty::PolyTraitPredicate<'tcx>, + pre_message: String, +) -> String { + if let ObligationCauseCode::MainFunctionType = obligation.cause.code() { + "consider using `()`, or a `Result`".to_owned() + } else { + let ty_desc = match trait_ref.skip_binder().self_ty().kind() { + ty::FnDef(_, _) => Some("fn item"), + ty::Closure(_, _) => Some("closure"), + _ => None, + }; + + match ty_desc { + Some(desc) => format!( + "{}the trait `{}` is not implemented for {} `{}`", + pre_message, + trait_predicate.print_modifiers_and_trait_path(), + desc, + tcx.short_ty_string(trait_ref.skip_binder().self_ty(), &mut None), + ), + None => format!( + "{}the trait `{}` is not implemented for `{}`", + pre_message, + trait_predicate.print_modifiers_and_trait_path(), + tcx.short_ty_string(trait_ref.skip_binder().self_ty(), &mut None), + ), + } + } +} + +// Replace `param` with `replace_ty` +struct ReplaceImplTraitFolder<'tcx> { + tcx: TyCtxt<'tcx>, + param: &'tcx ty::GenericParamDef, + replace_ty: Ty<'tcx>, +} + +impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ReplaceImplTraitFolder<'tcx> { + fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { + if let ty::Param(ty::ParamTy { index, .. }) = t.kind() { + if self.param.index == *index { + return self.replace_ty; + } + } + t.super_fold_with(self) + } + + fn interner(&self) -> TyCtxt<'tcx> { + self.tcx + } +} + +pub fn suggest_desugaring_async_fn_to_impl_future_in_trait<'tcx>( + tcx: TyCtxt<'tcx>, + sig: hir::FnSig<'tcx>, + body: hir::TraitFn<'tcx>, + opaque_def_id: LocalDefId, + add_bounds: &str, +) -> Option<Vec<(Span, String)>> { + let hir::IsAsync::Async(async_span) = sig.header.asyncness else { + return None; + }; + let Ok(async_span) = tcx.sess.source_map().span_extend_while(async_span, |c| c.is_whitespace()) + else { + return None; + }; + + let future = tcx.hir_node_by_def_id(opaque_def_id).expect_item().expect_opaque_ty(); + let [hir::GenericBound::Trait(trait_ref, _)] = future.bounds else { + // `async fn` should always lower to a single bound... but don't ICE. + return None; + }; + let Some(hir::PathSegment { args: Some(generics), .. }) = + trait_ref.trait_ref.path.segments.last() + else { + // desugaring to a single path segment for `Future<...>`. + return None; + }; + let Some(hir::TypeBindingKind::Equality { term: hir::Term::Ty(future_output_ty) }) = + generics.bindings.get(0).map(|binding| binding.kind) + else { + // Also should never happen. + return None; + }; + + let mut sugg = if future_output_ty.span.is_empty() { + vec![ + (async_span, String::new()), + ( + future_output_ty.span, + format!(" -> impl std::future::Future<Output = ()>{add_bounds}"), + ), + ] + } else { + vec![ + (future_output_ty.span.shrink_to_lo(), "impl std::future::Future<Output = ".to_owned()), + (future_output_ty.span.shrink_to_hi(), format!(">{add_bounds}")), + (async_span, String::new()), + ] + }; + + // If there's a body, we also need to wrap it in `async {}` + if let hir::TraitFn::Provided(body) = body { + let body = tcx.hir().body(body); + let body_span = body.value.span; + let body_span_without_braces = + body_span.with_lo(body_span.lo() + BytePos(1)).with_hi(body_span.hi() - BytePos(1)); + if body_span_without_braces.is_empty() { + sugg.push((body_span_without_braces, " async {} ".to_owned())); + } else { + sugg.extend([ + (body_span_without_braces.shrink_to_lo(), "async {".to_owned()), + (body_span_without_braces.shrink_to_hi(), "} ".to_owned()), + ]); + } + } + + Some(sugg) +} + +fn get_deref_type_and_refs(mut ty: Ty<'_>) -> (Ty<'_>, Vec<hir::Mutability>) { + let mut refs = vec![]; + + while let ty::Ref(_, new_ty, mutbl) = ty.kind() { + ty = *new_ty; + refs.push(*mutbl); + } + + (ty, refs) +} diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs new file mode 100644 index 00000000000..d2598b0defe --- /dev/null +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs @@ -0,0 +1,3597 @@ +// ignore-tidy-filelength :( + +use super::on_unimplemented::{AppendConstMessage, OnUnimplementedNote, TypeErrCtxtExt as _}; +use super::suggestions::{get_explanation_based_on_obligation, TypeErrCtxtExt as _}; +use crate::errors::{ClosureFnMutLabel, ClosureFnOnceLabel, ClosureKindMismatch}; +use crate::infer::error_reporting::{TyCategory, TypeAnnotationNeeded as ErrorCode}; +use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use crate::infer::InferCtxtExt as _; +use crate::infer::{self, InferCtxt}; +use crate::traits::error_reporting::infer_ctxt_ext::InferCtxtExt; +use crate::traits::error_reporting::{ambiguity, ambiguity::Ambiguity::*}; +use crate::traits::query::evaluate_obligation::InferCtxtExt as _; +use crate::traits::specialize::to_pretty_impl_header; +use crate::traits::NormalizeExt; +use crate::traits::{ + elaborate, FulfillmentError, FulfillmentErrorCode, MismatchedProjectionTypes, Obligation, + ObligationCause, ObligationCauseCode, ObligationCtxt, OutputTypeParameterMismatch, Overflow, + PredicateObligation, SelectionError, TraitNotObjectSafe, +}; +use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; +use rustc_errors::{ + pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, + MultiSpan, StashKey, Style, +}; +use rustc_hir as hir; +use rustc_hir::def::{DefKind, Namespace, Res}; +use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::intravisit::Visitor; +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::{DefiningAnchor, SelectionOutputTypeParameterMismatch}; +use rustc_middle::ty::abstract_const::NotConstEvaluatable; +use rustc_middle::ty::error::{ExpectedFound, TypeError}; +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::DumpSolverProofTree; +use rustc_session::Limit; +use rustc_span::def_id::LOCAL_CRATE; +use rustc_span::symbol::sym; +use rustc_span::{BytePos, ExpnKind, Span, Symbol, DUMMY_SP}; +use std::borrow::Cow; +use std::fmt; +use std::iter; + +use super::{ + dump_proof_tree, ArgKind, CandidateSimilarity, FindExprBySpan, FindTypeParam, + GetSafeTransmuteErrorAndReason, HasNumericInferVisitor, ImplCandidate, UnsatisfiedConst, +}; + +pub use rustc_infer::traits::error_reporting::*; + +pub trait TypeErrCtxtExt<'tcx> { + fn build_overflow_error<T>( + &self, + predicate: &T, + span: Span, + suggest_increasing_limit: bool, + ) -> DiagnosticBuilder<'tcx> + where + T: fmt::Display + TypeFoldable<TyCtxt<'tcx>> + Print<'tcx, FmtPrinter<'tcx, 'tcx>>; + + fn report_overflow_error<T>( + &self, + predicate: &T, + span: Span, + suggest_increasing_limit: bool, + mutate: impl FnOnce(&mut Diagnostic), + ) -> ! + where + T: fmt::Display + TypeFoldable<TyCtxt<'tcx>> + Print<'tcx, FmtPrinter<'tcx, 'tcx>>; + + fn report_overflow_no_abort(&self, obligation: PredicateObligation<'tcx>) -> ErrorGuaranteed; + + fn report_fulfillment_errors(&self, errors: Vec<FulfillmentError<'tcx>>) -> ErrorGuaranteed; + + fn report_overflow_obligation<T>( + &self, + obligation: &Obligation<'tcx, T>, + suggest_increasing_limit: bool, + ) -> ! + where + T: ToPredicate<'tcx> + Clone; + + fn suggest_new_overflow_limit(&self, err: &mut Diagnostic); + + fn report_overflow_obligation_cycle(&self, cycle: &[PredicateObligation<'tcx>]) -> !; + + /// The `root_obligation` parameter should be the `root_obligation` field + /// from a `FulfillmentError`. If no `FulfillmentError` is available, + /// then it should be the same as `obligation`. + fn report_selection_error( + &self, + obligation: PredicateObligation<'tcx>, + root_obligation: &PredicateObligation<'tcx>, + error: &SelectionError<'tcx>, + ); + + fn emit_specialized_closure_kind_error( + &self, + obligation: &PredicateObligation<'tcx>, + trait_ref: ty::PolyTraitRef<'tcx>, + ) -> Option<ErrorGuaranteed>; + + fn fn_arg_obligation(&self, obligation: &PredicateObligation<'tcx>) -> bool; + + fn try_conversion_context( + &self, + obligation: &PredicateObligation<'tcx>, + trait_ref: ty::TraitRef<'tcx>, + err: &mut Diagnostic, + ) -> bool; + + fn report_const_param_not_wf( + &self, + ty: Ty<'tcx>, + obligation: &PredicateObligation<'tcx>, + ) -> DiagnosticBuilder<'tcx>; +} + +impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { + fn report_fulfillment_errors( + &self, + mut errors: Vec<FulfillmentError<'tcx>>, + ) -> ErrorGuaranteed { + #[derive(Debug)] + struct ErrorDescriptor<'tcx> { + predicate: ty::Predicate<'tcx>, + index: Option<usize>, // None if this is an old error + } + + let mut error_map: FxIndexMap<_, Vec<_>> = self + .reported_trait_errors + .borrow() + .iter() + .map(|(&span, predicates)| { + ( + span, + predicates + .iter() + .map(|&predicate| ErrorDescriptor { predicate, index: None }) + .collect(), + ) + }) + .collect(); + + // Ensure `T: Sized` and `T: WF` obligations come last. This lets us display diagnostics + // with more relevant type information and hide redundant E0282 errors. + errors.sort_by_key(|e| match e.obligation.predicate.kind().skip_binder() { + ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) + if Some(pred.def_id()) == self.tcx.lang_items().sized_trait() => + { + 1 + } + ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(_)) => 3, + ty::PredicateKind::Coerce(_) => 2, + _ => 0, + }); + + for (index, error) in errors.iter().enumerate() { + // We want to ignore desugarings here: spans are equivalent even + // if one is the result of a desugaring and the other is not. + let mut span = error.obligation.cause.span; + let expn_data = span.ctxt().outer_expn_data(); + if let ExpnKind::Desugaring(_) = expn_data.kind { + span = expn_data.call_site; + } + + error_map.entry(span).or_default().push(ErrorDescriptor { + predicate: error.obligation.predicate, + index: Some(index), + }); + } + + // We do this in 2 passes because we want to display errors in order, though + // maybe it *is* better to sort errors by span or something. + let mut is_suppressed = vec![false; errors.len()]; + for (_, error_set) in error_map.iter() { + // We want to suppress "duplicate" errors with the same span. + for error in error_set { + if let Some(index) = error.index { + // Suppress errors that are either: + // 1) strictly implied by another error. + // 2) implied by an error with a smaller index. + for error2 in error_set { + if error2.index.is_some_and(|index2| is_suppressed[index2]) { + // Avoid errors being suppressed by already-suppressed + // errors, to prevent all errors from being suppressed + // at once. + continue; + } + + if self.error_implies(error2.predicate, error.predicate) + && !(error2.index >= error.index + && self.error_implies(error.predicate, error2.predicate)) + { + info!("skipping {:?} (implied by {:?})", error, error2); + is_suppressed[index] = true; + break; + } + } + } + } + } + + for from_expansion in [false, true] { + for (error, suppressed) in iter::zip(&errors, &is_suppressed) { + if !suppressed && error.obligation.cause.span.from_expansion() == from_expansion { + self.report_fulfillment_error(error); + // We want to ignore desugarings here: spans are equivalent even + // if one is the result of a desugaring and the other is not. + let mut span = error.obligation.cause.span; + let expn_data = span.ctxt().outer_expn_data(); + if let ExpnKind::Desugaring(_) = expn_data.kind { + span = expn_data.call_site; + } + self.reported_trait_errors + .borrow_mut() + .entry(span) + .or_default() + .push(error.obligation.predicate); + } + } + } + + self.dcx().span_delayed_bug(DUMMY_SP, "expected fulfillment errors") + } + + /// Reports that an overflow has occurred and halts compilation. We + /// halt compilation unconditionally because it is important that + /// overflows never be masked -- they basically represent computations + /// whose result could not be truly determined and thus we can't say + /// if the program type checks or not -- and they are unusual + /// occurrences in any case. + fn report_overflow_error<T>( + &self, + predicate: &T, + span: Span, + suggest_increasing_limit: bool, + mutate: impl FnOnce(&mut Diagnostic), + ) -> ! + where + T: fmt::Display + TypeFoldable<TyCtxt<'tcx>> + Print<'tcx, FmtPrinter<'tcx, 'tcx>>, + { + let mut err = self.build_overflow_error(predicate, span, suggest_increasing_limit); + mutate(&mut err); + err.emit(); + + self.dcx().abort_if_errors(); + // FIXME: this should be something like `build_overflow_error_fatal`, which returns + // `DiagnosticBuilder<', !>`. Then we don't even need anything after that `emit()`. + unreachable!( + "did not expect compilation to continue after `abort_if_errors`, \ + since an error was definitely emitted!" + ); + } + + fn build_overflow_error<T>( + &self, + predicate: &T, + span: Span, + suggest_increasing_limit: bool, + ) -> DiagnosticBuilder<'tcx> + where + T: fmt::Display + TypeFoldable<TyCtxt<'tcx>> + Print<'tcx, FmtPrinter<'tcx, 'tcx>>, + { + let predicate = self.resolve_vars_if_possible(predicate.clone()); + let mut pred_str = predicate.to_string(); + + if pred_str.len() > 50 { + // We don't need to save the type to a file, we will be talking about this type already + // in a separate note when we explain the obligation, so it will be available that way. + let mut cx: FmtPrinter<'_, '_> = + FmtPrinter::new_with_limit(self.tcx, Namespace::TypeNS, rustc_session::Limit(6)); + predicate.print(&mut cx).unwrap(); + pred_str = cx.into_buffer(); + } + let mut err = struct_span_err!( + self.dcx(), + span, + E0275, + "overflow evaluating the requirement `{}`", + pred_str, + ); + + if suggest_increasing_limit { + self.suggest_new_overflow_limit(&mut err); + } + + err + } + + /// Reports that an overflow has occurred and halts compilation. We + /// halt compilation unconditionally because it is important that + /// overflows never be masked -- they basically represent computations + /// whose result could not be truly determined and thus we can't say + /// if the program type checks or not -- and they are unusual + /// occurrences in any case. + fn report_overflow_obligation<T>( + &self, + obligation: &Obligation<'tcx, T>, + suggest_increasing_limit: bool, + ) -> ! + where + T: ToPredicate<'tcx> + Clone, + { + let predicate = obligation.predicate.clone().to_predicate(self.tcx); + let predicate = self.resolve_vars_if_possible(predicate); + self.report_overflow_error( + &predicate, + obligation.cause.span, + suggest_increasing_limit, + |err| { + self.note_obligation_cause_code( + obligation.cause.body_id, + err, + predicate, + obligation.param_env, + obligation.cause.code(), + &mut vec![], + &mut Default::default(), + ); + }, + ); + } + + fn suggest_new_overflow_limit(&self, err: &mut Diagnostic) { + let suggested_limit = match self.tcx.recursion_limit() { + Limit(0) => Limit(2), + limit => limit * 2, + }; + err.help(format!( + "consider increasing the recursion limit by adding a \ + `#![recursion_limit = \"{}\"]` attribute to your crate (`{}`)", + suggested_limit, + self.tcx.crate_name(LOCAL_CRATE), + )); + } + + /// Reports that a cycle was detected which led to overflow and halts + /// compilation. This is equivalent to `report_overflow_obligation` except + /// that we can give a more helpful error message (and, in particular, + /// we do not suggest increasing the overflow limit, which is not + /// going to help). + fn report_overflow_obligation_cycle(&self, cycle: &[PredicateObligation<'tcx>]) -> ! { + let cycle = self.resolve_vars_if_possible(cycle.to_owned()); + assert!(!cycle.is_empty()); + + debug!(?cycle, "report_overflow_error_cycle"); + + // The 'deepest' obligation is most likely to have a useful + // cause 'backtrace' + self.report_overflow_obligation( + cycle.iter().max_by_key(|p| p.recursion_depth).unwrap(), + false, + ); + } + + fn report_overflow_no_abort(&self, obligation: PredicateObligation<'tcx>) -> ErrorGuaranteed { + let obligation = self.resolve_vars_if_possible(obligation); + let mut err = self.build_overflow_error(&obligation.predicate, obligation.cause.span, true); + self.note_obligation_cause(&mut err, &obligation); + self.point_at_returns_when_relevant(&mut err, &obligation); + err.emit() + } + + fn report_selection_error( + &self, + mut obligation: PredicateObligation<'tcx>, + root_obligation: &PredicateObligation<'tcx>, + error: &SelectionError<'tcx>, + ) { + let tcx = self.tcx; + + if tcx.sess.opts.unstable_opts.next_solver.map(|c| c.dump_tree).unwrap_or_default() + == 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( + tcx.dcx().span_delayed_bug(span, "`report_selection_error` did not emit an error"), + ); + + let mut err = match *error { + SelectionError::Unimplemented => { + // If this obligation was generated as a result of well-formedness checking, see if we + // can get a better error message by performing HIR-based well-formedness checking. + if let ObligationCauseCode::WellFormed(Some(wf_loc)) = + root_obligation.cause.code().peel_derives() + && !obligation.predicate.has_non_region_infer() + { + if let Some(cause) = self + .tcx + .diagnostic_hir_wf_check((tcx.erase_regions(obligation.predicate), *wf_loc)) + { + obligation.cause = cause.clone(); + span = obligation.cause.span; + } + } + + if let ObligationCauseCode::CompareImplItemObligation { + impl_item_def_id, + trait_item_def_id, + kind: _, + } = *obligation.cause.code() + { + self.report_extra_impl_obligation( + span, + impl_item_def_id, + trait_item_def_id, + &format!("`{}`", obligation.predicate), + ) + .emit(); + 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::ClauseKind::Trait(trait_predicate)) => { + let trait_predicate = bound_predicate.rebind(trait_predicate); + let trait_predicate = self.resolve_vars_if_possible(trait_predicate); + let trait_ref = trait_predicate.to_poly_trait_ref(); + + if let Some(_guar) = self.emit_specialized_closure_kind_error(&obligation, trait_ref) { + return; + } + + // FIXME(effects) + let predicate_is_const = false; + + if self.dcx().has_errors().is_some() + && trait_predicate.references_error() + { + return; + } + if self.fn_arg_obligation(&obligation) { + // Silence redundant errors on binding acccess that are already + // reported on the binding definition (#56607). + return; + } + let mut file = None; + let (post_message, pre_message, type_def) = self + .get_parent_trait_ref(obligation.cause.code()) + .map(|(t, s)| { + let t = self.tcx.short_ty_string(t, &mut file); + ( + format!(" in `{t}`"), + format!("within `{t}`, "), + s.map(|s| (format!("within this `{t}`"), s)), + ) + }) + .unwrap_or_default(); + let file_note = file.map(|file| format!( + "the full trait has been written to '{}'", + file.display(), + )); + + let OnUnimplementedNote { + message, + label, + notes, + parent_label, + append_const_msg, + } = self.on_unimplemented_note(trait_ref, &obligation); + let have_alt_message = message.is_some() || label.is_some(); + let is_try_conversion = self.is_try_conversion(span, trait_ref.def_id()); + let is_unsize = + Some(trait_ref.def_id()) == self.tcx.lang_items().unsize_trait(); + let (message, notes, append_const_msg) = if is_try_conversion { + ( + Some(format!( + "`?` couldn't convert the error to `{}`", + trait_ref.skip_binder().self_ty(), + )), + vec![ + "the question mark operation (`?`) implicitly performs a \ + conversion on the error value using the `From` trait" + .to_owned(), + ], + Some(AppendConstMessage::Default), + ) + } else { + (message, notes, append_const_msg) + }; + + let err_msg = self.get_standard_error_message( + &trait_predicate, + message, + predicate_is_const, + append_const_msg, + post_message, + ); + + let (err_msg, safe_transmute_explanation) = if Some(trait_ref.def_id()) + == self.tcx.lang_items().transmute_trait() + { + // Recompute the safe transmute reason and use that for the error reporting + 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) + }; + + let mut err = struct_span_err!(self.dcx(), span, E0277, "{}", err_msg); + + let mut suggested = false; + if is_try_conversion { + suggested = self.try_conversion_context(&obligation, trait_ref.skip_binder(), &mut err); + } + + if is_try_conversion && let Some(ret_span) = self.return_type_span(&obligation) { + err.span_label( + ret_span, + format!( + "expected `{}` because of this", + trait_ref.skip_binder().self_ty() + ), + ); + } + + if Some(trait_ref.def_id()) == tcx.lang_items().tuple_trait() { + self.add_tuple_trait_message( + obligation.cause.code().peel_derives(), + &mut err, + ); + } + + if Some(trait_ref.def_id()) == tcx.lang_items().drop_trait() + && predicate_is_const + { + err.note("`~const Drop` was renamed to `~const Destruct`"); + err.note("See <https://github.com/rust-lang/rust/pull/94901> for more details"); + } + + let explanation = get_explanation_based_on_obligation( + self.tcx, + &obligation, + trait_ref, + &trait_predicate, + pre_message, + ); + + self.check_for_binding_assigned_block_without_tail_expression( + &obligation, + &mut err, + trait_predicate, + ); + if self.suggest_add_reference_to_arg( + &obligation, + &mut err, + trait_predicate, + have_alt_message, + ) { + self.note_obligation_cause(&mut err, &obligation); + err.emit(); + return; + } + + file_note.map(|note| err.note(note)); + if let Some(s) = label { + // If it has a custom `#[rustc_on_unimplemented]` + // error message, let's display it as the label! + err.span_label(span, s); + if !matches!(trait_ref.skip_binder().self_ty().kind(), ty::Param(_)) { + // When the self type is a type param We don't need to "the trait + // `std::marker::Sized` is not implemented for `T`" as we will point + // at the type param with a label to suggest constraining it. + err.help(explanation); + } + } else if let Some(custom_explanation) = safe_transmute_explanation { + err.span_label(span, custom_explanation); + } else { + err.span_label(span, explanation); + } + + if let ObligationCauseCode::Coercion { source, target } = + *obligation.cause.code().peel_derives() + { + if Some(trait_ref.def_id()) == self.tcx.lang_items().sized_trait() { + self.suggest_borrowing_for_object_cast( + &mut err, + root_obligation, + source, + target, + ); + } + } + + let UnsatisfiedConst(unsatisfied_const) = self + .maybe_add_note_for_unsatisfied_const( + &obligation, + trait_ref, + &trait_predicate, + &mut err, + span, + ); + + if let Some((msg, span)) = type_def { + err.span_label(span, msg); + } + for note in notes { + // If it has a custom `#[rustc_on_unimplemented]` note, let's display it + err.note(note); + } + if let Some(s) = parent_label { + let body = obligation.cause.body_id; + err.span_label(tcx.def_span(body), s); + } + + self.suggest_floating_point_literal(&obligation, &mut err, &trait_ref); + self.suggest_dereferencing_index(&obligation, &mut err, trait_predicate); + suggested |= self.suggest_dereferences(&obligation, &mut err, trait_predicate); + suggested |= self.suggest_fn_call(&obligation, &mut err, trait_predicate); + let impl_candidates = self.find_similar_impl_candidates(trait_predicate); + suggested = if let &[cand] = &impl_candidates[..] { + let cand = cand.trait_ref; + if let (ty::FnPtr(_), ty::FnDef(..)) = + (cand.self_ty().kind(), trait_ref.self_ty().skip_binder().kind()) + { + err.span_suggestion( + span.shrink_to_hi(), + format!( + "the trait `{}` is implemented for fn pointer `{}`, try casting using `as`", + cand.print_trait_sugared(), + cand.self_ty(), + ), + format!(" as {}", cand.self_ty()), + Applicability::MaybeIncorrect, + ); + true + } else { + false + } + } else { + false + } || suggested; + suggested |= + self.suggest_remove_reference(&obligation, &mut err, trait_predicate); + suggested |= self.suggest_semicolon_removal( + &obligation, + &mut err, + span, + trait_predicate, + ); + self.note_version_mismatch(&mut err, &trait_ref); + self.suggest_remove_await(&obligation, &mut err); + self.suggest_derive(&obligation, &mut err, trait_predicate); + + if Some(trait_ref.def_id()) == tcx.lang_items().try_trait() { + self.suggest_await_before_try( + &mut err, + &obligation, + trait_predicate, + span, + ); + } + + if self.suggest_add_clone_to_arg(&obligation, &mut err, trait_predicate) { + err.emit(); + return; + } + + if self.suggest_impl_trait(&mut err, &obligation, trait_predicate) { + err.emit(); + return; + } + + if is_unsize { + // If the obligation failed due to a missing implementation of the + // `Unsize` trait, give a pointer to why that might be the case + err.note( + "all implementations of `Unsize` are provided \ + automatically by the compiler, see \ + <https://doc.rust-lang.org/stable/std/marker/trait.Unsize.html> \ + for more information", + ); + } + + let is_fn_trait = tcx.is_fn_trait(trait_ref.def_id()); + let is_target_feature_fn = if let ty::FnDef(def_id, _) = + *trait_ref.skip_binder().self_ty().kind() + { + !self.tcx.codegen_fn_attrs(def_id).target_features.is_empty() + } else { + false + }; + if is_fn_trait && is_target_feature_fn { + err.note( + "`#[target_feature]` functions do not implement the `Fn` traits", + ); + } + + self.try_to_add_help_message( + &obligation, + trait_ref, + &trait_predicate, + &mut err, + span, + is_fn_trait, + suggested, + unsatisfied_const, + ); + + // Changing mutability doesn't make a difference to whether we have + // an `Unsize` impl (Fixes ICE in #71036) + if !is_unsize { + self.suggest_change_mut(&obligation, &mut err, trait_predicate); + } + + // If this error is due to `!: Trait` not implemented but `(): Trait` is + // implemented, and fallback has occurred, then it could be due to a + // variable that used to fallback to `()` now falling back to `!`. Issue a + // note informing about the change in behaviour. + if trait_predicate.skip_binder().self_ty().is_never() + && self.fallback_has_occurred + { + let predicate = trait_predicate.map_bound(|trait_pred| { + 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) { + err.note( + "this error might have been caused by changes to \ + Rust's type-inference algorithm (see issue #48950 \ + <https://github.com/rust-lang/rust/issues/48950> \ + for more information)", + ); + err.help("did you intend to use the type `()` here instead?"); + } + } + + self.explain_hrtb_projection(&mut err, trait_predicate, obligation.param_env, &obligation.cause); + self.suggest_desugaring_async_fn_in_trait(&mut err, trait_ref); + + // Return early if the trait is Debug or Display and the invocation + // originates within a standard library macro, because the output + // is otherwise overwhelming and unhelpful (see #85844 for an + // example). + + let in_std_macro = + match obligation.cause.span.ctxt().outer_expn_data().macro_def_id { + Some(macro_def_id) => { + let crate_name = tcx.crate_name(macro_def_id.krate); + crate_name == sym::std || crate_name == sym::core + } + None => false, + }; + + if in_std_macro + && matches!( + self.tcx.get_diagnostic_name(trait_ref.def_id()), + Some(sym::Debug | sym::Display) + ) + { + err.emit(); + return; + } + + err + } + + ty::PredicateKind::Subtype(predicate) => { + // Errors for Subtype predicates show up as + // `FulfillmentErrorCode::CodeSubtypeError`, + // not selection error. + span_bug!(span, "subtype requirement gave wrong error: `{:?}`", predicate) + } + + ty::PredicateKind::Coerce(predicate) => { + // Errors for Coerce predicates show up as + // `FulfillmentErrorCode::CodeSubtypeError`, + // not selection error. + span_bug!(span, "coerce requirement gave wrong error: `{:?}`", predicate) + } + + ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(..)) + | ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(..)) => { + span_bug!( + span, + "outlives clauses should not error outside borrowck. obligation: `{:?}`", + obligation + ) + } + + ty::PredicateKind::Clause(ty::ClauseKind::Projection(..)) => { + span_bug!( + span, + "projection clauses should be implied from elsewhere. obligation: `{:?}`", + obligation + ) + } + + ty::PredicateKind::ObjectSafe(trait_def_id) => { + let violations = self.tcx.object_safety_violations(trait_def_id); + report_object_safety_error(self.tcx, span, trait_def_id, violations) + } + + ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(ty)) => { + let ty = self.resolve_vars_if_possible(ty); + if self.next_trait_solver() { + // FIXME: we'll need a better message which takes into account + // which bounds actually failed to hold. + self.dcx().struct_span_err( + span, + format!("the type `{ty}` is not well-formed"), + ) + } else { + // WF predicates cannot themselves make + // errors. They can only block due to + // ambiguity; otherwise, they always + // degenerate into other obligations + // (which may fail). + span_bug!(span, "WF predicate not satisfied for {:?}", ty); + } + } + + ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..)) => { + // Errors for `ConstEvaluatable` predicates show up as + // `SelectionError::ConstEvalFailure`, + // not `Unimplemented`. + span_bug!( + span, + "const-evaluatable requirement gave wrong error: `{:?}`", + obligation + ) + } + + ty::PredicateKind::ConstEquate(..) => { + // Errors for `ConstEquate` predicates show up as + // `SelectionError::ConstEvalFailure`, + // not `Unimplemented`. + span_bug!( + span, + "const-equate requirement gave wrong error: `{:?}`", + obligation + ) + } + + ty::PredicateKind::Ambiguous => span_bug!(span, "ambiguous"), + + ty::PredicateKind::NormalizesTo(..) => span_bug!( + span, + "NormalizesTo predicate should never be the predicate cause of a SelectionError" + ), + + ty::PredicateKind::AliasRelate(..) => span_bug!( + span, + "AliasRelate predicate should never be the predicate cause of a SelectionError" + ), + + ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, ty)) => { + let mut diag = self.dcx().struct_span_err( + span, + format!("the constant `{ct}` is not of type `{ty}`"), + ); + self.note_type_err( + &mut diag, + &obligation.cause, + None, + None, + TypeError::Sorts(ty::error::ExpectedFound::new(true, ty, ct.ty())), + false, + false, + ); + diag + } + } + } + + OutputTypeParameterMismatch(box SelectionOutputTypeParameterMismatch { + found_trait_ref, + expected_trait_ref, + terr: terr @ TypeError::CyclicTy(_), + }) => self.report_type_parameter_mismatch_cyclic_type_error( + &obligation, + found_trait_ref, + expected_trait_ref, + terr, + ), + OutputTypeParameterMismatch(box SelectionOutputTypeParameterMismatch { + found_trait_ref, + expected_trait_ref, + terr: _, + }) => { + match self.report_type_parameter_mismatch_error( + &obligation, + span, + found_trait_ref, + expected_trait_ref, + ) { + Some(err) => err, + None => return, + } + } + + 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) + } + + SelectionError::NotConstEvaluatable(NotConstEvaluatable::MentionsInfer) => { + bug!( + "MentionsInfer should have been handled in `traits/fulfill.rs` or `traits/select/mod.rs`" + ) + } + SelectionError::NotConstEvaluatable(NotConstEvaluatable::MentionsParam) => { + match self.report_not_const_evaluatable_error(&obligation, span) { + Some(err) => err, + None => return, + } + } + + // Already reported in the query. + SelectionError::NotConstEvaluatable(NotConstEvaluatable::Error(_)) | + // Already reported. + Overflow(OverflowError::Error(_)) => return, + + Overflow(_) => { + bug!("overflow should be handled before the `report_selection_error` path"); + } + SelectionError::ErrorReporting => { + bug!("ErrorReporting Overflow should not reach `report_selection_err` call") + } + }; + + self.note_obligation_cause(&mut err, &obligation); + self.point_at_returns_when_relevant(&mut err, &obligation); + err.emit(); + } + + fn emit_specialized_closure_kind_error( + &self, + obligation: &PredicateObligation<'tcx>, + trait_ref: ty::PolyTraitRef<'tcx>, + ) -> Option<ErrorGuaranteed> { + if let ty::Closure(closure_def_id, closure_args) = *trait_ref.self_ty().skip_binder().kind() + && let Some(expected_kind) = self.tcx.fn_trait_kind_from_def_id(trait_ref.def_id()) + && let Some(found_kind) = self.closure_kind(closure_args) + && !found_kind.extends(expected_kind) + && let sig = closure_args.as_closure().sig() + && self.can_sub( + obligation.param_env, + trait_ref, + sig.map_bound(|sig| { + ty::TraitRef::new( + self.tcx, + trait_ref.def_id(), + [trait_ref.self_ty().skip_binder(), sig.inputs()[0]], + ) + }), + ) + { + let mut err = + self.report_closure_error(&obligation, closure_def_id, found_kind, expected_kind); + self.note_obligation_cause(&mut err, &obligation); + self.point_at_returns_when_relevant(&mut err, &obligation); + Some(err.emit()) + } else { + None + } + } + + fn fn_arg_obligation(&self, obligation: &PredicateObligation<'tcx>) -> bool { + if let ObligationCauseCode::FunctionArgumentObligation { arg_hir_id, .. } = + obligation.cause.code() + && let Some(Node::Expr(arg)) = self.tcx.opt_hir_node(*arg_hir_id) + && let arg = arg.peel_borrows() + && let hir::ExprKind::Path(hir::QPath::Resolved( + None, + hir::Path { res: hir::def::Res::Local(hir_id), .. }, + )) = arg.kind + && let Some(Node::Pat(pat)) = self.tcx.opt_hir_node(*hir_id) + && let Some(preds) = self.reported_trait_errors.borrow().get(&pat.span) + && preds.contains(&obligation.predicate) + { + return true; + } + false + } + + /// When the `E` of the resulting `Result<T, E>` in an expression `foo().bar().baz()?`, + /// identify thoe method chain sub-expressions that could or could not have been annotated + /// with `?`. + fn try_conversion_context( + &self, + obligation: &PredicateObligation<'tcx>, + trait_ref: ty::TraitRef<'tcx>, + err: &mut Diagnostic, + ) -> bool { + let span = obligation.cause.span; + struct V<'v> { + search_span: Span, + found: Option<&'v hir::Expr<'v>>, + } + impl<'v> Visitor<'v> for V<'v> { + fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) { + if let hir::ExprKind::Match(expr, _arms, hir::MatchSource::TryDesugar(_)) = ex.kind + { + if ex.span.with_lo(ex.span.hi() - BytePos(1)).source_equal(self.search_span) { + if let hir::ExprKind::Call(_, [expr, ..]) = expr.kind { + self.found = Some(expr); + return; + } + } + } + hir::intravisit::walk_expr(self, ex); + } + } + let hir_id = self.tcx.local_def_id_to_hir_id(obligation.cause.body_id); + let body_id = match self.tcx.opt_hir_node(hir_id) { + Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, _, body_id), .. })) => { + body_id + } + _ => return false, + }; + let mut v = V { search_span: span, found: None }; + v.visit_body(self.tcx.hir().body(*body_id)); + let Some(expr) = v.found else { + return false; + }; + let Some(typeck) = &self.typeck_results else { + return false; + }; + let Some((ObligationCauseCode::QuestionMark, Some(y))) = obligation.cause.code().parent() + else { + return false; + }; + if !self.tcx.is_diagnostic_item(sym::FromResidual, y.def_id()) { + return false; + } + let self_ty = trait_ref.self_ty(); + let found_ty = trait_ref.args.get(1).and_then(|a| a.as_type()); + + let mut prev_ty = self.resolve_vars_if_possible( + typeck.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(self.tcx)), + ); + + // We always look at the `E` type, because that's the only one affected by `?`. If the + // incorrect `Result<T, E>` is because of the `T`, we'll get an E0308 on the whole + // expression, after the `?` has "unwrapped" the `T`. + let get_e_type = |prev_ty: Ty<'tcx>| -> Option<Ty<'tcx>> { + let ty::Adt(def, args) = prev_ty.kind() else { + return None; + }; + let Some(arg) = args.get(1) else { + return None; + }; + if !self.tcx.is_diagnostic_item(sym::Result, def.did()) { + return None; + } + arg.as_type() + }; + + let mut suggested = false; + let mut chain = vec![]; + + // The following logic is simlar to `point_at_chain`, but that's focused on associated types + let mut expr = expr; + while let hir::ExprKind::MethodCall(path_segment, rcvr_expr, args, span) = expr.kind { + // Point at every method call in the chain with the `Result` type. + // let foo = bar.iter().map(mapper)?; + // ------ ----------- + expr = rcvr_expr; + chain.push((span, prev_ty)); + + let next_ty = self.resolve_vars_if_possible( + typeck.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(self.tcx)), + ); + + let is_diagnostic_item = |symbol: Symbol, ty: Ty<'tcx>| { + let ty::Adt(def, _) = ty.kind() else { + return false; + }; + self.tcx.is_diagnostic_item(symbol, def.did()) + }; + // For each method in the chain, see if this is `Result::map_err` or + // `Option::ok_or_else` and if it is, see if the closure passed to it has an incorrect + // trailing `;`. + if let Some(ty) = get_e_type(prev_ty) + && let Some(found_ty) = found_ty + // Ideally we would instead use `FnCtxt::lookup_method_for_diagnostic` for 100% + // accurate check, but we are in the wrong stage to do that and looking for + // `Result::map_err` by checking the Self type and the path segment is enough. + // sym::ok_or_else + && ( + ( // Result::map_err + path_segment.ident.name == sym::map_err + && is_diagnostic_item(sym::Result, next_ty) + ) || ( // Option::ok_or_else + path_segment.ident.name == sym::ok_or_else + && is_diagnostic_item(sym::Option, next_ty) + ) + ) + // Found `Result<_, ()>?` + && let ty::Tuple(tys) = found_ty.kind() + && tys.is_empty() + // The current method call returns `Result<_, ()>` + && self.can_eq(obligation.param_env, ty, found_ty) + // There's a single argument in the method call and it is a closure + && args.len() == 1 + && let Some(arg) = args.get(0) + && let hir::ExprKind::Closure(closure) = arg.kind + // The closure has a block for its body with no tail expression + && let body = self.tcx.hir().body(closure.body) + && let hir::ExprKind::Block(block, _) = body.value.kind + && let None = block.expr + // The last statement is of a type that can be converted to the return error type + && let [.., stmt] = block.stmts + && let hir::StmtKind::Semi(expr) = stmt.kind + && let expr_ty = self.resolve_vars_if_possible( + typeck.expr_ty_adjusted_opt(expr) + .unwrap_or(Ty::new_misc_error(self.tcx)), + ) + && self + .infcx + .type_implements_trait( + self.tcx.get_diagnostic_item(sym::From).unwrap(), + [self_ty, expr_ty], + obligation.param_env, + ) + .must_apply_modulo_regions() + { + suggested = true; + err.span_suggestion_short( + stmt.span.with_lo(expr.span.hi()), + "remove this semicolon", + String::new(), + Applicability::MachineApplicable, + ); + } + + prev_ty = next_ty; + + if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind + && let hir::Path { res: hir::def::Res::Local(hir_id), .. } = path + && let Some(hir::Node::Pat(binding)) = self.tcx.opt_hir_node(*hir_id) + && let Some(parent) = self.tcx.hir().find_parent(binding.hir_id) + { + // We've reached the root of the method call chain... + if let hir::Node::Local(local) = parent + && let Some(binding_expr) = local.init + { + // ...and it is a binding. Get the binding creation and continue the chain. + expr = binding_expr; + } + if let hir::Node::Param(_param) = parent { + // ...and it is a an fn argument. + break; + } + } + } + // `expr` is now the "root" expression of the method call chain, which can be any + // expression kind, like a method call or a path. If this expression is `Result<T, E>` as + // well, then we also point at it. + prev_ty = self.resolve_vars_if_possible( + typeck.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(self.tcx)), + ); + chain.push((expr.span, prev_ty)); + + let mut prev = None; + for (span, err_ty) in chain.into_iter().rev() { + let err_ty = get_e_type(err_ty); + let err_ty = match (err_ty, prev) { + (Some(err_ty), Some(prev)) if !self.can_eq(obligation.param_env, err_ty, prev) => { + err_ty + } + (Some(err_ty), None) => err_ty, + _ => { + prev = err_ty; + continue; + } + }; + if self + .infcx + .type_implements_trait( + self.tcx.get_diagnostic_item(sym::From).unwrap(), + [self_ty, err_ty], + obligation.param_env, + ) + .must_apply_modulo_regions() + { + if !suggested { + err.span_label(span, format!("this has type `Result<_, {err_ty}>`")); + } + } else { + err.span_label( + span, + format!( + "this can't be annotated with `?` because it has type `Result<_, {err_ty}>`", + ), + ); + } + prev = Some(err_ty); + } + suggested + } + + fn report_const_param_not_wf( + &self, + ty: Ty<'tcx>, + obligation: &PredicateObligation<'tcx>, + ) -> DiagnosticBuilder<'tcx> { + 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.dcx(), + span, + E0741, + "`{ty}` is forbidden as the type of a const generic parameter", + ) + } + ty::FnPtr(_) => { + struct_span_err!( + self.dcx(), + span, + E0741, + "using function pointers as const generic parameters is forbidden", + ) + } + ty::RawPtr(_) => { + struct_span_err!( + self.dcx(), + 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.dcx(), + 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.dcx(), + 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 + } +} + +pub(super) trait InferCtxtPrivExt<'tcx> { + // returns if `cond` not occurring implies that `error` does not occur - i.e., that + // `error` occurring implies that `cond` occurs. + fn error_implies(&self, cond: ty::Predicate<'tcx>, error: ty::Predicate<'tcx>) -> bool; + + fn report_fulfillment_error(&self, error: &FulfillmentError<'tcx>); + + fn report_projection_error( + &self, + obligation: &PredicateObligation<'tcx>, + error: &MismatchedProjectionTypes<'tcx>, + ); + + fn maybe_detailed_projection_msg( + &self, + pred: ty::ProjectionPredicate<'tcx>, + normalized_ty: ty::Term<'tcx>, + expected_ty: ty::Term<'tcx>, + ) -> Option<String>; + + fn fuzzy_match_tys( + &self, + a: Ty<'tcx>, + b: Ty<'tcx>, + ignoring_lifetimes: bool, + ) -> Option<CandidateSimilarity>; + + fn describe_closure(&self, kind: hir::ClosureKind) -> &'static str; + + fn find_similar_impl_candidates( + &self, + trait_pred: ty::PolyTraitPredicate<'tcx>, + ) -> Vec<ImplCandidate<'tcx>>; + + fn report_similar_impl_candidates( + &self, + impl_candidates: &[ImplCandidate<'tcx>], + trait_ref: ty::PolyTraitRef<'tcx>, + body_def_id: LocalDefId, + err: &mut Diagnostic, + other: bool, + param_env: ty::ParamEnv<'tcx>, + ) -> bool; + + fn report_similar_impl_candidates_for_root_obligation( + &self, + obligation: &PredicateObligation<'tcx>, + trait_predicate: ty::Binder<'tcx, ty::TraitPredicate<'tcx>>, + body_def_id: LocalDefId, + err: &mut Diagnostic, + ); + + /// Gets the parent trait chain start + fn get_parent_trait_ref( + &self, + code: &ObligationCauseCode<'tcx>, + ) -> Option<(Ty<'tcx>, Option<Span>)>; + + /// If the `Self` type of the unsatisfied trait `trait_ref` implements a trait + /// with the same path as `trait_ref`, a help message about + /// a probable version mismatch is added to `err` + fn note_version_mismatch( + &self, + err: &mut Diagnostic, + trait_ref: &ty::PolyTraitRef<'tcx>, + ) -> bool; + + /// Creates a `PredicateObligation` with `new_self_ty` replacing the existing type in the + /// `trait_ref`. + /// + /// For this to work, `new_self_ty` must have no escaping bound variables. + fn mk_trait_obligation_with_new_self_ty( + &self, + param_env: ty::ParamEnv<'tcx>, + trait_ref_and_ty: ty::Binder<'tcx, (ty::TraitPredicate<'tcx>, Ty<'tcx>)>, + ) -> PredicateObligation<'tcx>; + + fn maybe_report_ambiguity(&self, obligation: &PredicateObligation<'tcx>); + + fn predicate_can_apply( + &self, + param_env: ty::ParamEnv<'tcx>, + pred: ty::PolyTraitPredicate<'tcx>, + ) -> bool; + + fn note_obligation_cause(&self, err: &mut Diagnostic, obligation: &PredicateObligation<'tcx>); + + fn suggest_unsized_bound_if_applicable( + &self, + err: &mut Diagnostic, + obligation: &PredicateObligation<'tcx>, + ); + + fn annotate_source_of_ambiguity( + &self, + err: &mut Diagnostic, + impls: &[ambiguity::Ambiguity], + predicate: ty::Predicate<'tcx>, + ); + + fn maybe_suggest_unsized_generics(&self, err: &mut Diagnostic, span: Span, node: Node<'tcx>); + + fn maybe_indirection_for_unsized( + &self, + err: &mut Diagnostic, + item: &'tcx Item<'tcx>, + param: &'tcx GenericParam<'tcx>, + ) -> bool; + + fn is_recursive_obligation( + &self, + obligated_types: &mut Vec<Ty<'tcx>>, + cause_code: &ObligationCauseCode<'tcx>, + ) -> bool; + + fn get_standard_error_message( + &self, + trait_predicate: &ty::PolyTraitPredicate<'tcx>, + message: Option<String>, + predicate_is_const: bool, + append_const_msg: Option<AppendConstMessage>, + post_message: String, + ) -> String; + + fn get_safe_transmute_error_and_reason( + &self, + obligation: PredicateObligation<'tcx>, + trait_ref: ty::PolyTraitRef<'tcx>, + span: Span, + ) -> GetSafeTransmuteErrorAndReason; + + fn add_tuple_trait_message( + &self, + obligation_cause_code: &ObligationCauseCode<'tcx>, + err: &mut Diagnostic, + ); + + fn try_to_add_help_message( + &self, + obligation: &PredicateObligation<'tcx>, + trait_ref: ty::PolyTraitRef<'tcx>, + trait_predicate: &ty::PolyTraitPredicate<'tcx>, + err: &mut Diagnostic, + span: Span, + is_fn_trait: bool, + suggested: bool, + unsatisfied_const: bool, + ); + + fn add_help_message_for_fn_trait( + &self, + trait_ref: ty::PolyTraitRef<'tcx>, + err: &mut Diagnostic, + implemented_kind: ty::ClosureKind, + params: ty::Binder<'tcx, Ty<'tcx>>, + ); + + fn maybe_add_note_for_unsatisfied_const( + &self, + obligation: &PredicateObligation<'tcx>, + trait_ref: ty::PolyTraitRef<'tcx>, + trait_predicate: &ty::PolyTraitPredicate<'tcx>, + err: &mut Diagnostic, + span: Span, + ) -> UnsatisfiedConst; + + fn report_closure_error( + &self, + obligation: &PredicateObligation<'tcx>, + closure_def_id: DefId, + found_kind: ty::ClosureKind, + kind: ty::ClosureKind, + ) -> DiagnosticBuilder<'tcx>; + + fn report_type_parameter_mismatch_cyclic_type_error( + &self, + obligation: &PredicateObligation<'tcx>, + found_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, + expected_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, + terr: TypeError<'tcx>, + ) -> DiagnosticBuilder<'tcx>; + + fn report_opaque_type_auto_trait_leakage( + &self, + obligation: &PredicateObligation<'tcx>, + def_id: DefId, + ) -> DiagnosticBuilder<'tcx>; + + fn report_type_parameter_mismatch_error( + &self, + obligation: &PredicateObligation<'tcx>, + span: Span, + found_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, + expected_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, + ) -> Option<DiagnosticBuilder<'tcx>>; + + fn report_not_const_evaluatable_error( + &self, + obligation: &PredicateObligation<'tcx>, + span: Span, + ) -> Option<DiagnosticBuilder<'tcx>>; +} + +impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { + // returns if `cond` not occurring implies that `error` does not occur - i.e., that + // `error` occurring implies that `cond` occurs. + fn error_implies(&self, cond: ty::Predicate<'tcx>, error: ty::Predicate<'tcx>) -> bool { + if cond == error { + return true; + } + + // FIXME: It should be possible to deal with `ForAll` in a cleaner way. + let bound_error = error.kind(); + let (cond, error) = match (cond.kind().skip_binder(), bound_error.skip_binder()) { + ( + ty::PredicateKind::Clause(ty::ClauseKind::Trait(..)), + ty::PredicateKind::Clause(ty::ClauseKind::Trait(error)), + ) => (cond, bound_error.rebind(error)), + _ => { + // FIXME: make this work in other cases too. + return false; + } + }; + + for pred in elaborate(self.tcx, std::iter::once(cond)) { + let bound_predicate = pred.kind(); + if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(implication)) = + bound_predicate.skip_binder() + { + let error = error.to_poly_trait_ref(); + let implication = bound_predicate.rebind(implication.trait_ref); + // FIXME: I'm just not taking associated types at all here. + // Eventually I'll need to implement param-env-aware + // `Γ₁ ⊦ φ₁ => Γ₂ ⊦ φ₂` logic. + let param_env = ty::ParamEnv::empty(); + if self.can_sub(param_env, error, implication) { + debug!("error_implies: {:?} -> {:?} -> {:?}", cond, error, implication); + return true; + } + } + } + + false + } + + #[instrument(skip(self), level = "debug")] + fn report_fulfillment_error(&self, error: &FulfillmentError<'tcx>) { + if self.tcx.sess.opts.unstable_opts.next_solver.map(|c| c.dump_tree).unwrap_or_default() + == DumpSolverProofTree::OnError + { + dump_proof_tree(&error.root_obligation, self.infcx); + } + + match error.code { + FulfillmentErrorCode::CodeSelectionError(ref selection_error) => { + self.report_selection_error( + error.obligation.clone(), + &error.root_obligation, + selection_error, + ); + } + FulfillmentErrorCode::CodeProjectionError(ref e) => { + self.report_projection_error(&error.obligation, e); + } + FulfillmentErrorCode::CodeAmbiguity { overflow: false } => { + self.maybe_report_ambiguity(&error.obligation); + } + FulfillmentErrorCode::CodeAmbiguity { overflow: true } => { + self.report_overflow_no_abort(error.obligation.clone()); + } + FulfillmentErrorCode::CodeSubtypeError(ref expected_found, ref err) => { + self.report_mismatched_types( + &error.obligation.cause, + expected_found.expected, + expected_found.found, + *err, + ) + .emit(); + } + FulfillmentErrorCode::CodeConstEquateError(ref expected_found, ref err) => { + let mut diag = self.report_mismatched_consts( + &error.obligation.cause, + expected_found.expected, + expected_found.found, + *err, + ); + let code = error.obligation.cause.code().peel_derives().peel_match_impls(); + if let ObligationCauseCode::BindingObligation(..) + | ObligationCauseCode::ItemObligation(..) + | ObligationCauseCode::ExprBindingObligation(..) + | ObligationCauseCode::ExprItemObligation(..) = code + { + self.note_obligation_cause_code( + error.obligation.cause.body_id, + &mut diag, + error.obligation.predicate, + error.obligation.param_env, + code, + &mut vec![], + &mut Default::default(), + ); + } + diag.emit(); + } + FulfillmentErrorCode::CodeCycle(ref cycle) => { + self.report_overflow_obligation_cycle(cycle); + } + } + } + + #[instrument(level = "debug", skip_all)] + fn report_projection_error( + &self, + obligation: &PredicateObligation<'tcx>, + error: &MismatchedProjectionTypes<'tcx>, + ) { + let predicate = self.resolve_vars_if_possible(obligation.predicate); + + if predicate.references_error() { + return; + } + + self.probe(|_| { + 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::ClauseKind::Projection(data)) = + bound_predicate.skip_binder() + { + let data = self.instantiate_binder_with_fresh_vars( + obligation.cause.span, + infer::BoundRegionConversionTime::HigherRankedType, + bound_predicate.rebind(data), + ); + let unnormalized_term = match data.term.unpack() { + ty::TermKind::Ty(_) => Ty::new_projection( + self.tcx, + data.projection_ty.def_id, + data.projection_ty.args, + ) + .into(), + ty::TermKind::Const(ct) => ty::Const::new_unevaluated( + self.tcx, + ty::UnevaluatedConst { + def: data.projection_ty.def_id, + args: data.projection_ty.args, + }, + ct.ty(), + ) + .into(), + }; + // FIXME(-Znext-solver): For diagnostic purposes, it would be nice + // to deeply normalize this type. + let normalized_term = + ocx.normalize(&obligation.cause, obligation.param_env, unnormalized_term); + + debug!(?obligation.cause, ?obligation.param_env); + + debug!(?normalized_term, data.ty = ?data.term); + + let is_normalized_term_expected = !matches!( + obligation.cause.code().peel_derives(), + ObligationCauseCode::ItemObligation(_) + | ObligationCauseCode::BindingObligation(_, _) + | ObligationCauseCode::ExprItemObligation(..) + | ObligationCauseCode::ExprBindingObligation(..) + | ObligationCauseCode::Coercion { .. } + ); + + // constrain inference variables a bit more to nested obligations from normalize so + // we can have more helpful errors. + // + // we intentionally drop errors from normalization here, + // since the normalization is just done to improve the error message. + let _ = ocx.select_where_possible(); + + if let Err(new_err) = ocx.eq_exp( + &obligation.cause, + obligation.param_env, + is_normalized_term_expected, + normalized_term, + data.term, + ) { + (Some((data, is_normalized_term_expected, normalized_term, data.term)), new_err) + } else { + (None, error.err) + } + } else { + (None, error.err) + }; + + let msg = values + .and_then(|(predicate, _, normalized_term, expected_term)| { + self.maybe_detailed_projection_msg(predicate, normalized_term, expected_term) + }) + .unwrap_or_else(|| { + let mut cx = FmtPrinter::new_with_limit( + self.tcx, + Namespace::TypeNS, + rustc_session::Limit(10), + ); + with_forced_trimmed_paths!(format!("type mismatch resolving `{}`", { + self.resolve_vars_if_possible(predicate).print(&mut cx).unwrap(); + cx.into_buffer() + })) + }); + let mut diag = struct_span_err!(self.dcx(), obligation.cause.span, E0271, "{msg}"); + + let secondary_span = (|| { + let ty::PredicateKind::Clause(ty::ClauseKind::Projection(proj)) = + predicate.kind().skip_binder() + else { + return None; + }; + + let trait_assoc_item = self.tcx.opt_associated_item(proj.projection_ty.def_id)?; + let trait_assoc_ident = trait_assoc_item.ident(self.tcx); + + let mut associated_items = vec![]; + self.tcx.for_each_relevant_impl( + self.tcx.trait_of_item(proj.projection_ty.def_id)?, + proj.projection_ty.self_ty(), + |impl_def_id| { + associated_items.extend( + self.tcx + .associated_items(impl_def_id) + .in_definition_order() + .find(|assoc| assoc.ident(self.tcx) == trait_assoc_ident), + ); + }, + ); + + let [associated_item]: &[ty::AssocItem] = &associated_items[..] else { + return None; + }; + match self.tcx.hir().get_if_local(associated_item.def_id) { + Some( + hir::Node::TraitItem(hir::TraitItem { + kind: hir::TraitItemKind::Type(_, Some(ty)), + .. + }) + | hir::Node::ImplItem(hir::ImplItem { + kind: hir::ImplItemKind::Type(ty), + .. + }), + ) => Some(( + ty.span, + with_forced_trimmed_paths!(Cow::from(format!( + "type mismatch resolving `{}`", + { + let mut cx = FmtPrinter::new_with_limit( + self.tcx, + Namespace::TypeNS, + rustc_session::Limit(5), + ); + self.resolve_vars_if_possible(predicate).print(&mut cx).unwrap(); + cx.into_buffer() + } + ))), + )), + _ => None, + } + })(); + + self.note_type_err( + &mut diag, + &obligation.cause, + secondary_span, + values.map(|(_, is_normalized_ty_expected, normalized_ty, expected_ty)| { + infer::ValuePairs::Terms(ExpectedFound::new( + is_normalized_ty_expected, + normalized_ty, + expected_ty, + )) + }), + err, + true, + false, + ); + self.note_obligation_cause(&mut diag, obligation); + diag.emit(); + }); + } + + fn maybe_detailed_projection_msg( + &self, + pred: ty::ProjectionPredicate<'tcx>, + normalized_ty: ty::Term<'tcx>, + expected_ty: ty::Term<'tcx>, + ) -> Option<String> { + let trait_def_id = pred.projection_ty.trait_def_id(self.tcx); + let self_ty = pred.projection_ty.self_ty(); + + with_forced_trimmed_paths! { + if Some(pred.projection_ty.def_id) == self.tcx.lang_items().fn_once_output() { + let fn_kind = self_ty.prefix_string(self.tcx); + let item = match self_ty.kind() { + ty::FnDef(def, _) => self.tcx.item_name(*def).to_string(), + _ => self_ty.to_string(), + }; + Some(format!( + "expected `{item}` to be a {fn_kind} that returns `{expected_ty}`, but it \ + returns `{normalized_ty}`", + )) + } else if Some(trait_def_id) == self.tcx.lang_items().future_trait() { + Some(format!( + "expected `{self_ty}` to be a future that resolves to `{expected_ty}`, but it \ + resolves to `{normalized_ty}`" + )) + } else if Some(trait_def_id) == self.tcx.get_diagnostic_item(sym::Iterator) { + Some(format!( + "expected `{self_ty}` to be an iterator that yields `{expected_ty}`, but it \ + yields `{normalized_ty}`" + )) + } else { + None + } + } + } + + fn fuzzy_match_tys( + &self, + mut a: Ty<'tcx>, + mut b: Ty<'tcx>, + ignoring_lifetimes: bool, + ) -> Option<CandidateSimilarity> { + /// returns the fuzzy category of a given type, or None + /// if the type can be equated to any type. + fn type_category(tcx: TyCtxt<'_>, t: Ty<'_>) -> Option<u32> { + match t.kind() { + ty::Bool => Some(0), + ty::Char => Some(1), + ty::Str => Some(2), + ty::Adt(def, _) if Some(def.did()) == tcx.lang_items().string() => Some(2), + ty::Int(..) + | ty::Uint(..) + | ty::Float(..) + | ty::Infer(ty::IntVar(..) | ty::FloatVar(..)) => Some(4), + ty::Ref(..) | ty::RawPtr(..) => Some(5), + ty::Array(..) | ty::Slice(..) => Some(6), + ty::FnDef(..) | ty::FnPtr(..) => Some(7), + ty::Dynamic(..) => Some(8), + ty::Closure(..) => Some(9), + ty::Tuple(..) => Some(10), + ty::Param(..) => Some(11), + ty::Alias(ty::Projection, ..) => Some(12), + ty::Alias(ty::Inherent, ..) => Some(13), + ty::Alias(ty::Opaque, ..) => Some(14), + ty::Alias(ty::Weak, ..) => Some(15), + ty::Never => Some(16), + ty::Adt(..) => Some(17), + ty::Coroutine(..) => Some(18), + ty::Foreign(..) => Some(19), + ty::CoroutineWitness(..) => Some(20), + ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) | ty::Error(_) => None, + } + } + + let strip_references = |mut t: Ty<'tcx>| -> Ty<'tcx> { + loop { + match t.kind() { + ty::Ref(_, inner, _) | ty::RawPtr(ty::TypeAndMut { ty: inner, .. }) => { + t = *inner + } + _ => break t, + } + } + }; + + if !ignoring_lifetimes { + a = strip_references(a); + b = strip_references(b); + } + + let cat_a = type_category(self.tcx, a)?; + let cat_b = type_category(self.tcx, b)?; + if a == b { + Some(CandidateSimilarity::Exact { ignoring_lifetimes }) + } else if cat_a == cat_b { + match (a.kind(), b.kind()) { + (ty::Adt(def_a, _), ty::Adt(def_b, _)) => def_a == def_b, + (ty::Foreign(def_a), ty::Foreign(def_b)) => def_a == def_b, + // Matching on references results in a lot of unhelpful + // suggestions, so let's just not do that for now. + // + // We still upgrade successful matches to `ignoring_lifetimes: true` + // to prioritize that impl. + (ty::Ref(..) | ty::RawPtr(..), ty::Ref(..) | ty::RawPtr(..)) => { + self.fuzzy_match_tys(a, b, true).is_some() + } + _ => true, + } + .then_some(CandidateSimilarity::Fuzzy { ignoring_lifetimes }) + } else if ignoring_lifetimes { + None + } else { + self.fuzzy_match_tys(a, b, true) + } + } + + fn describe_closure(&self, kind: hir::ClosureKind) -> &'static str { + match kind { + hir::ClosureKind::Closure => "a closure", + hir::ClosureKind::Coroutine(kind) => match kind { + hir::CoroutineKind::Coroutine(_) => "a coroutine", + hir::CoroutineKind::Desugared( + hir::CoroutineDesugaring::Async, + hir::CoroutineSource::Block, + ) => "an async block", + hir::CoroutineKind::Desugared( + hir::CoroutineDesugaring::Async, + hir::CoroutineSource::Fn, + ) => "an async function", + hir::CoroutineKind::Desugared( + hir::CoroutineDesugaring::Async, + hir::CoroutineSource::Closure, + ) => "an async closure", + hir::CoroutineKind::Desugared( + hir::CoroutineDesugaring::AsyncGen, + hir::CoroutineSource::Block, + ) => "an async gen block", + hir::CoroutineKind::Desugared( + hir::CoroutineDesugaring::AsyncGen, + hir::CoroutineSource::Fn, + ) => "an async gen function", + hir::CoroutineKind::Desugared( + hir::CoroutineDesugaring::AsyncGen, + hir::CoroutineSource::Closure, + ) => "an async gen closure", + hir::CoroutineKind::Desugared( + hir::CoroutineDesugaring::Gen, + hir::CoroutineSource::Block, + ) => "a gen block", + hir::CoroutineKind::Desugared( + hir::CoroutineDesugaring::Gen, + hir::CoroutineSource::Fn, + ) => "a gen function", + hir::CoroutineKind::Desugared( + hir::CoroutineDesugaring::Gen, + hir::CoroutineSource::Closure, + ) => "a gen closure", + }, + } + } + + fn find_similar_impl_candidates( + &self, + trait_pred: ty::PolyTraitPredicate<'tcx>, + ) -> Vec<ImplCandidate<'tcx>> { + let mut candidates: Vec<_> = self + .tcx + .all_impls(trait_pred.def_id()) + .filter_map(|def_id| { + if self.tcx.impl_polarity(def_id) == ty::ImplPolarity::Negative + || !self.tcx.is_user_visible_dep(def_id.krate) + { + return None; + } + + let imp = self.tcx.impl_trait_ref(def_id).unwrap().skip_binder(); + + self.fuzzy_match_tys(trait_pred.skip_binder().self_ty(), imp.self_ty(), false).map( + |similarity| ImplCandidate { trait_ref: imp, similarity, impl_def_id: def_id }, + ) + }) + .collect(); + if candidates.iter().any(|c| matches!(c.similarity, CandidateSimilarity::Exact { .. })) { + // If any of the candidates is a perfect match, we don't want to show all of them. + // This is particularly relevant for the case of numeric types (as they all have the + // same category). + candidates.retain(|c| matches!(c.similarity, CandidateSimilarity::Exact { .. })); + } + candidates + } + + fn report_similar_impl_candidates( + &self, + impl_candidates: &[ImplCandidate<'tcx>], + trait_ref: ty::PolyTraitRef<'tcx>, + body_def_id: LocalDefId, + err: &mut Diagnostic, + other: bool, + param_env: ty::ParamEnv<'tcx>, + ) -> bool { + // If we have a single implementation, try to unify it with the trait ref + // that failed. This should uncover a better hint for what *is* implemented. + if let [single] = &impl_candidates { + if self.probe(|_| { + let ocx = ObligationCtxt::new(self); + let obligation_trait_ref = self.instantiate_binder_with_placeholders(trait_ref); + let impl_args = self.fresh_args_for_item(DUMMY_SP, single.impl_def_id); + let impl_trait_ref = ocx.normalize( + &ObligationCause::dummy(), + param_env, + ty::EarlyBinder::bind(single.trait_ref).instantiate(self.tcx, impl_args), + ); + + ocx.register_obligations( + self.tcx + .predicates_of(single.impl_def_id) + .instantiate(self.tcx, impl_args) + .into_iter() + .map(|(clause, _)| { + Obligation::new(self.tcx, ObligationCause::dummy(), param_env, clause) + }), + ); + if !ocx.select_where_possible().is_empty() { + return false; + } + + let mut terrs = vec![]; + for (obligation_arg, impl_arg) in + std::iter::zip(obligation_trait_ref.args, impl_trait_ref.args) + { + if let Err(terr) = + ocx.eq(&ObligationCause::dummy(), param_env, impl_arg, obligation_arg) + { + terrs.push(terr); + } + if !ocx.select_where_possible().is_empty() { + return false; + } + } + + // Literally nothing unified, just give up. + if terrs.len() == impl_trait_ref.args.len() { + return false; + } + + let cand = + self.resolve_vars_if_possible(impl_trait_ref).fold_with(&mut BottomUpFolder { + tcx: self.tcx, + ty_op: |ty| ty, + lt_op: |lt| lt, + ct_op: |ct| ct.normalize(self.tcx, ty::ParamEnv::empty()), + }); + err.highlighted_help(vec![ + (format!("the trait `{}` ", cand.print_trait_sugared()), Style::NoStyle), + ("is".to_string(), Style::Highlight), + (" implemented for `".to_string(), Style::NoStyle), + (cand.self_ty().to_string(), Style::Highlight), + ("`".to_string(), Style::NoStyle), + ]); + + if let [TypeError::Sorts(exp_found)] = &terrs[..] { + let exp_found = self.resolve_vars_if_possible(*exp_found); + err.help(format!( + "for that trait implementation, expected `{}`, found `{}`", + exp_found.expected, exp_found.found + )); + } + + true + }) { + return true; + } + } + + let other = if other { "other " } else { "" }; + let report = |candidates: Vec<TraitRef<'tcx>>, err: &mut Diagnostic| { + if candidates.is_empty() { + return false; + } + if let &[cand] = &candidates[..] { + let (desc, mention_castable) = + match (cand.self_ty().kind(), trait_ref.self_ty().skip_binder().kind()) { + (ty::FnPtr(_), ty::FnDef(..)) => { + (" implemented for fn pointer `", ", cast using `as`") + } + (ty::FnPtr(_), _) => (" implemented for fn pointer `", ""), + _ => (" implemented for `", ""), + }; + err.highlighted_help(vec![ + (format!("the trait `{}` ", cand.print_trait_sugared()), Style::NoStyle), + ("is".to_string(), Style::Highlight), + (desc.to_string(), Style::NoStyle), + (cand.self_ty().to_string(), Style::Highlight), + ("`".to_string(), Style::NoStyle), + (mention_castable.to_string(), Style::NoStyle), + ]); + return true; + } + let trait_ref = TraitRef::identity(self.tcx, candidates[0].def_id); + // Check if the trait is the same in all cases. If so, we'll only show the type. + let mut traits: Vec<_> = + 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 args[1..] is the same. + let all_traits_equal = traits.len() == 1; + + let candidates: Vec<String> = candidates + .into_iter() + .map(|c| { + if all_traits_equal { + format!("\n {}", c.self_ty()) + } else { + format!("\n {c}") + } + }) + .collect(); + + let end = if candidates.len() <= 9 { candidates.len() } else { 8 }; + err.help(format!( + "the following {other}types implement trait `{}`:{}{}", + trait_ref.print_trait_sugared(), + candidates[..end].join(""), + if candidates.len() > 9 { + format!("\nand {} others", candidates.len() - 8) + } else { + String::new() + } + )); + true + }; + + let def_id = trait_ref.def_id(); + if impl_candidates.is_empty() { + if self.tcx.trait_is_auto(def_id) + || self.tcx.lang_items().iter().any(|(_, id)| id == def_id) + || self.tcx.get_diagnostic_name(def_id).is_some() + { + // Mentioning implementers of `Copy`, `Debug` and friends is not useful. + return false; + } + let mut impl_candidates: Vec<_> = self + .tcx + .all_impls(def_id) + // Ignore automatically derived impls and `!Trait` impls. + .filter(|&def_id| { + self.tcx.impl_polarity(def_id) != ty::ImplPolarity::Negative + || self.tcx.is_automatically_derived(def_id) + }) + .filter_map(|def_id| self.tcx.impl_trait_ref(def_id)) + .map(ty::EarlyBinder::instantiate_identity) + .filter(|trait_ref| { + let self_ty = trait_ref.self_ty(); + // Avoid mentioning type parameters. + if let ty::Param(_) = self_ty.kind() { + false + } + // Avoid mentioning types that are private to another crate + else if let ty::Adt(def, _) = self_ty.peel_refs().kind() { + // FIXME(compiler-errors): This could be generalized, both to + // be more granular, and probably look past other `#[fundamental]` + // types, too. + self.tcx.visibility(def.did()).is_accessible_from(body_def_id, self.tcx) + } else { + true + } + }) + .collect(); + + impl_candidates.sort(); + impl_candidates.dedup(); + return report(impl_candidates, err); + } + + // Sort impl candidates so that ordering is consistent for UI tests. + // because the ordering of `impl_candidates` may not be deterministic: + // https://github.com/rust-lang/rust/pull/57475#issuecomment-455519507 + // + // Prefer more similar candidates first, then sort lexicographically + // by their normalized string representation. + let mut impl_candidates: Vec<_> = impl_candidates + .iter() + .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.normalize(self.tcx, ty::ParamEnv::empty()), + }); + cand + }) + .collect(); + impl_candidates.sort_by_key(|cand| (cand.similarity, cand.trait_ref)); + let mut impl_candidates: Vec<_> = + impl_candidates.into_iter().map(|cand| cand.trait_ref).collect(); + impl_candidates.dedup(); + + report(impl_candidates, err) + } + + fn report_similar_impl_candidates_for_root_obligation( + &self, + obligation: &PredicateObligation<'tcx>, + trait_predicate: ty::Binder<'tcx, ty::TraitPredicate<'tcx>>, + body_def_id: LocalDefId, + err: &mut Diagnostic, + ) { + // This is *almost* equivalent to + // `obligation.cause.code().peel_derives()`, but it gives us the + // trait predicate for that corresponding root obligation. This + // lets us get a derived obligation from a type parameter, like + // when calling `string.strip_suffix(p)` where `p` is *not* an + // implementer of `Pattern<'_>`. + let mut code = obligation.cause.code(); + let mut trait_pred = trait_predicate; + let mut peeled = false; + while let Some((parent_code, parent_trait_pred)) = code.parent() { + code = parent_code; + if let Some(parent_trait_pred) = parent_trait_pred { + trait_pred = parent_trait_pred; + peeled = true; + } + } + let def_id = trait_pred.def_id(); + // Mention *all* the `impl`s for the *top most* obligation, the + // user might have meant to use one of them, if any found. We skip + // 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) + { + let trait_ref = trait_pred.to_poly_trait_ref(); + let impl_candidates = self.find_similar_impl_candidates(trait_pred); + self.report_similar_impl_candidates( + &impl_candidates, + trait_ref, + body_def_id, + err, + true, + obligation.param_env, + ); + } + } + + /// Gets the parent trait chain start + fn get_parent_trait_ref( + &self, + code: &ObligationCauseCode<'tcx>, + ) -> Option<(Ty<'tcx>, Option<Span>)> { + match code { + ObligationCauseCode::BuiltinDerivedObligation(data) => { + let parent_trait_ref = self.resolve_vars_if_possible(data.parent_trait_pred); + match self.get_parent_trait_ref(&data.parent_code) { + Some(t) => Some(t), + None => { + let ty = parent_trait_ref.skip_binder().self_ty(); + let span = TyCategory::from_ty(self.tcx, ty) + .map(|(_, def_id)| self.tcx.def_span(def_id)); + Some((ty, span)) + } + } + } + ObligationCauseCode::FunctionArgumentObligation { parent_code, .. } => { + self.get_parent_trait_ref(parent_code) + } + _ => None, + } + } + + /// If the `Self` type of the unsatisfied trait `trait_ref` implements a trait + /// with the same path as `trait_ref`, a help message about + /// a probable version mismatch is added to `err` + fn note_version_mismatch( + &self, + err: &mut Diagnostic, + trait_ref: &ty::PolyTraitRef<'tcx>, + ) -> bool { + let get_trait_impls = |trait_def_id| { + let mut trait_impls = vec![]; + self.tcx.for_each_relevant_impl( + trait_def_id, + trait_ref.skip_binder().self_ty(), + |impl_def_id| { + trait_impls.push(impl_def_id); + }, + ); + trait_impls + }; + + let required_trait_path = self.tcx.def_path_str(trait_ref.def_id()); + let traits_with_same_path: std::collections::BTreeSet<_> = self + .tcx + .all_traits() + .filter(|trait_def_id| *trait_def_id != trait_ref.def_id()) + .filter(|trait_def_id| self.tcx.def_path_str(*trait_def_id) == required_trait_path) + .collect(); + let mut suggested = false; + for trait_with_same_path in traits_with_same_path { + let trait_impls = get_trait_impls(trait_with_same_path); + if trait_impls.is_empty() { + continue; + } + let impl_spans: Vec<_> = + trait_impls.iter().map(|impl_def_id| self.tcx.def_span(*impl_def_id)).collect(); + err.span_help( + impl_spans, + format!("trait impl{} with same name found", pluralize!(trait_impls.len())), + ); + let trait_crate = self.tcx.crate_name(trait_with_same_path.krate); + let crate_msg = + format!("perhaps two different versions of crate `{trait_crate}` are being used?"); + err.note(crate_msg); + suggested = true; + } + suggested + } + + fn mk_trait_obligation_with_new_self_ty( + &self, + param_env: ty::ParamEnv<'tcx>, + trait_ref_and_ty: ty::Binder<'tcx, (ty::TraitPredicate<'tcx>, Ty<'tcx>)>, + ) -> PredicateObligation<'tcx> { + let trait_pred = + trait_ref_and_ty.map_bound(|(tr, new_self_ty)| tr.with_self_ty(self.tcx, new_self_ty)); + + Obligation::new(self.tcx, ObligationCause::dummy(), param_env, trait_pred) + } + + #[instrument(skip(self), level = "debug")] + fn maybe_report_ambiguity(&self, obligation: &PredicateObligation<'tcx>) { + // Unable to successfully determine, probably means + // insufficient type information, but could mean + // ambiguous impls. The latter *ought* to be a + // coherence violation, so we don't report it here. + + let predicate = self.resolve_vars_if_possible(obligation.predicate); + let span = obligation.cause.span; + + debug!(?predicate, obligation.cause.code = ?obligation.cause.code()); + + // Ambiguity errors are often caused as fallout from earlier errors. + // We ignore them if this `infcx` is tainted in some cases below. + + let bound_predicate = predicate.kind(); + let mut err = match bound_predicate.skip_binder() { + ty::PredicateKind::Clause(ty::ClauseKind::Trait(data)) => { + let trait_ref = bound_predicate.rebind(data.trait_ref); + debug!(?trait_ref); + + if predicate.references_error() { + return; + } + + // This is kind of a hack: it frequently happens that some earlier + // error prevents types from being fully inferred, and then we get + // a bunch of uninteresting errors saying something like "<generic + // #0> doesn't implement Sized". It may even be true that we + // could just skip over all checks where the self-ty is an + // inference variable, but I was afraid that there might be an + // inference variable created, registered as an obligation, and + // then never forced by writeback, and hence by skipping here we'd + // be ignoring the fact that we don't KNOW the type works + // out. Though even that would probably be harmless, given that + // we're only talking about builtin traits, which are known to be + // inhabited. We used to check for `self.tcx.sess.has_errors()` to + // avoid inundating the user with unnecessary errors, but we now + // check upstream for type errors and don't add the obligations to + // begin with in those cases. + if self.tcx.lang_items().sized_trait() == Some(trait_ref.def_id()) { + if let None = self.tainted_by_errors() { + let err = self.emit_inference_failure_err( + obligation.cause.body_id, + span, + trait_ref.self_ty().skip_binder().into(), + ErrorCode::E0282, + false, + ); + err.stash(span, StashKey::MaybeForgetReturn); + } + return; + } + + // Typically, this ambiguity should only happen if + // there are unresolved type inference variables + // (otherwise it would suggest a coherence + // failure). But given #21974 that is not necessarily + // the case -- we can have multiple where clauses that + // are only distinguished by a region, which results + // in an ambiguity even when all types are fully + // known, since we don't dispatch based on region + // relationships. + + // Pick the first substitution that still contains inference variables as the one + // we're going to emit an error for. If there are none (see above), fall back to + // a more general error. + let subst = data.trait_ref.args.iter().find(|s| s.has_non_region_infer()); + + let mut err = if let Some(subst) = subst { + self.emit_inference_failure_err( + obligation.cause.body_id, + span, + subst, + ErrorCode::E0283, + true, + ) + } else { + struct_span_err!( + self.dcx(), + span, + E0283, + "type annotations needed: cannot satisfy `{}`", + predicate, + ) + }; + + let mut ambiguities = ambiguity::recompute_applicable_impls( + self.infcx, + &obligation.with(self.tcx, trait_ref), + ); + let has_non_region_infer = + trait_ref.skip_binder().args.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 there are a lot of them, but only a few of them have no type + // params, we only show those, as they are more likely to be useful/intended. + if ambiguities.len() > 5 { + let infcx = self.infcx; + if !ambiguities.iter().all(|option| match option { + DefId(did) => infcx.fresh_args_for_item(DUMMY_SP, *did).is_empty(), + ParamEnv(_) => true, + }) { + // If not all are blanket impls, we filter blanked impls out. + ambiguities.retain(|option| match option { + DefId(did) => infcx.fresh_args_for_item(DUMMY_SP, *did).is_empty(), + ParamEnv(_) => true, + }); + } + } + 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() < 40 { + self.report_similar_impl_candidates( + impl_candidates.as_slice(), + trait_ref, + obligation.cause.body_id, + &mut err, + false, + obligation.param_env, + ); + } + } + + if let ObligationCauseCode::ItemObligation(def_id) + | ObligationCauseCode::ExprItemObligation(def_id, ..) = *obligation.cause.code() + { + self.suggest_fully_qualified_path(&mut err, def_id, span, trait_ref.def_id()); + } + + if let Some(ty::GenericArgKind::Type(_)) = subst.map(|subst| subst.unpack()) + && let Some(body_id) = + self.tcx.hir().maybe_body_owned_by(obligation.cause.body_id) + { + let mut expr_finder = FindExprBySpan::new(span); + expr_finder.visit_expr(self.tcx.hir().body(body_id).value); + + if let Some(hir::Expr { + kind: hir::ExprKind::Path(hir::QPath::Resolved(None, path)), + .. + }) = expr_finder.result + && let [ + .., + trait_path_segment @ hir::PathSegment { + res: Res::Def(DefKind::Trait, trait_id), + .. + }, + hir::PathSegment { + ident: assoc_item_name, + res: Res::Def(_, item_id), + .. + }, + ] = path.segments + && data.trait_ref.def_id == *trait_id + && self.tcx.trait_of_item(*item_id) == Some(*trait_id) + && let None = self.tainted_by_errors() + { + let (verb, noun) = match self.tcx.associated_item(item_id).kind { + ty::AssocKind::Const => ("refer to the", "constant"), + ty::AssocKind::Fn => ("call", "function"), + // This is already covered by E0223, but this following single match + // arm doesn't hurt here. + ty::AssocKind::Type => ("refer to the", "type"), + }; + + // Replace the more general E0283 with a more specific error + err.cancel(); + err = self.dcx().struct_span_err_with_code( + span, + format!( + "cannot {verb} associated {noun} on trait without specifying the \ + corresponding `impl` type", + ), + rustc_errors::error_code!(E0790), + ); + + if let Some(local_def_id) = data.trait_ref.def_id.as_local() + && let Some(hir::Node::Item(hir::Item { + ident: trait_name, + kind: hir::ItemKind::Trait(_, _, _, _, trait_item_refs), + .. + })) = self.tcx.opt_hir_node_by_def_id(local_def_id) + && let Some(method_ref) = trait_item_refs + .iter() + .find(|item_ref| item_ref.ident == *assoc_item_name) + { + err.span_label( + method_ref.span, + format!("`{trait_name}::{assoc_item_name}` defined here"), + ); + } + + err.span_label(span, format!("cannot {verb} associated {noun} of trait")); + + let trait_impls = self.tcx.trait_impls_of(data.trait_ref.def_id); + + if 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(); + // If there is only one implementation of the trait, suggest using it. + // Otherwise, use a placeholder comment for the implementation. + let (message, self_type) = if non_blanket_impl_count == 1 { + ( + "use the fully-qualified path to the only available \ + implementation", + format!( + "{}", + self.tcx.type_of(impl_def_id).instantiate_identity() + ), + ) + } else { + ( + "use a fully-qualified path to a specific available \ + implementation", + "/* self type */".to_string(), + ) + }; + let mut suggestions = + vec![(path.span.shrink_to_lo(), format!("<{self_type} as "))]; + if let Some(generic_arg) = trait_path_segment.args { + let between_span = + trait_path_segment.ident.span.between(generic_arg.span_ext); + // get rid of :: between Trait and <type> + // must be '::' between them, otherwise the parser won't accept the code + suggestions.push((between_span, "".to_string())); + suggestions + .push((generic_arg.span_ext.shrink_to_hi(), ">".to_string())); + } else { + suggestions.push(( + trait_path_segment.ident.span.shrink_to_hi(), + ">".to_string(), + )); + } + err.multipart_suggestion( + message, + suggestions, + Applicability::MaybeIncorrect, + ); + } + } + }; + + err + } + + ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => { + // Same hacky approach as above to avoid deluging user + // with error messages. + if arg.references_error() + || self.dcx().has_errors().is_some() + || self.tainted_by_errors().is_some() + { + return; + } + + self.emit_inference_failure_err( + obligation.cause.body_id, + span, + arg, + ErrorCode::E0282, + false, + ) + } + + ty::PredicateKind::Subtype(data) => { + if data.references_error() + || self.dcx().has_errors().is_some() + || self.tainted_by_errors().is_some() + { + // no need to overload user in such cases + return; + } + let SubtypePredicate { a_is_expected: _, a, b } = data; + // both must be type variables, or the other would've been instantiated + assert!(a.is_ty_var() && b.is_ty_var()); + self.emit_inference_failure_err( + obligation.cause.body_id, + span, + a.into(), + ErrorCode::E0282, + true, + ) + } + ty::PredicateKind::Clause(ty::ClauseKind::Projection(data)) => { + if predicate.references_error() || self.tainted_by_errors().is_some() { + return; + } + let subst = data + .projection_ty + .args + .iter() + .chain(Some(data.term.into_arg())) + .find(|g| g.has_non_region_infer()); + if let Some(subst) = subst { + let mut err = self.emit_inference_failure_err( + obligation.cause.body_id, + span, + subst, + ErrorCode::E0284, + true, + ); + err.note(format!("cannot satisfy `{predicate}`")); + err + } else { + // If we can't find a substitution, just print a generic error + let mut err = struct_span_err!( + self.dcx(), + span, + E0284, + "type annotations needed: cannot satisfy `{}`", + predicate, + ); + err.span_label(span, format!("cannot satisfy `{predicate}`")); + err + } + } + + ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(data)) => { + if predicate.references_error() || self.tainted_by_errors().is_some() { + return; + } + let subst = data.walk().find(|g| g.is_non_region_infer()); + if let Some(subst) = subst { + let err = self.emit_inference_failure_err( + obligation.cause.body_id, + span, + subst, + ErrorCode::E0284, + true, + ); + err + } else { + // If we can't find a substitution, just print a generic error + let mut err = struct_span_err!( + self.dcx(), + span, + E0284, + "type annotations needed: cannot satisfy `{}`", + predicate, + ); + err.span_label(span, format!("cannot satisfy `{predicate}`")); + err + } + } + _ => { + if self.dcx().has_errors().is_some() || self.tainted_by_errors().is_some() { + return; + } + let mut err = struct_span_err!( + self.dcx(), + span, + E0284, + "type annotations needed: cannot satisfy `{}`", + predicate, + ); + err.span_label(span, format!("cannot satisfy `{predicate}`")); + err + } + }; + self.note_obligation_cause(&mut err, obligation); + err.emit(); + } + + fn annotate_source_of_ambiguity( + &self, + err: &mut Diagnostic, + ambiguities: &[ambiguity::Ambiguity], + predicate: ty::Predicate<'tcx>, + ) { + let mut spans = vec![]; + let mut crates = vec![]; + let mut post = vec![]; + let mut has_param_env = false; + for ambiguity in ambiguities { + match ambiguity { + ambiguity::Ambiguity::DefId(impl_def_id) => { + match self.tcx.span_of_impl(*impl_def_id) { + Ok(span) => spans.push(span), + Err(name) => { + crates.push(name); + if let Some(header) = to_pretty_impl_header(self.tcx, *impl_def_id) { + post.push(header); + } + } + } + } + ambiguity::Ambiguity::ParamEnv(span) => { + has_param_env = true; + spans.push(*span); + } + } + } + let mut crate_names: Vec<_> = crates.iter().map(|n| format!("`{n}`")).collect(); + crate_names.sort(); + crate_names.dedup(); + post.sort(); + post.dedup(); + + if self.tainted_by_errors().is_some() + && (crate_names.len() == 1 + && spans.len() == 0 + && ["`core`", "`alloc`", "`std`"].contains(&crate_names[0].as_str()) + || predicate.visit_with(&mut HasNumericInferVisitor).is_break()) + { + // Avoid complaining about other inference issues for expressions like + // `42 >> 1`, where the types are still `{integer}`, but we want to + // Do we need `trait_ref.skip_binder().self_ty().is_numeric() &&` too? + // NOTE(eddyb) this was `.cancel()`, but `err` + // is borrowed, so we can't fully defuse it. + err.downgrade_to_delayed_bug(); + return; + } + + let msg = format!( + "multiple `impl`s{} satisfying `{}` found", + if has_param_env { " or `where` clauses" } else { "" }, + predicate + ); + let post = if post.len() > 1 || (post.len() == 1 && post[0].contains('\n')) { + format!(":\n{}", post.iter().map(|p| format!("- {p}")).collect::<Vec<_>>().join("\n"),) + } else if post.len() == 1 { + format!(": `{}`", post[0]) + } else { + String::new() + }; + + match (spans.len(), crates.len(), crate_names.len()) { + (0, 0, 0) => { + err.note(format!("cannot satisfy `{predicate}`")); + } + (0, _, 1) => { + err.note(format!("{} in the `{}` crate{}", msg, crates[0], post,)); + } + (0, _, _) => { + err.note(format!( + "{} in the following crates: {}{}", + msg, + crate_names.join(", "), + post, + )); + } + (_, 0, 0) => { + let span: MultiSpan = spans.into(); + err.span_note(span, msg); + } + (_, 1, 1) => { + let span: MultiSpan = spans.into(); + err.span_note(span, msg); + err.note(format!("and another `impl` found in the `{}` crate{}", crates[0], post,)); + } + _ => { + let span: MultiSpan = spans.into(); + err.span_note(span, msg); + err.note(format!( + "and more `impl`s found in the following crates: {}{}", + crate_names.join(", "), + post, + )); + } + } + } + + /// Returns `true` if the trait predicate may apply for *some* assignment + /// to the type parameters. + fn predicate_can_apply( + &self, + param_env: ty::ParamEnv<'tcx>, + pred: ty::PolyTraitPredicate<'tcx>, + ) -> bool { + struct ParamToVarFolder<'a, 'tcx> { + infcx: &'a InferCtxt<'tcx>, + var_map: FxHashMap<Ty<'tcx>, Ty<'tcx>>, + } + + impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for ParamToVarFolder<'a, 'tcx> { + fn interner(&self) -> TyCtxt<'tcx> { + self.infcx.tcx + } + + fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { + 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::MiscVariable, + span: DUMMY_SP, + }) + }) + } else { + ty.super_fold_with(self) + } + } + } + + self.probe(|_| { + let cleaned_pred = + pred.fold_with(&mut ParamToVarFolder { infcx: self, var_map: Default::default() }); + + let InferOk { value: cleaned_pred, .. } = + self.infcx.at(&ObligationCause::dummy(), param_env).normalize(cleaned_pred); + + let obligation = + Obligation::new(self.tcx, ObligationCause::dummy(), param_env, cleaned_pred); + + self.predicate_may_hold(&obligation) + }) + } + + fn note_obligation_cause(&self, err: &mut Diagnostic, obligation: &PredicateObligation<'tcx>) { + // First, attempt to add note to this error with an async-await-specific + // message, and fall back to regular note otherwise. + if !self.maybe_note_obligation_cause_for_async_await(err, obligation) { + self.note_obligation_cause_code( + obligation.cause.body_id, + err, + obligation.predicate, + obligation.param_env, + obligation.cause.code(), + &mut vec![], + &mut Default::default(), + ); + self.suggest_unsized_bound_if_applicable(err, obligation); + } + } + + #[instrument(level = "debug", skip_all)] + fn suggest_unsized_bound_if_applicable( + &self, + err: &mut Diagnostic, + obligation: &PredicateObligation<'tcx>, + ) { + 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; + }; + debug!(?pred, ?item_def_id, ?span); + + let (Some(node), true) = ( + self.tcx.hir().get_if_local(item_def_id), + Some(pred.def_id()) == self.tcx.lang_items().sized_trait(), + ) else { + return; + }; + self.maybe_suggest_unsized_generics(err, span, node); + } + + #[instrument(level = "debug", skip_all)] + fn maybe_suggest_unsized_generics(&self, err: &mut Diagnostic, span: Span, node: Node<'tcx>) { + let Some(generics) = node.generics() else { + return; + }; + let sized_trait = self.tcx.lang_items().sized_trait(); + debug!(?generics.params); + debug!(?generics.predicates); + let Some(param) = generics.params.iter().find(|param| param.span == span) else { + return; + }; + // Check that none of the explicit trait bounds is `Sized`. Assume that an explicit + // `Sized` bound is there intentionally and we don't need to suggest relaxing it. + let explicitly_sized = generics + .bounds_for_param(param.def_id) + .flat_map(|bp| bp.bounds) + .any(|bound| bound.trait_ref().and_then(|tr| tr.trait_def_id()) == sized_trait); + if explicitly_sized { + return; + } + debug!(?param); + match node { + hir::Node::Item( + item @ hir::Item { + // Only suggest indirection for uses of type parameters in ADTs. + kind: + hir::ItemKind::Enum(..) | hir::ItemKind::Struct(..) | hir::ItemKind::Union(..), + .. + }, + ) => { + if self.maybe_indirection_for_unsized(err, item, param) { + return; + } + } + _ => {} + }; + // Didn't add an indirection suggestion, so add a general suggestion to relax `Sized`. + let (span, separator) = if let Some(s) = generics.bounds_span_for_suggestions(param.def_id) + { + (s, " +") + } else { + (span.shrink_to_hi(), ":") + }; + err.span_suggestion_verbose( + span, + "consider relaxing the implicit `Sized` restriction", + format!("{separator} ?Sized"), + Applicability::MachineApplicable, + ); + } + + fn maybe_indirection_for_unsized( + &self, + err: &mut Diagnostic, + item: &Item<'tcx>, + param: &GenericParam<'tcx>, + ) -> bool { + // Suggesting `T: ?Sized` is only valid in an ADT if `T` is only used in a + // borrow. `struct S<'a, T: ?Sized>(&'a T);` is valid, `struct S<T: ?Sized>(T);` + // is not. Look for invalid "bare" parameter uses, and suggest using indirection. + let mut visitor = + FindTypeParam { param: param.name.ident().name, invalid_spans: vec![], nested: false }; + visitor.visit_item(item); + if visitor.invalid_spans.is_empty() { + return false; + } + let mut multispan: MultiSpan = param.span.into(); + multispan.push_span_label( + param.span, + format!("this could be changed to `{}: ?Sized`...", param.name.ident()), + ); + for sp in visitor.invalid_spans { + multispan.push_span_label( + sp, + format!("...if indirection were used here: `Box<{}>`", param.name.ident()), + ); + } + err.span_help( + multispan, + format!( + "you could relax the implicit `Sized` bound on `{T}` if it were \ + used through indirection like `&{T}` or `Box<{T}>`", + T = param.name.ident(), + ), + ); + true + } + + fn is_recursive_obligation( + &self, + obligated_types: &mut Vec<Ty<'tcx>>, + cause_code: &ObligationCauseCode<'tcx>, + ) -> bool { + if let ObligationCauseCode::BuiltinDerivedObligation(ref data) = cause_code { + let parent_trait_ref = self.resolve_vars_if_possible(data.parent_trait_pred); + let self_ty = parent_trait_ref.skip_binder().self_ty(); + if obligated_types.iter().any(|ot| ot == &self_ty) { + return true; + } + if let ty::Adt(def, args) = self_ty.kind() + && let [arg] = &args[..] + && let ty::GenericArgKind::Type(ty) = arg.unpack() + && let ty::Adt(inner_def, _) = ty.kind() + && inner_def == def + { + return true; + } + } + false + } + + fn get_standard_error_message( + &self, + trait_predicate: &ty::PolyTraitPredicate<'tcx>, + message: Option<String>, + predicate_is_const: bool, + append_const_msg: Option<AppendConstMessage>, + post_message: String, + ) -> String { + message + .and_then(|cannot_do_this| { + match (predicate_is_const, append_const_msg) { + // do nothing if predicate is not const + (false, _) => Some(cannot_do_this), + // suggested using default post message + (true, Some(AppendConstMessage::Default)) => { + Some(format!("{cannot_do_this} in const contexts")) + } + // overridden post message + (true, Some(AppendConstMessage::Custom(custom_msg, _))) => { + Some(format!("{cannot_do_this}{custom_msg}")) + } + // fallback to generic message + (true, None) => None, + } + }) + .unwrap_or_else(|| { + format!("the trait bound `{trait_predicate}` is not satisfied{post_message}") + }) + } + + fn get_safe_transmute_error_and_reason( + &self, + obligation: PredicateObligation<'tcx>, + trait_ref: ty::PolyTraitRef<'tcx>, + span: Span, + ) -> 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.instantiate_bound_regions_with_erased(trait_ref)); + + let src_and_dst = rustc_transmute::Types { + dst: trait_ref.args.type_at(0), + src: trait_ref.args.type_at(1), + }; + let scope = trait_ref.args.type_at(2); + let Some(assume) = rustc_transmute::Assume::from_const( + self.infcx.tcx, + obligation.param_env, + trait_ref.args.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, + ) { + Answer::No(reason) => { + let dst = trait_ref.args.type_at(0); + let src = trait_ref.args.type_at(1); + let err_msg = format!( + "`{src}` cannot be safely transmuted into `{dst}` in the defining scope of `{scope}`" + ); + let safe_transmute_explanation = match reason { + rustc_transmute::Reason::SrcIsUnspecified => { + format!("`{src}` does not have a well-specified layout") + } + + rustc_transmute::Reason::DstIsUnspecified => { + format!("`{dst}` does not have a well-specified layout") + } + + rustc_transmute::Reason::DstIsBitIncompatible => { + format!("At least one value of `{src}` isn't a bit-valid value of `{dst}`") + } + + rustc_transmute::Reason::DstIsPrivate => format!( + "`{dst}` is or contains a type or field that is not visible in that scope" + ), + rustc_transmute::Reason::DstIsTooBig => { + format!("The size of `{src}` is smaller than the size of `{dst}`") + } + rustc_transmute::Reason::SrcSizeOverflow => { + format!( + "values of the type `{src}` are too big for the current architecture" + ) + } + rustc_transmute::Reason::DstSizeOverflow => { + format!( + "values of the type `{dst}` are too big for the current architecture" + ) + } + 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") + } + }; + 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. + Answer::Yes => span_bug!( + span, + "Inconsistent rustc_transmute::is_transmutable(...) result, got Yes", + ), + other => span_bug!(span, "Unsupported rustc_transmute::Answer variant: `{other:?}`"), + } + } + + fn add_tuple_trait_message( + &self, + obligation_cause_code: &ObligationCauseCode<'tcx>, + err: &mut Diagnostic, + ) { + match obligation_cause_code { + ObligationCauseCode::RustCall => { + err.set_primary_message("functions with the \"rust-call\" ABI must take a single non-self tuple argument"); + } + ObligationCauseCode::BindingObligation(def_id, _) + | ObligationCauseCode::ItemObligation(def_id) + if self.tcx.is_fn_trait(*def_id) => + { + err.code(rustc_errors::error_code!(E0059)); + err.set_primary_message(format!( + "type parameter to bare `{}` trait must be a tuple", + self.tcx.def_path_str(*def_id) + )); + } + _ => {} + } + } + + fn try_to_add_help_message( + &self, + obligation: &PredicateObligation<'tcx>, + trait_ref: ty::PolyTraitRef<'tcx>, + trait_predicate: &ty::PolyTraitPredicate<'tcx>, + err: &mut Diagnostic, + span: Span, + is_fn_trait: bool, + suggested: bool, + unsatisfied_const: bool, + ) { + let body_def_id = obligation.cause.body_id; + let span = if let ObligationCauseCode::BinOp { rhs_span: Some(rhs_span), .. } = + obligation.cause.code() + { + *rhs_span + } else { + span + }; + + // Try to report a help message + if is_fn_trait + && let Ok((implemented_kind, params)) = self.type_implements_fn_trait( + obligation.param_env, + trait_ref.self_ty(), + trait_predicate.skip_binder().polarity, + ) + { + self.add_help_message_for_fn_trait(trait_ref, err, implemented_kind, params); + } else if !trait_ref.has_non_region_infer() + && self.predicate_can_apply(obligation.param_env, *trait_predicate) + { + // If a where-clause may be useful, remind the + // user that they can add it. + // + // don't display an on-unimplemented note, as + // these notes will often be of the form + // "the type `T` can't be frobnicated" + // which is somewhat confusing. + self.suggest_restricting_param_bound( + err, + *trait_predicate, + None, + obligation.cause.body_id, + ); + } else if trait_ref.def_id().is_local() + && self.tcx.trait_impls_of(trait_ref.def_id()).is_empty() + && !self.tcx.trait_is_auto(trait_ref.def_id()) + && !self.tcx.trait_is_alias(trait_ref.def_id()) + { + err.span_help( + self.tcx.def_span(trait_ref.def_id()), + crate::fluent_generated::trait_selection_trait_has_no_impls, + ); + } else if !suggested && !unsatisfied_const { + // Can't show anything else useful, try to find similar impls. + let impl_candidates = self.find_similar_impl_candidates(*trait_predicate); + if !self.report_similar_impl_candidates( + &impl_candidates, + trait_ref, + body_def_id, + err, + true, + obligation.param_env, + ) { + self.report_similar_impl_candidates_for_root_obligation( + obligation, + *trait_predicate, + body_def_id, + err, + ); + } + + self.suggest_convert_to_slice( + err, + obligation, + trait_ref, + impl_candidates.as_slice(), + span, + ); + } + } + + fn add_help_message_for_fn_trait( + &self, + trait_ref: ty::PolyTraitRef<'tcx>, + err: &mut Diagnostic, + implemented_kind: ty::ClosureKind, + params: ty::Binder<'tcx, Ty<'tcx>>, + ) { + // If the type implements `Fn`, `FnMut`, or `FnOnce`, suppress the following + // suggestion to add trait bounds for the type, since we only typically implement + // these traits once. + + // Note if the `FnMut` or `FnOnce` is less general than the trait we're trying + // to implement. + let selected_kind = self + .tcx + .fn_trait_kind_from_def_id(trait_ref.def_id()) + .expect("expected to map DefId to ClosureKind"); + if !implemented_kind.extends(selected_kind) { + err.note(format!( + "`{}` implements `{}`, but it must implement `{}`, which is more general", + trait_ref.skip_binder().self_ty(), + implemented_kind, + selected_kind + )); + } + + // Note any argument mismatches + let given_ty = params.skip_binder(); + let expected_ty = trait_ref.skip_binder().args.type_at(1); + if let ty::Tuple(given) = given_ty.kind() + && let ty::Tuple(expected) = expected_ty.kind() + { + if expected.len() != given.len() { + // Note number of types that were expected and given + err.note( + format!( + "expected a closure taking {} argument{}, but one taking {} argument{} was given", + given.len(), + pluralize!(given.len()), + expected.len(), + pluralize!(expected.len()), + ) + ); + } else if !self.same_type_modulo_infer(given_ty, expected_ty) { + // Print type mismatch + let (expected_args, given_args) = self.cmp(given_ty, expected_ty); + err.note_expected_found( + &"a closure with arguments", + expected_args, + &"a closure with arguments", + given_args, + ); + } + } + } + + fn maybe_add_note_for_unsatisfied_const( + &self, + _obligation: &PredicateObligation<'tcx>, + _trait_ref: ty::PolyTraitRef<'tcx>, + _trait_predicate: &ty::PolyTraitPredicate<'tcx>, + _err: &mut Diagnostic, + _span: Span, + ) -> UnsatisfiedConst { + let unsatisfied_const = UnsatisfiedConst(false); + // FIXME(effects) + unsatisfied_const + } + + fn report_closure_error( + &self, + obligation: &PredicateObligation<'tcx>, + closure_def_id: DefId, + found_kind: ty::ClosureKind, + kind: ty::ClosureKind, + ) -> DiagnosticBuilder<'tcx> { + let closure_span = self.tcx.def_span(closure_def_id); + + let mut err = ClosureKindMismatch { + closure_span, + expected: kind, + found: found_kind, + cause_span: obligation.cause.span, + fn_once_label: None, + fn_mut_label: None, + }; + + // Additional context information explaining why the closure only implements + // a particular trait. + if let Some(typeck_results) = &self.typeck_results { + let hir_id = self.tcx.local_def_id_to_hir_id(closure_def_id.expect_local()); + match (found_kind, typeck_results.closure_kind_origins().get(hir_id)) { + (ty::ClosureKind::FnOnce, Some((span, place))) => { + err.fn_once_label = Some(ClosureFnOnceLabel { + span: *span, + place: ty::place_to_string_for_capture(self.tcx, place), + }) + } + (ty::ClosureKind::FnMut, Some((span, place))) => { + err.fn_mut_label = Some(ClosureFnMutLabel { + span: *span, + place: ty::place_to_string_for_capture(self.tcx, place), + }) + } + _ => {} + } + } + + self.dcx().create_err(err) + } + + fn report_type_parameter_mismatch_cyclic_type_error( + &self, + obligation: &PredicateObligation<'tcx>, + found_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, + expected_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, + terr: TypeError<'tcx>, + ) -> DiagnosticBuilder<'tcx> { + let self_ty = found_trait_ref.self_ty().skip_binder(); + let (cause, terr) = if let ty::Closure(def_id, _) = self_ty.kind() { + ( + ObligationCause::dummy_with_span(self.tcx.def_span(def_id)), + TypeError::CyclicTy(self_ty), + ) + } else { + (obligation.cause.clone(), terr) + }; + self.report_and_explain_type_error( + TypeTrace::poly_trait_refs(&cause, true, expected_trait_ref, found_trait_ref), + terr, + ) + } + + fn report_opaque_type_auto_trait_leakage( + &self, + obligation: &PredicateObligation<'tcx>, + def_id: DefId, + ) -> DiagnosticBuilder<'tcx> { + let name = match self.tcx.opaque_type_origin(def_id.expect_local()) { + hir::OpaqueTyOrigin::FnReturn(_) | hir::OpaqueTyOrigin::AsyncFn(_) => { + "opaque type".to_string() + } + hir::OpaqueTyOrigin::TyAlias { .. } => { + format!("`{}`", self.tcx.def_path_debug_str(def_id)) + } + }; + let mut err = self.dcx().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(), + ); + } + }; + + if let Some(diag) = self.dcx().steal_diagnostic(self.tcx.def_span(def_id), StashKey::Cycle) + { + diag.cancel(); + } + + err + } + + fn report_type_parameter_mismatch_error( + &self, + obligation: &PredicateObligation<'tcx>, + span: Span, + found_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, + expected_trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, + ) -> Option<DiagnosticBuilder<'tcx>> { + let found_trait_ref = self.resolve_vars_if_possible(found_trait_ref); + let expected_trait_ref = self.resolve_vars_if_possible(expected_trait_ref); + + if expected_trait_ref.self_ty().references_error() { + return None; + } + + let Some(found_trait_ty) = found_trait_ref.self_ty().no_bound_vars() else { + return None; + }; + + let found_did = match *found_trait_ty.kind() { + ty::Closure(did, _) | ty::Foreign(did) | ty::FnDef(did, _) | ty::Coroutine(did, ..) => { + Some(did) + } + ty::Adt(def, _) => Some(def.did()), + _ => None, + }; + + let found_node = found_did.and_then(|did| self.tcx.hir().get_if_local(did)); + let found_span = found_did.and_then(|did| self.tcx.hir().span_if_local(did)); + + if self.reported_closure_mismatch.borrow().contains(&(span, found_span)) { + // We check closures twice, with obligations flowing in different directions, + // but we want to complain about them only once. + return None; + } + + self.reported_closure_mismatch.borrow_mut().insert((span, found_span)); + + let mut not_tupled = false; + + let found = match found_trait_ref.skip_binder().args.type_at(1).kind() { + ty::Tuple(tys) => vec![ArgKind::empty(); tys.len()], + _ => { + not_tupled = true; + vec![ArgKind::empty()] + } + }; + + let expected_ty = expected_trait_ref.skip_binder().args.type_at(1); + let expected = match expected_ty.kind() { + ty::Tuple(tys) => { + tys.iter().map(|t| ArgKind::from_expected_ty(t, Some(span))).collect() + } + _ => { + not_tupled = true; + vec![ArgKind::Arg("_".to_owned(), expected_ty.to_string())] + } + }; + + // If this is a `Fn` family trait and either the expected or found + // is not tupled, then fall back to just a regular mismatch error. + // This shouldn't be common unless manually implementing one of the + // traits manually, but don't make it more confusing when it does + // happen. + Some( + if Some(expected_trait_ref.def_id()) != self.tcx.lang_items().coroutine_trait() + && not_tupled + { + self.report_and_explain_type_error( + TypeTrace::poly_trait_refs( + &obligation.cause, + true, + expected_trait_ref, + found_trait_ref, + ), + ty::error::TypeError::Mismatch, + ) + } else if found.len() == expected.len() { + self.report_closure_arg_mismatch( + span, + found_span, + found_trait_ref, + expected_trait_ref, + obligation.cause.code(), + found_node, + obligation.param_env, + ) + } else { + let (closure_span, closure_arg_span, found) = found_did + .and_then(|did| { + let node = self.tcx.hir().get_if_local(did)?; + let (found_span, closure_arg_span, found) = + self.get_fn_like_arguments(node)?; + Some((Some(found_span), closure_arg_span, found)) + }) + .unwrap_or((found_span, None, found)); + + self.report_arg_count_mismatch( + span, + closure_span, + expected, + found, + found_trait_ty.is_closure(), + closure_arg_span, + ) + }, + ) + } + + fn report_not_const_evaluatable_error( + &self, + obligation: &PredicateObligation<'tcx>, + span: Span, + ) -> Option<DiagnosticBuilder<'tcx>> { + if !self.tcx.features().generic_const_exprs { + let mut err = self + .dcx() + .struct_span_err(span, "constant expression depends on a generic parameter"); + // FIXME(const_generics): we should suggest to the user how they can resolve this + // issue. However, this is currently not actually possible + // (see https://github.com/rust-lang/rust/issues/66962#issuecomment-575907083). + // + // Note that with `feature(generic_const_exprs)` this case should not + // be reachable. + err.note("this may fail depending on what value the parameter takes"); + err.emit(); + return None; + } + + match obligation.predicate.kind().skip_binder() { + ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(ct)) => match ct.kind() { + ty::ConstKind::Unevaluated(uv) => { + let mut err = + self.dcx().struct_span_err(span, "unconstrained generic constant"); + let const_span = self.tcx.def_span(uv.def); + match self.tcx.sess.source_map().span_to_snippet(const_span) { + Ok(snippet) => err.help(format!( + "try adding a `where` bound using this expression: `where [(); {snippet}]:`" + )), + _ => err.help("consider adding a `where` bound using this expression"), + }; + Some(err) + } + ty::ConstKind::Expr(_) => { + let err = self + .dcx() + .struct_span_err(span, format!("unconstrained generic constant `{ct}`")); + Some(err) + } + _ => { + bug!("const evaluatable failed for non-unevaluated const `{ct:?}`"); + } + }, + _ => { + span_bug!( + span, + "unexpected non-ConstEvaluatable predicate, this should not be reachable" + ) + } + } + } +} diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs new file mode 100644 index 00000000000..045d7e444b6 --- /dev/null +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -0,0 +1,826 @@ +use crate::infer::{InferCtxt, TyOrConstInferVar}; +use crate::traits::error_reporting::TypeErrCtxtExt; +use rustc_data_structures::captures::Captures; +use rustc_data_structures::obligation_forest::ProcessResult; +use rustc_data_structures::obligation_forest::{Error, ForestObligation, Outcome}; +use rustc_data_structures::obligation_forest::{ObligationForest, ObligationProcessor}; +use rustc_infer::infer::DefineOpaqueTypes; +use rustc_infer::traits::ProjectionCacheKey; +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}; +use rustc_middle::ty::GenericArgsRef; +use rustc_middle::ty::{self, Binder, Const, TypeVisitableExt}; +use std::marker::PhantomData; + +use super::const_evaluatable; +use super::project::{self, ProjectAndUnifyResult}; +use super::select::SelectionContext; +use super::wf; +use super::CodeAmbiguity; +use super::CodeProjectionError; +use super::CodeSelectionError; +use super::EvaluationResult; +use super::PredicateObligation; +use super::Unimplemented; +use super::{FulfillmentError, FulfillmentErrorCode}; + +use crate::traits::project::PolyProjectionObligation; +use crate::traits::project::ProjectionCacheKeyExt as _; +use crate::traits::query::evaluate_obligation::InferCtxtExt; + +impl<'tcx> ForestObligation for PendingPredicateObligation<'tcx> { + /// Note that we include both the `ParamEnv` and the `Predicate`, + /// as the `ParamEnv` can influence whether fulfillment succeeds + /// or fails. + type CacheKey = ty::ParamEnvAnd<'tcx, ty::Predicate<'tcx>>; + + fn as_cache_key(&self) -> Self::CacheKey { + self.obligation.param_env.and(self.obligation.predicate) + } +} + +/// The fulfillment context is used to drive trait resolution. It +/// consists of a list of obligations that must be (eventually) +/// satisfied. The job is to track which are satisfied, which yielded +/// errors, and which are still pending. At any point, users can call +/// `select_where_possible`, and the fulfillment context will try to do +/// selection, retaining only those obligations that remain +/// ambiguous. This may be helpful in pushing type inference +/// along. Once all type inference constraints have been generated, the +/// 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. + predicates: ObligationForest<PendingPredicateObligation<'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, +} + +#[derive(Clone, Debug)] +pub struct PendingPredicateObligation<'tcx> { + pub obligation: PredicateObligation<'tcx>, + // This is far more often read than modified, meaning that we + // should mostly optimize for reading speed, while modifying is not as relevant. + // + // For whatever reason using a boxed slice is slower than using a `Vec` here. + pub stalled_on: Vec<TyOrConstInferVar>, +} + +// `PendingPredicateObligation` is used a lot. Make sure it doesn't unintentionally get bigger. +#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +static_assert_size!(PendingPredicateObligation<'_>, 72); + +impl<'tcx> FulfillmentContext<'tcx> { + /// Creates a new fulfillment context. + 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<'_, 'tcx>) -> Vec<FulfillmentError<'tcx>> { + let span = debug_span!("select", obligation_forest_size = ?self.predicates.len()); + let _enter = span.enter(); + + // Process pending obligations. + let outcome: Outcome<_, _> = + self.predicates.process_obligations(&mut FulfillProcessor { selcx }); + + // FIXME: if we kept the original cache key, we could mark projection + // obligations as complete for the projection cache here. + + let errors: Vec<FulfillmentError<'tcx>> = + outcome.errors.into_iter().map(to_fulfillment_error).collect(); + + debug!( + "select({} predicates remaining, {} errors) done", + self.predicates.len(), + errors.len() + ); + + errors + } +} + +impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> { + #[inline] + fn register_predicate_obligation( + &mut self, + infcx: &InferCtxt<'tcx>, + mut 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. + debug_assert!(!obligation.param_env.has_non_region_infer()); + obligation.predicate = infcx.resolve_vars_if_possible(obligation.predicate); + + debug!(?obligation, "register_predicate_obligation"); + + self.predicates + .register_obligation(PendingPredicateObligation { obligation, stalled_on: vec![] }); + } + + fn collect_remaining_errors( + &mut self, + _infcx: &InferCtxt<'tcx>, + ) -> Vec<FulfillmentError<'tcx>> { + self.predicates + .to_errors(CodeAmbiguity { overflow: false }) + .into_iter() + .map(to_fulfillment_error) + .collect() + } + + fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>> { + let selcx = SelectionContext::new(infcx); + self.select(selcx) + } + + fn drain_unstalled_obligations( + &mut self, + infcx: &InferCtxt<'tcx>, + ) -> Vec<PredicateObligation<'tcx>> { + let mut processor = DrainProcessor { removed_predicates: Vec::new(), infcx }; + let outcome: Outcome<_, _> = self.predicates.process_obligations(&mut processor); + assert!(outcome.errors.is_empty()); + return processor.removed_predicates; + + struct DrainProcessor<'a, 'tcx> { + infcx: &'a InferCtxt<'tcx>, + removed_predicates: Vec<PredicateObligation<'tcx>>, + } + + impl<'tcx> ObligationProcessor for DrainProcessor<'_, 'tcx> { + type Obligation = PendingPredicateObligation<'tcx>; + type Error = !; + type OUT = Outcome<Self::Obligation, Self::Error>; + + fn needs_process_obligation(&self, pending_obligation: &Self::Obligation) -> bool { + pending_obligation + .stalled_on + .iter() + .any(|&var| self.infcx.ty_or_const_infer_var_changed(var)) + } + + fn process_obligation( + &mut self, + pending_obligation: &mut PendingPredicateObligation<'tcx>, + ) -> ProcessResult<PendingPredicateObligation<'tcx>, !> { + assert!(self.needs_process_obligation(pending_obligation)); + self.removed_predicates.push(pending_obligation.obligation.clone()); + ProcessResult::Changed(vec![]) + } + + fn process_backedge<'c, I>( + &mut self, + cycle: I, + _marker: PhantomData<&'c PendingPredicateObligation<'tcx>>, + ) -> Result<(), !> + where + I: Clone + Iterator<Item = &'c PendingPredicateObligation<'tcx>>, + { + self.removed_predicates.extend(cycle.map(|c| c.obligation.clone())); + Ok(()) + } + } + } + + fn pending_obligations(&self) -> Vec<PredicateObligation<'tcx>> { + self.predicates.map_pending_obligations(|o| o.obligation.clone()) + } +} + +struct FulfillProcessor<'a, 'tcx> { + selcx: SelectionContext<'a, 'tcx>, +} + +fn mk_pending(os: Vec<PredicateObligation<'_>>) -> Vec<PendingPredicateObligation<'_>> { + os.into_iter() + .map(|o| PendingPredicateObligation { obligation: o, stalled_on: vec![] }) + .collect() +} + +impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { + type Obligation = PendingPredicateObligation<'tcx>; + type Error = FulfillmentErrorCode<'tcx>; + type OUT = Outcome<Self::Obligation, Self::Error>; + + /// Compared to `needs_process_obligation` this and its callees + /// contain some optimizations that come at the price of false negatives. + /// + /// They + /// - reduce branching by covering only the most common case + /// - take a read-only view of the unification tables which allows skipping undo_log + /// construction. + /// - bail out on value-cache misses in ena to avoid pointer chasing + /// - hoist RefCell locking out of the loop + #[inline] + fn skippable_obligations<'b>( + &'b self, + it: impl Iterator<Item = &'b Self::Obligation>, + ) -> usize { + let is_unchanged = self.selcx.infcx.is_ty_infer_var_definitely_unchanged(); + + it.take_while(|o| match o.stalled_on.as_slice() { + [o] => is_unchanged(*o), + _ => false, + }) + .count() + } + + /// Identifies whether a predicate obligation needs processing. + /// + /// This is always inlined because it has a single callsite and it is + /// called *very* frequently. Be careful modifying this code! Several + /// compile-time benchmarks are very sensitive to even small changes. + #[inline(always)] + fn needs_process_obligation(&self, pending_obligation: &Self::Obligation) -> bool { + // If we were stalled on some unresolved variables, first check whether + // any of them have been resolved; if not, don't bother doing more work + // yet. + let stalled_on = &pending_obligation.stalled_on; + match stalled_on.len() { + // This case is the hottest most of the time, being hit up to 99% + // of the time. `keccak` and `cranelift-codegen-0.82.1` are + // benchmarks that particularly stress this path. + 1 => self.selcx.infcx.ty_or_const_infer_var_changed(stalled_on[0]), + + // In this case we haven't changed, but wish to make a change. Note + // that this is a special case, and is not equivalent to the `_` + // case below, which would return `false` for an empty `stalled_on` + // vector. + // + // This case is usually hit only 1% of the time or less, though it + // reaches 20% in `wasmparser-0.101.0`. + 0 => true, + + // This case is usually hit only 1% of the time or less, though it + // reaches 95% in `mime-0.3.16`, 64% in `wast-54.0.0`, and 12% in + // `inflate-0.4.5`. + // + // The obvious way of writing this, with a call to `any()` and no + // closure, is currently slower than this version. + _ => (|| { + for &infer_var in stalled_on { + if self.selcx.infcx.ty_or_const_infer_var_changed(infer_var) { + return true; + } + } + false + })(), + } + } + + /// Processes a predicate obligation and returns either: + /// - `Changed(v)` if the predicate is true, presuming that `v` are also true + /// - `Unchanged` if we don't have enough info to be sure + /// - `Error(e)` if the predicate does not hold + /// + /// This is called much less often than `needs_process_obligation`, so we + /// never inline it. + #[inline(never)] + #[instrument(level = "debug", skip(self, pending_obligation))] + fn process_obligation( + &mut self, + pending_obligation: &mut PendingPredicateObligation<'tcx>, + ) -> ProcessResult<PendingPredicateObligation<'tcx>, FulfillmentErrorCode<'tcx>> { + pending_obligation.stalled_on.truncate(0); + + let obligation = &mut pending_obligation.obligation; + + debug!(?obligation, "pre-resolve"); + + if obligation.predicate.has_non_region_infer() { + obligation.predicate = self.selcx.infcx.resolve_vars_if_possible(obligation.predicate); + } + + let obligation = &pending_obligation.obligation; + + let infcx = self.selcx.infcx; + + if obligation.predicate.has_projections() { + let mut obligations = Vec::new(); + let predicate = crate::traits::project::try_normalize_with_depth_to( + &mut self.selcx, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + obligation.predicate, + &mut obligations, + ); + if predicate != obligation.predicate { + obligations.push(obligation.with(infcx.tcx, predicate)); + return ProcessResult::Changed(mk_pending(obligations)); + } + } + let binder = obligation.predicate.kind(); + match binder.no_bound_vars() { + None => match binder.skip_binder() { + // 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::ClauseKind::Trait(trait_ref)) => { + let trait_obligation = obligation.with(infcx.tcx, binder.rebind(trait_ref)); + + self.process_trait_obligation( + obligation, + trait_obligation, + &mut pending_obligation.stalled_on, + ) + } + ty::PredicateKind::Clause(ty::ClauseKind::Projection(data)) => { + let project_obligation = obligation.with(infcx.tcx, binder.rebind(data)); + + self.process_projection_obligation( + obligation, + project_obligation, + &mut pending_obligation.stalled_on, + ) + } + 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::Subtype(_) + | ty::PredicateKind::Coerce(_) + | 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::NormalizesTo(..) => { + bug!("NormalizesTo is only used by the new solver") + } + ty::PredicateKind::AliasRelate(..) => { + bug!("AliasRelate is only used by the new solver") + } + }, + Some(pred) => match pred { + ty::PredicateKind::Clause(ty::ClauseKind::Trait(data)) => { + let trait_obligation = obligation.with(infcx.tcx, Binder::dummy(data)); + + self.process_trait_obligation( + obligation, + trait_obligation, + &mut pending_obligation.stalled_on, + ) + } + + ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(data)) => { + if infcx.considering_regions { + infcx.region_outlives_predicate(&obligation.cause, Binder::dummy(data)); + } + + ProcessResult::Changed(vec![]) + } + + ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate( + t_a, + r_b, + ))) => { + if infcx.considering_regions { + infcx.register_region_obligation_with_cause(t_a, r_b, &obligation.cause); + } + ProcessResult::Changed(vec![]) + } + + ty::PredicateKind::Clause(ty::ClauseKind::Projection(ref data)) => { + let project_obligation = obligation.with(infcx.tcx, Binder::dummy(*data)); + + self.process_projection_obligation( + obligation, + project_obligation, + &mut pending_obligation.stalled_on, + ) + } + + ty::PredicateKind::ObjectSafe(trait_def_id) => { + if !self.selcx.tcx().check_is_object_safe(trait_def_id) { + ProcessResult::Error(CodeSelectionError(Unimplemented)) + } else { + ProcessResult::Changed(vec![]) + } + } + + ty::PredicateKind::Ambiguous => ProcessResult::Unchanged, + ty::PredicateKind::NormalizesTo(..) => { + bug!("NormalizesTo is only used by the new solver") + } + ty::PredicateKind::AliasRelate(..) => { + bug!("AliasRelate is only used by the new solver") + } + + // General case overflow check. Allow `process_trait_obligation` + // and `process_projection_obligation` to handle checking for + // the recursion limit themselves. Also don't check some + // predicate kinds that don't give further obligations. + _ if !self + .selcx + .tcx() + .recursion_limit() + .value_within_limit(obligation.recursion_depth) => + { + self.selcx.infcx.err_ctxt().report_overflow_error( + &obligation.predicate, + obligation.cause.span, + false, + |_| {}, + ); + } + + ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => { + match wf::obligations( + self.selcx.infcx, + obligation.param_env, + obligation.cause.body_id, + obligation.recursion_depth + 1, + arg, + obligation.cause.span, + ) { + None => { + pending_obligation.stalled_on = + vec![TyOrConstInferVar::maybe_from_generic_arg(arg).unwrap()]; + ProcessResult::Unchanged + } + Some(os) => ProcessResult::Changed(mk_pending(os)), + } + } + + ty::PredicateKind::Subtype(subtype) => { + match self.selcx.infcx.subtype_predicate( + &obligation.cause, + obligation.param_env, + Binder::dummy(subtype), + ) { + Err((a, b)) => { + // None means that both are unresolved. + pending_obligation.stalled_on = + vec![TyOrConstInferVar::Ty(a), TyOrConstInferVar::Ty(b)]; + ProcessResult::Unchanged + } + Ok(Ok(mut ok)) => { + for subobligation in &mut ok.obligations { + subobligation.set_depth_from_parent(obligation.recursion_depth); + } + ProcessResult::Changed(mk_pending(ok.obligations)) + } + Ok(Err(err)) => { + let expected_found = + ExpectedFound::new(subtype.a_is_expected, subtype.a, subtype.b); + ProcessResult::Error(FulfillmentErrorCode::CodeSubtypeError( + expected_found, + err, + )) + } + } + } + + ty::PredicateKind::Coerce(coerce) => { + match self.selcx.infcx.coerce_predicate( + &obligation.cause, + obligation.param_env, + Binder::dummy(coerce), + ) { + Err((a, b)) => { + // None means that both are unresolved. + pending_obligation.stalled_on = + vec![TyOrConstInferVar::Ty(a), TyOrConstInferVar::Ty(b)]; + ProcessResult::Unchanged + } + Ok(Ok(ok)) => ProcessResult::Changed(mk_pending(ok.obligations)), + Ok(Err(err)) => { + let expected_found = ExpectedFound::new(false, coerce.a, coerce.b); + ProcessResult::Error(FulfillmentErrorCode::CodeSubtypeError( + expected_found, + err, + )) + } + } + } + + ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(uv)) => { + match const_evaluatable::is_const_evaluatable( + self.selcx.infcx, + uv, + obligation.param_env, + obligation.cause.span, + ) { + Ok(()) => ProcessResult::Changed(vec![]), + Err(NotConstEvaluatable::MentionsInfer) => { + pending_obligation.stalled_on.clear(); + pending_obligation.stalled_on.extend( + uv.walk().filter_map(TyOrConstInferVar::maybe_from_generic_arg), + ); + ProcessResult::Unchanged + } + Err( + e @ NotConstEvaluatable::MentionsParam + | e @ NotConstEvaluatable::Error(_), + ) => ProcessResult::Error(CodeSelectionError( + SelectionError::NotConstEvaluatable(e), + )), + } + } + + ty::PredicateKind::ConstEquate(c1, c2) => { + let tcx = self.selcx.tcx(); + assert!( + tcx.features().generic_const_exprs, + "`ConstEquate` without a feature gate: {c1:?} {c2:?}", + ); + // FIXME: we probably should only try to unify abstract constants + // if the constants depend on generic parameters. + // + // Let's just see where this breaks :shrug: + { + let c1 = tcx.expand_abstract_consts(c1); + let c2 = tcx.expand_abstract_consts(c2); + debug!("equating consts:\nc1= {:?}\nc2= {:?}", c1, c2); + + use rustc_hir::def::DefKind; + use ty::Unevaluated; + match (c1.kind(), c2.kind()) { + (Unevaluated(a), Unevaluated(b)) + if a.def == b.def && tcx.def_kind(a.def) == DefKind::AssocConst => + { + if let Ok(new_obligations) = infcx + .at(&obligation.cause, obligation.param_env) + .trace(c1, c2) + .eq(DefineOpaqueTypes::No, a.args, b.args) + { + return ProcessResult::Changed(mk_pending( + new_obligations.into_obligations(), + )); + } + } + (_, Unevaluated(_)) | (Unevaluated(_), _) => (), + (_, _) => { + if let Ok(new_obligations) = infcx + .at(&obligation.cause, obligation.param_env) + .eq(DefineOpaqueTypes::No, c1, c2) + { + return ProcessResult::Changed(mk_pending( + new_obligations.into_obligations(), + )); + } + } + } + } + + let stalled_on = &mut pending_obligation.stalled_on; + + let mut evaluate = |c: Const<'tcx>| { + if let ty::ConstKind::Unevaluated(unevaluated) = c.kind() { + match self.selcx.infcx.try_const_eval_resolve( + obligation.param_env, + unevaluated, + c.ty(), + Some(obligation.cause.span), + ) { + Ok(val) => Ok(val), + Err(e) => { + match e { + ErrorHandled::TooGeneric(..) => { + stalled_on.extend(unevaluated.args.iter().filter_map( + TyOrConstInferVar::maybe_from_generic_arg, + )); + } + _ => {} + } + Err(e) + } + } + } else { + Ok(c) + } + }; + + match (evaluate(c1), evaluate(c2)) { + (Ok(c1), Ok(c2)) => { + match self.selcx.infcx.at(&obligation.cause, obligation.param_env).eq( + DefineOpaqueTypes::No, + c1, + c2, + ) { + Ok(inf_ok) => { + ProcessResult::Changed(mk_pending(inf_ok.into_obligations())) + } + Err(err) => ProcessResult::Error( + FulfillmentErrorCode::CodeConstEquateError( + ExpectedFound::new(true, c1, c2), + err, + ), + ), + } + } + (Err(ErrorHandled::Reported(reported, _)), _) + | (_, Err(ErrorHandled::Reported(reported, _))) => ProcessResult::Error( + CodeSelectionError(SelectionError::NotConstEvaluatable( + NotConstEvaluatable::Error(reported.into()), + )), + ), + (Err(ErrorHandled::TooGeneric(_)), _) + | (_, Err(ErrorHandled::TooGeneric(_))) => { + if c1.has_non_region_infer() || c2.has_non_region_infer() { + ProcessResult::Unchanged + } else { + // Two different constants using generic parameters ~> error. + let expected_found = ExpectedFound::new(true, c1, c2); + ProcessResult::Error(FulfillmentErrorCode::CodeConstEquateError( + expected_found, + TypeError::ConstMismatch(expected_found), + )) + } + } + } + } + ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, ty)) => { + match self.selcx.infcx.at(&obligation.cause, obligation.param_env).eq( + DefineOpaqueTypes::No, + ct.ty(), + ty, + ) { + Ok(inf_ok) => ProcessResult::Changed(mk_pending(inf_ok.into_obligations())), + Err(_) => ProcessResult::Error(FulfillmentErrorCode::CodeSelectionError( + SelectionError::Unimplemented, + )), + } + } + }, + } + } + + #[inline(never)] + fn process_backedge<'c, I>( + &mut self, + cycle: I, + _marker: PhantomData<&'c PendingPredicateObligation<'tcx>>, + ) -> Result<(), FulfillmentErrorCode<'tcx>> + where + I: Clone + Iterator<Item = &'c PendingPredicateObligation<'tcx>>, + { + if self.selcx.coinductive_match(cycle.clone().map(|s| s.obligation.predicate)) { + debug!("process_child_obligations: coinductive match"); + Ok(()) + } else { + let cycle: Vec<_> = cycle.map(|c| c.obligation.clone()).collect(); + Err(FulfillmentErrorCode::CodeCycle(cycle)) + } + } +} + +impl<'a, 'tcx> FulfillProcessor<'a, 'tcx> { + #[instrument(level = "debug", skip(self, obligation, stalled_on))] + fn process_trait_obligation( + &mut self, + obligation: &PredicateObligation<'tcx>, + trait_obligation: PolyTraitObligation<'tcx>, + stalled_on: &mut Vec<TyOrConstInferVar>, + ) -> ProcessResult<PendingPredicateObligation<'tcx>, FulfillmentErrorCode<'tcx>> { + let infcx = self.selcx.infcx; + if obligation.predicate.is_global() && !self.selcx.is_intercrate() { + // no type variables present, can use evaluation for better caching. + // FIXME: consider caching errors too. + if infcx.predicate_must_hold_considering_regions(obligation) { + debug!( + "selecting trait at depth {} evaluated to holds", + obligation.recursion_depth + ); + return ProcessResult::Changed(vec![]); + } + } + + 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())) + } + Ok(None) => { + debug!("selecting trait at depth {} yielded Ok(None)", obligation.recursion_depth); + + // This is a bit subtle: for the most part, the + // only reason we can fail to make progress on + // trait selection is because we don't have enough + // information about the types in the trait. + stalled_on.clear(); + stalled_on.extend(args_infer_vars( + &self.selcx, + trait_obligation.predicate.map_bound(|pred| pred.trait_ref.args), + )); + + debug!( + "process_predicate: pending obligation {:?} now stalled on {:?}", + infcx.resolve_vars_if_possible(obligation.clone()), + stalled_on + ); + + ProcessResult::Unchanged + } + Err(selection_err) => { + debug!("selecting trait at depth {} yielded Err", obligation.recursion_depth); + + ProcessResult::Error(CodeSelectionError(selection_err)) + } + } + } + + fn process_projection_obligation( + &mut self, + obligation: &PredicateObligation<'tcx>, + project_obligation: PolyProjectionObligation<'tcx>, + stalled_on: &mut Vec<TyOrConstInferVar>, + ) -> ProcessResult<PendingPredicateObligation<'tcx>, FulfillmentErrorCode<'tcx>> { + let tcx = self.selcx.tcx(); + + if obligation.predicate.is_global() && !self.selcx.is_intercrate() { + // no type variables present, can use evaluation for better caching. + // FIXME: consider caching errors too. + if self.selcx.infcx.predicate_must_hold_considering_regions(obligation) { + if let Some(key) = ProjectionCacheKey::from_poly_projection_predicate( + &mut self.selcx, + project_obligation.predicate, + ) { + // If `predicate_must_hold_considering_regions` succeeds, then we've + // evaluated all sub-obligations. We can therefore mark the 'root' + // obligation as complete, and skip evaluating sub-obligations. + self.selcx + .infcx + .inner + .borrow_mut() + .projection_cache() + .complete(key, EvaluationResult::EvaluatedToOk); + } + return ProcessResult::Changed(vec![]); + } else { + debug!("Does NOT hold: {:?}", obligation); + } + } + + match project::poly_project_and_unify_type(&mut self.selcx, &project_obligation) { + ProjectAndUnifyResult::Holds(os) => ProcessResult::Changed(mk_pending(os)), + ProjectAndUnifyResult::FailedNormalization => { + stalled_on.clear(); + stalled_on.extend(args_infer_vars( + &self.selcx, + project_obligation.predicate.map_bound(|pred| pred.projection_ty.args), + )); + ProcessResult::Unchanged + } + // Let the caller handle the recursion + ProjectAndUnifyResult::Recursive => ProcessResult::Changed(mk_pending(vec![ + project_obligation.with(tcx, project_obligation.predicate), + ])), + ProjectAndUnifyResult::MismatchedProjectionTypes(e) => { + ProcessResult::Error(CodeProjectionError(e)) + } + } + } +} + +/// Returns the set of inference variables contained in `args`. +fn args_infer_vars<'a, 'tcx>( + selcx: &SelectionContext<'a, 'tcx>, + args: ty::Binder<'tcx, GenericArgsRef<'tcx>>, +) -> impl Iterator<Item = TyOrConstInferVar> + Captures<'tcx> { + selcx + .infcx + .resolve_vars_if_possible(args) + .skip_binder() // ok because this check doesn't care about regions + .iter() + .filter(|arg| arg.has_non_region_infer()) + .flat_map(|arg| { + let mut walker = arg.walk(); + while let Some(c) = walker.next() { + if !c.has_non_region_infer() { + walker.visited.remove(&c); + walker.skip_current_subtree(); + } + } + walker.visited.into_iter() + }) + .filter_map(TyOrConstInferVar::maybe_from_generic_arg) +} + +fn to_fulfillment_error<'tcx>( + error: Error<PendingPredicateObligation<'tcx>, FulfillmentErrorCode<'tcx>>, +) -> FulfillmentError<'tcx> { + let mut iter = error.backtrace.into_iter(); + let obligation = iter.next().unwrap().obligation; + // The root obligation is the last item in the backtrace - if there's only + // one item, then it's the same as the main obligation + let root_obligation = iter.next_back().map_or_else(|| obligation.clone(), |e| e.obligation); + FulfillmentError::new(obligation, error.error, root_obligation) +} diff --git a/compiler/rustc_trait_selection/src/traits/misc.rs b/compiler/rustc_trait_selection/src/traits/misc.rs new file mode 100644 index 00000000000..073174127d6 --- /dev/null +++ b/compiler/rustc_trait_selection/src/traits/misc.rs @@ -0,0 +1,224 @@ +//! Miscellaneous type-system utilities that are too small to deserve their own modules. + +use crate::traits::{self, ObligationCause, ObligationCtxt}; + +use hir::LangItem; +use rustc_data_structures::fx::FxIndexSet; +use rustc_hir as hir; +use rustc_infer::infer::canonical::Canonical; +use rustc_infer::infer::{RegionResolutionError, TyCtxtInferExt}; +use rustc_infer::traits::query::NoSolution; +use rustc_infer::{infer::outlives::env::OutlivesEnvironment, traits::FulfillmentError}; +use rustc_middle::ty::{self, AdtDef, GenericArg, List, Ty, TyCtxt, TypeVisitableExt}; +use rustc_span::DUMMY_SP; + +use super::outlives_bounds::InferCtxtExt; + +pub enum CopyImplementationError<'tcx> { + InfringingFields(Vec<(&'tcx ty::FieldDef, Ty<'tcx>, InfringingFieldsReason<'tcx>)>), + NotAnAdt, + HasDestructor, +} + +pub enum ConstParamTyImplementationError<'tcx> { + InfrigingFields(Vec<(&'tcx ty::FieldDef, Ty<'tcx>, InfringingFieldsReason<'tcx>)>), + NotAnAdtOrBuiltinAllowed, +} + +pub enum InfringingFieldsReason<'tcx> { + Fulfill(Vec<FulfillmentError<'tcx>>), + Regions(Vec<RegionResolutionError<'tcx>>), +} + +/// Checks that the fields of the type (an ADT) all implement copy. +/// +/// If fields don't implement copy, return an error containing a list of +/// those violating fields. +/// +/// If it's not an ADT, int ty, `bool`, float ty, `char`, raw pointer, `!`, +/// a reference or an array returns `Err(NotAnAdt)`. +pub fn type_allowed_to_implement_copy<'tcx>( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + self_type: Ty<'tcx>, + parent_cause: ObligationCause<'tcx>, +) -> Result<(), CopyImplementationError<'tcx>> { + let (adt, args) = match self_type.kind() { + // These types used to have a builtin impl. + // Now libcore provides that impl. + ty::Uint(_) + | ty::Int(_) + | ty::Bool + | ty::Float(_) + | ty::Char + | ty::RawPtr(..) + | ty::Never + | ty::Ref(_, _, hir::Mutability::Not) + | ty::Array(..) => return Ok(()), + + &ty::Adt(adt, args) => (adt, args), + + _ => return Err(CopyImplementationError::NotAnAdt), + }; + + all_fields_implement_trait( + tcx, + param_env, + self_type, + adt, + args, + parent_cause, + hir::LangItem::Copy, + ) + .map_err(CopyImplementationError::InfringingFields)?; + + if adt.has_dtor(tcx) { + return Err(CopyImplementationError::HasDestructor); + } + + Ok(()) +} + +/// Checks that the fields of the type (an ADT) all implement `ConstParamTy`. +/// +/// If fields don't implement `ConstParamTy`, return an error containing a list of +/// those violating fields. +/// +/// If it's not an ADT, int ty, `bool` or `char`, returns `Err(NotAnAdtOrBuiltinAllowed)`. +pub fn type_allowed_to_implement_const_param_ty<'tcx>( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + self_type: Ty<'tcx>, + parent_cause: ObligationCause<'tcx>, +) -> Result<(), ConstParamTyImplementationError<'tcx>> { + let (adt, args) = match self_type.kind() { + // `core` provides these impls. + ty::Uint(_) + | ty::Int(_) + | ty::Bool + | ty::Char + | ty::Str + | ty::Array(..) + | ty::Slice(_) + | ty::Ref(.., hir::Mutability::Not) + | ty::Tuple(_) => return Ok(()), + + &ty::Adt(adt, args) => (adt, args), + + _ => return Err(ConstParamTyImplementationError::NotAnAdtOrBuiltinAllowed), + }; + + all_fields_implement_trait( + tcx, + param_env, + self_type, + adt, + args, + parent_cause, + hir::LangItem::ConstParamTy, + ) + .map_err(ConstParamTyImplementationError::InfrigingFields)?; + + Ok(()) +} + +/// Check that all fields of a given `adt` implement `lang_item` trait. +pub fn all_fields_implement_trait<'tcx>( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + self_type: Ty<'tcx>, + adt: AdtDef<'tcx>, + args: &'tcx List<GenericArg<'tcx>>, + parent_cause: ObligationCause<'tcx>, + lang_item: LangItem, +) -> Result<(), Vec<(&'tcx ty::FieldDef, Ty<'tcx>, InfringingFieldsReason<'tcx>)>> { + let trait_def_id = tcx.require_lang_item(lang_item, Some(parent_cause.span)); + + let mut infringing = Vec::new(); + for variant in adt.variants() { + for field in &variant.fields { + // Do this per-field to get better error messages. + let infcx = tcx.infer_ctxt().build(); + let ocx = traits::ObligationCtxt::new(&infcx); + + let unnormalized_ty = field.ty(tcx, args); + if unnormalized_ty.references_error() { + continue; + } + + let field_span = tcx.def_span(field.did); + let field_ty_span = match tcx.hir().get_if_local(field.did) { + Some(hir::Node::Field(field_def)) => field_def.ty.span, + _ => field_span, + }; + + // FIXME(compiler-errors): This gives us better spans for bad + // projection types like in issue-50480. + // If the ADT has args, point to the cause we are given. + // If it does not, then this field probably doesn't normalize + // to begin with, and point to the bad field's span instead. + let normalization_cause = if field + .ty(tcx, traits::GenericArgs::identity_for_item(tcx, adt.did())) + .has_non_region_param() + { + parent_cause.clone() + } else { + ObligationCause::dummy_with_span(field_ty_span) + }; + let ty = ocx.normalize(&normalization_cause, param_env, unnormalized_ty); + let normalization_errors = ocx.select_where_possible(); + + // NOTE: The post-normalization type may also reference errors, + // such as when we project to a missing type or we have a mismatch + // between expected and found const-generic types. Don't report an + // additional copy error here, since it's not typically useful. + if !normalization_errors.is_empty() || ty.references_error() { + tcx.dcx().span_delayed_bug(field_span, format!("couldn't normalize struct field `{unnormalized_ty}` when checking {tr} implementation", tr = tcx.def_path_str(trait_def_id))); + continue; + } + + ocx.register_bound( + ObligationCause::dummy_with_span(field_ty_span), + param_env, + ty, + trait_def_id, + ); + let errors = ocx.select_all_or_error(); + if !errors.is_empty() { + infringing.push((field, ty, InfringingFieldsReason::Fulfill(errors))); + } + + // Check regions assuming the self type of the impl is WF + let outlives_env = OutlivesEnvironment::with_bounds( + param_env, + infcx.implied_bounds_tys( + param_env, + parent_cause.body_id, + FxIndexSet::from_iter([self_type]), + ), + ); + let errors = infcx.resolve_regions(&outlives_env); + if !errors.is_empty() { + infringing.push((field, ty, InfringingFieldsReason::Regions(errors))); + } + } + } + + if infringing.is_empty() { Ok(()) } else { Err(infringing) } +} + +pub fn check_tys_might_be_eq<'tcx>( + tcx: TyCtxt<'tcx>, + canonical: Canonical<'tcx, ty::ParamEnvAnd<'tcx, (Ty<'tcx>, Ty<'tcx>)>>, +) -> Result<(), NoSolution> { + let (infcx, key, _) = tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &canonical); + let (param_env, (ty_a, ty_b)) = key.into_parts(); + let ocx = ObligationCtxt::new(&infcx); + + let result = ocx.eq(&ObligationCause::dummy(), param_env, ty_a, ty_b); + // use `select_where_possible` instead of `select_all_or_error` so that + // we don't get errors from obligations being ambiguous. + let errors = ocx.select_where_possible(); + + if errors.len() > 0 || result.is_err() { Err(NoSolution) } else { Ok(()) } +} diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs new file mode 100644 index 00000000000..c52b8f37ef3 --- /dev/null +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -0,0 +1,549 @@ +//! Trait Resolution. See the [rustc dev guide] for more information on how this works. +//! +//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/resolution.html + +pub mod auto_trait; +pub(crate) mod coherence; +pub mod const_evaluatable; +mod engine; +pub mod error_reporting; +mod fulfill; +pub mod misc; +mod object_safety; +pub mod outlives_bounds; +pub mod project; +pub mod query; +#[allow(hidden_glob_reexports)] +mod select; +mod specialize; +mod structural_match; +mod structural_normalize; +#[allow(hidden_glob_reexports)] +mod util; +pub mod vtable; +pub mod wf; + +use crate::infer::outlives::env::OutlivesEnvironment; +use crate::infer::{InferCtxt, TyCtxtInferExt}; +use crate::traits::error_reporting::TypeErrCtxtExt as _; +use crate::traits::query::evaluate_obligation::InferCtxtExt as _; +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, TypeFolder, TypeSuperVisitable}; +use rustc_middle::ty::{GenericArgs, GenericArgsRef}; +use rustc_span::def_id::DefId; +use rustc_span::Span; + +use std::fmt::Debug; +use std::ops::ControlFlow; + +pub(crate) use self::project::{needs_normalization, BoundVarReplacer, PlaceholderReplacer}; + +pub use self::coherence::{add_placeholder_note, orphan_check, overlapping_impls}; +pub use self::coherence::{OrphanCheckErr, OverlapResult}; +pub use self::engine::{ObligationCtxt, TraitEngineExt}; +pub use self::fulfill::{FulfillmentContext, PendingPredicateObligation}; +pub use self::object_safety::astconv_object_safety_violations; +pub use self::object_safety::is_vtable_safe_method; +pub use self::object_safety::object_safety_violations_for_assoc_item; +pub use self::object_safety::ObjectSafetyViolation; +pub use self::project::NormalizeExt; +pub use self::project::{normalize_inherent_projection, normalize_projection_type}; +pub use self::select::{EvaluationCache, SelectionCache, SelectionContext}; +pub use self::select::{EvaluationResult, IntercrateAmbiguityCause, OverflowError}; +pub use self::specialize::specialization_graph::FutureCompatOverlapError; +pub use self::specialize::specialization_graph::FutureCompatOverlapErrorKind; +pub use self::specialize::{ + specialization_graph, translate_args, translate_args_with_cause, OverlapError, +}; +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::{ + check_args_compatible, supertrait_def_ids, supertraits, transitive_bounds, + transitive_bounds_that_define_assoc_item, SupertraitDefIds, +}; +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::*; + +/// Whether to skip the leak check, as part of a future compatibility warning step. +/// +/// The "default" for skip-leak-check corresponds to the current +/// behavior (do not skip the leak check) -- not the behavior we are +/// transitioning into. +#[derive(Copy, Clone, PartialEq, Eq, Debug, Default)] +pub enum SkipLeakCheck { + Yes, + #[default] + No, +} + +impl SkipLeakCheck { + fn is_yes(self) -> bool { + self == SkipLeakCheck::Yes + } +} + +/// The mode that trait queries run in. +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub enum TraitQueryMode { + /// Standard/un-canonicalized queries get accurate + /// spans etc. passed in and hence can do reasonable + /// error reporting on their own. + Standard, + /// Canonical queries get dummy spans and hence + /// must generally propagate errors to + /// pre-canonicalization callsites. + Canonical, +} + +/// Creates predicate obligations from the generic bounds. +#[instrument(level = "debug", skip(cause, param_env))] +pub fn predicates_for_generics<'tcx>( + cause: impl Fn(usize, Span) -> ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + generic_bounds: ty::InstantiatedPredicates<'tcx>, +) -> impl Iterator<Item = PredicateObligation<'tcx>> { + generic_bounds.into_iter().enumerate().map(move |(idx, (clause, span))| Obligation { + cause: cause(idx, span), + recursion_depth: 0, + param_env, + predicate: clause.as_predicate(), + }) +} + +/// Determines whether the type `ty` is known to meet `bound` and +/// returns true if so. Returns false if `ty` either does not meet +/// `bound` or is not known to meet bound (note that this is +/// conservative towards *no impl*, which is the opposite of the +/// `evaluate` methods). +pub fn type_known_to_meet_bound_modulo_regions<'tcx>( + infcx: &InferCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ty: Ty<'tcx>, + def_id: DefId, +) -> bool { + let trait_ref = ty::TraitRef::new(infcx.tcx, def_id, [ty]); + pred_known_to_hold_modulo_regions(infcx, param_env, trait_ref) +} + +/// FIXME(@lcnr): this function doesn't seem right and shouldn't exist? +/// +/// Ping me on zulip if you want to use this method and need help with finding +/// an appropriate replacement. +#[instrument(level = "debug", skip(infcx, param_env, pred), ret)] +fn pred_known_to_hold_modulo_regions<'tcx>( + infcx: &InferCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + pred: impl ToPredicate<'tcx>, +) -> bool { + let obligation = Obligation::new(infcx.tcx, ObligationCause::dummy(), param_env, pred); + + let result = infcx.evaluate_obligation_no_overflow(&obligation); + debug!(?result); + + if result.must_apply_modulo_regions() { + true + } else if result.may_apply() { + // Sometimes obligations are ambiguous because the recursive evaluator + // is not smart enough, so we fall back to fulfillment when we're not certain + // that an obligation holds or not. Even still, we must make sure that + // 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(infcx); + ocx.register_obligation(obligation); + + let errors = ocx.select_all_or_error(); + match errors.as_slice() { + // Only known to hold if we did no inference. + [] => infcx.shallow_resolve(goal) == goal, + + errors => { + debug!(?errors); + false + } + } + }) + } else { + false + } +} + +#[instrument(level = "debug", skip(tcx, elaborated_env))] +fn do_normalize_predicates<'tcx>( + tcx: TyCtxt<'tcx>, + cause: ObligationCause<'tcx>, + elaborated_env: ty::ParamEnv<'tcx>, + 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 + // behavior (i.e., doesn't cause any new bugs), and it would + // take some further refactoring to actually solve them. In + // particular, we would have to handle implied bounds + // properly, and that code is currently largely confined to + // regionck (though I made some efforts to extract it + // out). -nmatsakis + // + // @arielby: In any case, these obligations are checked + // by wfcheck anyway, so I'm not sure we have to check + // them here too, and we will remove this function when + // we move over to lazy normalization *anyway*. + let infcx = tcx.infer_ctxt().ignoring_regions().build(); + let predicates = match fully_normalize(&infcx, cause, elaborated_env, predicates) { + Ok(predicates) => predicates, + Err(errors) => { + let reported = infcx.err_ctxt().report_fulfillment_errors(errors); + return Err(reported); + } + }; + + debug!("do_normalize_predicates: normalized predicates = {:?}", predicates); + + // We can use the `elaborated_env` here; the region code only + // cares about declarations like `'a: 'b`. + let outlives_env = OutlivesEnvironment::new(elaborated_env); + + // FIXME: It's very weird that we ignore region obligations but apparently + // still need to use `resolve_regions` as we need the resolved regions in + // the normalized predicates. + let errors = infcx.resolve_regions(&outlives_env); + if !errors.is_empty() { + tcx.dcx().span_delayed_bug( + span, + format!("failed region resolution while normalizing {elaborated_env:?}: {errors:?}"), + ); + } + + match infcx.fully_resolve(predicates) { + Ok(predicates) => Ok(predicates), + Err(fixup_err) => { + // If we encounter a fixup error, it means that some type + // variable wound up unconstrained. I actually don't know + // if this can happen, and I certainly don't expect it to + // happen often, but if it did happen it probably + // represents a legitimate failure due to some kind of + // unconstrained variable. + // + // @lcnr: Let's still ICE here for now. I want a test case + // for that. + span_bug!( + span, + "inference variables in normalized parameter environment: {}", + fixup_err + ); + } + } +} + +// FIXME: this is gonna need to be removed ... +/// Normalizes the parameter environment, reporting errors if they occur. +#[instrument(level = "debug", skip(tcx))] +pub fn normalize_param_env_or_error<'tcx>( + tcx: TyCtxt<'tcx>, + unnormalized_env: ty::ParamEnv<'tcx>, + cause: ObligationCause<'tcx>, +) -> ty::ParamEnv<'tcx> { + // I'm not wild about reporting errors here; I'd prefer to + // have the errors get reported at a defined place (e.g., + // during typeck). Instead I have all parameter + // environments, in effect, going through this function + // and hence potentially reporting errors. This ensures of + // course that we never forget to normalize (the + // alternative seemed like it would involve a lot of + // manual invocations of this fn -- and then we'd have to + // deal with the errors at each of those sites). + // + // In any case, in practice, typeck constructs all the + // 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().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.normalize(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(-Znext-solver): 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_clauses(&predicates), unnormalized_env.reveal()); + + // HACK: we are trying to normalize the param-env inside *itself*. The problem is that + // normalization expects its param-env to be already normalized, which means we have + // a circularity. + // + // The way we handle this is by normalizing the param-env inside an unnormalized version + // of the param-env, which means that if the param-env contains unnormalized projections, + // we'll have some normalization failures. This is unfortunate. + // + // Lazy normalization would basically handle this by treating just the + // normalizing-a-trait-ref-requires-itself cycles as evaluation failures. + // + // Inferred outlives bounds can create a lot of `TypeOutlives` predicates for associated + // types, so to make the situation less bad, we normalize all the predicates *but* + // the `TypeOutlives` predicates first inside the unnormalized parameter environment, and + // then we normalize the `TypeOutlives` bounds inside the normalized parameter environment. + // + // 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 + .extract_if(|predicate| { + matches!(predicate.kind().skip_binder(), ty::ClauseKind::TypeOutlives(..)) + }) + .collect(); + + debug!( + "normalize_param_env_or_error: predicates=(non-outlives={:?}, outlives={:?})", + predicates, outlives_predicates + ); + let Ok(non_outlives_predicates) = + do_normalize_predicates(tcx, cause.clone(), elaborated_env, predicates) + else { + // An unnormalized env is better than nothing. + debug!("normalize_param_env_or_error: errored resolving non-outlives predicates"); + return elaborated_env; + }; + + debug!("normalize_param_env_or_error: non-outlives predicates={:?}", non_outlives_predicates); + + // Not sure whether it is better to include the unnormalized TypeOutlives predicates + // here. I believe they should not matter, because we are ignoring TypeOutlives param-env + // 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_clauses_from_iter(outlives_env), unnormalized_env.reveal()); + let Ok(outlives_predicates) = + do_normalize_predicates(tcx, cause, outlives_env, outlives_predicates) + else { + // An unnormalized env is better than nothing. + debug!("normalize_param_env_or_error: errored resolving outlives predicates"); + return elaborated_env; + }; + debug!("normalize_param_env_or_error: outlives predicates={:?}", outlives_predicates); + + let mut predicates = non_outlives_predicates; + predicates.extend(outlives_predicates); + debug!("normalize_param_env_or_error: final predicates={:?}", predicates); + ty::ParamEnv::new(tcx.mk_clauses(&predicates), unnormalized_env.reveal()) +} + +/// Normalize a type and process all resulting obligations, returning any errors. +/// +/// FIXME(-Znext-solver): 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>, + cause: ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + value: T, +) -> Result<T, Vec<FulfillmentError<'tcx>>> +where + T: TypeFoldable<TyCtxt<'tcx>>, +{ + let ocx = ObligationCtxt::new(infcx); + debug!(?value); + let normalized_value = ocx.normalize(&cause, param_env, value); + debug!(?normalized_value); + debug!("select_all_or_error start"); + let errors = ocx.select_all_or_error(); + if !errors.is_empty() { + return Err(errors); + } + debug!("select_all_or_error complete"); + let resolved_value = infcx.resolve_vars_if_possible(normalized_value); + debug!(?resolved_value); + Ok(resolved_value) +} + +/// 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::Clause<'tcx>>) -> bool { + debug!("impossible_predicates(predicates={:?})", predicates); + + let infcx = tcx.infer_ctxt().build(); + let param_env = ty::ParamEnv::reveal_all(); + let ocx = ObligationCtxt::new(&infcx); + let predicates = ocx.normalize(&ObligationCause::dummy(), param_env, predicates); + for predicate in predicates { + let obligation = Obligation::new(tcx, ObligationCause::dummy(), param_env, predicate); + ocx.register_obligation(obligation); + } + let errors = ocx.select_all_or_error(); + + let result = !errors.is_empty(); + debug!("impossible_predicates = {:?}", result); + result +} + +fn subst_and_check_impossible_predicates<'tcx>( + tcx: TyCtxt<'tcx>, + key: (DefId, GenericArgsRef<'tcx>), +) -> bool { + debug!("subst_and_check_impossible_predicates(key={:?})", key); + + let mut predicates = tcx.predicates_of(key.0).instantiate(tcx, key.1).predicates; + + // Specifically check trait fulfillment to avoid an error when trying to resolve + // associated items. + if let Some(trait_def_id) = tcx.trait_of_item(key.0) { + let trait_ref = ty::TraitRef::from_method(tcx, trait_def_id, key.1); + predicates.push(ty::Binder::dummy(trait_ref).to_predicate(tcx)); + } + + predicates.retain(|predicate| !predicate.has_param()); + let result = impossible_predicates(tcx, predicates); + + debug!("subst_and_check_impossible_predicates(key={:?}) = {:?}", key, result); + result +} + +/// Checks whether a trait's associated item is impossible to reference on a given impl. +/// +/// This only considers predicates that reference the impl's generics, and not +/// those that reference the method's generics. +fn is_impossible_associated_item( + tcx: TyCtxt<'_>, + (impl_def_id, trait_item_def_id): (DefId, DefId), +) -> bool { + struct ReferencesOnlyParentGenerics<'tcx> { + tcx: TyCtxt<'tcx>, + generics: &'tcx ty::Generics, + trait_item_def_id: DefId, + } + impl<'tcx> ty::TypeVisitor<TyCtxt<'tcx>> for ReferencesOnlyParentGenerics<'tcx> { + type BreakTy = (); + fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { + // If this is a parameter from the trait item's own generics, then bail + if let ty::Param(param) = t.kind() + && let param_def_id = self.generics.type_param(param, self.tcx).def_id + && self.tcx.parent(param_def_id) == self.trait_item_def_id + { + return ControlFlow::Break(()); + } + t.super_visit_with(self) + } + fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> { + if let ty::ReEarlyParam(param) = r.kind() + && let param_def_id = self.generics.region_param(¶m, self.tcx).def_id + && self.tcx.parent(param_def_id) == self.trait_item_def_id + { + return ControlFlow::Break(()); + } + ControlFlow::Continue(()) + } + fn visit_const(&mut self, ct: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> { + if let ty::ConstKind::Param(param) = ct.kind() + && let param_def_id = self.generics.const_param(¶m, self.tcx).def_id + && self.tcx.parent(param_def_id) == self.trait_item_def_id + { + return ControlFlow::Break(()); + } + ct.super_visit_with(self) + } + } + + let generics = tcx.generics_of(trait_item_def_id); + let predicates = tcx.predicates_of(trait_item_def_id); + let impl_trait_ref = tcx + .impl_trait_ref(impl_def_id) + .expect("expected impl to correspond to trait") + .instantiate_identity(); + let param_env = tcx.param_env(impl_def_id); + + let mut visitor = ReferencesOnlyParentGenerics { tcx, generics, trait_item_def_id }; + let predicates_for_trait = predicates.predicates.iter().filter_map(|(pred, span)| { + pred.visit_with(&mut visitor).is_continue().then(|| { + Obligation::new( + tcx, + ObligationCause::dummy_with_span(*span), + param_env, + ty::EarlyBinder::bind(*pred).instantiate(tcx, impl_trait_ref.args), + ) + }) + }); + + let infcx = tcx.infer_ctxt().ignoring_regions().build(); + for obligation in predicates_for_trait { + // Ignore overflow error, to be conservative. + if let Ok(result) = infcx.evaluate_obligation(&obligation) + && !result.may_apply() + { + return true; + } + } + false +} + +pub fn provide(providers: &mut Providers) { + object_safety::provide(providers); + vtable::provide(providers); + *providers = Providers { + specialization_graph_of: specialize::specialization_graph_provider, + specializes: specialize::specializes, + subst_and_check_impossible_predicates, + check_tys_might_be_eq: misc::check_tys_might_be_eq, + is_impossible_associated_item, + ..*providers + }; +} diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs new file mode 100644 index 00000000000..6e68dee76a2 --- /dev/null +++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs @@ -0,0 +1,930 @@ +//! "Object safety" refers to the ability for a trait to be converted +//! to an object. In general, traits may only be converted to an +//! object if all of their methods meet certain criteria. In particular, +//! they must: +//! +//! - have a suitable receiver from which we can extract a vtable and coerce to a "thin" version +//! that doesn't contain the vtable; +//! - not reference the erased type `Self` except for in this receiver; +//! - not have generic type parameters. + +use super::elaborate; + +use crate::infer::TyCtxtInferExt; +use crate::traits::query::evaluate_obligation::InferCtxtExt; +use crate::traits::{self, Obligation, ObligationCause}; +use rustc_errors::{DelayDm, FatalError, MultiSpan}; +use rustc_hir as hir; +use rustc_hir::def_id::DefId; +use rustc_middle::query::Providers; +use rustc_middle::ty::{ + self, EarlyBinder, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, +}; +use rustc_middle::ty::{GenericArg, GenericArgs}; +use rustc_middle::ty::{ToPredicate, TypeVisitableExt}; +use rustc_session::lint::builtin::WHERE_CLAUSES_OBJECT_SAFETY; +use rustc_span::symbol::Symbol; +use rustc_span::Span; +use smallvec::SmallVec; + +use std::iter; +use std::ops::ControlFlow; + +pub use crate::traits::{MethodViolationCode, ObjectSafetyViolation}; + +/// Returns the object safety violations that affect +/// astconv -- currently, `Self` in supertraits. This is needed +/// because `object_safety_violations` can't be used during +/// type collection. +pub fn astconv_object_safety_violations( + tcx: TyCtxt<'_>, + trait_def_id: DefId, +) -> Vec<ObjectSafetyViolation> { + debug_assert!(tcx.generics_of(trait_def_id).has_self); + let violations = traits::supertrait_def_ids(tcx, trait_def_id) + .map(|def_id| predicates_reference_self(tcx, def_id, true)) + .filter(|spans| !spans.is_empty()) + .map(ObjectSafetyViolation::SupertraitSelf) + .collect(); + + debug!("astconv_object_safety_violations(trait_def_id={:?}) = {:?}", trait_def_id, violations); + + violations +} + +fn object_safety_violations(tcx: TyCtxt<'_>, trait_def_id: DefId) -> &'_ [ObjectSafetyViolation] { + debug_assert!(tcx.generics_of(trait_def_id).has_self); + debug!("object_safety_violations: {:?}", trait_def_id); + + tcx.arena.alloc_from_iter( + traits::supertrait_def_ids(tcx, trait_def_id) + .flat_map(|def_id| object_safety_violations_for_trait(tcx, def_id)), + ) +} + +fn check_is_object_safe(tcx: TyCtxt<'_>, trait_def_id: DefId) -> bool { + let violations = tcx.object_safety_violations(trait_def_id); + + if violations.is_empty() { + return true; + } + + // If the trait contains any other violations, then let the error reporting path + // report it instead of emitting a warning here. + if violations.iter().all(|violation| { + matches!( + violation, + ObjectSafetyViolation::Method(_, MethodViolationCode::WhereClauseReferencesSelf, _) + ) + }) { + for violation in violations { + if let ObjectSafetyViolation::Method( + _, + MethodViolationCode::WhereClauseReferencesSelf, + span, + ) = violation + { + lint_object_unsafe_trait(tcx, *span, trait_def_id, violation); + } + } + return true; + } + + false +} + +/// We say a method is *vtable safe* if it can be invoked on a trait +/// object. Note that object-safe traits can have some +/// non-vtable-safe methods, so long as they require `Self: Sized` or +/// otherwise ensure that they cannot be used when `Self = Trait`. +/// +/// [`MethodViolationCode::WhereClauseReferencesSelf`] is considered object safe due to backwards +/// compatibility, see <https://github.com/rust-lang/rust/issues/51443> and +/// [`WHERE_CLAUSES_OBJECT_SAFETY`]. +pub fn is_vtable_safe_method(tcx: TyCtxt<'_>, trait_def_id: DefId, method: ty::AssocItem) -> bool { + 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 tcx.generics_require_sized_self(method.def_id) { + return false; + } + + virtual_call_violations_for_method(tcx, trait_def_id, method) + .iter() + .all(|v| matches!(v, MethodViolationCode::WhereClauseReferencesSelf)) +} + +fn object_safety_violations_for_trait( + tcx: TyCtxt<'_>, + trait_def_id: DefId, +) -> Vec<ObjectSafetyViolation> { + // Check assoc items for violations. + let mut violations: Vec<_> = tcx + .associated_items(trait_def_id) + .in_definition_order() + .flat_map(|&item| object_safety_violations_for_assoc_item(tcx, trait_def_id, item)) + .collect(); + + // Check the trait itself. + if trait_has_sized_self(tcx, trait_def_id) { + // We don't want to include the requirement from `Sized` itself to be `Sized` in the list. + let spans = get_sized_bounds(tcx, trait_def_id); + violations.push(ObjectSafetyViolation::SizedSelf(spans)); + } + let spans = predicates_reference_self(tcx, trait_def_id, false); + if !spans.is_empty() { + violations.push(ObjectSafetyViolation::SupertraitSelf(spans)); + } + let spans = bounds_reference_self(tcx, trait_def_id); + if !spans.is_empty() { + violations.push(ObjectSafetyViolation::SupertraitSelf(spans)); + } + let spans = super_predicates_have_non_lifetime_binders(tcx, trait_def_id); + if !spans.is_empty() { + violations.push(ObjectSafetyViolation::SupertraitNonLifetimeBinder(spans)); + } + + debug!( + "object_safety_violations_for_trait(trait_def_id={:?}) = {:?}", + trait_def_id, violations + ); + + violations +} + +/// Lint object-unsafe trait. +fn lint_object_unsafe_trait( + tcx: TyCtxt<'_>, + span: Span, + trait_def_id: DefId, + violation: &ObjectSafetyViolation, +) { + // Using `CRATE_NODE_ID` is wrong, but it's hard to get a more precise id. + // It's also hard to get a use site span, so we use the method definition span. + tcx.struct_span_lint_hir( + WHERE_CLAUSES_OBJECT_SAFETY, + hir::CRATE_HIR_ID, + span, + DelayDm(|| format!("the trait `{}` cannot be made into an object", tcx.def_path_str(trait_def_id))), + |err| { + let node = tcx.hir().get_if_local(trait_def_id); + let mut spans = MultiSpan::from_span(span); + if let Some(hir::Node::Item(item)) = node { + spans.push_span_label( + item.ident.span, + "this trait cannot be made into an object...", + ); + spans.push_span_label(span, format!("...because {}", violation.error_msg())); + } else { + spans.push_span_label( + span, + format!( + "the trait cannot be made into an object because {}", + violation.error_msg() + ), + ); + }; + err.span_note( + spans, + "for a trait to be \"object safe\" it needs to allow building a vtable to allow the \ + call to be resolvable dynamically; for more information visit \ + <https://doc.rust-lang.org/reference/items/traits.html#object-safety>", + ); + if node.is_some() { + // Only provide the help if its a local trait, otherwise it's not + violation.solution().add_to(err); + } + }, + ); +} + +fn sized_trait_bound_spans<'tcx>( + tcx: TyCtxt<'tcx>, + bounds: hir::GenericBounds<'tcx>, +) -> impl 'tcx + Iterator<Item = Span> { + bounds.iter().filter_map(move |b| match b { + hir::GenericBound::Trait(trait_ref, hir::TraitBoundModifier::None) + if trait_has_sized_self( + tcx, + trait_ref.trait_ref.trait_def_id().unwrap_or_else(|| FatalError.raise()), + ) => + { + // Fetch spans for supertraits that are `Sized`: `trait T: Super` + Some(trait_ref.span) + } + _ => None, + }) +} + +fn get_sized_bounds(tcx: TyCtxt<'_>, trait_def_id: DefId) -> SmallVec<[Span; 1]> { + tcx.hir() + .get_if_local(trait_def_id) + .and_then(|node| match node { + hir::Node::Item(hir::Item { + kind: hir::ItemKind::Trait(.., generics, bounds, _), + .. + }) => Some( + generics + .predicates + .iter() + .filter_map(|pred| { + match pred { + hir::WherePredicate::BoundPredicate(pred) + if pred.bounded_ty.hir_id.owner.to_def_id() == trait_def_id => + { + // Fetch spans for trait bounds that are Sized: + // `trait T where Self: Pred` + Some(sized_trait_bound_spans(tcx, pred.bounds)) + } + _ => None, + } + }) + .flatten() + // Fetch spans for supertraits that are `Sized`: `trait T: Super`. + .chain(sized_trait_bound_spans(tcx, bounds)) + .collect::<SmallVec<[Span; 1]>>(), + ), + _ => None, + }) + .unwrap_or_else(SmallVec::new) +} + +fn predicates_reference_self( + tcx: TyCtxt<'_>, + trait_def_id: DefId, + supertraits_only: bool, +) -> SmallVec<[Span; 1]> { + let trait_ref = ty::Binder::dummy(ty::TraitRef::identity(tcx, trait_def_id)); + let predicates = if supertraits_only { + tcx.super_predicates_of(trait_def_id) + } else { + tcx.predicates_of(trait_def_id) + }; + predicates + .predicates + .iter() + .map(|&(predicate, sp)| (predicate.subst_supertrait(tcx, &trait_ref), sp)) + .filter_map(|predicate| predicate_references_self(tcx, predicate)) + .collect() +} + +fn bounds_reference_self(tcx: TyCtxt<'_>, trait_def_id: DefId) -> SmallVec<[Span; 1]> { + tcx.associated_items(trait_def_id) + .in_definition_order() + .filter(|item| item.kind == ty::AssocKind::Type) + .flat_map(|item| tcx.explicit_item_bounds(item.def_id).instantiate_identity_iter_copied()) + .filter_map(|c| predicate_references_self(tcx, c)) + .collect() +} + +fn predicate_references_self<'tcx>( + tcx: TyCtxt<'tcx>, + (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::ClauseKind::Trait(ref data) => { + // In the case of a trait predicate, we can skip the "self" type. + data.trait_ref.args[1..].iter().any(has_self_ty).then_some(sp) + } + 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 + // the check to be safe. + // + // It's also won't be redundant if we allow type-generic associated + // types for trait objects. + // + // Note that we *do* allow projection *outputs* to contain + // `self` (i.e., `trait Foo: Bar<Output=Self::Result> { type Result; }`), + // we just require the user to specify *both* outputs + // in the object type (i.e., `dyn Foo<Output=(), Result=()>`). + // + // This is ALT2 in issue #56288, see that for discussion of the + // possible alternatives. + data.projection_ty.args[1..].iter().any(has_self_ty).then_some(sp) + } + ty::ClauseKind::ConstArgHasType(_ct, ty) => has_self_ty(&ty.into()).then_some(sp), + + ty::ClauseKind::WellFormed(..) + | ty::ClauseKind::TypeOutlives(..) + | ty::ClauseKind::RegionOutlives(..) + // FIXME(generic_const_exprs): this can mention `Self` + | ty::ClauseKind::ConstEvaluatable(..) + => None, + } +} + +fn super_predicates_have_non_lifetime_binders( + tcx: TyCtxt<'_>, + trait_def_id: DefId, +) -> SmallVec<[Span; 1]> { + // If non_lifetime_binders is disabled, then exit early + if !tcx.features().non_lifetime_binders { + return SmallVec::new(); + } + tcx.super_predicates_of(trait_def_id) + .predicates + .iter() + .filter_map(|(pred, span)| pred.has_non_region_bound_vars().then_some(*span)) + .collect() +} + +fn trait_has_sized_self(tcx: TyCtxt<'_>, trait_def_id: DefId) -> bool { + tcx.generics_require_sized_self(trait_def_id) +} + +fn generics_require_sized_self(tcx: TyCtxt<'_>, def_id: DefId) -> bool { + let Some(sized_def_id) = tcx.lang_items().sized_trait() else { + return false; /* No Sized trait, can't require it! */ + }; + + // Search for a predicate like `Self : Sized` amongst the trait bounds. + let predicates = tcx.predicates_of(def_id); + let predicates = predicates.instantiate_identity(tcx).predicates; + elaborate(tcx, predicates).any(|pred| match pred.kind().skip_binder() { + ty::ClauseKind::Trait(ref trait_pred) => { + trait_pred.def_id() == sized_def_id && trait_pred.self_ty().is_param(0) + } + ty::ClauseKind::RegionOutlives(_) + | ty::ClauseKind::TypeOutlives(_) + | ty::ClauseKind::Projection(_) + | ty::ClauseKind::ConstArgHasType(_, _) + | ty::ClauseKind::WellFormed(_) + | ty::ClauseKind::ConstEvaluatable(_) => false, + }) +} + +/// Returns `Some(_)` if this item makes the containing trait not object safe. +#[instrument(level = "debug", skip(tcx), ret)] +pub fn object_safety_violations_for_assoc_item( + tcx: TyCtxt<'_>, + trait_def_id: DefId, + item: ty::AssocItem, +) -> Vec<ObjectSafetyViolation> { + // Any item that has a `Self : Sized` requisite is otherwise + // exempt from the regulations. + if tcx.generics_require_sized_self(item.def_id) { + return Vec::new(); + } + + 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 => { + vec![ObjectSafetyViolation::AssocConst(item.name, item.ident(tcx).span)] + } + ty::AssocKind::Fn => virtual_call_violations_for_method(tcx, trait_def_id, item) + .into_iter() + .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) + }) + .collect(), + // 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.is_impl_trait_in_trait() + { + vec![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. + Vec::new() + } + } + } +} + +/// Returns `Some(_)` if this method cannot be called on a trait +/// object; this does not necessarily imply that the enclosing trait +/// is not object safe, because the method might have a where clause +/// `Self:Sized`. +fn virtual_call_violations_for_method<'tcx>( + tcx: TyCtxt<'tcx>, + trait_def_id: DefId, + method: ty::AssocItem, +) -> Vec<MethodViolationCode> { + let sig = tcx.fn_sig(method.def_id).instantiate_identity(); + + // The method's first parameter must be named `self` + if !method.fn_has_self_parameter { + let sugg = if let Some(hir::Node::TraitItem(hir::TraitItem { + generics, + kind: hir::TraitItemKind::Fn(sig, _), + .. + })) = tcx.hir().get_if_local(method.def_id).as_ref() + { + let sm = tcx.sess.source_map(); + Some(( + ( + format!("&self{}", if sig.decl.inputs.is_empty() { "" } else { ", " }), + sm.span_through_char(sig.span, '(').shrink_to_hi(), + ), + ( + format!("{} Self: Sized", generics.add_where_or_trailing_comma()), + generics.tail_span_for_predicate_suggestion(), + ), + )) + } else { + None + }; + + // Not having `self` parameter messes up the later checks, + // so we need to return instead of pushing + return vec![MethodViolationCode::StaticMethod(sugg)]; + } + + let mut errors = Vec::new(); + + for (i, &input_ty) in sig.skip_binder().inputs().iter().enumerate().skip(1) { + if contains_illegal_self_type_reference(tcx, trait_def_id, sig.rebind(input_ty)) { + let span = if let Some(hir::Node::TraitItem(hir::TraitItem { + kind: hir::TraitItemKind::Fn(sig, _), + .. + })) = tcx.hir().get_if_local(method.def_id).as_ref() + { + Some(sig.decl.inputs[i].span) + } else { + None + }; + errors.push(MethodViolationCode::ReferencesSelfInput(span)); + } + } + if contains_illegal_self_type_reference(tcx, trait_def_id, sig.output()) { + errors.push(MethodViolationCode::ReferencesSelfOutput); + } + if let Some(code) = contains_illegal_impl_trait_in_trait(tcx, method.def_id, sig.output()) { + errors.push(code); + } + + // We can't monomorphize things like `fn foo<A>(...)`. + let own_counts = tcx.generics_of(method.def_id).own_counts(); + if own_counts.types > 0 || own_counts.consts > 0 { + errors.push(MethodViolationCode::Generic); + } + + let receiver_ty = tcx.liberate_late_bound_regions(method.def_id, sig.input(0)); + + // Until `unsized_locals` is fully implemented, `self: Self` can't be dispatched on. + // However, this is already considered object-safe. We allow it as a special case here. + // FIXME(mikeyhew) get rid of this `if` statement once `receiver_is_dispatchable` allows + // `Receiver: Unsize<Receiver[Self => dyn Trait]>`. + if receiver_ty != tcx.types.self_param { + if !receiver_is_dispatchable(tcx, method, receiver_ty) { + let span = if let Some(hir::Node::TraitItem(hir::TraitItem { + kind: hir::TraitItemKind::Fn(sig, _), + .. + })) = tcx.hir().get_if_local(method.def_id).as_ref() + { + Some(sig.decl.inputs[0].span) + } else { + None + }; + errors.push(MethodViolationCode::UndispatchableReceiver(span)); + } else { + // Do sanity check to make sure the receiver actually has the layout of a pointer. + + use rustc_target::abi::Abi; + + let param_env = tcx.param_env(method.def_id); + + let abi_of_ty = |ty: Ty<'tcx>| -> Option<Abi> { + match tcx.layout_of(param_env.and(ty)) { + Ok(layout) => Some(layout.abi), + Err(err) => { + // #78372 + tcx.dcx().span_delayed_bug( + tcx.def_span(method.def_id), + format!("error: {err}\n while computing layout for type {ty:?}"), + ); + None + } + } + }; + + // e.g., `Rc<()>` + let unit_receiver_ty = + receiver_for_self_ty(tcx, receiver_ty, Ty::new_unit(tcx), method.def_id); + + match abi_of_ty(unit_receiver_ty) { + Some(Abi::Scalar(..)) => (), + abi => { + tcx.dcx().span_delayed_bug( + tcx.def_span(method.def_id), + format!( + "receiver when `Self = ()` should have a Scalar ABI; found {abi:?}" + ), + ); + } + } + + let trait_object_ty = object_ty_for_trait(tcx, trait_def_id, tcx.lifetimes.re_static); + + // e.g., `Rc<dyn Trait>` + let trait_object_receiver = + receiver_for_self_ty(tcx, receiver_ty, trait_object_ty, method.def_id); + + match abi_of_ty(trait_object_receiver) { + Some(Abi::ScalarPair(..)) => (), + abi => { + tcx.dcx().span_delayed_bug( + tcx.def_span(method.def_id), + format!( + "receiver when `Self = {trait_object_ty}` should have a ScalarPair ABI; found {abi:?}" + ), + ); + } + } + } + } + + // NOTE: This check happens last, because it results in a lint, and not a + // hard error. + if tcx.predicates_of(method.def_id).predicates.iter().any(|&(pred, span)| { + // dyn Trait is okay: + // + // trait Trait { + // fn f(&self) where Self: 'static; + // } + // + // 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.as_type_outlives_clause().is_some() { + return false; + } + + // dyn Trait is okay: + // + // auto trait AutoTrait {} + // + // trait Trait { + // fn f(&self) where Self: AutoTrait; + // } + // + // because `impl AutoTrait for dyn Trait` is disallowed by coherence. + // Traits with a default impl are implemented for a trait object if and + // 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::ClauseKind::Trait(ty::TraitPredicate { + trait_ref: pred_trait_ref, + polarity: ty::ImplPolarity::Positive, + }) = pred.kind().skip_binder() + && pred_trait_ref.self_ty() == tcx.types.self_param + && tcx.trait_is_auto(pred_trait_ref.def_id) + { + // Consider bounds like `Self: Bound<Self>`. Auto traits are not + // allowed to have generic parameters so `auto trait Bound<T> {}` + // would already have reported an error at the definition of the + // auto trait. + if pred_trait_ref.args.len() != 1 { + tcx.dcx().span_delayed_bug(span, "auto traits cannot have generic parameters"); + } + return false; + } + + contains_illegal_self_type_reference(tcx, trait_def_id, pred) + }) { + errors.push(MethodViolationCode::WhereClauseReferencesSelf); + } + + errors +} + +/// Performs a type substitution to produce the version of `receiver_ty` when `Self = self_ty`. +/// For example, for `receiver_ty = Rc<Self>` and `self_ty = Foo`, returns `Rc<Foo>`. +fn receiver_for_self_ty<'tcx>( + tcx: TyCtxt<'tcx>, + receiver_ty: Ty<'tcx>, + self_ty: Ty<'tcx>, + method_def_id: DefId, +) -> Ty<'tcx> { + debug!("receiver_for_self_ty({:?}, {:?}, {:?})", receiver_ty, self_ty, method_def_id); + let args = GenericArgs::for_item(tcx, method_def_id, |param, _| { + if param.index == 0 { self_ty.into() } else { tcx.mk_param_from_def(param) } + }); + + let result = EarlyBinder::bind(receiver_ty).instantiate(tcx, args); + debug!( + "receiver_for_self_ty({:?}, {:?}, {:?}) = {:?}", + receiver_ty, self_ty, method_def_id, result + ); + result +} + +/// Creates the object type for the current trait. For example, +/// if the current trait is `Deref`, then this will be +/// `dyn Deref<Target = Self::Target> + 'static`. +#[instrument(level = "trace", skip(tcx), ret)] +fn object_ty_for_trait<'tcx>( + tcx: TyCtxt<'tcx>, + trait_def_id: DefId, + lifetime: ty::Region<'tcx>, +) -> Ty<'tcx> { + let trait_ref = ty::TraitRef::identity(tcx, trait_def_id); + debug!(?trait_ref); + + let trait_predicate = ty::Binder::dummy(ty::ExistentialPredicate::Trait( + ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref), + )); + debug!(?trait_predicate); + + let pred: ty::Predicate<'tcx> = trait_ref.to_predicate(tcx); + let mut elaborated_predicates: Vec<_> = elaborate(tcx, [pred]) + .filter_map(|pred| { + debug!(?pred); + let pred = pred.to_opt_poly_projection_pred()?; + Some(pred.map_bound(|p| { + ty::ExistentialPredicate::Projection(ty::ExistentialProjection::erase_self_ty( + tcx, p, + )) + })) + }) + .collect(); + // NOTE: Since #37965, the existential predicates list has depended on the + // list of predicates to be sorted. This is mostly to enforce that the primary + // predicate comes first. + elaborated_predicates.sort_by(|a, b| a.skip_binder().stable_cmp(tcx, &b.skip_binder())); + elaborated_predicates.dedup(); + + let existential_predicates = tcx.mk_poly_existential_predicates_from_iter( + iter::once(trait_predicate).chain(elaborated_predicates), + ); + debug!(?existential_predicates); + + 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 +/// trait object. We require that `DispatchableFromDyn` be implemented for the receiver type +/// in the following way: +/// - let `Receiver` be the type of the `self` argument, i.e `Self`, `&Self`, `Rc<Self>`, +/// - require the following bound: +/// +/// ```ignore (not-rust) +/// Receiver[Self => T]: DispatchFromDyn<Receiver[Self => dyn Trait]> +/// ``` +/// +/// where `Foo[X => Y]` means "the same type as `Foo`, but with `X` replaced with `Y`" +/// (substitution notation). +/// +/// Some examples of receiver types and their required obligation: +/// - `&'a mut self` requires `&'a mut Self: DispatchFromDyn<&'a mut dyn Trait>`, +/// - `self: Rc<Self>` requires `Rc<Self>: DispatchFromDyn<Rc<dyn Trait>>`, +/// - `self: Pin<Box<Self>>` requires `Pin<Box<Self>>: DispatchFromDyn<Pin<Box<dyn Trait>>>`. +/// +/// The only case where the receiver is not dispatchable, but is still a valid receiver +/// type (just not object-safe), is when there is more than one level of pointer indirection. +/// E.g., `self: &&Self`, `self: &Rc<Self>`, `self: Box<Box<Self>>`. In these cases, there +/// is no way, or at least no inexpensive way, to coerce the receiver from the version where +/// `Self = dyn Trait` to the version where `Self = T`, where `T` is the unknown erased type +/// contained by the trait object, because the object that needs to be coerced is behind +/// a pointer. +/// +/// In practice, we cannot use `dyn Trait` explicitly in the obligation because it would result +/// in a new check that `Trait` is object safe, creating a cycle (until object_safe_for_dispatch +/// is stabilized, see tracking issue <https://github.com/rust-lang/rust/issues/43561>). +/// Instead, we fudge a little by introducing a new type parameter `U` such that +/// `Self: Unsize<U>` and `U: Trait + ?Sized`, and use `U` in place of `dyn Trait`. +/// Written as a chalk-style query: +/// ```ignore (not-rust) +/// forall (U: Trait + ?Sized) { +/// if (Self: Unsize<U>) { +/// Receiver: DispatchFromDyn<Receiver[Self => U]> +/// } +/// } +/// ``` +/// for `self: &'a mut Self`, this means `&'a mut Self: DispatchFromDyn<&'a mut U>` +/// for `self: Rc<Self>`, this means `Rc<Self>: DispatchFromDyn<Rc<U>>` +/// for `self: Pin<Box<Self>>`, this means `Pin<Box<Self>>: DispatchFromDyn<Pin<Box<U>>>` +// +// FIXME(mikeyhew) when unsized receivers are implemented as part of unsized rvalues, add this +// fallback query: `Receiver: Unsize<Receiver[Self => U]>` to support receivers like +// `self: Wrapper<Self>`. +fn receiver_is_dispatchable<'tcx>( + tcx: TyCtxt<'tcx>, + method: ty::AssocItem, + receiver_ty: Ty<'tcx>, +) -> bool { + debug!("receiver_is_dispatchable: method = {:?}, receiver_ty = {:?}", method, receiver_ty); + + let traits = (tcx.lang_items().unsize_trait(), tcx.lang_items().dispatch_from_dyn_trait()); + let (Some(unsize_did), Some(dispatch_from_dyn_did)) = traits else { + debug!("receiver_is_dispatchable: Missing Unsize or DispatchFromDyn traits"); + return false; + }; + + // the type `U` in the query + // use a bogus type parameter to mimic a forall(U) query using u32::MAX for now. + // 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> = + Ty::new_param(tcx, u32::MAX, Symbol::intern("RustaceansAreAwesome")); + + // `Receiver[Self => U]` + let unsized_receiver_ty = + receiver_for_self_ty(tcx, receiver_ty, unsized_self_ty, method.def_id); + + // create a modified param env, with `Self: Unsize<U>` and `U: Trait` added to caller bounds + // `U: ?Sized` is already implied here + let param_env = { + let param_env = tcx.param_env(method.def_id); + + // Self: Unsize<U> + let unsize_predicate = + ty::TraitRef::new(tcx, unsize_did, [tcx.types.self_param, unsized_self_ty]) + .to_predicate(tcx); + + // U: Trait<Arg1, ..., ArgN> + let trait_predicate = { + let trait_def_id = method.trait_container(tcx).unwrap(); + let args = GenericArgs::for_item(tcx, trait_def_id, |param, _| { + if param.index == 0 { unsized_self_ty.into() } else { tcx.mk_param_from_def(param) } + }); + + ty::TraitRef::new(tcx, trait_def_id, args).to_predicate(tcx) + }; + + let caller_bounds = + param_env.caller_bounds().iter().chain([unsize_predicate, trait_predicate]); + + ty::ParamEnv::new(tcx.mk_clauses_from_iter(caller_bounds), param_env.reveal()) + }; + + // Receiver: DispatchFromDyn<Receiver[Self => U]> + let obligation = { + let predicate = + ty::TraitRef::new(tcx, dispatch_from_dyn_did, [receiver_ty, unsized_receiver_ty]); + + Obligation::new(tcx, ObligationCause::dummy(), param_env, predicate) + }; + + let infcx = tcx.infer_ctxt().build(); + // the receiver is dispatchable iff the obligation holds + infcx.predicate_must_hold_modulo_regions(&obligation) +} + +fn contains_illegal_self_type_reference<'tcx, T: TypeVisitable<TyCtxt<'tcx>>>( + tcx: TyCtxt<'tcx>, + trait_def_id: DefId, + value: T, +) -> bool { + // This is somewhat subtle. In general, we want to forbid + // references to `Self` in the argument and return types, + // since the value of `Self` is erased. However, there is one + // exception: it is ok to reference `Self` in order to access + // an associated type of the current trait, since we retain + // the value of those associated types in the object type + // itself. + // + // ```rust + // trait SuperTrait { + // type X; + // } + // + // trait Trait : SuperTrait { + // type Y; + // fn foo(&self, x: Self) // bad + // fn foo(&self) -> Self // bad + // fn foo(&self) -> Option<Self> // bad + // fn foo(&self) -> Self::Y // OK, desugars to next example + // fn foo(&self) -> <Self as Trait>::Y // OK + // fn foo(&self) -> Self::X // OK, desugars to next example + // fn foo(&self) -> <Self as SuperTrait>::X // OK + // } + // ``` + // + // However, it is not as simple as allowing `Self` in a projected + // type, because there are illegal ways to use `Self` as well: + // + // ```rust + // trait Trait : SuperTrait { + // ... + // fn foo(&self) -> <Self as SomeOtherTrait>::X; + // } + // ``` + // + // Here we will not have the type of `X` recorded in the + // object type, and we cannot resolve `Self as SomeOtherTrait` + // without knowing what `Self` is. + + struct IllegalSelfTypeVisitor<'tcx> { + tcx: TyCtxt<'tcx>, + trait_def_id: DefId, + supertraits: Option<Vec<DefId>>, + } + + impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for IllegalSelfTypeVisitor<'tcx> { + type BreakTy = (); + + fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { + match t.kind() { + ty::Param(_) => { + if t == self.tcx.types.self_param { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) + } + } + ty::Alias(ty::Projection, ref data) + if self.tcx.is_impl_trait_in_trait(data.def_id) => + { + // We'll deny these later in their own pass + ControlFlow::Continue(()) + } + ty::Alias(ty::Projection, ref data) => { + // This is a projected type `<Foo as SomeTrait>::X`. + + // Compute supertraits of current trait lazily. + if self.supertraits.is_none() { + let trait_ref = + ty::Binder::dummy(ty::TraitRef::identity(self.tcx, self.trait_def_id)); + self.supertraits = Some( + traits::supertraits(self.tcx, trait_ref).map(|t| t.def_id()).collect(), + ); + } + + // Determine whether the trait reference `Foo as + // SomeTrait` is in fact a supertrait of the + // current trait. In that case, this type is + // legal, because the type `X` will be specified + // in the object type. Note that we can just use + // direct equality here because all of these types + // are part of the formal parameter listing, and + // hence there should be no inference variables. + let is_supertrait_of_current_trait = self + .supertraits + .as_ref() + .unwrap() + .contains(&data.trait_ref(self.tcx).def_id); + + if is_supertrait_of_current_trait { + ControlFlow::Continue(()) // do not walk contained types, do not report error, do collect $200 + } else { + t.super_visit_with(self) // DO walk contained types, POSSIBLY reporting an error + } + } + _ => t.super_visit_with(self), // walk contained types, if any + } + } + + fn visit_const(&mut self, ct: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> { + // Constants can only influence object safety if they are generic and reference `Self`. + // This is only possible for unevaluated constants, so we walk these here. + self.tcx.expand_abstract_consts(ct).super_visit_with(self) + } + } + + value + .visit_with(&mut IllegalSelfTypeVisitor { tcx, trait_def_id, supertraits: None }) + .is_break() +} + +pub fn contains_illegal_impl_trait_in_trait<'tcx>( + tcx: TyCtxt<'tcx>, + fn_def_id: DefId, + ty: ty::Binder<'tcx, Ty<'tcx>>, +) -> Option<MethodViolationCode> { + // This would be caught below, but rendering the error as a separate + // `async-specific` message is better. + if tcx.asyncness(fn_def_id).is_async() { + return Some(MethodViolationCode::AsyncFn); + } + + // FIXME(RPITIT): Perhaps we should use a visitor here? + ty.skip_binder().walk().find_map(|arg| { + if let ty::GenericArgKind::Type(ty) = arg.unpack() + && let ty::Alias(ty::Projection, proj) = ty.kind() + && tcx.is_impl_trait_in_trait(proj.def_id) + { + Some(MethodViolationCode::ReferencesImplTraitInTrait(tcx.def_span(proj.def_id))) + } else { + None + } + }) +} + +pub fn provide(providers: &mut 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 new file mode 100644 index 00000000000..9749ff9cbc1 --- /dev/null +++ b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs @@ -0,0 +1,132 @@ +use crate::infer::InferCtxt; +use crate::traits::{ObligationCause, ObligationCtxt}; +use rustc_data_structures::fx::FxIndexSet; +use rustc_infer::infer::resolve::OpportunisticRegionResolver; +use rustc_infer::infer::InferOk; +use rustc_middle::infer::canonical::{OriginalQueryValues, QueryRegionConstraints}; +use rustc_middle::ty::{self, ParamEnv, Ty, TypeFolder, TypeVisitableExt}; +use rustc_span::def_id::LocalDefId; + +pub use rustc_middle::traits::query::OutlivesBound; + +pub type Bounds<'a, 'tcx: 'a> = impl Iterator<Item = OutlivesBound<'tcx>> + 'a; +pub trait InferCtxtExt<'a, 'tcx> { + fn implied_outlives_bounds( + &self, + param_env: ty::ParamEnv<'tcx>, + body_id: LocalDefId, + ty: Ty<'tcx>, + ) -> Vec<OutlivesBound<'tcx>>; + + fn implied_bounds_tys( + &'a self, + param_env: ty::ParamEnv<'tcx>, + body_id: LocalDefId, + tys: FxIndexSet<Ty<'tcx>>, + ) -> Bounds<'a, 'tcx>; +} + +impl<'a, 'tcx: 'a> InferCtxtExt<'a, 'tcx> for InferCtxt<'tcx> { + /// Implied bounds are region relationships that we deduce + /// automatically. The idea is that (e.g.) a caller must check that a + /// function's argument types are well-formed immediately before + /// calling that fn, and hence the *callee* can assume that its + /// argument types are well-formed. This may imply certain relationships + /// between generic parameters. For example: + /// ``` + /// fn foo<T>(x: &T) {} + /// ``` + /// can only be called with a `'a` and `T` such that `&'a T` is WF. + /// For `&'a T` to be WF, `T: 'a` must hold. So we can assume `T: 'a`. + /// + /// # Parameters + /// + /// - `param_env`, the where-clauses in scope + /// - `body_id`, the body-id to use when normalizing assoc types. + /// Note that this may cause outlives obligations to be injected + /// into the inference context with this body-id. + /// - `ty`, the type that we are supposed to assume is WF. + #[instrument(level = "debug", skip(self, param_env, body_id), ret)] + fn implied_outlives_bounds( + &self, + param_env: ty::ParamEnv<'tcx>, + body_id: LocalDefId, + ty: Ty<'tcx>, + ) -> Vec<OutlivesBound<'tcx>> { + let ty = self.resolve_vars_if_possible(ty); + 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 very rare cases. + // + // See `ui/implied-bounds/implied-bounds-unconstrained-2.rs` for + // an example. + assert!(!ty.has_non_region_infer()); + + let mut canonical_var_values = OriginalQueryValues::default(); + let canonical_ty = + self.canonicalize_query_keep_static(param_env.and(ty), &mut canonical_var_values); + let Ok(canonical_result) = self.tcx.implied_outlives_bounds(canonical_ty) else { + return vec![]; + }; + + let mut constraints = QueryRegionConstraints::default(); + let Ok(InferOk { value: mut bounds, obligations }) = self + .instantiate_nll_query_response_and_region_obligations( + &ObligationCause::dummy(), + param_env, + &canonical_var_values, + canonical_result, + &mut constraints, + ) + else { + return vec![]; + }; + assert_eq!(&obligations, &[]); + + // Because of #109628, we may have unexpected placeholders. Ignore them! + // FIXME(#109628): panic in this case once the issue is fixed. + bounds.retain(|bound| !bound.has_placeholders()); + + if !constraints.is_empty() { + let span = self.tcx.def_span(body_id); + + debug!(?constraints); + if !constraints.member_constraints.is_empty() { + span_bug!(span, "{:#?}", constraints.member_constraints); + } + + // Instantiation may have produced new inference variables and constraints on those + // variables. Process these constraints. + let ocx = ObligationCtxt::new(self); + let cause = ObligationCause::misc(span, body_id); + for &constraint in &constraints.outlives { + ocx.register_obligation(self.query_outlives_constraint_to_obligation( + constraint, + cause.clone(), + param_env, + )); + } + + let errors = ocx.select_all_or_error(); + if !errors.is_empty() { + self.dcx().span_delayed_bug( + span, + "implied_outlives_bounds failed to solve obligations from instantiation", + ); + } + }; + + bounds + } + + fn implied_bounds_tys( + &'a self, + param_env: ParamEnv<'tcx>, + body_id: LocalDefId, + tys: FxIndexSet<Ty<'tcx>>, + ) -> Bounds<'a, 'tcx> { + tys.into_iter().flat_map(move |ty| self.implied_outlives_bounds(param_env, body_id, ty)) + } +} diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs new file mode 100644 index 00000000000..dd4e69efe37 --- /dev/null +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -0,0 +1,2626 @@ +//! Code for projecting associated types out of trait references. + +use super::check_args_compatible; +use super::specialization_graph; +use super::translate_args; +use super::util; +use super::MismatchedProjectionTypes; +use super::Obligation; +use super::ObligationCause; +use super::PredicateObligation; +use super::Selection; +use super::SelectionContext; +use super::SelectionError; +use super::{Normalized, NormalizedTy, ProjectionCacheEntry, ProjectionCacheKey}; +use rustc_middle::traits::BuiltinImplSource; +use rustc_middle::traits::ImplSource; +use rustc_middle::traits::ImplSourceUserDefinedData; + +use crate::errors::InherentProjectionNormalizationOverflow; +use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use crate::infer::{BoundRegionConversionTime, InferCtxt, InferOk}; +use crate::traits::error_reporting::TypeErrCtxtExt as _; +use crate::traits::query::evaluate_obligation::InferCtxtExt as _; +use crate::traits::select::ProjectionMatchesProjection; +use rustc_data_structures::sso::SsoHashSet; +use rustc_data_structures::stack::ensure_sufficient_stack; +use rustc_errors::ErrorGuaranteed; +use rustc_hir::def::DefKind; +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::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}; +use rustc_middle::ty::{self, Term, ToPredicate, Ty, TyCtxt}; +use rustc_span::symbol::sym; + +use std::collections::BTreeMap; + +pub use rustc_middle::traits::Reveal; + +pub type PolyProjectionObligation<'tcx> = Obligation<'tcx, ty::PolyProjectionPredicate<'tcx>>; + +pub type ProjectionObligation<'tcx> = Obligation<'tcx, ty::ProjectionPredicate<'tcx>>; + +pub type ProjectionTyObligation<'tcx> = Obligation<'tcx, ty::AliasTy<'tcx>>; + +pub(super) struct InProgress; + +pub trait NormalizeExt<'tcx> { + /// Normalize a value using the `AssocTypeNormalizer`. + /// + /// This normalization should be used when the type contains inference variables or the + /// projection may be fallible. + fn normalize<T: TypeFoldable<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(-Znext-solver): 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> { + 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) } + } + } +} + +/// When attempting to resolve `<T as TraitRef>::Name` ... +#[derive(Debug)] +pub enum ProjectionError<'tcx> { + /// ...we found multiple sources of information and couldn't resolve the ambiguity. + TooManyCandidates, + + /// ...an error occurred matching `T : TraitRef` + TraitSelectionError(SelectionError<'tcx>), +} + +#[derive(PartialEq, Eq, Debug)] +enum ProjectionCandidate<'tcx> { + /// From a where-clause in the env or object type + ParamEnv(ty::PolyProjectionPredicate<'tcx>), + + /// From the definition of `Trait` when you have something like + /// `<<A as Trait>::B as Trait2>::C`. + TraitDef(ty::PolyProjectionPredicate<'tcx>), + + /// Bounds specified on an object type + Object(ty::PolyProjectionPredicate<'tcx>), + + /// From an "impl" (or a "pseudo-impl" returned by select) + Select(Selection<'tcx>), +} + +enum ProjectionCandidateSet<'tcx> { + None, + Single(ProjectionCandidate<'tcx>), + Ambiguous, + Error(SelectionError<'tcx>), +} + +impl<'tcx> ProjectionCandidateSet<'tcx> { + fn mark_ambiguous(&mut self) { + *self = ProjectionCandidateSet::Ambiguous; + } + + fn mark_error(&mut self, err: SelectionError<'tcx>) { + *self = ProjectionCandidateSet::Error(err); + } + + // Returns true if the push was successful, or false if the candidate + // was discarded -- this could be because of ambiguity, or because + // a higher-priority candidate is already there. + fn push_candidate(&mut self, candidate: ProjectionCandidate<'tcx>) -> bool { + use self::ProjectionCandidate::*; + use self::ProjectionCandidateSet::*; + + // This wacky variable is just used to try and + // make code readable and avoid confusing paths. + // It is assigned a "value" of `()` only on those + // paths in which we wish to convert `*self` to + // ambiguous (and return false, because the candidate + // was not used). On other paths, it is not assigned, + // and hence if those paths *could* reach the code that + // comes after the match, this fn would not compile. + let convert_to_ambiguous; + + match self { + None => { + *self = Single(candidate); + return true; + } + + Single(current) => { + // Duplicates can happen inside ParamEnv. In the case, we + // perform a lazy deduplication. + if current == &candidate { + return false; + } + + // Prefer where-clauses. As in select, if there are multiple + // candidates, we prefer where-clause candidates over impls. This + // may seem a bit surprising, since impls are the source of + // "truth" in some sense, but in fact some of the impls that SEEM + // applicable are not, because of nested obligations. Where + // clauses are the safer choice. See the comment on + // `select::SelectionCandidate` and #21974 for more details. + match (current, candidate) { + (ParamEnv(..), ParamEnv(..)) => convert_to_ambiguous = (), + (ParamEnv(..), _) => return false, + (_, ParamEnv(..)) => bug!( + "should never prefer non-param-env candidates over param-env candidates" + ), + (_, _) => convert_to_ambiguous = (), + } + } + + Ambiguous | Error(..) => { + return false; + } + } + + // We only ever get here when we moved from a single candidate + // to ambiguous. + let () = convert_to_ambiguous; + *self = Ambiguous; + false + } +} + +/// States returned from `poly_project_and_unify_type`. Takes the place +/// of the old return type, which was: +/// ```ignore (not-rust) +/// Result< +/// Result<Option<Vec<PredicateObligation<'tcx>>>, InProgress>, +/// MismatchedProjectionTypes<'tcx>, +/// > +/// ``` +pub(super) enum ProjectAndUnifyResult<'tcx> { + /// The projection bound holds subject to the given obligations. If the + /// projection cannot be normalized because the required trait bound does + /// not hold, this is returned, with `obligations` being a predicate that + /// cannot be proven. + Holds(Vec<PredicateObligation<'tcx>>), + /// The projection cannot be normalized due to ambiguity. Resolving some + /// inference variables in the projection may fix this. + FailedNormalization, + /// The project cannot be normalized because `poly_project_and_unify_type` + /// is called recursively while normalizing the same projection. + Recursive, + // the projection can be normalized, but is not equal to the expected type. + // Returns the type error that arose from the mismatch. + MismatchedProjectionTypes(MismatchedProjectionTypes<'tcx>), +} + +/// Evaluates constraints of the form: +/// ```ignore (not-rust) +/// for<...> <T as Trait>::U == V +/// ``` +/// If successful, this may result in additional obligations. Also returns +/// the projection cache key used to track these additional obligations. +#[instrument(level = "debug", skip(selcx))] +pub(super) fn poly_project_and_unify_type<'cx, 'tcx>( + selcx: &mut SelectionContext<'cx, 'tcx>, + obligation: &PolyProjectionObligation<'tcx>, +) -> ProjectAndUnifyResult<'tcx> { + let infcx = selcx.infcx; + let r = infcx.commit_if_ok(|_snapshot| { + let old_universe = infcx.universe(); + let placeholder_predicate = + infcx.instantiate_binder_with_placeholders(obligation.predicate); + let new_universe = infcx.universe(); + + let placeholder_obligation = obligation.with(infcx.tcx, placeholder_predicate); + match project_and_unify_type(selcx, &placeholder_obligation) { + ProjectAndUnifyResult::MismatchedProjectionTypes(e) => Err(e), + ProjectAndUnifyResult::Holds(obligations) + if old_universe != new_universe + && selcx.tcx().features().generic_associated_types_extended => + { + // If the `generic_associated_types_extended` feature is active, then we ignore any + // obligations references lifetimes from any universe greater than or equal to the + // universe just created. Otherwise, we can end up with something like `for<'a> I: 'a`, + // which isn't quite what we want. Ideally, we want either an implied + // `for<'a where I: 'a> I: 'a` or we want to "lazily" check these hold when we + // substitute concrete regions. There is design work to be done here; until then, + // however, this allows experimenting potential GAT features without running into + // well-formedness issues. + let new_obligations = obligations + .into_iter() + .filter(|obligation| { + let mut visitor = MaxUniverse::new(); + obligation.predicate.visit_with(&mut visitor); + visitor.max_universe() < new_universe + }) + .collect(); + Ok(ProjectAndUnifyResult::Holds(new_obligations)) + } + other => Ok(other), + } + }); + + match r { + Ok(inner) => inner, + Err(err) => ProjectAndUnifyResult::MismatchedProjectionTypes(err), + } +} + +/// Evaluates constraints of the form: +/// ```ignore (not-rust) +/// <T as Trait>::U == V +/// ``` +/// If successful, this may result in additional obligations. +/// +/// See [poly_project_and_unify_type] for an explanation of the return value. +#[instrument(level = "debug", skip(selcx))] +fn project_and_unify_type<'cx, 'tcx>( + selcx: &mut SelectionContext<'cx, 'tcx>, + obligation: &ProjectionObligation<'tcx>, +) -> ProjectAndUnifyResult<'tcx> { + let mut obligations = vec![]; + + let infcx = selcx.infcx; + let normalized = match opt_normalize_projection_type( + selcx, + obligation.param_env, + obligation.predicate.projection_ty, + obligation.cause.clone(), + obligation.recursion_depth, + &mut obligations, + ) { + Ok(Some(n)) => n, + Ok(None) => return ProjectAndUnifyResult::FailedNormalization, + Err(InProgress) => return ProjectAndUnifyResult::Recursive, + }; + debug!(?normalized, ?obligations, "project_and_unify_type result"); + let actual = obligation.predicate.term; + // For an example where this is necessary see tests/ui/impl-trait/nested-return-type2.rs + // This allows users to omit re-mentioning all bounds on an associated type and just use an + // `impl Trait` for the assoc type to add more bounds. + let InferOk { value: actual, obligations: new } = + selcx.infcx.replace_opaque_types_with_inference_vars( + actual, + obligation.cause.body_id, + obligation.cause.span, + obligation.param_env, + ); + obligations.extend(new); + + // Need to define opaque types to support nested opaque types like `impl Fn() -> impl Trait` + match infcx.at(&obligation.cause, obligation.param_env).eq( + DefineOpaqueTypes::Yes, + normalized, + actual, + ) { + Ok(InferOk { obligations: inferred_obligations, value: () }) => { + obligations.extend(inferred_obligations); + ProjectAndUnifyResult::Holds(obligations) + } + Err(err) => { + debug!("equating types encountered error {:?}", err); + ProjectAndUnifyResult::MismatchedProjectionTypes(MismatchedProjectionTypes { err }) + } + } +} + +/// As `normalize`, but with a custom depth. +pub(crate) fn normalize_with_depth<'a, 'b, 'tcx, T>( + selcx: &'a mut SelectionContext<'b, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + cause: ObligationCause<'tcx>, + depth: usize, + value: T, +) -> Normalized<'tcx, T> +where + T: TypeFoldable<TyCtxt<'tcx>>, +{ + let mut obligations = Vec::new(); + let value = normalize_with_depth_to(selcx, param_env, cause, depth, value, &mut obligations); + Normalized { value, obligations } +} + +#[instrument(level = "info", skip(selcx, param_env, cause, obligations))] +pub(crate) fn normalize_with_depth_to<'a, 'b, 'tcx, T>( + selcx: &'a mut SelectionContext<'b, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + cause: ObligationCause<'tcx>, + depth: usize, + value: T, + obligations: &mut Vec<PredicateObligation<'tcx>>, +) -> T +where + T: TypeFoldable<TyCtxt<'tcx>>, +{ + debug!(obligations.len = obligations.len()); + let mut normalizer = AssocTypeNormalizer::new(selcx, param_env, cause, depth, obligations); + let result = ensure_sufficient_stack(|| normalizer.fold(value)); + debug!(?result, obligations.len = normalizer.obligations.len()); + debug!(?normalizer.obligations,); + result +} + +#[instrument(level = "info", skip(selcx, param_env, cause, obligations))] +pub(crate) fn try_normalize_with_depth_to<'a, 'b, 'tcx, T>( + selcx: &'a mut SelectionContext<'b, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + cause: ObligationCause<'tcx>, + depth: usize, + value: T, + obligations: &mut Vec<PredicateObligation<'tcx>>, +) -> T +where + T: TypeFoldable<TyCtxt<'tcx>>, +{ + debug!(obligations.len = obligations.len()); + let mut normalizer = AssocTypeNormalizer::new_without_eager_inference_replacement( + selcx, + param_env, + cause, + depth, + obligations, + ); + let result = ensure_sufficient_stack(|| normalizer.fold(value)); + debug!(?result, obligations.len = normalizer.obligations.len()); + debug!(?normalizer.obligations,); + result +} + +pub(crate) fn needs_normalization<'tcx, T: TypeVisitable<TyCtxt<'tcx>>>( + value: &T, + reveal: Reveal, +) -> bool { + match reveal { + Reveal::UserFacing => value.has_type_flags( + ty::TypeFlags::HAS_TY_PROJECTION + | ty::TypeFlags::HAS_TY_INHERENT + | ty::TypeFlags::HAS_CT_PROJECTION, + ), + Reveal::All => value.has_type_flags( + ty::TypeFlags::HAS_TY_PROJECTION + | ty::TypeFlags::HAS_TY_INHERENT + | ty::TypeFlags::HAS_TY_OPAQUE + | ty::TypeFlags::HAS_CT_PROJECTION, + ), + } +} + +struct AssocTypeNormalizer<'a, 'b, 'tcx> { + selcx: &'a mut SelectionContext<'b, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + cause: ObligationCause<'tcx>, + obligations: &'a mut Vec<PredicateObligation<'tcx>>, + depth: usize, + universes: Vec<Option<ty::UniverseIndex>>, + /// If true, when a projection is unable to be completed, an inference + /// variable will be created and an obligation registered to project to that + /// inference variable. Also, constants will be eagerly evaluated. + eager_inference_replacement: bool, +} + +impl<'a, 'b, 'tcx> AssocTypeNormalizer<'a, 'b, 'tcx> { + fn new( + selcx: &'a mut SelectionContext<'b, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + cause: ObligationCause<'tcx>, + depth: usize, + obligations: &'a mut Vec<PredicateObligation<'tcx>>, + ) -> AssocTypeNormalizer<'a, 'b, 'tcx> { + debug_assert!(!selcx.infcx.next_trait_solver()); + AssocTypeNormalizer { + selcx, + param_env, + cause, + obligations, + depth, + universes: vec![], + eager_inference_replacement: true, + } + } + + fn new_without_eager_inference_replacement( + selcx: &'a mut SelectionContext<'b, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + cause: ObligationCause<'tcx>, + depth: usize, + obligations: &'a mut Vec<PredicateObligation<'tcx>>, + ) -> AssocTypeNormalizer<'a, 'b, 'tcx> { + AssocTypeNormalizer { + selcx, + param_env, + cause, + obligations, + depth, + universes: vec![], + eager_inference_replacement: false, + } + } + + fn fold<T: TypeFoldable<TyCtxt<'tcx>>>(&mut self, value: T) -> T { + let value = self.selcx.infcx.resolve_vars_if_possible(value); + debug!(?value); + + assert!( + !value.has_escaping_bound_vars(), + "Normalizing {value:?} without wrapping in a `Binder`" + ); + + if !needs_normalization(&value, self.param_env.reveal()) { + value + } else { + value.fold_with(self) + } + } +} + +impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTypeNormalizer<'a, 'b, 'tcx> { + fn interner(&self) -> TyCtxt<'tcx> { + self.selcx.tcx() + } + + fn fold_binder<T: TypeFoldable<TyCtxt<'tcx>>>( + &mut self, + t: ty::Binder<'tcx, T>, + ) -> ty::Binder<'tcx, T> { + self.universes.push(None); + let t = t.super_fold_with(self); + self.universes.pop(); + t + } + + fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { + if !needs_normalization(&ty, self.param_env.reveal()) { + return ty; + } + + let (kind, data) = match *ty.kind() { + ty::Alias(kind, alias_ty) => (kind, alias_ty), + _ => return ty.super_fold_with(self), + }; + + // We try to be a little clever here as a performance optimization in + // cases where there are nested projections under binders. + // For example: + // ``` + // for<'a> fn(<T as Foo>::One<'a, Box<dyn Bar<'a, Item=<T as Foo>::Two<'a>>>>) + // ``` + // We normalize the args on the projection before the projecting, but + // if we're naive, we'll + // replace bound vars on inner, project inner, replace placeholders on inner, + // replace bound vars on outer, project outer, replace placeholders on outer + // + // However, if we're a bit more clever, we can replace the bound vars + // on the entire type before normalizing nested projections, meaning we + // replace bound vars on outer, project inner, + // project outer, replace placeholders on outer + // + // This is possible because the inner `'a` will already be a placeholder + // when we need to normalize the inner projection + // + // On the other hand, this does add a bit of complexity, since we only + // replace bound vars if the current type is a `Projection` and we need + // to make sure we don't forget to fold the args regardless. + + match kind { + 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), + + Reveal::All => { + let recursion_limit = self.interner().recursion_limit(); + if !recursion_limit.value_within_limit(self.depth) { + self.selcx.infcx.err_ctxt().report_overflow_error( + &ty, + self.cause.span, + true, + |_| {}, + ); + } + + let args = data.args.fold_with(self); + let generic_ty = self.interner().type_of(data.def_id); + let concrete_ty = generic_ty.instantiate(self.interner(), args); + self.depth += 1; + let folded_ty = self.fold_ty(concrete_ty); + self.depth -= 1; + folded_ty + } + } + } + + ty::Projection if !data.has_escaping_bound_vars() => { + // This branch is *mostly* just an optimization: when we don't + // have escaping bound vars, we don't need to replace them with + // placeholders (see branch below). *Also*, we know that we can + // register an obligation to *later* project, since we know + // there won't be bound vars there. + let data = data.fold_with(self); + let normalized_ty = if self.eager_inference_replacement { + normalize_projection_type( + self.selcx, + self.param_env, + data, + self.cause.clone(), + self.depth, + self.obligations, + ) + } else { + opt_normalize_projection_type( + self.selcx, + self.param_env, + data, + self.cause.clone(), + self.depth, + self.obligations, + ) + .ok() + .flatten() + .unwrap_or_else(|| ty.super_fold_with(self).into()) + }; + debug!( + ?self.depth, + ?ty, + ?normalized_ty, + obligations.len = ?self.obligations.len(), + "AssocTypeNormalizer: normalized type" + ); + normalized_ty.ty().unwrap() + } + + ty::Projection => { + // If there are escaping bound vars, we temporarily replace the + // bound vars with placeholders. Note though, that in the case + // that we still can't project for whatever reason (e.g. self + // type isn't known enough), we *can't* register an obligation + // and return an inference variable (since then that obligation + // would have bound vars and that's a can of worms). Instead, + // we just give up and fall back to pretending like we never tried! + // + // Note: this isn't necessarily the final approach here; we may + // want to figure out how to register obligations with escaping vars + // or handle this some other way. + + let infcx = self.selcx.infcx; + let (data, mapped_regions, mapped_types, mapped_consts) = + BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, data); + let data = data.fold_with(self); + let normalized_ty = opt_normalize_projection_type( + self.selcx, + self.param_env, + data, + self.cause.clone(), + self.depth, + self.obligations, + ) + .ok() + .flatten() + .map(|term| term.ty().unwrap()) + .map(|normalized_ty| { + PlaceholderReplacer::replace_placeholders( + infcx, + mapped_regions, + mapped_types, + mapped_consts, + &self.universes, + normalized_ty, + ) + }) + .unwrap_or_else(|| ty.super_fold_with(self)); + + debug!( + ?self.depth, + ?ty, + ?normalized_ty, + obligations.len = ?self.obligations.len(), + "AssocTypeNormalizer: normalized type" + ); + normalized_ty + } + ty::Weak => { + let recursion_limit = self.interner().recursion_limit(); + if !recursion_limit.value_within_limit(self.depth) { + self.selcx.infcx.err_ctxt().report_overflow_error( + &ty, + self.cause.span, + false, + |diag| { + diag.note(crate::fluent_generated::trait_selection_ty_alias_overflow); + }, + ); + } + + let infcx = self.selcx.infcx; + self.obligations.extend( + infcx.tcx.predicates_of(data.def_id).instantiate_own(infcx.tcx, data.args).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) + }, + ), + ); + self.depth += 1; + let res = infcx + .tcx + .type_of(data.def_id) + .instantiate(infcx.tcx, data.args) + .fold_with(self); + self.depth -= 1; + res + } + + ty::Inherent if !data.has_escaping_bound_vars() => { + // This branch is *mostly* just an optimization: when we don't + // have escaping bound vars, we don't need to replace them with + // placeholders (see branch below). *Also*, we know that we can + // register an obligation to *later* project, since we know + // there won't be bound vars there. + + let data = data.fold_with(self); + + // FIXME(inherent_associated_types): Do we need to honor `self.eager_inference_replacement` + // here like `ty::Projection`? + normalize_inherent_projection( + self.selcx, + self.param_env, + data, + self.cause.clone(), + self.depth, + self.obligations, + ) + } + + ty::Inherent => { + let infcx = self.selcx.infcx; + let (data, mapped_regions, mapped_types, mapped_consts) = + BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, data); + let data = data.fold_with(self); + let ty = normalize_inherent_projection( + self.selcx, + self.param_env, + data, + self.cause.clone(), + self.depth, + self.obligations, + ); + + PlaceholderReplacer::replace_placeholders( + infcx, + mapped_regions, + mapped_types, + mapped_consts, + &self.universes, + ty, + ) + } + } + } + + #[instrument(skip(self), level = "debug")] + fn fold_const(&mut self, constant: ty::Const<'tcx>) -> ty::Const<'tcx> { + let tcx = self.selcx.tcx(); + if tcx.features().generic_const_exprs + || !needs_normalization(&constant, self.param_env.reveal()) + { + constant + } else { + let constant = constant.super_fold_with(self); + debug!(?constant, ?self.param_env); + with_replaced_escaping_bound_vars( + self.selcx.infcx, + &mut self.universes, + constant, + |constant| constant.normalize(tcx, self.param_env), + ) + } + } + + #[inline] + fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> { + if p.allow_normalization() && needs_normalization(&p, self.param_env.reveal()) { + p.super_fold_with(self) + } else { + p + } + } +} + +pub struct BoundVarReplacer<'me, 'tcx> { + infcx: &'me InferCtxt<'tcx>, + // These three maps track the bound variable that were replaced by placeholders. It might be + // nice to remove these since we already have the `kind` in the placeholder; we really just need + // the `var` (but we *could* bring that into scope if we were to track them as we pass them). + mapped_regions: BTreeMap<ty::PlaceholderRegion, ty::BoundRegion>, + mapped_types: BTreeMap<ty::PlaceholderType, ty::BoundTy>, + mapped_consts: BTreeMap<ty::PlaceholderConst, ty::BoundVar>, + // The current depth relative to *this* folding, *not* the entire normalization. In other words, + // the depth of binders we've passed here. + current_index: ty::DebruijnIndex, + // The `UniverseIndex` of the binding levels above us. These are optional, since we are lazy: + // we don't actually create a universe until we see a bound var we have to replace. + universe_indices: &'me mut Vec<Option<ty::UniverseIndex>>, +} + +/// Executes `f` on `value` after replacing all escaping bound variables with placeholders +/// and then replaces these placeholders with the original bound variables in the result. +/// +/// In most places, bound variables should be replaced right when entering a binder, making +/// this function unnecessary. However, normalization currently does not do that, so we have +/// to do this lazily. +/// +/// You should not add any additional uses of this function, at least not without first +/// discussing it with t-types. +/// +/// FIXME(@lcnr): We may even consider experimenting with eagerly replacing bound vars during +/// normalization as well, at which point this function will be unnecessary and can be removed. +pub fn with_replaced_escaping_bound_vars< + 'a, + 'tcx, + T: TypeFoldable<TyCtxt<'tcx>>, + R: TypeFoldable<TyCtxt<'tcx>>, +>( + infcx: &'a InferCtxt<'tcx>, + universe_indices: &'a mut Vec<Option<ty::UniverseIndex>>, + value: T, + f: impl FnOnce(T) -> R, +) -> R { + if value.has_escaping_bound_vars() { + let (value, mapped_regions, mapped_types, mapped_consts) = + BoundVarReplacer::replace_bound_vars(infcx, universe_indices, value); + let result = f(value); + PlaceholderReplacer::replace_placeholders( + infcx, + mapped_regions, + mapped_types, + mapped_consts, + universe_indices, + result, + ) + } else { + f(value) + } +} + +impl<'me, 'tcx> BoundVarReplacer<'me, 'tcx> { + /// Returns `Some` if we *were* able to replace bound vars. If there are any bound vars that + /// use a binding level above `universe_indices.len()`, we fail. + pub fn replace_bound_vars<T: TypeFoldable<TyCtxt<'tcx>>>( + infcx: &'me InferCtxt<'tcx>, + universe_indices: &'me mut Vec<Option<ty::UniverseIndex>>, + value: T, + ) -> ( + T, + BTreeMap<ty::PlaceholderRegion, ty::BoundRegion>, + BTreeMap<ty::PlaceholderType, ty::BoundTy>, + BTreeMap<ty::PlaceholderConst, ty::BoundVar>, + ) { + let mapped_regions: BTreeMap<ty::PlaceholderRegion, ty::BoundRegion> = BTreeMap::new(); + let mapped_types: BTreeMap<ty::PlaceholderType, ty::BoundTy> = BTreeMap::new(); + let mapped_consts: BTreeMap<ty::PlaceholderConst, ty::BoundVar> = BTreeMap::new(); + + let mut replacer = BoundVarReplacer { + infcx, + mapped_regions, + mapped_types, + mapped_consts, + current_index: ty::INNERMOST, + universe_indices, + }; + + let value = value.fold_with(&mut replacer); + + (value, replacer.mapped_regions, replacer.mapped_types, replacer.mapped_consts) + } + + fn universe_for(&mut self, debruijn: ty::DebruijnIndex) -> ty::UniverseIndex { + let infcx = self.infcx; + let index = + self.universe_indices.len() + self.current_index.as_usize() - debruijn.as_usize() - 1; + let universe = self.universe_indices[index].unwrap_or_else(|| { + for i in self.universe_indices.iter_mut().take(index + 1) { + *i = i.or_else(|| Some(infcx.create_next_universe())) + } + self.universe_indices[index].unwrap() + }); + universe + } +} + +impl<'tcx> TypeFolder<TyCtxt<'tcx>> for BoundVarReplacer<'_, 'tcx> { + fn interner(&self) -> TyCtxt<'tcx> { + self.infcx.tcx + } + + fn fold_binder<T: TypeFoldable<TyCtxt<'tcx>>>( + &mut self, + t: ty::Binder<'tcx, T>, + ) -> ty::Binder<'tcx, T> { + self.current_index.shift_in(1); + let t = t.super_fold_with(self); + self.current_index.shift_out(1); + t + } + + fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { + match *r { + ty::ReBound(debruijn, _) + if debruijn.as_usize() + >= self.current_index.as_usize() + self.universe_indices.len() => + { + bug!( + "Bound vars {r:#?} outside of `self.universe_indices`: {:#?}", + self.universe_indices + ); + } + ty::ReBound(debruijn, br) if debruijn >= self.current_index => { + let universe = self.universe_for(debruijn); + let p = ty::PlaceholderRegion { universe, bound: br }; + self.mapped_regions.insert(p, br); + ty::Region::new_placeholder(self.infcx.tcx, p) + } + _ => r, + } + } + + fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { + match *t.kind() { + ty::Bound(debruijn, _) + if debruijn.as_usize() + 1 + > self.current_index.as_usize() + self.universe_indices.len() => + { + bug!( + "Bound vars {t:#?} outside of `self.universe_indices`: {:#?}", + self.universe_indices + ); + } + ty::Bound(debruijn, bound_ty) if debruijn >= self.current_index => { + let universe = self.universe_for(debruijn); + let p = ty::PlaceholderType { universe, bound: bound_ty }; + self.mapped_types.insert(p, bound_ty); + Ty::new_placeholder(self.infcx.tcx, p) + } + _ if t.has_vars_bound_at_or_above(self.current_index) => t.super_fold_with(self), + _ => t, + } + } + + fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { + match ct.kind() { + ty::ConstKind::Bound(debruijn, _) + if debruijn.as_usize() + 1 + > self.current_index.as_usize() + self.universe_indices.len() => + { + bug!( + "Bound vars {ct:#?} outside of `self.universe_indices`: {:#?}", + self.universe_indices + ); + } + ty::ConstKind::Bound(debruijn, bound_const) if debruijn >= self.current_index => { + let universe = self.universe_for(debruijn); + let p = ty::PlaceholderConst { universe, bound: bound_const }; + self.mapped_consts.insert(p, bound_const); + ty::Const::new_placeholder(self.infcx.tcx, p, ct.ty()) + } + _ => ct.super_fold_with(self), + } + } + + fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> { + if p.has_vars_bound_at_or_above(self.current_index) { p.super_fold_with(self) } else { p } + } +} + +/// The inverse of [`BoundVarReplacer`]: replaces placeholders with the bound vars from which they came. +pub struct PlaceholderReplacer<'me, 'tcx> { + infcx: &'me InferCtxt<'tcx>, + mapped_regions: BTreeMap<ty::PlaceholderRegion, ty::BoundRegion>, + mapped_types: BTreeMap<ty::PlaceholderType, ty::BoundTy>, + mapped_consts: BTreeMap<ty::PlaceholderConst, ty::BoundVar>, + universe_indices: &'me [Option<ty::UniverseIndex>], + current_index: ty::DebruijnIndex, +} + +impl<'me, 'tcx> PlaceholderReplacer<'me, 'tcx> { + pub fn replace_placeholders<T: TypeFoldable<TyCtxt<'tcx>>>( + infcx: &'me InferCtxt<'tcx>, + mapped_regions: BTreeMap<ty::PlaceholderRegion, ty::BoundRegion>, + mapped_types: BTreeMap<ty::PlaceholderType, ty::BoundTy>, + mapped_consts: BTreeMap<ty::PlaceholderConst, ty::BoundVar>, + universe_indices: &'me [Option<ty::UniverseIndex>], + value: T, + ) -> T { + let mut replacer = PlaceholderReplacer { + infcx, + mapped_regions, + mapped_types, + mapped_consts, + universe_indices, + current_index: ty::INNERMOST, + }; + value.fold_with(&mut replacer) + } +} + +impl<'tcx> TypeFolder<TyCtxt<'tcx>> for PlaceholderReplacer<'_, 'tcx> { + fn interner(&self) -> TyCtxt<'tcx> { + self.infcx.tcx + } + + fn fold_binder<T: TypeFoldable<TyCtxt<'tcx>>>( + &mut self, + t: ty::Binder<'tcx, T>, + ) -> ty::Binder<'tcx, T> { + if !t.has_placeholders() && !t.has_infer() { + return t; + } + self.current_index.shift_in(1); + let t = t.super_fold_with(self); + self.current_index.shift_out(1); + t + } + + fn fold_region(&mut self, r0: ty::Region<'tcx>) -> ty::Region<'tcx> { + let r1 = match *r0 { + ty::ReVar(vid) => self + .infcx + .inner + .borrow_mut() + .unwrap_region_constraints() + .opportunistic_resolve_var(self.infcx.tcx, vid), + _ => r0, + }; + + let r2 = match *r1 { + ty::RePlaceholder(p) => { + let replace_var = self.mapped_regions.get(&p); + match replace_var { + Some(replace_var) => { + let index = self + .universe_indices + .iter() + .position(|u| matches!(u, Some(pu) if *pu == p.universe)) + .unwrap_or_else(|| bug!("Unexpected placeholder universe.")); + let db = ty::DebruijnIndex::from_usize( + self.universe_indices.len() - index + self.current_index.as_usize() - 1, + ); + ty::Region::new_bound(self.interner(), db, *replace_var) + } + None => r1, + } + } + _ => r1, + }; + + debug!(?r0, ?r1, ?r2, "fold_region"); + + r2 + } + + fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { + let ty = self.infcx.shallow_resolve(ty); + match *ty.kind() { + ty::Placeholder(p) => { + let replace_var = self.mapped_types.get(&p); + match replace_var { + Some(replace_var) => { + let index = self + .universe_indices + .iter() + .position(|u| matches!(u, Some(pu) if *pu == p.universe)) + .unwrap_or_else(|| bug!("Unexpected placeholder universe.")); + let db = ty::DebruijnIndex::from_usize( + self.universe_indices.len() - index + self.current_index.as_usize() - 1, + ); + Ty::new_bound(self.infcx.tcx, db, *replace_var) + } + None => { + if ty.has_infer() { + ty.super_fold_with(self) + } else { + ty + } + } + } + } + + _ if ty.has_placeholders() || ty.has_infer() => ty.super_fold_with(self), + _ => ty, + } + } + + fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { + let ct = self.infcx.shallow_resolve(ct); + if let ty::ConstKind::Placeholder(p) = ct.kind() { + let replace_var = self.mapped_consts.get(&p); + match replace_var { + Some(replace_var) => { + let index = self + .universe_indices + .iter() + .position(|u| matches!(u, Some(pu) if *pu == p.universe)) + .unwrap_or_else(|| bug!("Unexpected placeholder universe.")); + let db = ty::DebruijnIndex::from_usize( + self.universe_indices.len() - index + self.current_index.as_usize() - 1, + ); + ty::Const::new_bound(self.infcx.tcx, db, *replace_var, ct.ty()) + } + None => { + if ct.has_infer() { + ct.super_fold_with(self) + } else { + ct + } + } + } + } else { + ct.super_fold_with(self) + } + } +} + +/// The guts of `normalize`: normalize a specific projection like `<T +/// as Trait>::Item`. The result is always a type (and possibly +/// additional obligations). If ambiguity arises, which implies that +/// there are unresolved type variables in the projection, we will +/// substitute a fresh type variable `$X` and generate a new +/// obligation `<T as Trait>::Item == $X` for later. +pub fn normalize_projection_type<'a, 'b, 'tcx>( + selcx: &'a mut SelectionContext<'b, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + projection_ty: ty::AliasTy<'tcx>, + cause: ObligationCause<'tcx>, + depth: usize, + obligations: &mut Vec<PredicateObligation<'tcx>>, +) -> Term<'tcx> { + opt_normalize_projection_type( + selcx, + param_env, + projection_ty, + cause.clone(), + depth, + obligations, + ) + .ok() + .flatten() + .unwrap_or_else(move || { + // if we bottom out in ambiguity, create a type variable + // and a deferred predicate to resolve this when more type + // information is available. + + selcx.infcx.infer_projection(param_env, projection_ty, cause, depth + 1, obligations).into() + }) +} + +/// The guts of `normalize`: normalize a specific projection like `<T +/// as Trait>::Item`. The result is always a type (and possibly +/// additional obligations). Returns `None` in the case of ambiguity, +/// which indicates that there are unbound type variables. +/// +/// This function used to return `Option<NormalizedTy<'tcx>>`, which contains a +/// `Ty<'tcx>` and an obligations vector. But that obligation vector was very +/// often immediately appended to another obligations vector. So now this +/// function takes an obligations vector and appends to it directly, which is +/// slightly uglier but avoids the need for an extra short-lived allocation. +#[instrument(level = "debug", skip(selcx, param_env, cause, obligations))] +fn opt_normalize_projection_type<'a, 'b, 'tcx>( + selcx: &'a mut SelectionContext<'b, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + projection_ty: ty::AliasTy<'tcx>, + cause: ObligationCause<'tcx>, + depth: usize, + 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. + let use_cache = !selcx.is_intercrate(); + + let projection_ty = infcx.resolve_vars_if_possible(projection_ty); + let cache_key = ProjectionCacheKey::new(projection_ty); + + // FIXME(#20304) For now, I am caching here, which is good, but it + // means we don't capture the type variables that are created in + // the case of ambiguity. Which means we may create a large stream + // of such variables. OTOH, if we move the caching up a level, we + // would not benefit from caching when proving `T: Trait<U=Foo>` + // bounds. It might be the case that we want two distinct caches, + // or else another kind of cache entry. + + let cache_result = if use_cache { + infcx.inner.borrow_mut().projection_cache().try_start(cache_key) + } else { + Ok(()) + }; + match cache_result { + Ok(()) => debug!("no cache"), + Err(ProjectionCacheEntry::Ambiguous) => { + // If we found ambiguity the last time, that means we will continue + // to do so until some type in the key changes (and we know it + // hasn't, because we just fully resolved it). + debug!("found cache entry: ambiguous"); + return Ok(None); + } + Err(ProjectionCacheEntry::InProgress) => { + // Under lazy normalization, this can arise when + // bootstrapping. That is, imagine an environment with a + // where-clause like `A::B == u32`. Now, if we are asked + // to normalize `A::B`, we will want to check the + // where-clauses in scope. So we will try to unify `A::B` + // with `A::B`, which can trigger a recursive + // normalization. + + debug!("found cache entry: in-progress"); + + // Cache that normalizing this projection resulted in a cycle. This + // should ensure that, unless this happens within a snapshot that's + // rolled back, fulfillment or evaluation will notice the cycle. + + if use_cache { + infcx.inner.borrow_mut().projection_cache().recur(cache_key); + } + return Err(InProgress); + } + Err(ProjectionCacheEntry::Recur) => { + debug!("recur cache"); + return Err(InProgress); + } + Err(ProjectionCacheEntry::NormalizedTy { ty, complete: _ }) => { + // This is the hottest path in this function. + // + // If we find the value in the cache, then return it along + // with the obligations that went along with it. Note + // that, when using a fulfillment context, these + // obligations could in principle be ignored: they have + // already been registered when the cache entry was + // created (and hence the new ones will quickly be + // discarded as duplicated). But when doing trait + // evaluation this is not the case, and dropping the trait + // evaluations can causes ICEs (e.g., #43132). + debug!(?ty, "found normalized ty"); + obligations.extend(ty.obligations); + return Ok(Some(ty.value)); + } + Err(ProjectionCacheEntry::Error) => { + debug!("opt_normalize_projection_type: found error"); + let result = normalize_to_error(selcx, param_env, projection_ty, cause, depth); + obligations.extend(result.obligations); + return Ok(Some(result.value.into())); + } + } + + let obligation = + Obligation::with_depth(selcx.tcx(), cause.clone(), depth, param_env, projection_ty); + + match project(selcx, &obligation) { + Ok(Projected::Progress(Progress { + term: projected_term, + obligations: mut projected_obligations, + })) => { + // if projection succeeded, then what we get out of this + // is also non-normalized (consider: it was derived from + // an impl, where-clause etc) and hence we must + // re-normalize it + + let projected_term = selcx.infcx.resolve_vars_if_possible(projected_term); + + let mut result = if projected_term.has_projections() { + let mut normalizer = AssocTypeNormalizer::new( + selcx, + param_env, + cause, + depth + 1, + &mut projected_obligations, + ); + let normalized_ty = normalizer.fold(projected_term); + + Normalized { value: normalized_ty, obligations: projected_obligations } + } else { + Normalized { value: projected_term, obligations: projected_obligations } + }; + + let mut deduped = SsoHashSet::with_capacity(result.obligations.len()); + result.obligations.retain(|obligation| deduped.insert(obligation.clone())); + + if use_cache { + infcx.inner.borrow_mut().projection_cache().insert_term(cache_key, result.clone()); + } + obligations.extend(result.obligations); + Ok(Some(result.value)) + } + Ok(Projected::NoProgress(projected_ty)) => { + let result = Normalized { value: projected_ty, obligations: vec![] }; + if use_cache { + infcx.inner.borrow_mut().projection_cache().insert_term(cache_key, result.clone()); + } + // No need to extend `obligations`. + Ok(Some(result.value)) + } + Err(ProjectionError::TooManyCandidates) => { + debug!("opt_normalize_projection_type: too many candidates"); + if use_cache { + infcx.inner.borrow_mut().projection_cache().ambiguous(cache_key); + } + Ok(None) + } + Err(ProjectionError::TraitSelectionError(_)) => { + debug!("opt_normalize_projection_type: ERROR"); + // if we got an error processing the `T as Trait` part, + // just return `ty::err` but add the obligation `T : + // Trait`, which when processed will cause the error to be + // reported later + + if use_cache { + infcx.inner.borrow_mut().projection_cache().error(cache_key); + } + let result = normalize_to_error(selcx, param_env, projection_ty, cause, depth); + obligations.extend(result.obligations); + Ok(Some(result.value.into())) + } + } +} + +/// If we are projecting `<T as Trait>::Item`, but `T: Trait` does not +/// hold. In various error cases, we cannot generate a valid +/// normalized projection. Therefore, we create an inference variable +/// return an associated obligation that, when fulfilled, will lead to +/// an error. +/// +/// Note that we used to return `Error` here, but that was quite +/// dubious -- the premise was that an error would *eventually* be +/// reported, when the obligation was processed. But in general once +/// you see an `Error` you are supposed to be able to assume that an +/// error *has been* reported, so that you can take whatever heuristic +/// paths you want to take. To make things worse, it was possible for +/// cycles to arise, where you basically had a setup like `<MyType<$0> +/// as Trait>::Foo == $0`. Here, normalizing `<MyType<$0> as +/// Trait>::Foo>` to `[type error]` would lead to an obligation of +/// `<MyType<[type error]> as Trait>::Foo`. We are supposed to report +/// an error for this obligation, but we legitimately should not, +/// because it contains `[type error]`. Yuck! (See issue #29857 for +/// one case where this arose.) +fn normalize_to_error<'a, 'tcx>( + selcx: &mut SelectionContext<'a, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + projection_ty: ty::AliasTy<'tcx>, + cause: ObligationCause<'tcx>, + depth: usize, +) -> NormalizedTy<'tcx> { + let trait_ref = ty::Binder::dummy(projection_ty.trait_ref(selcx.tcx())); + let trait_obligation = Obligation { + cause, + recursion_depth: depth, + param_env, + predicate: trait_ref.to_predicate(selcx.tcx()), + }; + let tcx = selcx.infcx.tcx; + let new_value = selcx.infcx.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::NormalizeProjectionType, + span: tcx.def_span(projection_ty.def_id), + }); + Normalized { value: new_value, obligations: vec![trait_obligation] } +} + +/// Confirm and normalize the given inherent projection. +#[instrument(level = "debug", skip(selcx, param_env, cause, obligations))] +pub fn normalize_inherent_projection<'a, 'b, 'tcx>( + selcx: &'a mut SelectionContext<'b, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + alias_ty: ty::AliasTy<'tcx>, + cause: ObligationCause<'tcx>, + depth: usize, + obligations: &mut Vec<PredicateObligation<'tcx>>, +) -> Ty<'tcx> { + let tcx = selcx.tcx(); + + if !tcx.recursion_limit().value_within_limit(depth) { + // Halt compilation because it is important that overflows never be masked. + tcx.dcx().emit_fatal(InherentProjectionNormalizationOverflow { + span: cause.span, + ty: alias_ty.to_string(), + }); + } + + let args = compute_inherent_assoc_ty_args( + selcx, + param_env, + alias_ty, + cause.clone(), + depth, + obligations, + ); + + // Register the obligations arising from the impl and from the associated type itself. + let predicates = tcx.predicates_of(alias_ty.def_id).instantiate(tcx, args); + for (predicate, span) in predicates { + let predicate = normalize_with_depth_to( + selcx, + param_env, + cause.clone(), + depth + 1, + predicate, + obligations, + ); + + let nested_cause = ObligationCause::new( + cause.span, + cause.body_id, + // FIXME(inherent_associated_types): Since we can't pass along the self type to the + // cause code, inherent projections will be printed with identity substitutions in + // diagnostics which is not ideal. + // Consider creating separate cause codes for this specific situation. + if span.is_dummy() { + super::ItemObligation(alias_ty.def_id) + } else { + super::BindingObligation(alias_ty.def_id, span) + }, + ); + + obligations.push(Obligation::with_depth( + tcx, + nested_cause, + depth + 1, + param_env, + predicate, + )); + } + + let ty = tcx.type_of(alias_ty.def_id).instantiate(tcx, args); + + let mut ty = selcx.infcx.resolve_vars_if_possible(ty); + if ty.has_projections() { + ty = normalize_with_depth_to(selcx, param_env, cause.clone(), depth + 1, ty, obligations); + } + + ty +} + +pub fn compute_inherent_assoc_ty_args<'a, 'b, 'tcx>( + selcx: &'a mut SelectionContext<'b, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + alias_ty: ty::AliasTy<'tcx>, + cause: ObligationCause<'tcx>, + depth: usize, + obligations: &mut Vec<PredicateObligation<'tcx>>, +) -> ty::GenericArgsRef<'tcx> { + let tcx = selcx.tcx(); + + let impl_def_id = tcx.parent(alias_ty.def_id); + let impl_args = selcx.infcx.fresh_args_for_item(cause.span, impl_def_id); + + let mut impl_ty = tcx.type_of(impl_def_id).instantiate(tcx, impl_args); + if !selcx.infcx.next_trait_solver() { + impl_ty = normalize_with_depth_to( + selcx, + param_env, + cause.clone(), + depth + 1, + impl_ty, + obligations, + ); + } + + // Infer the generic parameters of the impl by unifying the + // impl type with the self type of the projection. + let mut self_ty = alias_ty.self_ty(); + if !selcx.infcx.next_trait_solver() { + self_ty = normalize_with_depth_to( + selcx, + param_env, + cause.clone(), + depth + 1, + self_ty, + obligations, + ); + } + + match selcx.infcx.at(&cause, param_env).eq(DefineOpaqueTypes::No, impl_ty, self_ty) { + Ok(mut ok) => obligations.append(&mut ok.obligations), + Err(_) => { + tcx.dcx().span_delayed_bug( + cause.span, + format!( + "{self_ty:?} was a subtype of {impl_ty:?} during selection but now it is not" + ), + ); + } + } + + alias_ty.rebase_inherent_args_onto_impl(impl_args, tcx) +} + +enum Projected<'tcx> { + Progress(Progress<'tcx>), + NoProgress(ty::Term<'tcx>), +} + +struct Progress<'tcx> { + term: ty::Term<'tcx>, + obligations: Vec<PredicateObligation<'tcx>>, +} + +impl<'tcx> Progress<'tcx> { + fn error(tcx: TyCtxt<'tcx>, guar: ErrorGuaranteed) -> Self { + Progress { term: Ty::new_error(tcx, guar).into(), obligations: vec![] } + } + + fn with_addl_obligations(mut self, mut obligations: Vec<PredicateObligation<'tcx>>) -> Self { + self.obligations.append(&mut obligations); + self + } +} + +/// Computes the result of a projection type (if we can). +/// +/// IMPORTANT: +/// - `obligation` must be fully normalized +#[instrument(level = "info", skip(selcx))] +fn project<'cx, 'tcx>( + selcx: &mut SelectionContext<'cx, 'tcx>, + obligation: &ProjectionTyObligation<'tcx>, +) -> Result<Projected<'tcx>, ProjectionError<'tcx>> { + if !selcx.tcx().recursion_limit().value_within_limit(obligation.recursion_depth) { + // This should really be an immediate error, but some existing code + // relies on being able to recover from this. + return Err(ProjectionError::TraitSelectionError(SelectionError::Overflow( + OverflowError::Canonical, + ))); + } + + if let Err(guar) = obligation.predicate.error_reported() { + return Ok(Projected::Progress(Progress::error(selcx.tcx(), guar))); + } + + let mut candidates = ProjectionCandidateSet::None; + + // Make sure that the following procedures are kept in order. ParamEnv + // needs to be first because it has highest priority, and Select checks + // the return value of push_candidate which assumes it's ran at last. + assemble_candidates_from_param_env(selcx, obligation, &mut candidates); + + assemble_candidates_from_trait_def(selcx, obligation, &mut candidates); + + assemble_candidates_from_object_ty(selcx, obligation, &mut candidates); + + if let ProjectionCandidateSet::Single(ProjectionCandidate::Object(_)) = candidates { + // Avoid normalization cycle from selection (see + // `assemble_candidates_from_object_ty`). + // FIXME(lazy_normalization): Lazy normalization should save us from + // having to special case this. + } else { + assemble_candidates_from_impls(selcx, obligation, &mut candidates); + }; + + match candidates { + ProjectionCandidateSet::Single(candidate) => { + Ok(Projected::Progress(confirm_candidate(selcx, obligation, candidate))) + } + ProjectionCandidateSet::None => { + let tcx = selcx.tcx(); + let term = match tcx.def_kind(obligation.predicate.def_id) { + DefKind::AssocTy => { + Ty::new_projection(tcx, obligation.predicate.def_id, obligation.predicate.args) + .into() + } + DefKind::AssocConst => ty::Const::new_unevaluated( + tcx, + ty::UnevaluatedConst::new( + obligation.predicate.def_id, + obligation.predicate.args, + ), + tcx.type_of(obligation.predicate.def_id) + .instantiate(tcx, obligation.predicate.args), + ) + .into(), + kind => { + bug!("unknown projection def-id: {}", kind.descr(obligation.predicate.def_id)) + } + }; + + Ok(Projected::NoProgress(term)) + } + // Error occurred while trying to processing impls. + ProjectionCandidateSet::Error(e) => Err(ProjectionError::TraitSelectionError(e)), + // Inherent ambiguity that prevents us from even enumerating the + // candidates. + ProjectionCandidateSet::Ambiguous => Err(ProjectionError::TooManyCandidates), + } +} + +/// The first thing we have to do is scan through the parameter +/// environment to see whether there are any projection predicates +/// there that can answer this question. +fn assemble_candidates_from_param_env<'cx, 'tcx>( + selcx: &mut SelectionContext<'cx, 'tcx>, + obligation: &ProjectionTyObligation<'tcx>, + candidate_set: &mut ProjectionCandidateSet<'tcx>, +) { + assemble_candidates_from_predicates( + selcx, + obligation, + candidate_set, + ProjectionCandidate::ParamEnv, + obligation.param_env.caller_bounds().iter(), + false, + ); +} + +/// In the case of a nested projection like `<<A as Foo>::FooT as Bar>::BarT`, we may find +/// that the definition of `Foo` has some clues: +/// +/// ```ignore (illustrative) +/// trait Foo { +/// type FooT : Bar<BarT=i32> +/// } +/// ``` +/// +/// Here, for example, we could conclude that the result is `i32`. +fn assemble_candidates_from_trait_def<'cx, 'tcx>( + selcx: &mut SelectionContext<'cx, 'tcx>, + obligation: &ProjectionTyObligation<'tcx>, + candidate_set: &mut ProjectionCandidateSet<'tcx>, +) { + debug!("assemble_candidates_from_trait_def(..)"); + + let tcx = selcx.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 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).instantiate(tcx, data.args) + } + ty::Infer(ty::TyVar(_)) => { + // If the self-type is an inference variable, then it MAY wind up + // being a projected type, so induce an ambiguity. + candidate_set.mark_ambiguous(); + return; + } + _ => return, + }; + + assemble_candidates_from_predicates( + selcx, + obligation, + candidate_set, + ProjectionCandidate::TraitDef, + bounds.iter(), + true, + ); +} + +/// In the case of a trait object like +/// `<dyn Iterator<Item = ()> as Iterator>::Item` we can use the existential +/// predicate in the trait object. +/// +/// We don't go through the select candidate for these bounds to avoid cycles: +/// In the above case, `dyn Iterator<Item = ()>: Iterator` would create a +/// nested obligation of `<dyn Iterator<Item = ()> as Iterator>::Item: Sized`, +/// this then has to be normalized without having to prove +/// `dyn Iterator<Item = ()>: Iterator` again. +fn assemble_candidates_from_object_ty<'cx, 'tcx>( + selcx: &mut SelectionContext<'cx, 'tcx>, + obligation: &ProjectionTyObligation<'tcx>, + candidate_set: &mut ProjectionCandidateSet<'tcx>, +) { + debug!("assemble_candidates_from_object_ty(..)"); + + 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() { + ty::Dynamic(data, ..) => data, + ty::Infer(ty::TyVar(_)) => { + // If the self-type is an inference variable, then it MAY wind up + // being an object type, so induce an ambiguity. + candidate_set.mark_ambiguous(); + return; + } + _ => return, + }; + let env_predicates = data + .projection_bounds() + .filter(|bound| bound.item_def_id() == obligation.predicate.def_id) + .map(|p| p.with_self_ty(tcx, object_ty).to_predicate(tcx)); + + assemble_candidates_from_predicates( + selcx, + obligation, + candidate_set, + ProjectionCandidate::Object, + env_predicates, + false, + ); +} + +#[instrument( + level = "debug", + skip(selcx, candidate_set, ctor, env_predicates, potentially_unnormalized_candidates) +)] +fn assemble_candidates_from_predicates<'cx, 'tcx>( + selcx: &mut SelectionContext<'cx, 'tcx>, + obligation: &ProjectionTyObligation<'tcx>, + candidate_set: &mut ProjectionCandidateSet<'tcx>, + ctor: fn(ty::PolyProjectionPredicate<'tcx>) -> ProjectionCandidate<'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::ClauseKind::Projection(data) = predicate.kind().skip_binder() { + let data = bound_predicate.rebind(data); + if data.projection_def_id() != obligation.predicate.def_id { + continue; + } + + let is_match = infcx.probe(|_| { + selcx.match_projection_projections( + obligation, + data, + potentially_unnormalized_candidates, + ) + }); + + match is_match { + ProjectionMatchesProjection::Yes => { + candidate_set.push_candidate(ctor(data)); + + if potentially_unnormalized_candidates + && !obligation.predicate.has_non_region_infer() + { + // HACK: Pick the first trait def candidate for a fully + // inferred predicate. This is to allow duplicates that + // differ only in normalization. + return; + } + } + ProjectionMatchesProjection::Ambiguous => { + candidate_set.mark_ambiguous(); + } + ProjectionMatchesProjection::No => {} + } + } + } +} + +#[instrument(level = "debug", skip(selcx, obligation, candidate_set))] +fn assemble_candidates_from_impls<'cx, 'tcx>( + selcx: &mut SelectionContext<'cx, 'tcx>, + obligation: &ProjectionTyObligation<'tcx>, + candidate_set: &mut ProjectionCandidateSet<'tcx>, +) { + // If we are resolving `<T as TraitRef<...>>::Item == Type`, + // start out by selecting the predicate `T as TraitRef<...>`: + 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, + Ok(None) => { + candidate_set.mark_ambiguous(); + return Err(()); + } + Err(e) => { + debug!(error = ?e, "selection error"); + candidate_set.mark_error(e); + return Err(()); + } + }; + + let eligible = match &impl_source { + ImplSource::UserDefined(impl_data) => { + // We have to be careful when projecting out of an + // impl because of specialization. If we are not in + // codegen (i.e., projection mode is not "any"), and the + // impl's type is declared as default, then we disable + // projection (even if the trait ref is fully + // monomorphic). In the case where trait ref is not + // fully monomorphic (i.e., includes type parameters), + // this is because those type parameters may + // ultimately be bound to types from other crates that + // may have specialized impls we can't see. In the + // case where the trait ref IS fully monomorphic, this + // is a policy decision that we made in the RFC in + // order to preserve flexibility for the crate that + // defined the specializable impl to specialize later + // for existing types. + // + // In either case, we handle this by not adding a + // candidate for an impl if it contains a `default` + // type. + // + // NOTE: This should be kept in sync with the similar code in + // `rustc_ty_utils::instance::resolve_associated_item()`. + let node_item = + specialization_graph::assoc_def(selcx.tcx(), impl_data.impl_def_id, obligation.predicate.def_id) + .map_err(|ErrorGuaranteed { .. }| ())?; + + if node_item.is_final() { + // Non-specializable items are always projectable. + true + } else { + // Only reveal a specializable default if we're past type-checking + // and the obligation is monomorphic, otherwise passes such as + // transmute checking and polymorphic MIR optimizations could + // get a result which isn't correct for all monomorphizations. + 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(trait_ref); + !poly_trait_ref.still_further_specializable() + } else { + debug!( + assoc_ty = ?selcx.tcx().def_path_str(node_item.item.def_id), + ?obligation.predicate, + "assemble_candidates_from_impls: not eligible due to default", + ); + false + } + } + } + ImplSource::Builtin(BuiltinImplSource::Misc, _) => { + // While a builtin impl may be known to exist, the associated type may not yet + // be known. Any type with multiple potential associated types is therefore + // not eligible. + let self_ty = selcx.infcx.shallow_resolve(obligation.predicate.self_ty()); + + let lang_items = selcx.tcx().lang_items(); + if [ + lang_items.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(), + ].contains(&Some(trait_ref.def_id)) + { + true + }else if lang_items.discriminant_kind_trait() == Some(trait_ref.def_id) { + match self_ty.kind() { + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Adt(..) + | ty::Foreign(_) + | ty::Str + | ty::Array(..) + | ty::Slice(_) + | ty::RawPtr(..) + | ty::Ref(..) + | ty::FnDef(..) + | ty::FnPtr(..) + | ty::Dynamic(..) + | ty::Closure(..) + | ty::Coroutine(..) + | ty::CoroutineWitness(..) + | ty::Never + | ty::Tuple(..) + // Integers and floats always have `u8` as their discriminant. + | ty::Infer(ty::InferTy::IntVar(_) | ty::InferTy::FloatVar(..)) => true, + + // type parameters, opaques, and unnormalized projections have pointer + // metadata if they're known (e.g. by the param_env) to be sized + ty::Param(_) + | ty::Alias(..) + | ty::Bound(..) + | ty::Placeholder(..) + | ty::Infer(..) + | ty::Error(_) => false, + } + } else if lang_items.pointee_trait() == Some(trait_ref.def_id) { + let tail = selcx.tcx().struct_tail_with_normalize( + self_ty, + |ty| { + // We throw away any obligations we get from this, since we normalize + // and confirm these obligations once again during confirmation + normalize_with_depth( + selcx, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + ty, + ) + .value + }, + || {}, + ); + + match tail.kind() { + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Str + | ty::Array(..) + | ty::Slice(_) + | ty::RawPtr(..) + | ty::Ref(..) + | ty::FnDef(..) + | ty::FnPtr(..) + | ty::Dynamic(..) + | ty::Closure(..) + | ty::Coroutine(..) + | ty::CoroutineWitness(..) + | ty::Never + // Extern types have unit metadata, according to RFC 2850 + | ty::Foreign(_) + // If returned by `struct_tail_without_normalization` this is a unit struct + // without any fields, or not a struct, and therefore is Sized. + | ty::Adt(..) + // If returned by `struct_tail_without_normalization` this is the empty tuple. + | ty::Tuple(..) + // Integers and floats are always Sized, and so have unit type metadata. + | ty::Infer(ty::InferTy::IntVar(_) | ty::InferTy::FloatVar(..)) => true, + + // type parameters, opaques, and unnormalized projections have pointer + // metadata if they're known (e.g. by the param_env) to be sized + ty::Param(_) | ty::Alias(..) + if selcx.infcx.predicate_must_hold_modulo_regions( + &obligation.with( + selcx.tcx(), + ty::TraitRef::from_lang_item(selcx.tcx(), LangItem::Sized, obligation.cause.span(),[self_ty]), + ), + ) => + { + true + } + + // FIXME(compiler-errors): are Bound and Placeholder types ever known sized? + ty::Param(_) + | ty::Alias(..) + | ty::Bound(..) + | ty::Placeholder(..) + | ty::Infer(..) + | ty::Error(_) => { + if tail.has_infer_types() { + candidate_set.mark_ambiguous(); + } + false + } + } + } else { + bug!("unexpected builtin trait with associated type: {trait_ref:?}") + } + } + ImplSource::Param(..) => { + // This case tell us nothing about the value of an + // associated type. Consider: + // + // ``` + // trait SomeTrait { type Foo; } + // fn foo<T:SomeTrait>(...) { } + // ``` + // + // If the user writes `<T as SomeTrait>::Foo`, then the `T + // : SomeTrait` binding does not help us decide what the + // type `Foo` is (at least, not more specifically than + // what we already knew). + // + // But wait, you say! What about an example like this: + // + // ``` + // fn bar<T:SomeTrait<Foo=usize>>(...) { ... } + // ``` + // + // Doesn't the `T : SomeTrait<Foo=usize>` predicate help + // resolve `T::Foo`? And of course it does, but in fact + // that single predicate is desugared into two predicates + // in the compiler: a trait predicate (`T : SomeTrait`) and a + // projection. And the projection where clause is handled + // in `assemble_candidates_from_param_env`. + false + } + ImplSource::Builtin(BuiltinImplSource::Object { .. }, _) => { + // Handled by the `Object` projection candidate. See + // `assemble_candidates_from_object_ty` for an explanation of + // why we special case object types. + false + } + ImplSource::Builtin(BuiltinImplSource::TraitUpcasting { .. }, _) + | ImplSource::Builtin(BuiltinImplSource::TupleUnsizing, _) => { + // These traits have no associated types. + selcx.tcx().dcx().span_delayed_bug( + obligation.cause.span, + format!("Cannot project an associated type from `{impl_source:?}`"), + ); + return Err(()); + } + }; + + if eligible { + if candidate_set.push_candidate(ProjectionCandidate::Select(impl_source)) { + Ok(()) + } else { + Err(()) + } + } else { + Err(()) + } + }); +} + +fn confirm_candidate<'cx, 'tcx>( + selcx: &mut SelectionContext<'cx, 'tcx>, + obligation: &ProjectionTyObligation<'tcx>, + candidate: ProjectionCandidate<'tcx>, +) -> Progress<'tcx> { + debug!(?obligation, ?candidate, "confirm_candidate"); + let mut progress = match candidate { + ProjectionCandidate::ParamEnv(poly_projection) + | ProjectionCandidate::Object(poly_projection) => { + confirm_param_env_candidate(selcx, obligation, poly_projection, false) + } + + ProjectionCandidate::TraitDef(poly_projection) => { + confirm_param_env_candidate(selcx, obligation, poly_projection, true) + } + + ProjectionCandidate::Select(impl_source) => { + confirm_select_candidate(selcx, obligation, impl_source) + } + }; + + // When checking for cycle during evaluation, we compare predicates with + // "syntactic" equality. Since normalization generally introduces a type + // with new region variables, we need to resolve them to existing variables + // when possible for this to work. See `auto-trait-projection-recursion.rs` + // for a case where this matters. + if progress.term.has_infer_regions() { + progress.term = progress.term.fold_with(&mut OpportunisticRegionResolver::new(selcx.infcx)); + } + progress +} + +fn confirm_select_candidate<'cx, 'tcx>( + selcx: &mut SelectionContext<'cx, 'tcx>, + obligation: &ProjectionTyObligation<'tcx>, + impl_source: Selection<'tcx>, +) -> Progress<'tcx> { + match impl_source { + ImplSource::UserDefined(data) => confirm_impl_candidate(selcx, obligation, data), + ImplSource::Builtin(BuiltinImplSource::Misc, data) => { + let trait_def_id = obligation.predicate.trait_def_id(selcx.tcx()); + let lang_items = selcx.tcx().lang_items(); + if lang_items.coroutine_trait() == Some(trait_def_id) { + confirm_coroutine_candidate(selcx, obligation, data) + } else if lang_items.future_trait() == Some(trait_def_id) { + confirm_future_candidate(selcx, obligation, data) + } else if lang_items.iterator_trait() == Some(trait_def_id) { + confirm_iterator_candidate(selcx, obligation, data) + } else if lang_items.async_iterator_trait() == Some(trait_def_id) { + confirm_async_iterator_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) + } + } + ImplSource::Builtin(BuiltinImplSource::Object { .. }, _) + | ImplSource::Param(..) + | ImplSource::Builtin(BuiltinImplSource::TraitUpcasting { .. }, _) + | ImplSource::Builtin(BuiltinImplSource::TupleUnsizing, _) => { + // we don't create Select candidates with this kind of resolution + span_bug!( + obligation.cause.span, + "Cannot project an associated type from `{:?}`", + impl_source + ) + } + } +} + +fn confirm_coroutine_candidate<'cx, 'tcx>( + selcx: &mut SelectionContext<'cx, 'tcx>, + obligation: &ProjectionTyObligation<'tcx>, + nested: Vec<PredicateObligation<'tcx>>, +) -> Progress<'tcx> { + let self_ty = selcx.infcx.shallow_resolve(obligation.predicate.self_ty()); + let ty::Coroutine(_, args) = self_ty.kind() else { + unreachable!( + "expected coroutine self type for built-in coroutine candidate, found {self_ty}" + ) + }; + let coroutine_sig = args.as_coroutine().sig(); + let Normalized { value: coroutine_sig, obligations } = normalize_with_depth( + selcx, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + coroutine_sig, + ); + + debug!(?obligation, ?coroutine_sig, ?obligations, "confirm_coroutine_candidate"); + + let tcx = selcx.tcx(); + + let coroutine_def_id = tcx.require_lang_item(LangItem::Coroutine, None); + + let (trait_ref, yield_ty, return_ty) = super::util::coroutine_trait_ref_and_outputs( + tcx, + coroutine_def_id, + obligation.predicate.self_ty(), + coroutine_sig, + ); + + let name = tcx.associated_item(obligation.predicate.def_id).name; + let ty = if name == sym::Return { + return_ty + } else if name == sym::Yield { + yield_ty + } else { + span_bug!( + tcx.def_span(obligation.predicate.def_id), + "unexpected associated type: `Coroutine::{name}`" + ); + }; + + let predicate = ty::ProjectionPredicate { + projection_ty: ty::AliasTy::new(tcx, obligation.predicate.def_id, trait_ref.args), + term: ty.into(), + }; + + confirm_param_env_candidate(selcx, obligation, ty::Binder::dummy(predicate), false) + .with_addl_obligations(nested) + .with_addl_obligations(obligations) +} + +fn confirm_future_candidate<'cx, 'tcx>( + selcx: &mut SelectionContext<'cx, 'tcx>, + obligation: &ProjectionTyObligation<'tcx>, + nested: Vec<PredicateObligation<'tcx>>, +) -> Progress<'tcx> { + let self_ty = selcx.infcx.shallow_resolve(obligation.predicate.self_ty()); + let ty::Coroutine(_, args) = self_ty.kind() else { + unreachable!( + "expected coroutine self type for built-in async future candidate, found {self_ty}" + ) + }; + let coroutine_sig = args.as_coroutine().sig(); + let Normalized { value: coroutine_sig, obligations } = normalize_with_depth( + selcx, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + coroutine_sig, + ); + + debug!(?obligation, ?coroutine_sig, ?obligations, "confirm_future_candidate"); + + let tcx = selcx.tcx(); + let fut_def_id = tcx.require_lang_item(LangItem::Future, None); + + let (trait_ref, return_ty) = super::util::future_trait_ref_and_outputs( + tcx, + fut_def_id, + obligation.predicate.self_ty(), + coroutine_sig, + ); + + debug_assert_eq!(tcx.associated_item(obligation.predicate.def_id).name, sym::Output); + + let predicate = ty::ProjectionPredicate { + projection_ty: ty::AliasTy::new(tcx, obligation.predicate.def_id, trait_ref.args), + term: return_ty.into(), + }; + + confirm_param_env_candidate(selcx, obligation, ty::Binder::dummy(predicate), false) + .with_addl_obligations(nested) + .with_addl_obligations(obligations) +} + +fn confirm_iterator_candidate<'cx, 'tcx>( + selcx: &mut SelectionContext<'cx, 'tcx>, + obligation: &ProjectionTyObligation<'tcx>, + nested: Vec<PredicateObligation<'tcx>>, +) -> Progress<'tcx> { + let self_ty = selcx.infcx.shallow_resolve(obligation.predicate.self_ty()); + let ty::Coroutine(_, args) = self_ty.kind() else { + unreachable!("expected coroutine self type for built-in gen candidate, found {self_ty}") + }; + let gen_sig = args.as_coroutine().sig(); + let Normalized { value: gen_sig, obligations } = normalize_with_depth( + selcx, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + gen_sig, + ); + + debug!(?obligation, ?gen_sig, ?obligations, "confirm_iterator_candidate"); + + let tcx = selcx.tcx(); + let iter_def_id = tcx.require_lang_item(LangItem::Iterator, None); + + let (trait_ref, yield_ty) = super::util::iterator_trait_ref_and_outputs( + tcx, + iter_def_id, + obligation.predicate.self_ty(), + gen_sig, + ); + + debug_assert_eq!(tcx.associated_item(obligation.predicate.def_id).name, sym::Item); + + let predicate = ty::ProjectionPredicate { + projection_ty: ty::AliasTy::new(tcx, obligation.predicate.def_id, trait_ref.args), + term: yield_ty.into(), + }; + + confirm_param_env_candidate(selcx, obligation, ty::Binder::dummy(predicate), false) + .with_addl_obligations(nested) + .with_addl_obligations(obligations) +} + +fn confirm_async_iterator_candidate<'cx, 'tcx>( + selcx: &mut SelectionContext<'cx, 'tcx>, + obligation: &ProjectionTyObligation<'tcx>, + nested: Vec<PredicateObligation<'tcx>>, +) -> Progress<'tcx> { + let ty::Coroutine(_, args) = selcx.infcx.shallow_resolve(obligation.predicate.self_ty()).kind() + else { + unreachable!() + }; + let gen_sig = args.as_coroutine().sig(); + let Normalized { value: gen_sig, obligations } = normalize_with_depth( + selcx, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + gen_sig, + ); + + debug!(?obligation, ?gen_sig, ?obligations, "confirm_async_iterator_candidate"); + + let tcx = selcx.tcx(); + let iter_def_id = tcx.require_lang_item(LangItem::AsyncIterator, None); + + let (trait_ref, yield_ty) = super::util::async_iterator_trait_ref_and_outputs( + tcx, + iter_def_id, + obligation.predicate.self_ty(), + gen_sig, + ); + + debug_assert_eq!(tcx.associated_item(obligation.predicate.def_id).name, sym::Item); + + let ty::Adt(_poll_adt, args) = *yield_ty.kind() else { + bug!(); + }; + let ty::Adt(_option_adt, args) = *args.type_at(0).kind() else { + bug!(); + }; + let item_ty = args.type_at(0); + + let predicate = ty::ProjectionPredicate { + projection_ty: ty::AliasTy::new(tcx, obligation.predicate.def_id, trait_ref.args), + term: item_ty.into(), + }; + + confirm_param_env_candidate(selcx, obligation, ty::Binder::dummy(predicate), false) + .with_addl_obligations(nested) + .with_addl_obligations(obligations) +} + +fn confirm_builtin_candidate<'cx, 'tcx>( + selcx: &mut SelectionContext<'cx, 'tcx>, + obligation: &ProjectionTyObligation<'tcx>, + data: Vec<PredicateObligation<'tcx>>, +) -> Progress<'tcx> { + let tcx = selcx.tcx(); + let self_ty = obligation.predicate.self_ty(); + let args = tcx.mk_args(&[self_ty.into()]); + let lang_items = tcx.lang_items(); + let item_def_id = obligation.predicate.def_id; + let trait_def_id = tcx.trait_of_item(item_def_id).unwrap(); + let (term, obligations) = if lang_items.discriminant_kind_trait() == Some(trait_def_id) { + let discriminant_def_id = tcx.require_lang_item(LangItem::Discriminant, None); + assert_eq!(discriminant_def_id, item_def_id); + + (self_ty.discriminant_ty(tcx).into(), Vec::new()) + } else if lang_items.pointee_trait() == Some(trait_def_id) { + let metadata_def_id = tcx.require_lang_item(LangItem::Metadata, None); + assert_eq!(metadata_def_id, item_def_id); + + let mut obligations = Vec::new(); + let (metadata_ty, check_is_sized) = self_ty.ptr_metadata_ty(tcx, |ty| { + normalize_with_depth_to( + selcx, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + ty, + &mut obligations, + ) + }); + if check_is_sized { + let sized_predicate = ty::TraitRef::from_lang_item( + tcx, + LangItem::Sized, + obligation.cause.span(), + [self_ty], + ); + obligations.push(obligation.with(tcx, sized_predicate)); + } + (metadata_ty.into(), obligations) + } else { + bug!("unexpected builtin trait with associated type: {:?}", obligation.predicate); + }; + + let predicate = + ty::ProjectionPredicate { projection_ty: ty::AliasTy::new(tcx, item_def_id, args), term }; + + confirm_param_env_candidate(selcx, obligation, ty::Binder::dummy(predicate), false) + .with_addl_obligations(obligations) + .with_addl_obligations(data) +} + +fn confirm_fn_pointer_candidate<'cx, 'tcx>( + selcx: &mut SelectionContext<'cx, 'tcx>, + obligation: &ProjectionTyObligation<'tcx>, + nested: Vec<PredicateObligation<'tcx>>, +) -> Progress<'tcx> { + let tcx = selcx.tcx(); + let fn_type = selcx.infcx.shallow_resolve(obligation.predicate.self_ty()); + let sig = fn_type.fn_sig(tcx); + let Normalized { value: sig, obligations } = normalize_with_depth( + selcx, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + sig, + ); + + let host_effect_param = match *fn_type.kind() { + ty::FnDef(def_id, args) => tcx + .generics_of(def_id) + .host_effect_index + .map_or(tcx.consts.true_, |idx| args.const_at(idx)), + ty::FnPtr(_) => tcx.consts.true_, + _ => unreachable!("only expected FnPtr or FnDef in `confirm_fn_pointer_candidate`"), + }; + + confirm_callable_candidate( + selcx, + obligation, + sig, + util::TupleArgumentsFlag::Yes, + host_effect_param, + ) + .with_addl_obligations(nested) + .with_addl_obligations(obligations) +} + +fn confirm_closure_candidate<'cx, 'tcx>( + selcx: &mut SelectionContext<'cx, 'tcx>, + obligation: &ProjectionTyObligation<'tcx>, + nested: Vec<PredicateObligation<'tcx>>, +) -> Progress<'tcx> { + let self_ty = selcx.infcx.shallow_resolve(obligation.predicate.self_ty()); + let ty::Closure(_, args) = self_ty.kind() else { + unreachable!("expected closure self type for closure candidate, found {self_ty}") + }; + let closure_sig = args.as_closure().sig(); + let Normalized { value: closure_sig, obligations } = normalize_with_depth( + selcx, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + closure_sig, + ); + + debug!(?obligation, ?closure_sig, ?obligations, "confirm_closure_candidate"); + + confirm_callable_candidate( + selcx, + obligation, + closure_sig, + util::TupleArgumentsFlag::No, + // FIXME(effects): This doesn't handle const closures correctly! + selcx.tcx().consts.true_, + ) + .with_addl_obligations(nested) + .with_addl_obligations(obligations) +} + +fn confirm_callable_candidate<'cx, 'tcx>( + selcx: &mut SelectionContext<'cx, 'tcx>, + obligation: &ProjectionTyObligation<'tcx>, + fn_sig: ty::PolyFnSig<'tcx>, + flag: util::TupleArgumentsFlag, + fn_host_effect: ty::Const<'tcx>, +) -> Progress<'tcx> { + let tcx = selcx.tcx(); + + debug!(?obligation, ?fn_sig, "confirm_callable_candidate"); + + let fn_once_def_id = tcx.require_lang_item(LangItem::FnOnce, None); + let fn_once_output_def_id = tcx.require_lang_item(LangItem::FnOnceOutput, None); + + let predicate = super::util::closure_trait_ref_and_return_type( + tcx, + fn_once_def_id, + obligation.predicate.self_ty(), + fn_sig, + flag, + fn_host_effect, + ) + .map_bound(|(trait_ref, ret_type)| ty::ProjectionPredicate { + projection_ty: ty::AliasTy::new(tcx, fn_once_output_def_id, trait_ref.args), + term: ret_type.into(), + }); + + confirm_param_env_candidate(selcx, obligation, predicate, true) +} + +fn confirm_param_env_candidate<'cx, 'tcx>( + selcx: &mut SelectionContext<'cx, 'tcx>, + obligation: &ProjectionTyObligation<'tcx>, + poly_cache_entry: ty::PolyProjectionPredicate<'tcx>, + potentially_unnormalized_candidate: bool, +) -> Progress<'tcx> { + let infcx = selcx.infcx; + let cause = &obligation.cause; + let param_env = obligation.param_env; + + let cache_entry = infcx.instantiate_binder_with_fresh_vars( + cause.span, + BoundRegionConversionTime::HigherRankedType, + poly_cache_entry, + ); + + let cache_projection = cache_entry.projection_ty; + let mut nested_obligations = Vec::new(); + let obligation_projection = obligation.predicate; + let obligation_projection = ensure_sufficient_stack(|| { + normalize_with_depth_to( + selcx, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + obligation_projection, + &mut nested_obligations, + ) + }); + let cache_projection = if potentially_unnormalized_candidate { + ensure_sufficient_stack(|| { + normalize_with_depth_to( + selcx, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + cache_projection, + &mut nested_obligations, + ) + }) + } else { + cache_projection + }; + + debug!(?cache_projection, ?obligation_projection); + + match infcx.at(cause, param_env).eq( + DefineOpaqueTypes::No, + cache_projection, + obligation_projection, + ) { + Ok(InferOk { value: _, obligations }) => { + nested_obligations.extend(obligations); + assoc_ty_own_obligations(selcx, obligation, &mut nested_obligations); + // FIXME(associated_const_equality): Handle consts here as well? Maybe this progress type should just take + // a term instead. + Progress { term: cache_entry.term, obligations: nested_obligations } + } + Err(e) => { + let msg = format!( + "Failed to unify obligation `{obligation:?}` with poly_projection `{poly_cache_entry:?}`: {e:?}", + ); + debug!("confirm_param_env_candidate: {}", msg); + let err = Ty::new_error_with_message(infcx.tcx, obligation.cause.span, msg); + Progress { term: err.into(), obligations: vec![] } + } + } +} + +fn confirm_impl_candidate<'cx, 'tcx>( + selcx: &mut SelectionContext<'cx, 'tcx>, + obligation: &ProjectionTyObligation<'tcx>, + impl_impl_source: ImplSourceUserDefinedData<'tcx, PredicateObligation<'tcx>>, +) -> Progress<'tcx> { + let tcx = selcx.tcx(); + + let ImplSourceUserDefinedData { impl_def_id, args, mut nested } = impl_impl_source; + let assoc_item_id = obligation.predicate.def_id; + let trait_def_id = tcx.trait_id_of_impl(impl_def_id).unwrap(); + + let param_env = obligation.param_env; + let assoc_ty = match specialization_graph::assoc_def(tcx, impl_def_id, assoc_item_id) { + Ok(assoc_ty) => assoc_ty, + Err(guar) => return Progress::error(tcx, guar), + }; + + if !assoc_ty.item.defaultness(tcx).has_value() { + // This means that the impl is missing a definition for the + // associated type. This error will be reported by the type + // checker method `check_impl_items_against_trait`, so here we + // just return Error. + debug!( + "confirm_impl_candidate: no associated type {:?} for {:?}", + assoc_ty.item.name, obligation.predicate + ); + 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: + // + // * `obligation.predicate.args` is `[Vec<u32>, S]` + // * `args` is `[u32]` + // * `args` ends up as `[u32, S]` + let args = obligation.predicate.args.rebase_onto(tcx, trait_def_id, args); + let args = translate_args(selcx.infcx, param_env, impl_def_id, args, assoc_ty.defining_node); + let ty = tcx.type_of(assoc_ty.item.def_id); + let is_const = matches!(tcx.def_kind(assoc_ty.item.def_id), DefKind::AssocConst); + let term: ty::EarlyBinder<ty::Term<'tcx>> = if is_const { + let did = assoc_ty.item.def_id; + let identity_args = crate::traits::GenericArgs::identity_for_item(tcx, did); + let uv = ty::UnevaluatedConst::new(did, identity_args); + ty.map_bound(|ty| ty::Const::new_unevaluated(tcx, uv, ty).into()) + } else { + ty.map_bound(|ty| ty.into()) + }; + if !check_args_compatible(tcx, assoc_ty.item, args) { + let err = Ty::new_error_with_message( + tcx, + obligation.cause.span, + "impl item and trait item have different parameters", + ); + Progress { term: err.into(), obligations: nested } + } else { + assoc_ty_own_obligations(selcx, obligation, &mut nested); + Progress { term: term.instantiate(tcx, args), obligations: nested } + } +} + +// Get obligations corresponding to the predicates from the where-clause of the +// associated type itself. +fn assoc_ty_own_obligations<'cx, 'tcx>( + selcx: &mut SelectionContext<'cx, 'tcx>, + obligation: &ProjectionTyObligation<'tcx>, + nested: &mut Vec<PredicateObligation<'tcx>>, +) { + let tcx = selcx.tcx(); + let predicates = tcx + .predicates_of(obligation.predicate.def_id) + .instantiate_own(tcx, obligation.predicate.args); + for (predicate, span) in predicates { + let normalized = normalize_with_depth_to( + selcx, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + predicate, + nested, + ); + + let nested_cause = if matches!( + obligation.cause.code(), + super::CompareImplItemObligation { .. } + | super::CheckAssociatedTypeBounds { .. } + | super::AscribeUserTypeProvePredicate(..) + ) { + obligation.cause.clone() + } else if span.is_dummy() { + ObligationCause::new( + obligation.cause.span, + obligation.cause.body_id, + super::ItemObligation(obligation.predicate.def_id), + ) + } else { + ObligationCause::new( + obligation.cause.span, + obligation.cause.body_id, + super::BindingObligation(obligation.predicate.def_id, span), + ) + }; + nested.push(Obligation::with_depth( + tcx, + nested_cause, + obligation.recursion_depth + 1, + obligation.param_env, + normalized, + )); + } +} + +pub(crate) trait ProjectionCacheKeyExt<'cx, 'tcx>: Sized { + fn from_poly_projection_predicate( + selcx: &mut SelectionContext<'cx, 'tcx>, + predicate: ty::PolyProjectionPredicate<'tcx>, + ) -> Option<Self>; +} + +impl<'cx, 'tcx> ProjectionCacheKeyExt<'cx, 'tcx> for ProjectionCacheKey<'tcx> { + fn from_poly_projection_predicate( + selcx: &mut SelectionContext<'cx, 'tcx>, + predicate: ty::PolyProjectionPredicate<'tcx>, + ) -> Option<Self> { + let infcx = selcx.infcx; + // We don't do cross-snapshot caching of obligations with escaping regions, + // so there's no cache key to use + predicate.no_bound_vars().map(|predicate| { + ProjectionCacheKey::new( + // We don't attempt to match up with a specific type-variable state + // from a specific call to `opt_normalize_projection_type` - if + // there's no precise match, the original cache entry is "stranded" + // anyway. + infcx.resolve_vars_if_possible(predicate.projection_ty), + ) + }) + } +} diff --git a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs new file mode 100644 index 00000000000..138bc6129f7 --- /dev/null +++ b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs @@ -0,0 +1,340 @@ +use crate::traits::query::normalize::QueryNormalizeExt; +use crate::traits::query::NoSolution; +use crate::traits::{Normalized, ObligationCause, ObligationCtxt}; + +use rustc_data_structures::fx::FxHashSet; +use rustc_middle::traits::query::{DropckConstraint, DropckOutlivesResult}; +use rustc_middle::ty::{self, EarlyBinder, ParamEnvAnd, Ty, TyCtxt}; +use rustc_span::{Span, DUMMY_SP}; + +/// This returns true if the type `ty` is "trivial" for +/// dropck-outlives -- that is, if it doesn't require any types to +/// outlive. This is similar but not *quite* the same as the +/// `needs_drop` test in the compiler already -- that is, for every +/// type T for which this function return true, needs-drop would +/// return `false`. But the reverse does not hold: in particular, +/// `needs_drop` returns false for `PhantomData`, but it is not +/// trivial for dropck-outlives. +/// +/// Note also that `needs_drop` requires a "global" type (i.e., one +/// with erased regions), but this function does not. +/// +// FIXME(@lcnr): remove this module and move this function somewhere else. +pub fn trivial_dropck_outlives<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { + match ty.kind() { + // None of these types have a destructor and hence they do not + // require anything in particular to outlive the dtor's + // execution. + ty::Infer(ty::FreshIntTy(_)) + | ty::Infer(ty::FreshFloatTy(_)) + | ty::Bool + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Never + | ty::FnDef(..) + | ty::FnPtr(_) + | ty::Char + | ty::CoroutineWitness(..) + | ty::RawPtr(_) + | ty::Ref(..) + | ty::Str + | ty::Foreign(..) + | ty::Error(_) => true, + + // [T; N] and [T] have same properties as T. + ty::Array(ty, _) | ty::Slice(ty) => trivial_dropck_outlives(tcx, *ty), + + // (T1..Tn) and closures have same properties as T1..Tn -- + // check if *all* of them are trivial. + ty::Tuple(tys) => tys.iter().all(|t| trivial_dropck_outlives(tcx, t)), + ty::Closure(_, args) => trivial_dropck_outlives(tcx, args.as_closure().tupled_upvars_ty()), + + ty::Adt(def, _) => { + if Some(def.did()) == tcx.lang_items().manually_drop() { + // `ManuallyDrop` never has a dtor. + true + } else { + // Other types might. Moreover, PhantomData doesn't + // have a dtor, but it is considered to own its + // content, so it is non-trivial. Unions can have `impl Drop`, + // and hence are non-trivial as well. + false + } + } + + // The following *might* require a destructor: needs deeper inspection. + ty::Dynamic(..) + | ty::Alias(..) + | ty::Param(_) + | ty::Placeholder(..) + | ty::Infer(_) + | ty::Bound(..) + | ty::Coroutine(..) => false, + } +} + +pub fn compute_dropck_outlives_inner<'tcx>( + ocx: &ObligationCtxt<'_, 'tcx>, + goal: ParamEnvAnd<'tcx, Ty<'tcx>>, +) -> Result<DropckOutlivesResult<'tcx>, NoSolution> { + let tcx = ocx.infcx.tcx; + let ParamEnvAnd { param_env, value: for_ty } = goal; + + let mut result = DropckOutlivesResult { kinds: vec![], overflows: vec![] }; + + // A stack of types left to process. Each round, we pop + // something from the stack and invoke + // `dtorck_constraint_for_ty_inner`. This may produce new types that + // have to be pushed on the stack. This continues until we have explored + // all the reachable types from the type `for_ty`. + // + // Example: Imagine that we have the following code: + // + // ```rust + // struct A { + // value: B, + // children: Vec<A>, + // } + // + // struct B { + // value: u32 + // } + // + // fn f() { + // let a: A = ...; + // .. + // } // here, `a` is dropped + // ``` + // + // at the point where `a` is dropped, we need to figure out + // which types inside of `a` contain region data that may be + // accessed by any destructors in `a`. We begin by pushing `A` + // onto the stack, as that is the type of `a`. We will then + // invoke `dtorck_constraint_for_ty_inner` which will expand `A` + // into the types of its fields `(B, Vec<A>)`. These will get + // pushed onto the stack. Eventually, expanding `Vec<A>` will + // lead to us trying to push `A` a second time -- to prevent + // infinite recursion, we notice that `A` was already pushed + // once and stop. + let mut ty_stack = vec![(for_ty, 0)]; + + // Set used to detect infinite recursion. + let mut ty_set = FxHashSet::default(); + + let cause = ObligationCause::dummy(); + let mut constraints = DropckConstraint::empty(); + while let Some((ty, depth)) = ty_stack.pop() { + debug!( + "{} kinds, {} overflows, {} ty_stack", + result.kinds.len(), + result.overflows.len(), + ty_stack.len() + ); + dtorck_constraint_for_ty_inner(tcx, param_env, DUMMY_SP, depth, ty, &mut constraints)?; + + // "outlives" represent types/regions that may be touched + // by a destructor. + result.kinds.append(&mut constraints.outlives); + result.overflows.append(&mut constraints.overflows); + + // If we have even one overflow, we should stop trying to evaluate further -- + // chances are, the subsequent overflows for this evaluation won't provide useful + // information and will just decrease the speed at which we can emit these errors + // (since we'll be printing for just that much longer for the often enormous types + // that result here). + if !result.overflows.is_empty() { + break; + } + + // dtorck types are "types that will get dropped but which + // do not themselves define a destructor", more or less. We have + // to push them onto the stack to be expanded. + for ty in constraints.dtorck_types.drain(..) { + let Normalized { value: ty, obligations } = + ocx.infcx.at(&cause, param_env).query_normalize(ty)?; + ocx.register_obligations(obligations); + + debug!("dropck_outlives: ty from dtorck_types = {:?}", ty); + + match ty.kind() { + // All parameters live for the duration of the + // function. + ty::Param(..) => {} + + // A projection that we couldn't resolve - it + // might have a destructor. + ty::Alias(..) => { + result.kinds.push(ty.into()); + } + + _ => { + if ty_set.insert(ty) { + ty_stack.push((ty, depth + 1)); + } + } + } + } + } + + debug!("dropck_outlives: result = {:#?}", result); + Ok(result) +} + +/// Returns a set of constraints that needs to be satisfied in +/// order for `ty` to be valid for destruction. +#[instrument(level = "debug", skip(tcx, param_env, span, constraints))] +pub fn dtorck_constraint_for_ty_inner<'tcx>( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + span: Span, + depth: usize, + ty: Ty<'tcx>, + constraints: &mut DropckConstraint<'tcx>, +) -> Result<(), NoSolution> { + if !tcx.recursion_limit().value_within_limit(depth) { + constraints.overflows.push(ty); + return Ok(()); + } + + if trivial_dropck_outlives(tcx, ty) { + return Ok(()); + } + + match ty.kind() { + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Str + | ty::Never + | ty::Foreign(..) + | ty::RawPtr(..) + | ty::Ref(..) + | ty::FnDef(..) + | ty::FnPtr(_) + | ty::CoroutineWitness(..) => { + // these types never have a destructor + } + + ty::Array(ety, _) | ty::Slice(ety) => { + // single-element containers, behave like their element + rustc_data_structures::stack::ensure_sufficient_stack(|| { + dtorck_constraint_for_ty_inner(tcx, param_env, span, depth + 1, *ety, constraints) + })?; + } + + ty::Tuple(tys) => rustc_data_structures::stack::ensure_sufficient_stack(|| { + for ty in tys.iter() { + dtorck_constraint_for_ty_inner(tcx, param_env, span, depth + 1, ty, constraints)?; + } + Ok::<_, NoSolution>(()) + })?, + + ty::Closure(_, args) => { + if !args.as_closure().is_valid() { + // By the time this code runs, all type variables ought to + // be fully resolved. + + tcx.dcx().span_delayed_bug( + span, + format!("upvar_tys for closure not found. Expected capture information for closure {ty}",), + ); + return Err(NoSolution); + } + + rustc_data_structures::stack::ensure_sufficient_stack(|| { + for ty in args.as_closure().upvar_tys() { + dtorck_constraint_for_ty_inner( + tcx, + param_env, + span, + depth + 1, + ty, + constraints, + )?; + } + Ok::<_, NoSolution>(()) + })? + } + + ty::Coroutine(_, args) => { + // rust-lang/rust#49918: types can be constructed, stored + // in the interior, and sit idle when coroutine yields + // (and is subsequently dropped). + // + // It would be nice to descend into interior of a + // coroutine to determine what effects dropping it might + // have (by looking at any drop effects associated with + // its interior). + // + // However, the interior's representation uses things like + // CoroutineWitness that explicitly assume they are not + // traversed in such a manner. So instead, we will + // simplify things for now by treating all coroutines as + // if they were like trait objects, where its upvars must + // all be alive for the coroutine's (potential) + // destructor. + // + // In particular, skipping over `_interior` is safe + // because any side-effects from dropping `_interior` can + // only take place through references with lifetimes + // derived from lifetimes attached to the upvars and resume + // argument, and we *do* incorporate those here. + let args = args.as_coroutine(); + if !args.is_valid() { + // By the time this code runs, all type variables ought to + // be fully resolved. + tcx.dcx().span_delayed_bug( + span, + format!("upvar_tys for coroutine not found. Expected capture information for coroutine {ty}",), + ); + return Err(NoSolution); + } + + // While we conservatively assume that all coroutines require drop + // to avoid query cycles during MIR building, we can check the actual + // witness during borrowck to avoid unnecessary liveness constraints. + if args.witness().needs_drop(tcx, tcx.erase_regions(param_env)) { + constraints.outlives.extend(args.upvar_tys().iter().map(ty::GenericArg::from)); + constraints.outlives.push(args.resume_ty().into()); + } + } + + ty::Adt(def, args) => { + let DropckConstraint { dtorck_types, outlives, overflows } = + tcx.at(span).adt_dtorck_constraint(def.did())?; + // FIXME: we can try to recursively `dtorck_constraint_on_ty` + // there, but that needs some way to handle cycles. + constraints + .dtorck_types + .extend(dtorck_types.iter().map(|t| EarlyBinder::bind(*t).instantiate(tcx, args))); + constraints + .outlives + .extend(outlives.iter().map(|t| EarlyBinder::bind(*t).instantiate(tcx, args))); + constraints + .overflows + .extend(overflows.iter().map(|t| EarlyBinder::bind(*t).instantiate(tcx, args))); + } + + // Objects must be alive in order for their destructor + // to be called. + ty::Dynamic(..) => { + constraints.outlives.push(ty.into()); + } + + // Types that can't be resolved. Pass them forward. + ty::Alias(..) | ty::Param(..) => { + constraints.dtorck_types.push(ty); + } + + ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) | ty::Error(_) => { + // By the time this code runs, all type variables ought to + // be fully resolved. + return Err(NoSolution); + } + } + + Ok(()) +} diff --git a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs new file mode 100644 index 00000000000..d812d537d8c --- /dev/null +++ b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs @@ -0,0 +1,129 @@ +use rustc_infer::traits::{TraitEngine, TraitEngineExt}; + +use crate::infer::canonical::OriginalQueryValues; +use crate::infer::InferCtxt; +use crate::traits::{EvaluationResult, OverflowError, PredicateObligation, SelectionContext}; + +pub trait InferCtxtExt<'tcx> { + fn predicate_may_hold(&self, obligation: &PredicateObligation<'tcx>) -> bool; + + fn predicate_must_hold_considering_regions( + &self, + obligation: &PredicateObligation<'tcx>, + ) -> bool; + + fn predicate_must_hold_modulo_regions(&self, obligation: &PredicateObligation<'tcx>) -> bool; + + fn evaluate_obligation( + &self, + obligation: &PredicateObligation<'tcx>, + ) -> Result<EvaluationResult, OverflowError>; + + // Helper function that canonicalizes and runs the query. If an + // overflow results, we re-run it in the local context so we can + // report a nice error. + /*crate*/ + fn evaluate_obligation_no_overflow( + &self, + obligation: &PredicateObligation<'tcx>, + ) -> EvaluationResult; +} + +impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> { + /// Evaluates whether the predicate can be satisfied (by any means) + /// in the given `ParamEnv`. + fn predicate_may_hold(&self, obligation: &PredicateObligation<'tcx>) -> bool { + self.evaluate_obligation_no_overflow(obligation).may_apply() + } + + /// Evaluates whether the predicate can be satisfied in the given + /// `ParamEnv`, and returns `false` if not certain. However, this is + /// not entirely accurate if inference variables are involved. + /// + /// This version may conservatively fail when outlives obligations + /// are required. + fn predicate_must_hold_considering_regions( + &self, + obligation: &PredicateObligation<'tcx>, + ) -> bool { + self.evaluate_obligation_no_overflow(obligation).must_apply_considering_regions() + } + + /// Evaluates whether the predicate can be satisfied in the given + /// `ParamEnv`, and returns `false` if not certain. However, this is + /// not entirely accurate if inference variables are involved. + /// + /// This version ignores all outlives constraints. + fn predicate_must_hold_modulo_regions(&self, obligation: &PredicateObligation<'tcx>) -> bool { + self.evaluate_obligation_no_overflow(obligation).must_apply_modulo_regions() + } + + /// Evaluate a given predicate, capturing overflow and propagating it back. + fn evaluate_obligation( + &self, + obligation: &PredicateObligation<'tcx>, + ) -> Result<EvaluationResult, OverflowError> { + let mut _orig_values = OriginalQueryValues::default(); + + let param_env = obligation.param_env; + + if self.next_trait_solver() { + self.probe(|snapshot| { + let mut fulfill_cx = crate::solve::FulfillmentCtxt::new(self); + fulfill_cx.register_predicate_obligation(self, obligation.clone()); + // True errors + // FIXME(-Znext-solver): Overflows are reported as ambig here, is that OK? + if !fulfill_cx.select_where_possible(self).is_empty() { + Ok(EvaluationResult::EvaluatedToErr) + } else if !fulfill_cx.select_all_or_error(self).is_empty() { + Ok(EvaluationResult::EvaluatedToAmbig) + } else if self.opaque_types_added_in_snapshot(snapshot) { + Ok(EvaluationResult::EvaluatedToOkModuloOpaqueTypes) + } 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, + ); + self.tcx.at(obligation.cause.span()).evaluate_obligation(c_pred) + } + } + + // Helper function that canonicalizes and runs the query. If an + // overflow results, we re-run it in the local context so we can + // report a nice error. + fn evaluate_obligation_no_overflow( + &self, + obligation: &PredicateObligation<'tcx>, + ) -> EvaluationResult { + // Run canonical query. If overflow occurs, rerun from scratch but this time + // in standard trait query mode so that overflow is handled appropriately + // within `SelectionContext`. + match self.evaluate_obligation(obligation) { + Ok(result) => result, + Err(OverflowError::Canonical) => { + let mut selcx = SelectionContext::new(self); + selcx.evaluate_root_obligation(obligation).unwrap_or_else(|r| match r { + OverflowError::Canonical => { + span_bug!( + obligation.cause.span, + "Overflow should be caught earlier in standard query mode: {:?}, {:?}", + obligation, + r, + ) + } + OverflowError::ErrorReporting => EvaluationResult::EvaluatedToErr, + OverflowError::Error(_) => EvaluationResult::EvaluatedToErr, + }) + } + Err(OverflowError::ErrorReporting) => EvaluationResult::EvaluatedToErr, + Err(OverflowError::Error(_)) => EvaluationResult::EvaluatedToErr, + } + } +} diff --git a/compiler/rustc_trait_selection/src/traits/query/method_autoderef.rs b/compiler/rustc_trait_selection/src/traits/query/method_autoderef.rs new file mode 100644 index 00000000000..3c0ebec9335 --- /dev/null +++ b/compiler/rustc_trait_selection/src/traits/query/method_autoderef.rs @@ -0,0 +1,3 @@ +pub use rustc_middle::traits::query::{ + CandidateStep, MethodAutoderefBadTy, MethodAutoderefStepsResult, +}; diff --git a/compiler/rustc_trait_selection/src/traits/query/mod.rs b/compiler/rustc_trait_selection/src/traits/query/mod.rs new file mode 100644 index 00000000000..ef349367813 --- /dev/null +++ b/compiler/rustc_trait_selection/src/traits/query/mod.rs @@ -0,0 +1,14 @@ +//! Experimental types for the trait query interface. The methods +//! defined in this module are all based on **canonicalization**, +//! which makes a canonical query by replacing unbound inference +//! variables and regions, so that results can be reused more broadly. +//! The providers for the queries defined here can be found in +//! `rustc_traits`. + +pub mod dropck_outlives; +pub mod evaluate_obligation; +pub mod method_autoderef; +pub mod normalize; +pub mod type_op; + +pub use rustc_middle::traits::query::*; diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs new file mode 100644 index 00000000000..e8867187a40 --- /dev/null +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -0,0 +1,371 @@ +//! Code for the 'normalization' query. This consists of a wrapper +//! which folds deeply, invoking the underlying +//! `normalize_projection_ty` query when it encounters projections. + +use crate::infer::at::At; +use crate::infer::canonical::OriginalQueryValues; +use crate::infer::{InferCtxt, InferOk}; +use crate::traits::error_reporting::TypeErrCtxtExt; +use crate::traits::project::{needs_normalization, BoundVarReplacer, PlaceholderReplacer}; +use crate::traits::{ObligationCause, PredicateObligation, Reveal}; +use rustc_data_structures::sso::SsoHashMap; +use rustc_data_structures::stack::ensure_sufficient_stack; +use rustc_infer::traits::Normalized; +use rustc_middle::ty::fold::{FallibleTypeFolder, TypeFoldable, TypeSuperFoldable}; +use rustc_middle::ty::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitor}; +use rustc_span::DUMMY_SP; + +use std::ops::ControlFlow; + +use super::NoSolution; + +pub use rustc_middle::traits::query::NormalizationResult; + +pub trait QueryNormalizeExt<'tcx> { + /// Normalize a value using the `QueryNormalizer`. + /// + /// This normalization should *only* be used when the projection does not + /// have possible ambiguity or may not be well-formed. + /// + /// After codegen, when lifetimes do not matter, it is preferable to instead + /// use [`TyCtxt::normalize_erasing_regions`], which wraps this procedure. + fn query_normalize<T>(self, value: T) -> Result<Normalized<'tcx, T>, NoSolution> + where + T: TypeFoldable<TyCtxt<'tcx>>; +} + +impl<'cx, 'tcx> QueryNormalizeExt<'tcx> for At<'cx, 'tcx> { + /// Normalize `value` in the context of the inference context, + /// yielding a resulting type, or an error if `value` cannot be + /// normalized. If you don't care about regions, you should prefer + /// `normalize_erasing_regions`, which is more efficient. + /// + /// If the normalization succeeds and is unambiguous, returns back + /// the normalized value along with various outlives relations (in + /// the form of obligations that must be discharged). + /// + /// N.B., this will *eventually* be the main means of + /// 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> + where + T: TypeFoldable<TyCtxt<'tcx>>, + { + debug!( + "normalize::<{}>(value={:?}, param_env={:?}, cause={:?})", + std::any::type_name::<T>(), + value, + self.param_env, + self.cause, + ); + + // This is actually a consequence by the way `normalize_erasing_regions` works currently. + // Because it needs to call the `normalize_generic_arg_after_erasing_regions`, it folds + // through tys and consts in a `TypeFoldable`. Importantly, it skips binders, leaving us + // with trying to normalize with escaping bound vars. + // + // Here, we just add the universes that we *would* have created had we passed through the binders. + // + // We *could* replace escaping bound vars eagerly here, but it doesn't seem really necessary. + // The rest of the code is already set up to be lazy about replacing bound vars, + // and only when we actually have to normalize. + let universes = if value.has_escaping_bound_vars() { + let mut max_visitor = + MaxEscapingBoundVarVisitor { outer_index: ty::INNERMOST, escaping: 0 }; + value.visit_with(&mut max_visitor); + vec![None; max_visitor.escaping] + } else { + vec![] + }; + + if self.infcx.next_trait_solver() { + match crate::solve::deeply_normalize_with_skipped_universes(self, value, universes) { + 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![] }); + } + + let mut normalizer = QueryNormalizer { + infcx: self.infcx, + cause: self.cause, + param_env: self.param_env, + obligations: vec![], + cache: SsoHashMap::new(), + anon_depth: 0, + universes, + }; + + let result = value.try_fold_with(&mut normalizer); + info!( + "normalize::<{}>: result={:?} with {} obligations", + std::any::type_name::<T>(), + result, + normalizer.obligations.len(), + ); + debug!( + "normalize::<{}>: obligations={:?}", + std::any::type_name::<T>(), + normalizer.obligations, + ); + result.map(|value| Normalized { value, obligations: normalizer.obligations }) + } +} + +// Visitor to find the maximum escaping bound var +struct MaxEscapingBoundVarVisitor { + // The index which would count as escaping + outer_index: ty::DebruijnIndex, + escaping: usize, +} + +impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for MaxEscapingBoundVarVisitor { + fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>( + &mut self, + t: &ty::Binder<'tcx, T>, + ) -> ControlFlow<Self::BreakTy> { + self.outer_index.shift_in(1); + let result = t.super_visit_with(self); + self.outer_index.shift_out(1); + result + } + + #[inline] + fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { + if t.outer_exclusive_binder() > self.outer_index { + self.escaping = self + .escaping + .max(t.outer_exclusive_binder().as_usize() - self.outer_index.as_usize()); + } + ControlFlow::Continue(()) + } + + #[inline] + fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> { + match *r { + ty::ReBound(debruijn, _) if debruijn > self.outer_index => { + self.escaping = + self.escaping.max(debruijn.as_usize() - self.outer_index.as_usize()); + } + _ => {} + } + ControlFlow::Continue(()) + } + + fn visit_const(&mut self, ct: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> { + if ct.outer_exclusive_binder() > self.outer_index { + self.escaping = self + .escaping + .max(ct.outer_exclusive_binder().as_usize() - self.outer_index.as_usize()); + } + ControlFlow::Continue(()) + } +} + +struct QueryNormalizer<'cx, 'tcx> { + infcx: &'cx InferCtxt<'tcx>, + cause: &'cx ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + obligations: Vec<PredicateObligation<'tcx>>, + cache: SsoHashMap<Ty<'tcx>, Ty<'tcx>>, + anon_depth: usize, + universes: Vec<Option<ty::UniverseIndex>>, +} + +impl<'cx, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'cx, 'tcx> { + type Error = NoSolution; + + fn interner(&self) -> TyCtxt<'tcx> { + self.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(); + t + } + + #[instrument(level = "debug", skip(self))] + fn try_fold_ty(&mut self, ty: Ty<'tcx>) -> Result<Ty<'tcx>, Self::Error> { + if !needs_normalization(&ty, self.param_env.reveal()) { + return Ok(ty); + } + + if let Some(ty) = self.cache.get(&ty) { + return Ok(*ty); + } + + let (kind, data) = match *ty.kind() { + ty::Alias(kind, data) => (kind, data), + _ => { + let res = ty.try_super_fold_with(self)?; + self.cache.insert(ty, res); + return Ok(res); + } + }; + + // See note in `rustc_trait_selection::traits::project` about why we + // wait to fold the args. + + // Wrap this in a closure so we don't accidentally return from the outer function + let res = match kind { + 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)?, + + Reveal::All => { + let args = data.args.try_fold_with(self)?; + let recursion_limit = self.interner().recursion_limit(); + + if !recursion_limit.value_within_limit(self.anon_depth) { + let guar = self + .infcx + .err_ctxt() + .build_overflow_error(&ty, self.cause.span, true) + .delay_as_bug(); + return Ok(Ty::new_error(self.interner(), guar)); + } + + let generic_ty = self.interner().type_of(data.def_id); + let concrete_ty = generic_ty.instantiate(self.interner(), args); + self.anon_depth += 1; + if concrete_ty == ty { + bug!( + "infinite recursion generic_ty: {:#?}, args: {:#?}, \ + concrete_ty: {:#?}, ty: {:#?}", + generic_ty, + args, + concrete_ty, + ty + ); + } + let folded_ty = ensure_sufficient_stack(|| self.try_fold_ty(concrete_ty)); + self.anon_depth -= 1; + folded_ty? + } + } + } + + ty::Projection | ty::Inherent | ty::Weak => { + // See note in `rustc_trait_selection::traits::project` + + let infcx = self.infcx; + let tcx = infcx.tcx; + // Just an optimization: When we don't have escaping bound vars, + // we don't need to replace them with placeholders. + let (data, maps) = if data.has_escaping_bound_vars() { + let (data, mapped_regions, mapped_types, mapped_consts) = + BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, data); + (data, Some((mapped_regions, mapped_types, mapped_consts))) + } else { + (data, None) + }; + let data = data.try_fold_with(self)?; + + let mut orig_values = OriginalQueryValues::default(); + // HACK(matthewjasper) `'static` is special-cased in selection, + // so we cannot canonicalize it. + let c_data = infcx + .canonicalize_query_keep_static(self.param_env.and(data), &mut orig_values); + debug!("QueryNormalizer: c_data = {:#?}", c_data); + 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), + kind => unreachable!("did not expect {kind:?} due to match arm above"), + }?; + // We don't expect ambiguity. + if !result.value.is_proven() { + // Rustdoc normalizes possibly not well-formed types, so only + // treat this as a bug if we're not in rustdoc. + if !tcx.sess.opts.actually_rustdoc { + tcx.dcx().span_delayed_bug( + DUMMY_SP, + format!("unexpected ambiguity: {c_data:?} {result:?}"), + ); + } + return Err(NoSolution); + } + let InferOk { value: result, obligations } = infcx + .instantiate_query_response_and_region_obligations( + self.cause, + self.param_env, + &orig_values, + result, + )?; + debug!("QueryNormalizer: result = {:#?}", result); + debug!("QueryNormalizer: obligations = {:#?}", obligations); + self.obligations.extend(obligations); + let res = if let Some((mapped_regions, mapped_types, mapped_consts)) = maps { + PlaceholderReplacer::replace_placeholders( + infcx, + mapped_regions, + mapped_types, + mapped_consts, + &self.universes, + result.normalized_ty, + ) + } else { + result.normalized_ty + }; + // `tcx.normalize_projection_ty` may normalize to a type that still has + // unevaluated consts, so keep normalizing here if that's the case. + // 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 + } + } + }; + + self.cache.insert(ty, res); + Ok(res) + } + + fn try_fold_const( + &mut self, + constant: ty::Const<'tcx>, + ) -> Result<ty::Const<'tcx>, Self::Error> { + if !needs_normalization(&constant, self.param_env.reveal()) { + return Ok(constant); + } + + let constant = constant.try_super_fold_with(self)?; + debug!(?constant, ?self.param_env); + Ok(crate::traits::project::with_replaced_escaping_bound_vars( + self.infcx, + &mut self.universes, + constant, + |constant| constant.normalize(self.infcx.tcx, self.param_env), + )) + } + + #[inline] + fn try_fold_predicate( + &mut self, + p: ty::Predicate<'tcx>, + ) -> Result<ty::Predicate<'tcx>, Self::Error> { + if p.allow_normalization() && needs_normalization(&p, self.param_env.reveal()) { + p.try_super_fold_with(self) + } else { + Ok(p) + } + } +} diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs new file mode 100644 index 00000000000..152ceeee869 --- /dev/null +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs @@ -0,0 +1,143 @@ +use crate::infer::canonical::{Canonical, CanonicalQueryResponse}; +use crate::traits::ObligationCtxt; +use rustc_hir::def_id::{DefId, CRATE_DEF_ID}; +use rustc_infer::traits::Obligation; +use rustc_middle::traits::query::NoSolution; +use rustc_middle::traits::{ObligationCause, ObligationCauseCode}; +use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt, UserArgs, UserSelfTy, UserType}; + +pub use rustc_middle::traits::query::type_op::AscribeUserType; +use rustc_span::{Span, DUMMY_SP}; + +impl<'tcx> super::QueryTypeOp<'tcx> for AscribeUserType<'tcx> { + type QueryResponse = (); + + fn try_fast_path( + _tcx: TyCtxt<'tcx>, + _key: &ParamEnvAnd<'tcx, Self>, + ) -> Option<Self::QueryResponse> { + None + } + + fn perform_query( + tcx: TyCtxt<'tcx>, + canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Self>>, + ) -> Result<CanonicalQueryResponse<'tcx, ()>, NoSolution> { + tcx.type_op_ascribe_user_type(canonicalized) + } + + fn perform_locally_with_next_solver( + ocx: &ObligationCtxt<'_, 'tcx>, + key: ParamEnvAnd<'tcx, Self>, + ) -> Result<Self::QueryResponse, NoSolution> { + type_op_ascribe_user_type_with_span(ocx, key, None) + } +} + +/// The core of the `type_op_ascribe_user_type` query: for diagnostics purposes in NLL HRTB errors, +/// this query can be re-run to better track the span of the obligation cause, and improve the error +/// message. Do not call directly unless you're in that very specific context. +pub fn type_op_ascribe_user_type_with_span<'tcx>( + ocx: &ObligationCtxt<'_, 'tcx>, + key: ParamEnvAnd<'tcx, AscribeUserType<'tcx>>, + span: Option<Span>, +) -> Result<(), NoSolution> { + let (param_env, AscribeUserType { mir_ty, user_ty }) = key.into_parts(); + debug!("type_op_ascribe_user_type: mir_ty={:?} user_ty={:?}", mir_ty, user_ty); + let span = span.unwrap_or(DUMMY_SP); + match user_ty { + UserType::Ty(user_ty) => relate_mir_and_user_ty(ocx, param_env, span, mir_ty, user_ty)?, + UserType::TypeOf(def_id, user_args) => { + relate_mir_and_user_args(ocx, param_env, span, mir_ty, def_id, user_args)? + } + }; + Ok(()) +} + +#[instrument(level = "debug", skip(ocx, param_env, span))] +fn relate_mir_and_user_ty<'tcx>( + ocx: &ObligationCtxt<'_, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + span: Span, + mir_ty: Ty<'tcx>, + user_ty: Ty<'tcx>, +) -> Result<(), NoSolution> { + let cause = ObligationCause::dummy_with_span(span); + let user_ty = ocx.normalize(&cause, param_env, user_ty); + 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::Clause(ty::ClauseKind::WellFormed(user_ty.into()))); + ocx.register_obligation(Obligation::new(ocx.infcx.tcx, cause, param_env, predicate)); + Ok(()) +} + +#[instrument(level = "debug", skip(ocx, param_env, span))] +fn relate_mir_and_user_args<'tcx>( + ocx: &ObligationCtxt<'_, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + span: Span, + mir_ty: Ty<'tcx>, + def_id: DefId, + user_args: UserArgs<'tcx>, +) -> Result<(), NoSolution> { + let UserArgs { user_self_ty, args } = user_args; + let tcx = ocx.infcx.tcx; + let cause = ObligationCause::dummy_with_span(span); + + let ty = tcx.type_of(def_id).instantiate(tcx, args); + let ty = ocx.normalize(&cause, param_env, ty); + debug!("relate_type_and_user_type: ty of def-id is {:?}", ty); + + ocx.eq(&cause, param_env, mir_ty, ty)?; + + // Prove the predicates coming along with `def_id`. + // + // Also, normalize the `instantiated_predicates` + // because otherwise we wind up with duplicate "type + // outlives" error messages. + let instantiated_predicates = tcx.predicates_of(def_id).instantiate(tcx, args); + + debug!(?instantiated_predicates); + for (instantiated_predicate, predicate_span) in instantiated_predicates { + let span = if span == DUMMY_SP { predicate_span } else { span }; + let cause = ObligationCause::new( + span, + CRATE_DEF_ID, + ObligationCauseCode::AscribeUserTypeProvePredicate(predicate_span), + ); + let instantiated_predicate = + ocx.normalize(&cause.clone(), param_env, instantiated_predicate); + + ocx.register_obligation(Obligation::new(tcx, cause, param_env, instantiated_predicate)); + } + + if let Some(UserSelfTy { impl_def_id, self_ty }) = user_self_ty { + let self_ty = ocx.normalize(&cause, param_env, self_ty); + let impl_self_ty = tcx.type_of(impl_def_id).instantiate(tcx, args); + 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::Clause(ty::ClauseKind::WellFormed( + impl_self_ty.into(), + ))); + ocx.register_obligation(Obligation::new(tcx, cause.clone(), param_env, predicate)); + } + + // In addition to proving the predicates, we have to + // prove that `ty` is well-formed -- this is because + // the WF of `ty` is predicated on the args being + // well-formed, and we haven't proven *that*. We don't + // want to prove the WF of types from `args` directly because they + // haven't been normalized. + // + // FIXME(nmatsakis): Well, perhaps we should normalize + // 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::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 new file mode 100644 index 00000000000..f4baae2711f --- /dev/null +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs @@ -0,0 +1,127 @@ +use crate::infer::canonical::query_response; +use crate::infer::InferCtxt; +use crate::traits::query::type_op::TypeOpOutput; +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::{Span, DUMMY_SP}; + +use std::fmt; + +pub struct CustomTypeOp<F> { + closure: F, + description: &'static str, +} + +impl<F> CustomTypeOp<F> { + pub fn new<'tcx, R>(closure: F, description: &'static str) -> Self + where + F: FnOnce(&ObligationCtxt<'_, 'tcx>) -> Result<R, NoSolution>, + { + CustomTypeOp { closure, description } + } +} + +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 + /// we can use `!` to enforce that the implementation never provides it. + type ErrorInfo = !; + + /// Processes the operation and all resulting obligations, + /// returning the final result along with any region constraints + /// (they will be given over to the NLL region solver). + fn fully_perform( + self, + infcx: &InferCtxt<'tcx>, + span: Span, + ) -> Result<TypeOpOutput<'tcx, Self>, ErrorGuaranteed> { + if cfg!(debug_assertions) { + info!("fully_perform({:?})", self); + } + + Ok(scrape_region_constraints(infcx, self.closure, self.description, span)?.0) + } +} + +impl<F> fmt::Debug for CustomTypeOp<F> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.description.fmt(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, R>( + infcx: &InferCtxt<'tcx>, + op: impl FnOnce(&ObligationCtxt<'_, 'tcx>) -> Result<R, NoSolution>, + name: &'static str, + span: Span, +) -> 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 + // obligations that resulted). So this vector should be empty on + // entry. + let pre_obligations = infcx.take_registered_region_obligations(); + assert!( + pre_obligations.is_empty(), + "scrape_region_constraints: incoming region obligations = {pre_obligations:#?}", + ); + + let value = infcx.commit_if_ok(|_| { + let ocx = ObligationCtxt::new(infcx); + let value = op(&ocx).map_err(|_| { + infcx.dcx().span_delayed_bug(span, format!("error performing operation: {name}")) + })?; + let errors = ocx.select_all_or_error(); + if errors.is_empty() { + Ok(value) + } else { + Err(infcx.dcx().span_delayed_bug( + DUMMY_SP, + format!("errors selecting obligation during MIR typeck: {errors:?}"), + )) + } + })?; + + // 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( + infcx.tcx, + region_obligations + .iter() + .map(|r_o| (r_o.sup_type, r_o.sub_region, r_o.origin.to_constraint_category())) + .map(|(ty, r, cc)| (infcx.resolve_vars_if_possible(ty), r, cc)), + ®ion_constraint_data, + ); + + if region_constraints.is_empty() { + Ok(( + TypeOpOutput { output: value, constraints: None, error_info: None }, + region_constraint_data, + )) + } else { + Ok(( + TypeOpOutput { + output: value, + constraints: Some(infcx.tcx.arena.alloc(region_constraints)), + error_info: None, + }, + region_constraint_data, + )) + } +} diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/eq.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/eq.rs new file mode 100644 index 00000000000..57e649f3e43 --- /dev/null +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/eq.rs @@ -0,0 +1,33 @@ +use crate::infer::canonical::{Canonical, CanonicalQueryResponse}; +use crate::traits::ObligationCtxt; +use rustc_middle::traits::query::NoSolution; +use rustc_middle::traits::ObligationCause; +use rustc_middle::ty::{ParamEnvAnd, TyCtxt}; + +pub use rustc_middle::traits::query::type_op::Eq; + +impl<'tcx> super::QueryTypeOp<'tcx> for Eq<'tcx> { + type QueryResponse = (); + + fn try_fast_path( + _tcx: TyCtxt<'tcx>, + key: &ParamEnvAnd<'tcx, Eq<'tcx>>, + ) -> Option<Self::QueryResponse> { + if key.value.a == key.value.b { Some(()) } else { None } + } + + fn perform_query( + tcx: TyCtxt<'tcx>, + canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Self>>, + ) -> Result<CanonicalQueryResponse<'tcx, ()>, NoSolution> { + tcx.type_op_eq(canonicalized) + } + + fn perform_locally_with_next_solver( + ocx: &ObligationCtxt<'_, 'tcx>, + key: ParamEnvAnd<'tcx, Self>, + ) -> Result<Self::QueryResponse, NoSolution> { + ocx.eq(&ObligationCause::dummy(), key.param_env, key.value.a, key.value.b)?; + Ok(()) + } +} 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 new file mode 100644 index 00000000000..ba6ed298774 --- /dev/null +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs @@ -0,0 +1,232 @@ +use crate::solve; +use crate::traits::query::NoSolution; +use crate::traits::wf; +use crate::traits::ObligationCtxt; + +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::DUMMY_SP; +use smallvec::{smallvec, SmallVec}; + +#[derive(Copy, Clone, Debug, HashStable, TypeFoldable, TypeVisitable)] +pub struct ImpliedOutlivesBounds<'tcx> { + pub ty: Ty<'tcx>, +} + +impl<'tcx> super::QueryTypeOp<'tcx> for ImpliedOutlivesBounds<'tcx> { + type QueryResponse = Vec<OutlivesBound<'tcx>>; + + fn try_fast_path( + _tcx: TyCtxt<'tcx>, + key: &ParamEnvAnd<'tcx, Self>, + ) -> Option<Self::QueryResponse> { + // Don't go into the query for things that can't possibly have lifetimes. + match key.value.ty.kind() { + ty::Tuple(elems) if elems.is_empty() => Some(vec![]), + ty::Never | ty::Str | ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) => { + Some(vec![]) + } + _ => None, + } + } + + fn perform_query( + tcx: TyCtxt<'tcx>, + canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Self>>, + ) -> Result<CanonicalQueryResponse<'tcx, Self::QueryResponse>, NoSolution> { + // FIXME this `unchecked_map` is only necessary because the + // query is defined as taking a `ParamEnvAnd<Ty>`; it should + // take an `ImpliedOutlivesBounds` instead + let canonicalized = canonicalized.unchecked_map(|ParamEnvAnd { param_env, value }| { + let ImpliedOutlivesBounds { ty } = value; + param_env.and(ty) + }); + + tcx.implied_outlives_bounds(canonicalized) + } + + fn perform_locally_with_next_solver( + ocx: &ObligationCtxt<'_, 'tcx>, + key: ParamEnvAnd<'tcx, Self>, + ) -> Result<Self::QueryResponse, NoSolution> { + compute_implied_outlives_bounds_inner(ocx, key.param_env, key.value.ty) + } +} + +pub fn compute_implied_outlives_bounds_inner<'tcx>( + ocx: &ObligationCtxt<'_, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + ty: Ty<'tcx>, +) -> Result<Vec<OutlivesBound<'tcx>>, NoSolution> { + let tcx = ocx.infcx.tcx; + + // Sometimes when we ask what it takes for T: WF, we get back that + // U: WF is required; in that case, we push U onto this stack and + // process it next. Because the resulting predicates aren't always + // guaranteed to be a subset of the original type, so we need to store the + // WF args we've computed in a set. + let mut checked_wf_args = rustc_data_structures::fx::FxHashSet::default(); + let mut wf_args = vec![ty.into()]; + + let mut outlives_bounds: Vec<ty::OutlivesPredicate<ty::GenericArg<'tcx>, ty::Region<'tcx>>> = + vec![]; + + while let Some(arg) = wf_args.pop() { + if !checked_wf_args.insert(arg) { + continue; + } + + // Compute the obligations for `arg` to be well-formed. If `arg` is + // an unresolved inference variable, just substituted an empty set + // -- because the return type here is going to be things we *add* + // to the environment, it's always ok for this set to be smaller + // than the ultimate set. (Note: normally there won't be + // unresolved inference variables here anyway, but there might be + // during typeck under some circumstances.) + // + // FIXME(@lcnr): It's not really "always fine", having fewer implied + // bounds can be backward incompatible, e.g. #101951 was caused by + // us not dealing with inference vars in `TypeOutlives` predicates. + let obligations = wf::obligations(ocx.infcx, param_env, CRATE_DEF_ID, 0, arg, DUMMY_SP) + .unwrap_or_default(); + + for obligation in obligations { + debug!(?obligation); + assert!(!obligation.has_escaping_bound_vars()); + + // While these predicates should all be implied by other parts of + // the program, they are still relevant as they may constrain + // inference variables, which is necessary to add the correct + // implied bounds in some cases, mostly when dealing with projections. + // + // Another important point here: we only register `Projection` + // predicates, since otherwise we might register outlives + // predicates containing inference variables, and we don't + // learn anything new from those. + if obligation.predicate.has_non_region_infer() { + match obligation.predicate.kind().skip_binder() { + ty::PredicateKind::Clause(ty::ClauseKind::Projection(..)) + | ty::PredicateKind::AliasRelate(..) => { + ocx.register_obligation(obligation.clone()); + } + _ => {} + } + } + + let pred = match obligation.predicate.kind().no_bound_vars() { + None => continue, + Some(pred) => pred, + }; + match pred { + // FIXME(const_generics): Make sure that `<'a, 'b, const N: &'a &'b u32>` is sound + // if we ever support that + ty::PredicateKind::Clause(ty::ClauseKind::Trait(..)) + | ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(..)) + | ty::PredicateKind::Subtype(..) + | ty::PredicateKind::Coerce(..) + | ty::PredicateKind::Clause(ty::ClauseKind::Projection(..)) + | ty::PredicateKind::ObjectSafe(..) + | ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..)) + | ty::PredicateKind::ConstEquate(..) + | ty::PredicateKind::Ambiguous + | ty::PredicateKind::NormalizesTo(..) + | ty::PredicateKind::AliasRelate(..) => {} + + // We need to search through *all* WellFormed predicates + ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => { + wf_args.push(arg); + } + + // We need to register region relationships + 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::ClauseKind::TypeOutlives(ty::OutlivesPredicate( + ty_a, + r_b, + ))) => outlives_bounds.push(ty::OutlivesPredicate(ty_a.into(), r_b)), + } + } + } + + // This call to `select_all_or_error` is necessary to constrain inference variables, which we + // use further down when computing the implied bounds. + match ocx.select_all_or_error().as_slice() { + [] => (), + _ => return Err(NoSolution), + } + + // We lazily compute the outlives components as + // `select_all_or_error` constrains inference variables. + 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 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.extend(implied_bounds_from_components(r_b, components)) + } + ty::GenericArgKind::Const(_) => { + unreachable!("consts do not participate in outlives bounds") + } + } + } + + Ok(implied_bounds) +} + +/// When we have an implied bound that `T: 'a`, we can further break +/// this down to determine what relationships would have to hold for +/// `T: 'a` to hold. We get to assume that the caller has validated +/// those relationships. +fn implied_bounds_from_components<'tcx>( + sub_region: ty::Region<'tcx>, + sup_components: SmallVec<[Component<'tcx>; 4]>, +) -> Vec<OutlivesBound<'tcx>> { + sup_components + .into_iter() + .filter_map(|component| { + match component { + Component::Region(r) => Some(OutlivesBound::RegionSubRegion(sub_region, r)), + Component::Param(p) => Some(OutlivesBound::RegionSubParam(sub_region, p)), + Component::Alias(p) => Some(OutlivesBound::RegionSubAlias(sub_region, p)), + Component::Placeholder(_p) => { + // FIXME(non_lifetime_binders): Placeholders don't currently + // imply anything for outlives, though they could easily. + None + } + Component::EscapingAlias(_) => + // If the projection has escaping regions, don't + // try to infer any implied bounds even for its + // free components. This is conservative, because + // the caller will still have to prove that those + // free components outlive `sub_region`. But the + // idea is that the WAY that the caller proves + // that may change in the future and we want to + // give ourselves room to get smarter here. + { + None + } + Component::UnresolvedInferenceVariable(..) => None, + } + }) + .collect() +} 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 new file mode 100644 index 00000000000..cab2a62ed7e --- /dev/null +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs @@ -0,0 +1,208 @@ +use crate::infer::canonical::{ + Canonical, CanonicalQueryResponse, OriginalQueryValues, QueryRegionConstraints, +}; +use crate::infer::{InferCtxt, InferOk}; +use crate::traits::{ObligationCause, ObligationCtxt}; +use rustc_errors::ErrorGuaranteed; +use rustc_infer::infer::canonical::Certainty; +use rustc_infer::traits::PredicateObligations; +use rustc_middle::traits::query::NoSolution; +use rustc_middle::ty::fold::TypeFoldable; +use rustc_middle::ty::{ParamEnvAnd, TyCtxt}; +use rustc_span::Span; +use std::fmt; + +pub mod ascribe_user_type; +pub mod custom; +pub mod eq; +pub mod implied_outlives_bounds; +pub mod normalize; +pub mod outlives; +pub mod prove_predicate; +pub mod subtype; + +pub use rustc_middle::traits::query::type_op::*; + +use self::custom::scrape_region_constraints; + +/// "Type ops" are used in NLL to perform some particular action and +/// extract out the resulting region constraints (or an error if it +/// cannot be completed). +pub trait TypeOp<'tcx>: Sized + fmt::Debug { + type Output: fmt::Debug; + type ErrorInfo; + + /// Processes the operation and all resulting obligations, + /// returning the final result along with any region constraints + /// (they will be given over to the NLL region solver). + fn fully_perform( + self, + infcx: &InferCtxt<'tcx>, + span: Span, + ) -> Result<TypeOpOutput<'tcx, Self>, ErrorGuaranteed>; +} + +/// The output from performing a type op +pub struct TypeOpOutput<'tcx, Op: TypeOp<'tcx>> { + /// The output from the type op. + pub output: Op::Output, + /// Any region constraints from performing the type op. + pub constraints: Option<&'tcx QueryRegionConstraints<'tcx>>, + /// Used for error reporting to be able to rerun the query + pub error_info: Option<Op::ErrorInfo>, +} + +/// "Query type ops" are type ops that are implemented using a +/// [canonical query][c]. The `Self` type here contains the kernel of +/// information needed to do the operation -- `TypeOp` is actually +/// implemented for `ParamEnvAnd<Self>`, since we always need to bring +/// along a parameter environment as well. For query type-ops, we will +/// first canonicalize the key and then invoke the query on the tcx, +/// which produces the resulting query region constraints. +/// +/// [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html +pub trait QueryTypeOp<'tcx>: fmt::Debug + Copy + TypeFoldable<TyCtxt<'tcx>> + 'tcx { + type QueryResponse: TypeFoldable<TyCtxt<'tcx>>; + + /// Give query the option for a simple fast path that never + /// actually hits the tcx cache lookup etc. Return `Some(r)` with + /// a final result or `None` to do the full path. + fn try_fast_path( + tcx: TyCtxt<'tcx>, + key: &ParamEnvAnd<'tcx, Self>, + ) -> Option<Self::QueryResponse>; + + /// Performs the actual query with the canonicalized key -- the + /// real work happens here. This method is not given an `infcx` + /// because it shouldn't need one -- and if it had access to one, + /// it might do things like invoke `sub_regions`, which would be + /// bad, because it would create subregion relationships that are + /// not captured in the return value. + fn perform_query( + tcx: TyCtxt<'tcx>, + canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Self>>, + ) -> Result<CanonicalQueryResponse<'tcx, Self::QueryResponse>, NoSolution>; + + /// In the new trait solver, we already do caching in the solver itself, + /// so there's no need to canonicalize and cache via the query system. + /// Additionally, even if we were to canonicalize, we'd still need to + /// make sure to feed it predefined opaque types and the defining anchor + /// and that would require duplicating all of the tcx queries. Instead, + /// just perform these ops locally. + fn perform_locally_with_next_solver( + ocx: &ObligationCtxt<'_, 'tcx>, + key: ParamEnvAnd<'tcx, Self>, + ) -> Result<Self::QueryResponse, NoSolution>; + + fn fully_perform_into( + query_key: ParamEnvAnd<'tcx, Self>, + infcx: &InferCtxt<'tcx>, + output_query_region_constraints: &mut QueryRegionConstraints<'tcx>, + ) -> Result< + ( + Self::QueryResponse, + Option<Canonical<'tcx, ParamEnvAnd<'tcx, Self>>>, + PredicateObligations<'tcx>, + Certainty, + ), + NoSolution, + > { + if let Some(result) = QueryTypeOp::try_fast_path(infcx.tcx, &query_key) { + return Ok((result, None, vec![], Certainty::Proven)); + } + + // FIXME(#33684) -- We need to use + // `canonicalize_query_keep_static` here because of things + // like the subtype query, which go awry around + // `'static` otherwise. + let mut canonical_var_values = OriginalQueryValues::default(); + let old_param_env = query_key.param_env; + let canonical_self = + infcx.canonicalize_query_keep_static(query_key, &mut canonical_var_values); + let canonical_result = Self::perform_query(infcx.tcx, canonical_self)?; + + let InferOk { value, obligations } = infcx + .instantiate_nll_query_response_and_region_obligations( + &ObligationCause::dummy(), + old_param_env, + &canonical_var_values, + canonical_result, + output_query_region_constraints, + )?; + + Ok((value, Some(canonical_self), obligations, canonical_result.value.certainty)) + } +} + +impl<'tcx, Q> TypeOp<'tcx> for ParamEnvAnd<'tcx, Q> +where + Q: QueryTypeOp<'tcx>, +{ + type Output = Q::QueryResponse; + type ErrorInfo = Canonical<'tcx, ParamEnvAnd<'tcx, Q>>; + + fn fully_perform( + self, + infcx: &InferCtxt<'tcx>, + span: Span, + ) -> Result<TypeOpOutput<'tcx, Self>, ErrorGuaranteed> { + if infcx.next_trait_solver() { + return Ok(scrape_region_constraints( + infcx, + |ocx| QueryTypeOp::perform_locally_with_next_solver(ocx, self), + "query type op", + span, + )? + .0); + } + + let mut region_constraints = QueryRegionConstraints::default(); + let (output, error_info, mut obligations, _) = + Q::fully_perform_into(self, infcx, &mut region_constraints).map_err(|_| { + infcx.dcx().span_delayed_bug(span, format!("error performing {self:?}")) + })?; + + // Typically, instantiating NLL query results does not + // create obligations. However, in some cases there + // are unresolved type variables, and unify them *can* + // create obligations. In that case, we have to go + // fulfill them. We do this via a (recursive) query. + while !obligations.is_empty() { + trace!("{:#?}", obligations); + let mut progress = false; + for obligation in std::mem::take(&mut obligations) { + let obligation = infcx.resolve_vars_if_possible(obligation); + match ProvePredicate::fully_perform_into( + obligation.param_env.and(ProvePredicate::new(obligation.predicate)), + infcx, + &mut region_constraints, + ) { + Ok(((), _, new, certainty)) => { + obligations.extend(new); + progress = true; + if let Certainty::Ambiguous = certainty { + obligations.push(obligation); + } + } + Err(_) => obligations.push(obligation), + } + } + if !progress { + return Err(infcx.dcx().span_delayed_bug( + span, + format!("ambiguity processing {obligations:?} from {self:?}"), + )); + } + } + + Ok(TypeOpOutput { + output, + constraints: if region_constraints.is_empty() { + None + } else { + Some(infcx.tcx.arena.alloc(region_constraints)) + }, + error_info, + }) + } +} 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 new file mode 100644 index 00000000000..3b33f6e6144 --- /dev/null +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs @@ -0,0 +1,78 @@ +use crate::infer::canonical::{Canonical, CanonicalQueryResponse}; +use crate::traits::ObligationCtxt; +use rustc_middle::traits::query::NoSolution; +use rustc_middle::traits::ObligationCause; +use rustc_middle::ty::fold::TypeFoldable; +use rustc_middle::ty::{self, Lift, ParamEnvAnd, Ty, TyCtxt, TypeVisitableExt}; +use std::fmt; + +pub use rustc_middle::traits::query::type_op::Normalize; + +impl<'tcx, T> super::QueryTypeOp<'tcx> for Normalize<T> +where + T: Normalizable<'tcx> + 'tcx, +{ + type QueryResponse = T; + + fn try_fast_path(_tcx: TyCtxt<'tcx>, key: &ParamEnvAnd<'tcx, Self>) -> Option<T> { + if !key.value.value.has_projections() { Some(key.value.value) } else { None } + } + + fn perform_query( + tcx: TyCtxt<'tcx>, + canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Self>>, + ) -> Result<CanonicalQueryResponse<'tcx, Self::QueryResponse>, NoSolution> { + T::type_op_method(tcx, canonicalized) + } + + fn perform_locally_with_next_solver( + ocx: &ObligationCtxt<'_, 'tcx>, + key: ParamEnvAnd<'tcx, Self>, + ) -> Result<Self::QueryResponse, NoSolution> { + // FIXME(-Znext-solver): shouldn't be using old normalizer + Ok(ocx.normalize(&ObligationCause::dummy(), key.param_env, key.value.value)) + } +} + +pub trait Normalizable<'tcx>: fmt::Debug + TypeFoldable<TyCtxt<'tcx>> + Lift<'tcx> + Copy { + fn type_op_method( + tcx: TyCtxt<'tcx>, + canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Normalize<Self>>>, + ) -> Result<CanonicalQueryResponse<'tcx, Self>, NoSolution>; +} + +impl<'tcx> Normalizable<'tcx> for Ty<'tcx> { + fn type_op_method( + tcx: TyCtxt<'tcx>, + canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Normalize<Self>>>, + ) -> Result<CanonicalQueryResponse<'tcx, Self>, NoSolution> { + tcx.type_op_normalize_ty(canonicalized) + } +} + +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_clause(canonicalized) + } +} + +impl<'tcx> Normalizable<'tcx> for ty::PolyFnSig<'tcx> { + fn type_op_method( + tcx: TyCtxt<'tcx>, + canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Normalize<Self>>>, + ) -> Result<CanonicalQueryResponse<'tcx, Self>, NoSolution> { + tcx.type_op_normalize_poly_fn_sig(canonicalized) + } +} + +impl<'tcx> Normalizable<'tcx> for ty::FnSig<'tcx> { + fn type_op_method( + tcx: TyCtxt<'tcx>, + canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Normalize<Self>>>, + ) -> Result<CanonicalQueryResponse<'tcx, Self>, NoSolution> { + tcx.type_op_normalize_fn_sig(canonicalized) + } +} diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs new file mode 100644 index 00000000000..07587e37411 --- /dev/null +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs @@ -0,0 +1,51 @@ +use crate::infer::canonical::{Canonical, CanonicalQueryResponse}; +use crate::traits::query::dropck_outlives::{ + compute_dropck_outlives_inner, trivial_dropck_outlives, +}; +use crate::traits::ObligationCtxt; +use rustc_middle::traits::query::{DropckOutlivesResult, NoSolution}; +use rustc_middle::ty::{ParamEnvAnd, Ty, TyCtxt}; + +#[derive(Copy, Clone, Debug, HashStable, TypeFoldable, TypeVisitable)] +pub struct DropckOutlives<'tcx> { + dropped_ty: Ty<'tcx>, +} + +impl<'tcx> DropckOutlives<'tcx> { + pub fn new(dropped_ty: Ty<'tcx>) -> Self { + DropckOutlives { dropped_ty } + } +} + +impl<'tcx> super::QueryTypeOp<'tcx> for DropckOutlives<'tcx> { + type QueryResponse = DropckOutlivesResult<'tcx>; + + fn try_fast_path( + tcx: TyCtxt<'tcx>, + key: &ParamEnvAnd<'tcx, Self>, + ) -> Option<Self::QueryResponse> { + trivial_dropck_outlives(tcx, key.value.dropped_ty).then(DropckOutlivesResult::default) + } + + fn perform_query( + tcx: TyCtxt<'tcx>, + canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Self>>, + ) -> Result<CanonicalQueryResponse<'tcx, Self::QueryResponse>, NoSolution> { + // FIXME convert to the type expected by the `dropck_outlives` + // query. This should eventually be fixed by changing the + // *underlying query*. + let canonicalized = canonicalized.unchecked_map(|ParamEnvAnd { param_env, value }| { + let DropckOutlives { dropped_ty } = value; + param_env.and(dropped_ty) + }); + + tcx.dropck_outlives(canonicalized) + } + + fn perform_locally_with_next_solver( + ocx: &ObligationCtxt<'_, 'tcx>, + key: ParamEnvAnd<'tcx, Self>, + ) -> Result<Self::QueryResponse, NoSolution> { + compute_dropck_outlives_inner(ocx, key.param_env.and(key.value.dropped_ty)) + } +} 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 new file mode 100644 index 00000000000..e21ede47f6d --- /dev/null +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs @@ -0,0 +1,55 @@ +use crate::infer::canonical::{Canonical, CanonicalQueryResponse}; +use crate::traits::ObligationCtxt; +use rustc_infer::traits::Obligation; +use rustc_middle::traits::query::NoSolution; +use rustc_middle::traits::ObligationCause; +use rustc_middle::ty::{self, ParamEnvAnd, TyCtxt}; + +pub use rustc_middle::traits::query::type_op::ProvePredicate; + +impl<'tcx> super::QueryTypeOp<'tcx> for ProvePredicate<'tcx> { + type QueryResponse = (); + + fn try_fast_path( + tcx: TyCtxt<'tcx>, + key: &ParamEnvAnd<'tcx, Self>, + ) -> Option<Self::QueryResponse> { + // Proving Sized, very often on "obviously sized" types like + // `&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::ClauseKind::Trait(trait_ref)) = + key.value.predicate.kind().skip_binder() + { + if let Some(sized_def_id) = tcx.lang_items().sized_trait() { + if trait_ref.def_id() == sized_def_id { + if trait_ref.self_ty().is_trivially_sized(tcx) { + return Some(()); + } + } + } + } + + None + } + + fn perform_query( + tcx: TyCtxt<'tcx>, + canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Self>>, + ) -> Result<CanonicalQueryResponse<'tcx, ()>, NoSolution> { + tcx.type_op_prove_predicate(canonicalized) + } + + fn perform_locally_with_next_solver( + ocx: &ObligationCtxt<'_, 'tcx>, + key: ParamEnvAnd<'tcx, Self>, + ) -> Result<Self::QueryResponse, NoSolution> { + ocx.register_obligation(Obligation::new( + ocx.infcx.tcx, + ObligationCause::dummy(), + key.param_env, + key.value.predicate, + )); + Ok(()) + } +} diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/subtype.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/subtype.rs new file mode 100644 index 00000000000..ae11b0825bd --- /dev/null +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/subtype.rs @@ -0,0 +1,30 @@ +use crate::infer::canonical::{Canonical, CanonicalQueryResponse}; +use crate::traits::ObligationCtxt; +use rustc_middle::traits::query::NoSolution; +use rustc_middle::traits::ObligationCause; +use rustc_middle::ty::{ParamEnvAnd, TyCtxt}; + +pub use rustc_middle::traits::query::type_op::Subtype; + +impl<'tcx> super::QueryTypeOp<'tcx> for Subtype<'tcx> { + type QueryResponse = (); + + fn try_fast_path(_tcx: TyCtxt<'tcx>, key: &ParamEnvAnd<'tcx, Self>) -> Option<()> { + if key.value.sub == key.value.sup { Some(()) } else { None } + } + + fn perform_query( + tcx: TyCtxt<'tcx>, + canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Self>>, + ) -> Result<CanonicalQueryResponse<'tcx, ()>, NoSolution> { + tcx.type_op_subtype(canonicalized) + } + + fn perform_locally_with_next_solver( + ocx: &ObligationCtxt<'_, 'tcx>, + key: ParamEnvAnd<'tcx, Self>, + ) -> Result<Self::QueryResponse, NoSolution> { + ocx.sub(&ObligationCause::dummy(), key.param_env, key.value.sub, key.value.sup)?; + Ok(()) + } +} diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs new file mode 100644 index 00000000000..54b91ab1d4d --- /dev/null +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -0,0 +1,1107 @@ +//! Candidate assembly. +//! +//! The selection process begins by examining all in-scope impls, +//! caller obligations, and so forth and assembling a list of +//! candidates. See the [rustc dev guide] for more details. +//! +//! [rustc dev guide]:https://rustc-dev-guide.rust-lang.org/traits/resolution.html#candidate-assembly + +use hir::def_id::DefId; +use hir::LangItem; +use rustc_hir as hir; +use rustc_infer::traits::{Obligation, PolyTraitObligation, SelectionError}; +use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; +use rustc_middle::ty::{self, Ty, TypeVisitableExt}; + +use crate::traits::util; + +use super::BuiltinImplConditions; +use super::SelectionCandidate::*; +use super::{SelectionCandidateSet, SelectionContext, TraitObligationStack}; + +impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { + #[instrument(skip(self, stack), level = "debug")] + pub(super) fn assemble_candidates<'o>( + &mut self, + stack: &TraitObligationStack<'o, 'tcx>, + ) -> Result<SelectionCandidateSet<'tcx>, SelectionError<'tcx>> { + let TraitObligationStack { obligation, .. } = *stack; + let obligation = &Obligation { + param_env: obligation.param_env, + cause: obligation.cause.clone(), + recursion_depth: obligation.recursion_depth, + predicate: self.infcx.resolve_vars_if_possible(obligation.predicate), + }; + + if obligation.predicate.skip_binder().self_ty().is_ty_var() { + debug!(ty = ?obligation.predicate.skip_binder().self_ty(), "ambiguous inference var or opaque type"); + // Self is a type variable (e.g., `_: AsRef<str>`). + // + // This is somewhat problematic, as the current scheme can't really + // handle it turning to be a projection. This does end up as truly + // ambiguous in most cases anyway. + // + // Take the fast path out - this also improves + // performance by preventing assemble_candidates_from_impls from + // matching every impl for this trait. + return Ok(SelectionCandidateSet { vec: vec![], ambiguous: true }); + } + + let mut candidates = SelectionCandidateSet { vec: Vec::new(), ambiguous: false }; + + // Negative trait predicates have different rules than positive trait predicates. + if obligation.polarity() == ty::ImplPolarity::Negative { + self.assemble_candidates_for_trait_alias(obligation, &mut candidates); + self.assemble_candidates_from_impls(obligation, &mut candidates); + self.assemble_candidates_from_caller_bounds(stack, &mut candidates)?; + } else { + self.assemble_candidates_for_trait_alias(obligation, &mut candidates); + + // Other bounds. Consider both in-scope bounds from fn decl + // and applicable impls. There is a certain set of precedence rules here. + let def_id = obligation.predicate.def_id(); + let lang_items = self.tcx().lang_items(); + + if lang_items.copy_trait() == Some(def_id) { + debug!(obligation_self_ty = ?obligation.predicate.skip_binder().self_ty()); + + // User-defined copy impls are permitted, but only for + // structs and enums. + self.assemble_candidates_from_impls(obligation, &mut candidates); + + // For other types, we'll use the builtin rules. + let copy_conditions = self.copy_clone_conditions(obligation); + self.assemble_builtin_bound_candidates(copy_conditions, &mut candidates); + } else if lang_items.discriminant_kind_trait() == Some(def_id) { + // `DiscriminantKind` is automatically implemented for every type. + candidates.vec.push(BuiltinCandidate { has_nested: false }); + } else if lang_items.pointee_trait() == Some(def_id) { + // `Pointee` is automatically implemented for every type. + candidates.vec.push(BuiltinCandidate { has_nested: false }); + } else if lang_items.sized_trait() == Some(def_id) { + // Sized is never implementable by end-users, it is + // always automatically computed. + let sized_conditions = self.sized_conditions(obligation); + self.assemble_builtin_bound_candidates(sized_conditions, &mut candidates); + } else if lang_items.unsize_trait() == Some(def_id) { + self.assemble_candidates_for_unsizing(obligation, &mut candidates); + } else if lang_items.destruct_trait() == Some(def_id) { + self.assemble_const_destruct_candidates(obligation, &mut candidates); + } else if lang_items.transmute_trait() == Some(def_id) { + // User-defined transmutability impls are permitted. + self.assemble_candidates_from_impls(obligation, &mut candidates); + self.assemble_candidates_for_transmutability(obligation, &mut candidates); + } else if lang_items.tuple_trait() == Some(def_id) { + self.assemble_candidate_for_tuple(obligation, &mut candidates); + } else if lang_items.pointer_like() == Some(def_id) { + self.assemble_candidate_for_pointer_like(obligation, &mut candidates); + } else if lang_items.fn_ptr_trait() == Some(def_id) { + self.assemble_candidates_for_fn_ptr_trait(obligation, &mut candidates); + } else { + if lang_items.clone_trait() == Some(def_id) { + // Same builtin conditions as `Copy`, i.e., every type which has builtin support + // for `Copy` also has builtin support for `Clone`, and tuples/arrays of `Clone` + // types have builtin support for `Clone`. + let clone_conditions = self.copy_clone_conditions(obligation); + self.assemble_builtin_bound_candidates(clone_conditions, &mut candidates); + } + + if lang_items.coroutine_trait() == Some(def_id) { + self.assemble_coroutine_candidates(obligation, &mut candidates); + } else if lang_items.future_trait() == Some(def_id) { + self.assemble_future_candidates(obligation, &mut candidates); + } else if lang_items.iterator_trait() == Some(def_id) { + self.assemble_iterator_candidates(obligation, &mut candidates); + } else if lang_items.async_iterator_trait() == Some(def_id) { + self.assemble_async_iterator_candidates(obligation, &mut candidates); + } + + self.assemble_closure_candidates(obligation, &mut candidates); + self.assemble_fn_pointer_candidates(obligation, &mut candidates); + self.assemble_candidates_from_impls(obligation, &mut candidates); + self.assemble_candidates_from_object_ty(obligation, &mut candidates); + } + + self.assemble_candidates_from_projected_tys(obligation, &mut candidates); + self.assemble_candidates_from_caller_bounds(stack, &mut candidates)?; + self.assemble_candidates_from_auto_impls(obligation, &mut candidates); + } + debug!("candidate list size: {}", candidates.vec.len()); + Ok(candidates) + } + + #[instrument(level = "debug", skip(self, candidates))] + fn assemble_candidates_from_projected_tys( + &mut self, + 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 and type aliases here as they don't have meaningful item bounds. + ty::Alias(ty::Projection | ty::Opaque, _) => {} + ty::Infer(ty::TyVar(_)) => { + span_bug!( + obligation.cause.span, + "Self=_ should have been handled by assemble_candidates" + ); + } + _ => return, + } + + let result = self + .infcx + .probe(|_| self.match_projection_obligation_against_definition_bounds(obligation)); + + candidates.vec.extend(result.into_iter().map(|idx| ProjectionCandidate(idx))); + } + + /// Given an obligation like `<SomeTrait for T>`, searches the obligations that the caller + /// supplied to find out whether it is listed among them. + /// + /// Never affects the inference environment. + #[instrument(level = "debug", skip(self, stack, candidates))] + fn assemble_candidates_from_caller_bounds<'o>( + &mut self, + stack: &TraitObligationStack<'o, 'tcx>, + candidates: &mut SelectionCandidateSet<'tcx>, + ) -> Result<(), SelectionError<'tcx>> { + debug!(?stack.obligation); + + let all_bounds = stack + .obligation + .param_env + .caller_bounds() + .iter() + .filter(|p| !p.references_error()) + .filter_map(|p| p.as_trait_clause()); + + // Micro-optimization: filter out predicates relating to different traits. + let matching_bounds = + all_bounds.filter(|p| p.def_id() == stack.obligation.predicate.def_id()); + + // Keep only those bounds which may apply, and propagate overflow if it occurs. + for bound in matching_bounds { + if bound.skip_binder().polarity != stack.obligation.predicate.skip_binder().polarity { + continue; + } + + // FIXME(oli-obk): it is suspicious that we are dropping the constness and + // polarity here. + let wc = self.where_clause_may_apply(stack, bound.map_bound(|t| t.trait_ref))?; + if wc.may_apply() { + candidates.vec.push(ParamCandidate(bound)); + } + } + + Ok(()) + } + + fn assemble_coroutine_candidates( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + candidates: &mut SelectionCandidateSet<'tcx>, + ) { + // Okay to skip binder because the args on coroutine types never + // touch bound regions, they just capture the in-scope + // type/region parameters. + let self_ty = obligation.self_ty().skip_binder(); + match self_ty.kind() { + // `async`/`gen` constructs get lowered to a special kind of coroutine that + // should *not* `impl Coroutine`. + ty::Coroutine(did, ..) if self.tcx().is_general_coroutine(*did) => { + debug!(?self_ty, ?obligation, "assemble_coroutine_candidates",); + + candidates.vec.push(CoroutineCandidate); + } + ty::Infer(ty::TyVar(_)) => { + debug!("assemble_coroutine_candidates: ambiguous self-type"); + candidates.ambiguous = true; + } + _ => {} + } + } + + fn assemble_future_candidates( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + candidates: &mut SelectionCandidateSet<'tcx>, + ) { + let self_ty = obligation.self_ty().skip_binder(); + if let ty::Coroutine(did, ..) = self_ty.kind() { + // async constructs get lowered to a special kind of coroutine that + // should directly `impl Future`. + if self.tcx().coroutine_is_async(*did) { + debug!(?self_ty, ?obligation, "assemble_future_candidates",); + + candidates.vec.push(FutureCandidate); + } + } + } + + fn assemble_iterator_candidates( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + candidates: &mut SelectionCandidateSet<'tcx>, + ) { + let self_ty = obligation.self_ty().skip_binder(); + if let ty::Coroutine(did, ..) = self_ty.kind() { + // gen constructs get lowered to a special kind of coroutine that + // should directly `impl Iterator`. + if self.tcx().coroutine_is_gen(*did) { + debug!(?self_ty, ?obligation, "assemble_iterator_candidates",); + + candidates.vec.push(IteratorCandidate); + } + } + } + + fn assemble_async_iterator_candidates( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + candidates: &mut SelectionCandidateSet<'tcx>, + ) { + let self_ty = obligation.self_ty().skip_binder(); + if let ty::Coroutine(did, args) = *self_ty.kind() { + // gen constructs get lowered to a special kind of coroutine that + // should directly `impl AsyncIterator`. + if self.tcx().coroutine_is_async_gen(did) { + debug!(?self_ty, ?obligation, "assemble_iterator_candidates",); + + // Can only confirm this candidate if we have constrained + // the `Yield` type to at least `Poll<Option<?0>>`.. + let ty::Adt(_poll_def, args) = *args.as_coroutine().yield_ty().kind() else { + candidates.ambiguous = true; + return; + }; + let ty::Adt(_option_def, _) = *args.type_at(0).kind() else { + candidates.ambiguous = true; + return; + }; + + candidates.vec.push(AsyncIteratorCandidate); + } + } + } + + /// Checks for the artificial impl that the compiler will create for an obligation like `X : + /// FnMut<..>` where `X` is a closure type. + /// + /// Note: the type parameters on a closure candidate are modeled as *output* type + /// parameters and hence do not affect whether this trait is a match or not. They will be + /// unified during the confirmation step. + fn assemble_closure_candidates( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + candidates: &mut SelectionCandidateSet<'tcx>, + ) { + let Some(kind) = self.tcx().fn_trait_kind_from_def_id(obligation.predicate.def_id()) else { + return; + }; + + // Okay to skip binder because the args on closure types never + // touch bound regions, they just capture the in-scope + // type/region parameters + match *obligation.self_ty().skip_binder().kind() { + ty::Closure(def_id, closure_args) => { + let is_const = self.tcx().is_const_fn_raw(def_id); + debug!(?kind, ?obligation, "assemble_unboxed_candidates"); + match self.infcx.closure_kind(closure_args) { + Some(closure_kind) => { + debug!(?closure_kind, "assemble_unboxed_candidates"); + if closure_kind.extends(kind) { + candidates.vec.push(ClosureCandidate { is_const }); + } + } + None => { + if kind == ty::ClosureKind::FnOnce { + candidates.vec.push(ClosureCandidate { is_const }); + } else { + candidates.ambiguous = true; + } + } + } + } + ty::Infer(ty::TyVar(_)) => { + debug!("assemble_unboxed_closure_candidates: ambiguous self-type"); + candidates.ambiguous = true; + } + _ => {} + } + } + + /// Implements one of the `Fn()` family for a fn pointer. + fn assemble_fn_pointer_candidates( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + candidates: &mut SelectionCandidateSet<'tcx>, + ) { + // We provide impl of all fn traits for fn pointers. + if !self.tcx().is_fn_trait(obligation.predicate.def_id()) { + return; + } + + // Keep this function in sync with extract_tupled_inputs_and_output_from_callable + // until the old solver (and thus this function) is removed. + + // Okay to skip binder because what we are inspecting doesn't involve bound regions. + let self_ty = obligation.self_ty().skip_binder(); + match *self_ty.kind() { + ty::Infer(ty::TyVar(_)) => { + debug!("assemble_fn_pointer_candidates: ambiguous self-type"); + candidates.ambiguous = true; // Could wind up being a fn() type. + } + // Provide an impl, but only for suitable `fn` pointers. + ty::FnPtr(sig) => { + if sig.is_fn_trait_compatible() { + candidates + .vec + .push(FnPointerCandidate { fn_host_effect: self.tcx().consts.true_ }); + } + } + // Provide an impl for suitable functions, rejecting `#[target_feature]` functions (RFC 2396). + ty::FnDef(def_id, args) => { + let tcx = self.tcx(); + if tcx.fn_sig(def_id).skip_binder().is_fn_trait_compatible() + && tcx.codegen_fn_attrs(def_id).target_features.is_empty() + { + candidates.vec.push(FnPointerCandidate { + fn_host_effect: tcx + .generics_of(def_id) + .host_effect_index + .map_or(tcx.consts.true_, |idx| args.const_at(idx)), + }); + } + } + _ => {} + } + } + + /// Searches for impls that might apply to `obligation`. + #[instrument(level = "debug", skip(self, candidates))] + fn assemble_candidates_from_impls( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + candidates: &mut SelectionCandidateSet<'tcx>, + ) { + // Essentially any user-written impl will match with an error type, + // so creating `ImplCandidates` isn't useful. However, we might + // end up finding a candidate elsewhere (e.g. a `BuiltinCandidate` for `Sized`) + // This helps us avoid overflow: see issue #72839 + // Since compilation is already guaranteed to fail, this is just + // to try to show the 'nicest' possible errors to the user. + // We don't check for errors in the `ParamEnv` - in practice, + // it seems to cause us to be overly aggressive in deciding + // to give up searching for candidates, leading to spurious errors. + if obligation.predicate.references_error() { + return; + } + + let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::ForLookup }; + let obligation_args = obligation.predicate.skip_binder().trait_ref.args; + self.tcx().for_each_relevant_impl( + obligation.predicate.def_id(), + obligation.predicate.skip_binder().trait_ref.self_ty(), + |impl_def_id| { + // Before we create the substitutions and everything, first + // 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.args_may_unify(obligation_args, impl_trait_ref.skip_binder().args) { + return; + } + if self.reject_fn_ptr_impls( + impl_def_id, + obligation, + impl_trait_ref.skip_binder().self_ty(), + ) { + return; + } + + self.infcx.probe(|_| { + if let Ok(_args) = self.match_impl(impl_def_id, impl_trait_ref, obligation) { + candidates.vec.push(ImplCandidate(impl_def_id)); + } + }); + }, + ); + } + + /// The various `impl<T: FnPtr> Trait for T` in libcore are more like builtin impls for all function items + /// and function pointers and less like blanket impls. Rejecting them when they can't possibly apply (because + /// the obligation's self-type does not implement `FnPtr`) avoids reporting that the self type does not implement + /// `FnPtr`, when we wanted to report that it doesn't implement `Trait`. + #[instrument(level = "trace", skip(self), ret)] + fn reject_fn_ptr_impls( + &mut self, + impl_def_id: DefId, + obligation: &PolyTraitObligation<'tcx>, + impl_self_ty: Ty<'tcx>, + ) -> bool { + // Let `impl<T: FnPtr> Trait for Vec<T>` go through the normal rejection path. + if !matches!(impl_self_ty.kind(), ty::Param(..)) { + return false; + } + let Some(fn_ptr_trait) = self.tcx().lang_items().fn_ptr_trait() else { + return false; + }; + + for &(predicate, _) in self.tcx().predicates_of(impl_def_id).predicates { + let ty::ClauseKind::Trait(pred) = predicate.kind().skip_binder() else { continue }; + if fn_ptr_trait != pred.trait_ref.def_id { + continue; + } + trace!(?pred); + // Not the bound we're looking for + if pred.self_ty() != impl_self_ty { + continue; + } + + match obligation.self_ty().skip_binder().kind() { + // 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::Bound(_, _) => {} + + // These can't possibly implement `FnPtr` as they are concrete types + // and not `FnPtr` + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Adt(_, _) + | ty::Foreign(_) + | ty::Str + | ty::Array(_, _) + | ty::Slice(_) + | ty::RawPtr(_) + | ty::Ref(_, _, _) + | ty::Closure(_, _) + | ty::Coroutine(_, _) + | ty::CoroutineWitness(..) + | ty::Never + | ty::Tuple(_) + | ty::Error(_) => return true, + // FIXME: Function definitions could actually implement `FnPtr` by + // casting the ZST function def to a function pointer. + ty::FnDef(_, _) => return true, + } + + // Generic params can implement `FnPtr` if the predicate + // holds within its own environment. + let obligation = Obligation::new( + self.tcx(), + obligation.cause.clone(), + obligation.param_env, + 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::ClauseKind::Trait(pred)) + })), + ); + if let Ok(r) = self.evaluate_root_obligation(&obligation) { + if !r.may_apply() { + return true; + } + } + } + false + } + + fn assemble_candidates_from_auto_impls( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + candidates: &mut SelectionCandidateSet<'tcx>, + ) { + // Okay to skip binder here because the tests we do below do not involve bound regions. + let self_ty = obligation.self_ty().skip_binder(); + debug!(?self_ty, "assemble_candidates_from_auto_impls"); + + let def_id = obligation.predicate.def_id(); + + if self.tcx().trait_is_auto(def_id) { + match *self_ty.kind() { + ty::Dynamic(..) => { + // For object types, we don't know what the closed + // over types are. This means we conservatively + // say nothing; a candidate may be added by + // `assemble_candidates_from_object_ty`. + } + ty::Foreign(..) => { + // Since the contents of foreign types is unknown, + // we don't add any `..` impl. Default traits could + // still be provided by a manual implementation for + // this trait and type. + } + ty::Param(..) + | ty::Alias(ty::Projection | ty::Inherent | ty::Weak, ..) + | ty::Placeholder(..) + | ty::Bound(..) => { + // In these cases, we don't know what the actual + // type is. Therefore, we cannot break it down + // into its constituent types. So we don't + // consider the `..` impl but instead just add no + // candidates: this means that typeck will only + // succeed if there is another reason to believe + // that this obligation holds. That could be a + // where-clause or, in the case of an object type, + // it could be that the object type lists the + // trait (e.g., `Foo+Send : Send`). See + // `ui/typeck/typeck-default-trait-impl-send-param.rs` + // for an example of a test case that exercises + // this path. + } + ty::Infer(ty::TyVar(_) | ty::IntVar(_) | ty::FloatVar(_)) => { + // The auto impl might apply; we don't know. + candidates.ambiguous = true; + } + ty::Coroutine(coroutine_def_id, _) + if self.tcx().lang_items().unpin_trait() == Some(def_id) => + { + match self.tcx().coroutine_movability(coroutine_def_id) { + hir::Movability::Static => { + // Immovable coroutines are never `Unpin`, so + // suppress the normal auto-impl candidate for it. + } + hir::Movability::Movable => { + // Movable coroutines are always `Unpin`, so add an + // unconditional builtin candidate. + candidates.vec.push(BuiltinCandidate { has_nested: false }); + } + } + } + + ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { + bug!( + "asked to assemble auto trait candidates of unexpected type: {:?}", + self_ty + ); + } + + ty::Alias(ty::Opaque, _) => { + if candidates.vec.iter().any(|c| matches!(c, ProjectionCandidate(_))) { + // We do not generate an auto impl candidate for `impl Trait`s which already + // reference our auto trait. + // + // For example during candidate assembly for `impl Send: Send`, we don't have + // to look at the constituent types for this opaque types to figure out that this + // trivially holds. + // + // Note that this is only sound as projection candidates of opaque types + // are always applicable for auto traits. + } else if self.infcx.intercrate { + // We do not emit auto trait candidates for opaque types in coherence. + // Doing so can result in weird dependency cycles. + candidates.ambiguous = true; + } else { + candidates.vec.push(AutoImplCandidate) + } + } + + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Str + | ty::Array(_, _) + | ty::Slice(_) + | ty::Adt(..) + | ty::RawPtr(_) + | ty::Ref(..) + | ty::FnDef(..) + | ty::FnPtr(_) + | ty::Closure(_, _) + | ty::Coroutine(..) + | ty::Never + | ty::Tuple(_) + | ty::CoroutineWitness(..) => { + // Only consider auto impls if there are no manual impls for the root of `self_ty`. + // + // For example, we only consider auto candidates for `&i32: Auto` if no explicit impl + // for `&SomeType: Auto` exists. Due to E0321 the only crate where impls + // for `&SomeType: Auto` can be defined is the crate where `Auto` has been defined. + // + // Generally, we have to guarantee that for all `SimplifiedType`s the only crate + // which may define impls for that type is either the crate defining the type + // or the trait. This should be guaranteed by the orphan check. + let mut has_impl = false; + self.tcx().for_each_relevant_impl(def_id, self_ty, |_| has_impl = true); + if !has_impl { + candidates.vec.push(AutoImplCandidate) + } + } + ty::Error(_) => {} // do not add an auto trait impl for `ty::Error` for now. + } + } + } + + /// Searches for impls that might apply to `obligation`. + fn assemble_candidates_from_object_ty( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + candidates: &mut SelectionCandidateSet<'tcx>, + ) { + debug!( + self_ty = ?obligation.self_ty().skip_binder(), + "assemble_candidates_from_object_ty", + ); + + if !self.tcx().trait_def(obligation.predicate.def_id()).implement_via_object { + return; + } + + self.infcx.probe(|_snapshot| { + let poly_trait_predicate = self.infcx.resolve_vars_if_possible(obligation.predicate); + let placeholder_trait_predicate = + self.infcx.instantiate_binder_with_placeholders(poly_trait_predicate); + + let self_ty = placeholder_trait_predicate.self_ty(); + let principal_trait_ref = match self_ty.kind() { + ty::Dynamic(data, ..) => { + if data.auto_traits().any(|did| did == obligation.predicate.def_id()) { + debug!( + "assemble_candidates_from_object_ty: matched builtin bound, \ + pushing candidate" + ); + candidates.vec.push(BuiltinObjectCandidate); + return; + } + + if let Some(principal) = data.principal() { + if !self.infcx.tcx.features().object_safe_for_dispatch { + principal.with_self_ty(self.tcx(), self_ty) + } else if self.tcx().check_is_object_safe(principal.def_id()) { + principal.with_self_ty(self.tcx(), self_ty) + } else { + return; + } + } else { + // Only auto trait bounds exist. + return; + } + } + ty::Infer(ty::TyVar(_)) => { + debug!("assemble_candidates_from_object_ty: ambiguous"); + candidates.ambiguous = true; // could wind up being an object type + return; + } + _ => return, + }; + + debug!(?principal_trait_ref, "assemble_candidates_from_object_ty"); + + // Count only those upcast versions that match the trait-ref + // we are looking for. Specifically, do not only check for the + // correct trait, but also the correct type parameters. + // For example, we may be trying to upcast `Foo` to `Bar<i32>`, + // but `Foo` is declared as `trait Foo: Bar<u32>`. + let candidate_supertraits = util::supertraits(self.tcx(), principal_trait_ref) + .enumerate() + .filter(|&(_, upcast_trait_ref)| { + self.infcx.probe(|_| { + self.match_normalize_trait_ref( + obligation, + upcast_trait_ref, + placeholder_trait_predicate.trait_ref, + ) + .is_ok() + }) + }) + .map(|(idx, _)| ObjectCandidate(idx)); + + candidates.vec.extend(candidate_supertraits); + }) + } + + /// Searches for unsizing that might apply to `obligation`. + fn assemble_candidates_for_unsizing( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + candidates: &mut SelectionCandidateSet<'tcx>, + ) { + // We currently never consider higher-ranked obligations e.g. + // `for<'a> &'a T: Unsize<Trait+'a>` to be implemented. This is not + // because they are a priori invalid, and we could potentially add support + // for them later, it's just that there isn't really a strong need for it. + // A `T: Unsize<U>` obligation is always used as part of a `T: CoerceUnsize<U>` + // impl, and those are generally applied to concrete types. + // + // That said, one might try to write a fn with a where clause like + // for<'a> Foo<'a, T>: Unsize<Foo<'a, Trait>> + // where the `'a` is kind of orthogonal to the relevant part of the `Unsize`. + // Still, you'd be more likely to write that where clause as + // T: Trait + // so it seems ok if we (conservatively) fail to accept that `Unsize` + // obligation above. Should be possible to extend this in the future. + let Some(source) = obligation.self_ty().no_bound_vars() else { + // Don't add any candidates if there are bound regions. + return; + }; + let target = obligation.predicate.skip_binder().trait_ref.args.type_at(1); + + debug!(?source, ?target, "assemble_candidates_for_unsizing"); + + match (source.kind(), target.kind()) { + // Trait+Kx+'a -> Trait+Ky+'b (upcasts). + (&ty::Dynamic(a_data, a_region, ty::Dyn), &ty::Dynamic(b_data, b_region, ty::Dyn)) => { + // Upcast coercions permit several things: + // + // 1. Dropping auto traits, e.g., `Foo + Send` to `Foo` + // 2. Tightening the region bound, e.g., `Foo + 'a` to `Foo + 'b` if `'a: 'b` + // 3. Tightening trait to its super traits, eg. `Foo` to `Bar` if `Foo: Bar` + // + // Note that neither of the first two of these changes requires any + // change at runtime. The third needs to change pointer metadata at runtime. + // + // We always perform upcasting coercions when we can because of reason + // #2 (region bounds). + let auto_traits_compatible = b_data + .auto_traits() + // All of a's auto traits need to be in b's auto traits. + .all(|b| a_data.auto_traits().any(|a| a == b)); + if auto_traits_compatible { + let principal_def_id_a = a_data.principal_def_id(); + let principal_def_id_b = b_data.principal_def_id(); + if principal_def_id_a == principal_def_id_b { + // no cyclic + candidates.vec.push(BuiltinUnsizeCandidate); + } else if principal_def_id_a.is_some() && principal_def_id_b.is_some() { + // not casual unsizing, now check whether this is trait upcasting coercion. + let principal_a = a_data.principal().unwrap(); + let target_trait_did = principal_def_id_b.unwrap(); + let source_trait_ref = principal_a.with_self_ty(self.tcx(), source); + + for (idx, upcast_trait_ref) in + util::supertraits(self.tcx(), source_trait_ref).enumerate() + { + self.infcx.probe(|_| { + if upcast_trait_ref.def_id() == target_trait_did + && let Ok(nested) = self.match_upcast_principal( + obligation, + upcast_trait_ref, + a_data, + b_data, + a_region, + b_region, + ) + { + if nested.is_none() { + candidates.ambiguous = true; + } + candidates.vec.push(TraitUpcastingUnsizeCandidate(idx)); + } + }) + } + } + } + } + + // `T` -> `Trait` + (_, &ty::Dynamic(_, _, ty::Dyn)) => { + candidates.vec.push(BuiltinUnsizeCandidate); + } + + // Ambiguous handling is below `T` -> `Trait`, because inference + // variables can still implement `Unsize<Trait>` and nested + // obligations will have the final say (likely deferred). + (&ty::Infer(ty::TyVar(_)), _) | (_, &ty::Infer(ty::TyVar(_))) => { + debug!("assemble_candidates_for_unsizing: ambiguous"); + candidates.ambiguous = true; + } + + // `[T; n]` -> `[T]` + (&ty::Array(..), &ty::Slice(_)) => { + candidates.vec.push(BuiltinUnsizeCandidate); + } + + // `Struct<T>` -> `Struct<U>` + (&ty::Adt(def_id_a, _), &ty::Adt(def_id_b, _)) if def_id_a.is_struct() => { + if def_id_a == def_id_b { + candidates.vec.push(BuiltinUnsizeCandidate); + } + } + + // `(.., T)` -> `(.., U)` + (&ty::Tuple(tys_a), &ty::Tuple(tys_b)) => { + if tys_a.len() == tys_b.len() { + candidates.vec.push(BuiltinUnsizeCandidate); + } + } + + _ => {} + }; + } + + #[instrument(level = "debug", skip(self, obligation, candidates))] + fn assemble_candidates_for_transmutability( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + candidates: &mut SelectionCandidateSet<'tcx>, + ) { + if obligation.predicate.has_non_region_param() { + return; + } + + if obligation.has_non_region_infer() { + candidates.ambiguous = true; + return; + } + + candidates.vec.push(TransmutabilityCandidate); + } + + #[instrument(level = "debug", skip(self, obligation, candidates))] + fn assemble_candidates_for_trait_alias( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + candidates: &mut SelectionCandidateSet<'tcx>, + ) { + // Okay to skip binder here because the tests we do below do not involve bound regions. + let self_ty = obligation.self_ty().skip_binder(); + debug!(?self_ty); + + let def_id = obligation.predicate.def_id(); + + if self.tcx().is_trait_alias(def_id) { + candidates.vec.push(TraitAliasCandidate); + } + } + + /// Assembles the trait which are built-in to the language itself: + /// `Copy`, `Clone` and `Sized`. + #[instrument(level = "debug", skip(self, candidates))] + fn assemble_builtin_bound_candidates( + &mut self, + conditions: BuiltinImplConditions<'tcx>, + candidates: &mut SelectionCandidateSet<'tcx>, + ) { + match conditions { + BuiltinImplConditions::Where(nested) => { + candidates + .vec + .push(BuiltinCandidate { has_nested: !nested.skip_binder().is_empty() }); + } + BuiltinImplConditions::None => {} + BuiltinImplConditions::Ambiguous => { + candidates.ambiguous = true; + } + } + } + + fn assemble_const_destruct_candidates( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + candidates: &mut SelectionCandidateSet<'tcx>, + ) { + // If the predicate is `~const Destruct` in a non-const environment, we don't actually need + // to check anything. We'll short-circuit checking any obligations in confirmation, too. + let Some(host_effect_index) = + self.tcx().generics_of(obligation.predicate.def_id()).host_effect_index + else { + candidates.vec.push(BuiltinCandidate { has_nested: false }); + return; + }; + // If the obligation has `host = true`, then the obligation is non-const and it's always + // trivially implemented. + if obligation.predicate.skip_binder().trait_ref.args.const_at(host_effect_index) + == self.tcx().consts.true_ + { + candidates.vec.push(BuiltinCandidate { has_nested: false }); + return; + } + + let self_ty = self.infcx.shallow_resolve(obligation.self_ty()); + match self_ty.skip_binder().kind() { + ty::Alias(..) + | ty::Dynamic(..) + | ty::Error(_) + | ty::Bound(..) + | ty::Param(_) + | ty::Placeholder(_) => { + // We don't know if these are `~const Destruct`, at least + // not structurally... so don't push a candidate. + } + + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Infer(ty::IntVar(_)) + | ty::Infer(ty::FloatVar(_)) + | ty::Str + | ty::RawPtr(_) + | ty::Ref(..) + | ty::FnDef(..) + | ty::FnPtr(_) + | ty::Never + | ty::Foreign(_) + | ty::Array(..) + | ty::Slice(_) + | ty::Closure(..) + | ty::Coroutine(..) + | ty::Tuple(_) + | ty::CoroutineWitness(..) => { + // These are built-in, and cannot have a custom `impl const Destruct`. + candidates.vec.push(ConstDestructCandidate(None)); + } + + ty::Adt(..) => { + let mut relevant_impl = None; + self.tcx().for_each_relevant_impl( + self.tcx().require_lang_item(LangItem::Drop, None), + obligation.predicate.skip_binder().trait_ref.self_ty(), + |impl_def_id| { + if let Some(old_impl_def_id) = relevant_impl { + self.tcx() + .dcx() + .struct_span_err( + self.tcx().def_span(impl_def_id), + "multiple drop impls found", + ) + .span_note(self.tcx().def_span(old_impl_def_id), "other impl here") + .delay_as_bug(); + } + + relevant_impl = Some(impl_def_id); + }, + ); + + if let Some(impl_def_id) = relevant_impl { + // Check that `impl Drop` is actually const, if there is a custom impl + if self.tcx().constness(impl_def_id) == hir::Constness::Const { + candidates.vec.push(ConstDestructCandidate(Some(impl_def_id))); + } + } else { + // Otherwise check the ADT like a built-in type (structurally) + candidates.vec.push(ConstDestructCandidate(None)); + } + } + + ty::Infer(_) => { + candidates.ambiguous = true; + } + } + } + + fn assemble_candidate_for_tuple( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + candidates: &mut SelectionCandidateSet<'tcx>, + ) { + let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder()); + match self_ty.kind() { + ty::Tuple(_) => { + candidates.vec.push(BuiltinCandidate { has_nested: false }); + } + ty::Infer(ty::TyVar(_)) => { + candidates.ambiguous = true; + } + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Adt(_, _) + | ty::Foreign(_) + | ty::Str + | ty::Array(_, _) + | ty::Slice(_) + | ty::RawPtr(_) + | ty::Ref(_, _, _) + | ty::FnDef(_, _) + | ty::FnPtr(_) + | ty::Dynamic(_, _, _) + | ty::Closure(_, _) + | ty::Coroutine(_, _) + | ty::CoroutineWitness(..) + | ty::Never + | ty::Alias(..) + | ty::Param(_) + | ty::Bound(_, _) + | ty::Error(_) + | ty::Infer(_) + | ty::Placeholder(_) => {} + } + } + + fn assemble_candidate_for_pointer_like( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + candidates: &mut SelectionCandidateSet<'tcx>, + ) { + // The regions of a type don't affect the size of the type + let tcx = self.tcx(); + let self_ty = tcx.instantiate_bound_regions_with_erased(obligation.predicate.self_ty()); + // We should erase regions from both the param-env and type, since both + // may have infer regions. Specifically, after canonicalizing and instantiating, + // early bound regions turn into region vars in both the new and old solver. + let key = tcx.erase_regions(obligation.param_env.and(self_ty)); + // But if there are inference variables, we have to wait until it's resolved. + if key.has_non_region_infer() { + candidates.ambiguous = true; + return; + } + + if let Ok(layout) = tcx.layout_of(key) + && layout.layout.is_pointer_like(&tcx.data_layout) + { + candidates.vec.push(BuiltinCandidate { has_nested: false }); + } + } + + fn assemble_candidates_for_fn_ptr_trait( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + candidates: &mut SelectionCandidateSet<'tcx>, + ) { + let self_ty = self.infcx.shallow_resolve(obligation.self_ty()); + + match self_ty.skip_binder().kind() { + ty::FnPtr(_) => candidates.vec.push(BuiltinCandidate { has_nested: false }), + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Adt(..) + | ty::Foreign(..) + | ty::Str + | ty::Array(..) + | ty::Slice(_) + | ty::RawPtr(_) + | ty::Ref(..) + | ty::FnDef(..) + | ty::Placeholder(..) + | ty::Dynamic(..) + | ty::Closure(..) + | ty::Coroutine(..) + | ty::CoroutineWitness(..) + | ty::Never + | ty::Tuple(..) + | ty::Alias(..) + | ty::Param(..) + | ty::Bound(..) + | ty::Error(_) + | ty::Infer( + ty::InferTy::IntVar(_) + | ty::InferTy::FloatVar(_) + | ty::InferTy::FreshIntTy(_) + | ty::InferTy::FreshFloatTy(_), + ) => {} + ty::Infer(ty::InferTy::TyVar(_) | ty::InferTy::FreshTy(_)) => { + candidates.ambiguous = true; + } + } + } +} diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs new file mode 100644 index 00000000000..e20bb06d777 --- /dev/null +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -0,0 +1,1370 @@ +//! Confirmation. +//! +//! Confirmation unifies the output type parameters of the trait +//! with the values found in the obligation, possibly yielding a +//! type error. See the [rustc dev guide] for more details. +//! +//! [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::BoundRegionConversionTime::HigherRankedType; +use rustc_infer::infer::{DefineOpaqueTypes, InferOk}; +use rustc_middle::traits::{BuiltinImplSource, SelectionOutputTypeParameterMismatch}; +use rustc_middle::ty::{ + self, GenericArgs, GenericArgsRef, GenericParamDefKind, ToPolyTraitRef, ToPredicate, + TraitPredicate, Ty, TyCtxt, TypeVisitableExt, +}; +use rustc_span::def_id::DefId; + +use crate::traits::project::{normalize_with_depth, normalize_with_depth_to}; +use crate::traits::util::{self, closure_trait_ref_and_return_type}; +use crate::traits::vtable::{ + count_own_vtable_entries, prepare_vtable_segments, vtable_trait_first_method_offset, + VtblSegment, +}; +use crate::traits::{ + BuiltinDerivedObligation, ImplDerivedObligation, ImplDerivedObligationCause, ImplSource, + ImplSourceUserDefinedData, Normalized, Obligation, ObligationCause, + OutputTypeParameterMismatch, PolyTraitObligation, PredicateObligation, Selection, + SelectionError, TraitNotObjectSafe, Unimplemented, +}; + +use super::BuiltinImplConditions; +use super::SelectionCandidate::{self, *}; +use super::SelectionContext; + +use std::iter; +use std::ops::ControlFlow; + +impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { + #[instrument(level = "debug", skip(self))] + pub(super) fn confirm_candidate( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + candidate: SelectionCandidate<'tcx>, + ) -> Result<Selection<'tcx>, SelectionError<'tcx>> { + let mut impl_src = match candidate { + BuiltinCandidate { has_nested } => { + let data = self.confirm_builtin_candidate(obligation, has_nested); + ImplSource::Builtin(BuiltinImplSource::Misc, data) + } + + TransmutabilityCandidate => { + let data = self.confirm_transmutability_candidate(obligation)?; + ImplSource::Builtin(BuiltinImplSource::Misc, data) + } + + ParamCandidate(param) => { + let obligations = + self.confirm_param_candidate(obligation, param.map_bound(|t| t.trait_ref)); + ImplSource::Param(obligations) + } + + ImplCandidate(impl_def_id) => { + ImplSource::UserDefined(self.confirm_impl_candidate(obligation, impl_def_id)) + } + + AutoImplCandidate => { + let data = self.confirm_auto_impl_candidate(obligation)?; + ImplSource::Builtin(BuiltinImplSource::Misc, data) + } + + ProjectionCandidate(idx) => { + let obligations = self.confirm_projection_candidate(obligation, idx)?; + ImplSource::Param(obligations) + } + + ObjectCandidate(idx) => self.confirm_object_candidate(obligation, idx)?, + + ClosureCandidate { .. } => { + let vtable_closure = self.confirm_closure_candidate(obligation)?; + ImplSource::Builtin(BuiltinImplSource::Misc, vtable_closure) + } + + CoroutineCandidate => { + let vtable_coroutine = self.confirm_coroutine_candidate(obligation)?; + ImplSource::Builtin(BuiltinImplSource::Misc, vtable_coroutine) + } + + FutureCandidate => { + let vtable_future = self.confirm_future_candidate(obligation)?; + ImplSource::Builtin(BuiltinImplSource::Misc, vtable_future) + } + + IteratorCandidate => { + let vtable_iterator = self.confirm_iterator_candidate(obligation)?; + ImplSource::Builtin(BuiltinImplSource::Misc, vtable_iterator) + } + + AsyncIteratorCandidate => { + let vtable_iterator = self.confirm_async_iterator_candidate(obligation)?; + ImplSource::Builtin(BuiltinImplSource::Misc, vtable_iterator) + } + + FnPointerCandidate { fn_host_effect } => { + let data = self.confirm_fn_pointer_candidate(obligation, fn_host_effect)?; + ImplSource::Builtin(BuiltinImplSource::Misc, data) + } + + TraitAliasCandidate => { + let data = self.confirm_trait_alias_candidate(obligation); + ImplSource::Builtin(BuiltinImplSource::Misc, 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::Builtin(BuiltinImplSource::Misc, Vec::new()) + } + + BuiltinUnsizeCandidate => self.confirm_builtin_unsize_candidate(obligation)?, + + TraitUpcastingUnsizeCandidate(idx) => { + self.confirm_trait_upcasting_unsize_candidate(obligation, idx)? + } + + ConstDestructCandidate(def_id) => { + let data = self.confirm_const_destruct_candidate(obligation, def_id)?; + ImplSource::Builtin(BuiltinImplSource::Misc, data) + } + }; + + // The obligations returned by confirmation are recursively evaluated + // so we need to make sure they have the correct depth. + for subobligation in impl_src.borrow_nested_obligations_mut() { + subobligation.set_depth_from_parent(obligation.recursion_depth); + } + + Ok(impl_src) + } + + fn confirm_projection_candidate( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + idx: usize, + ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> { + let tcx = self.tcx(); + + let trait_predicate = self.infcx.shallow_resolve(obligation.predicate); + let placeholder_trait_predicate = + self.infcx.instantiate_binder_with_placeholders(trait_predicate).trait_ref; + let placeholder_self_ty = placeholder_trait_predicate.self_ty(); + let placeholder_trait_predicate = ty::Binder::dummy(placeholder_trait_predicate); + let (def_id, args) = match *placeholder_self_ty.kind() { + // Excluding IATs and type aliases here as they don't have meaningful item bounds. + ty::Alias(ty::Projection | ty::Opaque, ty::AliasTy { def_id, args, .. }) => { + (def_id, args) + } + _ => bug!("projection candidate for unexpected type: {:?}", placeholder_self_ty), + }; + + let candidate_predicate = + tcx.item_bounds(def_id).map_bound(|i| i[idx]).instantiate(tcx, args); + let candidate = candidate_predicate + .as_trait_clause() + .expect("projection candidate is not a trait predicate") + .map_bound(|t| t.trait_ref); + let mut obligations = Vec::new(); + let candidate = normalize_with_depth_to( + self, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + candidate, + &mut obligations, + ); + + obligations.extend(self.infcx.commit_if_ok(|_| { + self.infcx + .at(&obligation.cause, obligation.param_env) + .sup(DefineOpaqueTypes::No, placeholder_trait_predicate, candidate) + .map(|InferOk { obligations, .. }| obligations) + .map_err(|_| Unimplemented) + })?); + + if let ty::Alias(ty::Projection, ..) = placeholder_self_ty.kind() { + let predicates = tcx.predicates_of(def_id).instantiate_own(tcx, args); + for (predicate, _) in predicates { + let normalized = normalize_with_depth_to( + self, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + predicate, + &mut obligations, + ); + obligations.push(Obligation::with_depth( + self.tcx(), + obligation.cause.clone(), + obligation.recursion_depth + 1, + obligation.param_env, + normalized, + )); + } + } + + Ok(obligations) + } + + fn confirm_param_candidate( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + param: ty::PolyTraitRef<'tcx>, + ) -> Vec<PredicateObligation<'tcx>> { + debug!(?obligation, ?param, "confirm_param_candidate"); + + // During evaluation, we already checked that this + // where-clause trait-ref could be unified with the obligation + // trait-ref. Repeat that unification now without any + // transactional boundary; it should not fail. + match self.match_where_clause_trait_ref(obligation, param) { + Ok(obligations) => obligations, + Err(()) => { + bug!( + "Where clause `{:?}` was applicable to `{:?}` but now is not", + param, + obligation + ); + } + } + } + + fn confirm_builtin_candidate( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + has_nested: bool, + ) -> Vec<PredicateObligation<'tcx>> { + debug!(?obligation, ?has_nested, "confirm_builtin_candidate"); + + let lang_items = self.tcx().lang_items(); + let obligations = if has_nested { + let trait_def = obligation.predicate.def_id(); + let conditions = if Some(trait_def) == lang_items.sized_trait() { + self.sized_conditions(obligation) + } else if Some(trait_def) == lang_items.copy_trait() { + self.copy_clone_conditions(obligation) + } else if Some(trait_def) == lang_items.clone_trait() { + self.copy_clone_conditions(obligation) + } else { + bug!("unexpected builtin trait {:?}", trait_def) + }; + let BuiltinImplConditions::Where(nested) = conditions else { + bug!("obligation {:?} had matched a builtin impl but now doesn't", obligation); + }; + + let cause = obligation.derived_cause(BuiltinDerivedObligation); + self.collect_predicates_for_types( + obligation.param_env, + cause, + obligation.recursion_depth + 1, + trait_def, + nested, + ) + } else { + vec![] + }; + + debug!(?obligations); + + obligations + } + + #[instrument(level = "debug", skip(self))] + fn confirm_transmutability_candidate( + &mut self, + 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.args.type_at(2); + let assume_const = predicate.trait_ref.args.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 + // care about other regions. Erasing late-bound regions is equivalent + // to instantiating the binder with placeholders then erasing those + // placeholder regions. + let predicate = self + .tcx() + .erase_regions(self.tcx().instantiate_bound_regions_with_erased(obligation.predicate)); + + let Some(assume) = rustc_transmute::Assume::from_const( + self.infcx.tcx, + obligation.param_env, + predicate.trait_ref.args.const_at(3), + ) else { + return Err(Unimplemented); + }; + + let dst = predicate.trait_ref.args.type_at(0); + let src = predicate.trait_ref.args.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, src }, + predicate.trait_ref.args.type_at(2), + assume, + ); + + 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. + /// The idea is that the impl applies to `X : Foo` if the following conditions are met: + /// + /// 1. For each constituent type `Y` in `X`, `Y : Foo` holds + /// 2. For each where-clause `C` declared on `Foo`, `[Self => X] C` holds. + fn confirm_auto_impl_candidate( + &mut self, + 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)?; + Ok(self.vtable_auto_impl(obligation, obligation.predicate.def_id(), types)) + } + + /// See `confirm_auto_impl_candidate`. + fn vtable_auto_impl( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + trait_def_id: DefId, + nested: ty::Binder<'tcx, Vec<Ty<'tcx>>>, + ) -> Vec<PredicateObligation<'tcx>> { + debug!(?nested, "vtable_auto_impl"); + ensure_sufficient_stack(|| { + let cause = obligation.derived_cause(BuiltinDerivedObligation); + + let poly_trait_ref = obligation.predicate.to_poly_trait_ref(); + let trait_ref = self.infcx.instantiate_binder_with_placeholders(poly_trait_ref); + let trait_obligations: Vec<PredicateObligation<'_>> = self.impl_or_trait_obligations( + &cause, + obligation.recursion_depth + 1, + obligation.param_env, + trait_def_id, + trait_ref.args, + obligation.predicate, + ); + + let mut obligations = self.collect_predicates_for_types( + obligation.param_env, + cause, + obligation.recursion_depth + 1, + trait_def_id, + nested, + ); + + // Adds the predicates from the trait. Note that this contains a `Self: Trait` + // predicate as usual. It won't have any effect since auto traits are coinductive. + obligations.extend(trait_obligations); + + debug!(?obligations, "vtable_auto_impl"); + + obligations + }) + } + + fn confirm_impl_candidate( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + impl_def_id: DefId, + ) -> ImplSourceUserDefinedData<'tcx, PredicateObligation<'tcx>> { + debug!(?obligation, ?impl_def_id, "confirm_impl_candidate"); + + // First, create the substitutions by matching the impl again, + // this time not in a probe. + let args = self.rematch_impl(impl_def_id, obligation); + debug!(?args, "impl args"); + ensure_sufficient_stack(|| { + self.vtable_impl( + impl_def_id, + args, + &obligation.cause, + obligation.recursion_depth + 1, + obligation.param_env, + obligation.predicate, + ) + }) + } + + fn vtable_impl( + &mut self, + impl_def_id: DefId, + args: Normalized<'tcx, GenericArgsRef<'tcx>>, + cause: &ObligationCause<'tcx>, + recursion_depth: usize, + param_env: ty::ParamEnv<'tcx>, + parent_trait_pred: ty::Binder<'tcx, ty::TraitPredicate<'tcx>>, + ) -> ImplSourceUserDefinedData<'tcx, PredicateObligation<'tcx>> { + debug!(?impl_def_id, ?args, ?recursion_depth, "vtable_impl"); + + let mut impl_obligations = self.impl_or_trait_obligations( + cause, + recursion_depth, + param_env, + impl_def_id, + args.value, + parent_trait_pred, + ); + + debug!(?impl_obligations, "vtable_impl"); + + // Because of RFC447, the impl-trait-ref and obligations + // are sufficient to determine the impl args, without + // relying on projections in the impl-trait-ref. + // + // e.g., `impl<U: Tr, V: Iterator<Item=U>> Foo<<U as Tr>::T> for V` + impl_obligations.extend(args.obligations); + + ImplSourceUserDefinedData { impl_def_id, args: args.value, nested: impl_obligations } + } + + fn confirm_object_candidate( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + index: usize, + ) -> Result<ImplSource<'tcx, PredicateObligation<'tcx>>, SelectionError<'tcx>> { + let tcx = self.tcx(); + debug!(?obligation, ?index, "confirm_object_candidate"); + + let trait_predicate = self.infcx.instantiate_binder_with_placeholders(obligation.predicate); + let self_ty = self.infcx.shallow_resolve(trait_predicate.self_ty()); + let obligation_trait_ref = ty::Binder::dummy(trait_predicate.trait_ref); + let ty::Dynamic(data, ..) = *self_ty.kind() else { + span_bug!(obligation.cause.span, "object candidate with non-object"); + }; + + let object_trait_ref = data.principal().unwrap_or_else(|| { + span_bug!(obligation.cause.span, "object candidate with no principal") + }); + let object_trait_ref = self.infcx.instantiate_binder_with_fresh_vars( + obligation.cause.span, + HigherRankedType, + object_trait_ref, + ); + let object_trait_ref = object_trait_ref.with_self_ty(self.tcx(), self_ty); + + let mut nested = vec![]; + + let mut supertraits = util::supertraits(tcx, ty::Binder::dummy(object_trait_ref)); + let unnormalized_upcast_trait_ref = + supertraits.nth(index).expect("supertraits iterator no longer has as many elements"); + + let upcast_trait_ref = normalize_with_depth_to( + self, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + unnormalized_upcast_trait_ref, + &mut nested, + ); + + nested.extend(self.infcx.commit_if_ok(|_| { + self.infcx + .at(&obligation.cause, obligation.param_env) + .sup(DefineOpaqueTypes::No, obligation_trait_ref, upcast_trait_ref) + .map(|InferOk { obligations, .. }| obligations) + .map_err(|_| Unimplemented) + })?); + + // Check supertraits hold. This is so that their associated type bounds + // will be checked in the code below. + for super_trait in tcx + .super_predicates_of(trait_predicate.def_id()) + .instantiate(tcx, trait_predicate.trait_ref.args) + .predicates + .into_iter() + { + let normalized_super_trait = normalize_with_depth_to( + self, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + super_trait, + &mut nested, + ); + nested.push(obligation.with(tcx, normalized_super_trait)); + } + + let assoc_types: Vec<_> = tcx + .associated_items(trait_predicate.def_id()) + .in_definition_order() + // Associated types that require `Self: Sized` do not show up in the built-in + // implementation of `Trait for dyn Trait`, and can be dropped here. + .filter(|item| !tcx.generics_require_sized_self(item.def_id)) + .filter_map( + |item| if item.kind == ty::AssocKind::Type { Some(item.def_id) } else { None }, + ) + .collect(); + + for assoc_type in assoc_types { + let defs: &ty::Generics = tcx.generics_of(assoc_type); + + if !defs.params.is_empty() && !tcx.features().generic_associated_types_extended { + tcx.dcx().span_delayed_bug( + obligation.cause.span, + "GATs in trait object shouldn't have been considered", + ); + return Err(SelectionError::TraitNotObjectSafe(trait_predicate.trait_ref.def_id)); + } + + // This maybe belongs in wf, but that can't (doesn't) handle + // higher-ranked things. + // Prevent, e.g., `dyn Iterator<Item = str>`. + for bound in self.tcx().item_bounds(assoc_type).transpose_iter() { + let subst_bound = if defs.count() == 0 { + bound.instantiate(tcx, trait_predicate.trait_ref.args) + } else { + let mut args = smallvec::SmallVec::with_capacity(defs.count()); + args.extend(trait_predicate.trait_ref.args.iter()); + let mut bound_vars: smallvec::SmallVec<[ty::BoundVariableKind; 8]> = + smallvec::SmallVec::with_capacity( + bound.skip_binder().kind().bound_vars().len() + defs.count(), + ); + bound_vars.extend(bound.skip_binder().kind().bound_vars().into_iter()); + GenericArgs::fill_single(&mut args, defs, &mut |param, _| match param.kind { + GenericParamDefKind::Type { .. } => { + let kind = ty::BoundTyKind::Param(param.def_id, param.name); + let bound_var = ty::BoundVariableKind::Ty(kind); + bound_vars.push(bound_var); + Ty::new_bound( + tcx, + ty::INNERMOST, + ty::BoundTy { + var: ty::BoundVar::from_usize(bound_vars.len() - 1), + kind, + }, + ) + .into() + } + GenericParamDefKind::Lifetime => { + let kind = ty::BoundRegionKind::BrNamed(param.def_id, param.name); + let bound_var = ty::BoundVariableKind::Region(kind); + bound_vars.push(bound_var); + ty::Region::new_bound( + tcx, + ty::INNERMOST, + ty::BoundRegion { + var: ty::BoundVar::from_usize(bound_vars.len() - 1), + kind, + }, + ) + .into() + } + GenericParamDefKind::Const { .. } => { + let bound_var = ty::BoundVariableKind::Const; + bound_vars.push(bound_var); + 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"), + ) + .into() + } + }); + let bound_vars = tcx.mk_bound_variable_kinds(&bound_vars); + let assoc_ty_args = tcx.mk_args(&args); + let bound = + bound.map_bound(|b| b.kind().skip_binder()).instantiate(tcx, assoc_ty_args); + ty::Binder::bind_with_vars(bound, bound_vars).to_predicate(tcx) + }; + let normalized_bound = normalize_with_depth_to( + self, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + subst_bound, + &mut nested, + ); + nested.push(obligation.with(tcx, normalized_bound)); + } + } + + debug!(?nested, "object nested obligations"); + + let vtable_base = vtable_trait_first_method_offset( + tcx, + (unnormalized_upcast_trait_ref, ty::Binder::dummy(object_trait_ref)), + ); + + Ok(ImplSource::Builtin(BuiltinImplSource::Object { vtable_base: vtable_base }, nested)) + } + + fn confirm_fn_pointer_candidate( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + fn_host_effect: ty::Const<'tcx>, + ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> { + debug!(?obligation, "confirm_fn_pointer_candidate"); + + let tcx = self.tcx(); + + let Some(self_ty) = self.infcx.shallow_resolve(obligation.self_ty().no_bound_vars()) else { + // FIXME: Ideally we'd support `for<'a> fn(&'a ()): Fn(&'a ())`, + // but we do not currently. Luckily, such a bound is not + // particularly useful, so we don't expect users to write + // them often. + return Err(SelectionError::Unimplemented); + }; + + let sig = self_ty.fn_sig(tcx); + let trait_ref = closure_trait_ref_and_return_type( + tcx, + obligation.predicate.def_id(), + self_ty, + sig, + util::TupleArgumentsFlag::Yes, + fn_host_effect, + ) + .map_bound(|(trait_ref, _)| trait_ref); + + let mut nested = self.confirm_poly_trait_refs(obligation, trait_ref)?; + let cause = obligation.derived_cause(BuiltinDerivedObligation); + + // Confirm the `type Output: Sized;` bound that is present on `FnOnce` + let output_ty = self.infcx.instantiate_binder_with_placeholders(sig.output()); + let output_ty = normalize_with_depth_to( + self, + obligation.param_env, + cause.clone(), + obligation.recursion_depth, + output_ty, + &mut nested, + ); + 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(nested) + } + + fn confirm_trait_alias_candidate( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + ) -> Vec<PredicateObligation<'tcx>> { + debug!(?obligation, "confirm_trait_alias_candidate"); + + let predicate = self.infcx.instantiate_binder_with_placeholders(obligation.predicate); + let trait_ref = predicate.trait_ref; + let trait_def_id = trait_ref.def_id; + let args = trait_ref.args; + + let trait_obligations = self.impl_or_trait_obligations( + &obligation.cause, + obligation.recursion_depth, + obligation.param_env, + trait_def_id, + args, + obligation.predicate, + ); + + debug!(?trait_def_id, ?trait_obligations, "trait alias obligations"); + + trait_obligations + } + + fn confirm_coroutine_candidate( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> { + // Okay to skip binder because the args on coroutine types never + // touch bound regions, they just capture the in-scope + // type/region parameters. + let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder()); + let ty::Coroutine(coroutine_def_id, args) = *self_ty.kind() else { + bug!("closure candidate for non-closure {:?}", obligation); + }; + + debug!(?obligation, ?coroutine_def_id, ?args, "confirm_coroutine_candidate"); + + let coroutine_sig = args.as_coroutine().sig(); + + // NOTE: The self-type is a coroutine type and hence is + // in fact unparameterized (or at least does not reference any + // regions bound in the obligation). + let self_ty = obligation + .predicate + .self_ty() + .no_bound_vars() + .expect("unboxed closure type should not capture bound vars from the predicate"); + + let (trait_ref, _, _) = super::util::coroutine_trait_ref_and_outputs( + self.tcx(), + obligation.predicate.def_id(), + self_ty, + coroutine_sig, + ); + + let nested = self.confirm_poly_trait_refs(obligation, ty::Binder::dummy(trait_ref))?; + debug!(?trait_ref, ?nested, "coroutine candidate obligations"); + + Ok(nested) + } + + fn confirm_future_candidate( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> { + // Okay to skip binder because the args on coroutine types never + // touch bound regions, they just capture the in-scope + // type/region parameters. + let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder()); + let ty::Coroutine(coroutine_def_id, args) = *self_ty.kind() else { + bug!("closure candidate for non-closure {:?}", obligation); + }; + + debug!(?obligation, ?coroutine_def_id, ?args, "confirm_future_candidate"); + + let coroutine_sig = args.as_coroutine().sig(); + + let (trait_ref, _) = super::util::future_trait_ref_and_outputs( + self.tcx(), + obligation.predicate.def_id(), + obligation.predicate.no_bound_vars().expect("future has no bound vars").self_ty(), + coroutine_sig, + ); + + let nested = self.confirm_poly_trait_refs(obligation, ty::Binder::dummy(trait_ref))?; + debug!(?trait_ref, ?nested, "future candidate obligations"); + + Ok(nested) + } + + fn confirm_iterator_candidate( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> { + // Okay to skip binder because the args on coroutine types never + // touch bound regions, they just capture the in-scope + // type/region parameters. + let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder()); + let ty::Coroutine(coroutine_def_id, args) = *self_ty.kind() else { + bug!("closure candidate for non-closure {:?}", obligation); + }; + + debug!(?obligation, ?coroutine_def_id, ?args, "confirm_iterator_candidate"); + + let gen_sig = args.as_coroutine().sig(); + + let (trait_ref, _) = super::util::iterator_trait_ref_and_outputs( + self.tcx(), + obligation.predicate.def_id(), + obligation.predicate.no_bound_vars().expect("iterator has no bound vars").self_ty(), + gen_sig, + ); + + let nested = self.confirm_poly_trait_refs(obligation, ty::Binder::dummy(trait_ref))?; + debug!(?trait_ref, ?nested, "iterator candidate obligations"); + + Ok(nested) + } + + fn confirm_async_iterator_candidate( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> { + // Okay to skip binder because the args on coroutine types never + // touch bound regions, they just capture the in-scope + // type/region parameters. + let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder()); + let ty::Coroutine(coroutine_def_id, args) = *self_ty.kind() else { + bug!("closure candidate for non-closure {:?}", obligation); + }; + + debug!(?obligation, ?coroutine_def_id, ?args, "confirm_async_iterator_candidate"); + + let gen_sig = args.as_coroutine().sig(); + + let (trait_ref, _) = super::util::async_iterator_trait_ref_and_outputs( + self.tcx(), + obligation.predicate.def_id(), + obligation.predicate.no_bound_vars().expect("iterator has no bound vars").self_ty(), + gen_sig, + ); + + let nested = self.confirm_poly_trait_refs(obligation, ty::Binder::dummy(trait_ref))?; + debug!(?trait_ref, ?nested, "iterator candidate obligations"); + + Ok(nested) + } + + #[instrument(skip(self), level = "debug")] + fn confirm_closure_candidate( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> { + // Okay to skip binder because the args on closure types never + // touch bound regions, they just capture the in-scope + // type/region parameters. + let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder()); + let ty::Closure(closure_def_id, args) = *self_ty.kind() else { + bug!("closure candidate for non-closure {:?}", obligation); + }; + + let trait_ref = + self.closure_trait_ref_unnormalized(obligation, args, self.tcx().consts.true_); + let nested = self.confirm_poly_trait_refs(obligation, trait_ref)?; + + debug!(?closure_def_id, ?trait_ref, ?nested, "confirm closure candidate obligations"); + + Ok(nested) + } + + /// In the case of closure types and fn pointers, + /// we currently treat the input type parameters on the trait as + /// outputs. This means that when we have a match we have only + /// considered the self type, so we have to go back and make sure + /// to relate the argument types too. This is kind of wrong, but + /// since we control the full set of impls, also not that wrong, + /// and it DOES yield better error messages (since we don't report + /// errors as if there is no applicable impl, but rather report + /// errors are about mismatched argument types. + /// + /// Here is an example. Imagine we have a closure expression + /// and we desugared it so that the type of the expression is + /// `Closure`, and `Closure` expects `i32` as argument. Then it + /// is "as if" the compiler generated this impl: + /// ```ignore (illustrative) + /// impl Fn(i32) for Closure { ... } + /// ``` + /// Now imagine our obligation is `Closure: Fn(usize)`. So far + /// we have matched the self type `Closure`. At this point we'll + /// compare the `i32` to `usize` and generate an error. + /// + /// Note that this checking occurs *after* the impl has selected, + /// because these output type parameters should not affect the + /// selection of the impl. Therefore, if there is a mismatch, we + /// report an error to the user. + #[instrument(skip(self), level = "trace")] + fn confirm_poly_trait_refs( + &mut self, + 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(); + // Normalize the obligation and expected trait refs together, because why not + let Normalized { obligations: nested, value: (obligation_trait_ref, expected_trait_ref) } = + ensure_sufficient_stack(|| { + normalize_with_depth( + self, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + (obligation_trait_ref, self_ty_trait_ref), + ) + }); + + // needed to define opaque types for tests/ui/type-alias-impl-trait/assoc-projection-ice.rs + self.infcx + .at(&obligation.cause, obligation.param_env) + .sup(DefineOpaqueTypes::Yes, obligation_trait_ref, expected_trait_ref) + .map(|InferOk { mut obligations, .. }| { + obligations.extend(nested); + obligations + }) + .map_err(|terr| { + OutputTypeParameterMismatch(Box::new(SelectionOutputTypeParameterMismatch { + expected_trait_ref: obligation_trait_ref, + found_trait_ref: expected_trait_ref, + terr, + })) + }) + } + + fn confirm_trait_upcasting_unsize_candidate( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + idx: usize, + ) -> Result<ImplSource<'tcx, PredicateObligation<'tcx>>, SelectionError<'tcx>> { + let tcx = self.tcx(); + + // `assemble_candidates_for_unsizing` should ensure there are no late-bound + // regions here. See the comment there for more details. + let predicate = obligation.predicate.no_bound_vars().unwrap(); + let a_ty = self.infcx.shallow_resolve(predicate.self_ty()); + let b_ty = self.infcx.shallow_resolve(predicate.trait_ref.args.type_at(1)); + + let ty::Dynamic(a_data, a_region, ty::Dyn) = *a_ty.kind() else { + bug!("expected `dyn` type in `confirm_trait_upcasting_unsize_candidate`") + }; + let ty::Dynamic(b_data, b_region, ty::Dyn) = *b_ty.kind() else { + bug!("expected `dyn` type in `confirm_trait_upcasting_unsize_candidate`") + }; + + let source_principal = a_data.principal().unwrap().with_self_ty(tcx, a_ty); + let unnormalized_upcast_principal = + util::supertraits(tcx, source_principal).nth(idx).unwrap(); + + let nested = self + .match_upcast_principal( + obligation, + unnormalized_upcast_principal, + a_data, + b_data, + a_region, + b_region, + )? + .expect("did not expect ambiguity during confirmation"); + + let vtable_segment_callback = { + let mut vptr_offset = 0; + move |segment| { + match segment { + VtblSegment::MetadataDSA => { + vptr_offset += TyCtxt::COMMON_VTABLE_ENTRIES.len(); + } + VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => { + vptr_offset += count_own_vtable_entries(tcx, trait_ref); + if trait_ref == unnormalized_upcast_principal { + if emit_vptr { + return ControlFlow::Break(Some(vptr_offset)); + } else { + return ControlFlow::Break(None); + } + } + + if emit_vptr { + vptr_offset += 1; + } + } + } + ControlFlow::Continue(()) + } + }; + + let vtable_vptr_slot = + prepare_vtable_segments(tcx, source_principal, vtable_segment_callback).unwrap(); + + Ok(ImplSource::Builtin(BuiltinImplSource::TraitUpcasting { vtable_vptr_slot }, nested)) + } + + fn confirm_builtin_unsize_candidate( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + ) -> Result<ImplSource<'tcx, PredicateObligation<'tcx>>, SelectionError<'tcx>> { + let tcx = self.tcx(); + + // `assemble_candidates_for_unsizing` should ensure there are no late-bound + // regions here. See the comment there for more details. + let source = self.infcx.shallow_resolve(obligation.self_ty().no_bound_vars().unwrap()); + let target = obligation.predicate.skip_binder().trait_ref.args.type_at(1); + let target = self.infcx.shallow_resolve(target); + debug!(?source, ?target, "confirm_builtin_unsize_candidate"); + + Ok(match (source.kind(), target.kind()) { + // Trait+Kx+'a -> Trait+Ky+'b (auto traits and lifetime subtyping). + (&ty::Dynamic(data_a, r_a, dyn_a), &ty::Dynamic(data_b, r_b, dyn_b)) + if dyn_a == dyn_b => + { + // See `assemble_candidates_for_unsizing` for more info. + // We already checked the compatibility of auto traits within `assemble_candidates_for_unsizing`. + let iter = data_a + .principal() + .map(|b| b.map_bound(ty::ExistentialPredicate::Trait)) + .into_iter() + .chain( + data_a + .projection_bounds() + .map(|b| b.map_bound(ty::ExistentialPredicate::Projection)), + ) + .chain( + data_b + .auto_traits() + .map(ty::ExistentialPredicate::AutoTrait) + .map(ty::Binder::dummy), + ); + let existential_predicates = tcx.mk_poly_existential_predicates_from_iter(iter); + 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. + let InferOk { mut obligations, .. } = self + .infcx + .at(&obligation.cause, obligation.param_env) + .sup(DefineOpaqueTypes::No, target, source_trait) + .map_err(|_| Unimplemented)?; + + // Register one obligation for 'a: 'b. + let outlives = ty::OutlivesPredicate(r_a, r_b); + obligations.push(Obligation::with_depth( + tcx, + obligation.cause.clone(), + obligation.recursion_depth + 1, + obligation.param_env, + obligation.predicate.rebind(outlives), + )); + + ImplSource::Builtin(BuiltinImplSource::Misc, obligations) + } + + // `T` -> `Trait` + (_, &ty::Dynamic(data, r, ty::Dyn)) => { + let mut object_dids = data.auto_traits().chain(data.principal_def_id()); + if let Some(did) = object_dids.find(|did| !tcx.check_is_object_safe(*did)) { + return Err(TraitNotObjectSafe(did)); + } + + let predicate_to_obligation = |predicate| { + Obligation::with_depth( + tcx, + obligation.cause.clone(), + obligation.recursion_depth + 1, + obligation.param_env, + predicate, + ) + }; + + // Create obligations: + // - Casting `T` to `Trait` + // - For all the various builtin bounds attached to the object cast. (In other + // words, if the object type is `Foo + Send`, this would create an obligation for + // the `Send` check.) + // - Projection predicates + let mut nested: Vec<_> = data + .iter() + .map(|predicate| predicate_to_obligation(predicate.with_self_ty(tcx, source))) + .collect(); + + // We can only make objects from sized types. + let tr = ty::TraitRef::from_lang_item( + tcx, + LangItem::Sized, + obligation.cause.span, + [source], + ); + 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(ty::ClauseKind::TypeOutlives(outlives)).to_predicate(tcx), + )); + + ImplSource::Builtin(BuiltinImplSource::Misc, nested) + } + + // `[T; n]` -> `[T]` + (&ty::Array(a, _), &ty::Slice(b)) => { + let InferOk { obligations, .. } = self + .infcx + .at(&obligation.cause, obligation.param_env) + .eq(DefineOpaqueTypes::No, b, a) + .map_err(|_| Unimplemented)?; + + ImplSource::Builtin(BuiltinImplSource::Misc, obligations) + } + + // `Struct<T>` -> `Struct<U>` + (&ty::Adt(def, args_a), &ty::Adt(_, args_b)) => { + let unsizing_params = tcx.unsizing_params_for_adt(def.did()); + if unsizing_params.is_empty() { + return Err(Unimplemented); + } + + let tail_field = def.non_enum_variant().tail(); + let tail_field_ty = tcx.type_of(tail_field.did); + + let mut nested = vec![]; + + // Extract `TailField<T>` and `TailField<U>` from `Struct<T>` and `Struct<U>`, + // normalizing in the process, since `type_of` returns something directly from + // astconv (which means it's un-normalized). + let source_tail = normalize_with_depth_to( + self, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + tail_field_ty.instantiate(tcx, args_a), + &mut nested, + ); + let target_tail = normalize_with_depth_to( + self, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + tail_field_ty.instantiate(tcx, args_b), + &mut nested, + ); + + // Check that the source struct with the target's + // unsizing parameters is equal to the target. + let args = + tcx.mk_args_from_iter(args_a.iter().enumerate().map(|(i, k)| { + if unsizing_params.contains(i as u32) { args_b[i] } else { k } + })); + let new_struct = Ty::new_adt(tcx, def, args); + let InferOk { obligations, .. } = self + .infcx + .at(&obligation.cause, obligation.param_env) + .eq(DefineOpaqueTypes::No, target, new_struct) + .map_err(|_| Unimplemented)?; + nested.extend(obligations); + + // Construct the nested `TailField<T>: Unsize<TailField<U>>` predicate. + let tail_unsize_obligation = obligation.with( + tcx, + ty::TraitRef::new( + tcx, + obligation.predicate.def_id(), + [source_tail, target_tail], + ), + ); + nested.push(tail_unsize_obligation); + + ImplSource::Builtin(BuiltinImplSource::Misc, nested) + } + + // `(.., T)` -> `(.., U)` + (&ty::Tuple(tys_a), &ty::Tuple(tys_b)) => { + assert_eq!(tys_a.len(), tys_b.len()); + + // The last field of the tuple has to exist. + let (&a_last, a_mid) = tys_a.split_last().ok_or(Unimplemented)?; + let &b_last = tys_b.last().unwrap(); + + // Check that the source tuple with the target's + // last element is equal to the target. + let new_tuple = + Ty::new_tup_from_iter(tcx, a_mid.iter().copied().chain(iter::once(b_last))); + let InferOk { mut obligations, .. } = self + .infcx + .at(&obligation.cause, obligation.param_env) + .eq(DefineOpaqueTypes::No, target, new_tuple) + .map_err(|_| Unimplemented)?; + + // Add a nested `T: Unsize<U>` predicate. + let last_unsize_obligation = obligation.with( + tcx, + ty::TraitRef::new(tcx, obligation.predicate.def_id(), [a_last, b_last]), + ); + obligations.push(last_unsize_obligation); + + ImplSource::Builtin(BuiltinImplSource::TupleUnsizing, obligations) + } + + _ => bug!("source: {source}, target: {target}"), + }) + } + + fn confirm_const_destruct_candidate( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + impl_def_id: Option<DefId>, + ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> { + let Some(host_effect_index) = + self.tcx().generics_of(obligation.predicate.def_id()).host_effect_index + else { + bug!() + }; + let host_effect_param: ty::GenericArg<'tcx> = + obligation.predicate.skip_binder().trait_ref.args.const_at(host_effect_index).into(); + + let drop_trait = self.tcx().require_lang_item(LangItem::Drop, None); + + let tcx = self.tcx(); + let self_ty = self.infcx.shallow_resolve(obligation.self_ty()); + + let mut nested = vec![]; + let cause = obligation.derived_cause(BuiltinDerivedObligation); + + // If we have a custom `impl const Drop`, then + // first check it like a regular impl candidate. + // This is copied from confirm_impl_candidate but remaps the predicate to `~const Drop` beforehand. + if let Some(impl_def_id) = impl_def_id { + let mut new_obligation = obligation.clone(); + new_obligation.predicate = new_obligation.predicate.map_bound(|mut trait_pred| { + trait_pred.trait_ref.def_id = drop_trait; + trait_pred + }); + let args = self.rematch_impl(impl_def_id, &new_obligation); + debug!(?args, "impl args"); + + let cause = obligation.derived_cause(|derived| { + ImplDerivedObligation(Box::new(ImplDerivedObligationCause { + derived, + impl_or_alias_def_id: impl_def_id, + impl_def_predicate_index: None, + span: obligation.cause.span, + })) + }); + let obligations = ensure_sufficient_stack(|| { + self.vtable_impl( + impl_def_id, + args, + &cause, + new_obligation.recursion_depth + 1, + new_obligation.param_env, + obligation.predicate, + ) + }); + nested.extend(obligations.nested); + } + + // We want to confirm the ADT's fields if we have an ADT + let mut stack = match *self_ty.skip_binder().kind() { + ty::Adt(def, args) => def.all_fields().map(|f| f.ty(tcx, args)).collect(), + _ => vec![self_ty.skip_binder()], + }; + + while let Some(nested_ty) = stack.pop() { + match *nested_ty.kind() { + // We know these types are trivially drop + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Infer(ty::IntVar(_)) + | ty::Infer(ty::FloatVar(_)) + | ty::Str + | ty::RawPtr(_) + | ty::Ref(..) + | ty::FnDef(..) + | ty::FnPtr(_) + | ty::Never + | ty::Foreign(_) => {} + + // `ManuallyDrop` is trivially drop + ty::Adt(def, _) if Some(def.did()) == tcx.lang_items().manually_drop() => {} + + // These types are built-in, so we can fast-track by registering + // nested predicates for their constituent type(s) + ty::Array(ty, _) | ty::Slice(ty) => { + stack.push(ty); + } + ty::Tuple(tys) => { + stack.extend(tys.iter()); + } + ty::Closure(_, args) => { + stack.push(args.as_closure().tupled_upvars_ty()); + } + ty::Coroutine(_, args) => { + let coroutine = args.as_coroutine(); + stack.extend([coroutine.tupled_upvars_ty(), coroutine.witness()]); + } + ty::CoroutineWitness(def_id, args) => { + let tcx = self.tcx(); + stack.extend(tcx.coroutine_hidden_types(def_id).map(|bty| { + let ty = bty.instantiate(tcx, args); + debug_assert!(!ty.has_bound_regions()); + ty + })) + } + + // If we have a projection type, make sure to normalize it so we replace it + // with a fresh infer variable + ty::Alias(ty::Projection | ty::Inherent, ..) => { + let predicate = normalize_with_depth_to( + self, + obligation.param_env, + cause.clone(), + obligation.recursion_depth + 1, + self_ty.rebind(ty::TraitPredicate { + trait_ref: ty::TraitRef::from_lang_item( + self.tcx(), + LangItem::Destruct, + cause.span, + [nested_ty.into(), host_effect_param], + ), + polarity: ty::ImplPolarity::Positive, + }), + &mut nested, + ); + + nested.push(Obligation::with_depth( + tcx, + cause.clone(), + obligation.recursion_depth + 1, + obligation.param_env, + predicate, + )); + } + + // If we have any other type (e.g. an ADT), just register a nested obligation + // since it's either not `const Drop` (and we raise an error during selection), + // or it's an ADT (and we need to check for a custom impl during selection) + _ => { + let predicate = self_ty.rebind(ty::TraitPredicate { + trait_ref: ty::TraitRef::from_lang_item( + self.tcx(), + LangItem::Destruct, + cause.span, + [nested_ty.into(), host_effect_param], + ), + polarity: ty::ImplPolarity::Positive, + }); + + nested.push(Obligation::with_depth( + tcx, + cause.clone(), + obligation.recursion_depth + 1, + obligation.param_env, + predicate, + )); + } + } + } + + Ok(nested) + } +} diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs new file mode 100644 index 00000000000..c45925295ee --- /dev/null +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -0,0 +1,3116 @@ +//! Candidate selection. See the [rustc dev guide] for more information on how this works. +//! +//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/resolution.html#selection + +use self::EvaluationResult::*; +use self::SelectionCandidate::*; + +use super::coherence::{self, Conflict}; +use super::const_evaluatable; +use super::project; +use super::project::normalize_with_depth_to; +use super::project::ProjectionTyObligation; +use super::util; +use super::util::closure_trait_ref_and_return_type; +use super::wf; +use super::{ + ErrorReporting, ImplDerivedObligation, ImplDerivedObligationCause, Normalized, Obligation, + 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; +use crate::traits::project::ProjectionCacheKeyExt; +use crate::traits::ProjectionCacheKey; +use crate::traits::Unimplemented; +use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; +use rustc_data_structures::stack::ensure_sufficient_stack; +use rustc_errors::Diagnostic; +use rustc_hir as hir; +use rustc_hir::def_id::DefId; +use rustc_infer::infer::BoundRegionConversionTime; +use rustc_infer::infer::DefineOpaqueTypes; +use rustc_infer::traits::TraitObligation; +use rustc_middle::dep_graph::dep_kinds; +use rustc_middle::dep_graph::DepNodeIndex; +use rustc_middle::mir::interpret::ErrorHandled; +use rustc_middle::ty::_match::MatchAgainstFreshVars; +use rustc_middle::ty::abstract_const::NotConstEvaluatable; +use rustc_middle::ty::fold::BottomUpFolder; +use rustc_middle::ty::relate::TypeRelation; +use rustc_middle::ty::GenericArgsRef; +use rustc_middle::ty::{self, EarlyBinder, PolyProjectionPredicate, ToPolyTraitRef, ToPredicate}; +use rustc_middle::ty::{Ty, TyCtxt, TypeFoldable, TypeVisitableExt}; +use rustc_span::symbol::sym; +use rustc_span::Symbol; + +use std::cell::{Cell, RefCell}; +use std::cmp; +use std::fmt::{self, Display}; +use std::iter; + +pub use rustc_middle::traits::select::*; +use rustc_middle::ty::print::with_no_trimmed_paths; + +mod candidate_assembly; +mod confirmation; + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub enum IntercrateAmbiguityCause<'tcx> { + DownstreamCrate { trait_ref: ty::TraitRef<'tcx>, self_ty: Option<Ty<'tcx>> }, + UpstreamCrateUpdate { trait_ref: ty::TraitRef<'tcx>, self_ty: Option<Ty<'tcx>> }, + ReservationImpl { message: Symbol }, +} + +impl<'tcx> IntercrateAmbiguityCause<'tcx> { + /// Emits notes when the overlap is caused by complex intercrate ambiguities. + /// See #23980 for details. + pub fn add_intercrate_ambiguity_hint(&self, err: &mut Diagnostic) { + err.note(self.intercrate_ambiguity_hint()); + } + + pub fn intercrate_ambiguity_hint(&self) -> String { + with_no_trimmed_paths!(match self { + IntercrateAmbiguityCause::DownstreamCrate { trait_ref, self_ty } => { + format!( + "downstream crates may implement trait `{trait_desc}`{self_desc}", + trait_desc = trait_ref.print_trait_sugared(), + self_desc = if let Some(self_ty) = self_ty { + format!(" for type `{self_ty}`") + } else { + String::new() + } + ) + } + IntercrateAmbiguityCause::UpstreamCrateUpdate { trait_ref, self_ty } => { + format!( + "upstream crates may add a new impl of trait `{trait_desc}`{self_desc} \ + in future versions", + trait_desc = trait_ref.print_trait_sugared(), + self_desc = if let Some(self_ty) = self_ty { + format!(" for type `{self_ty}`") + } else { + String::new() + } + ) + } + IntercrateAmbiguityCause::ReservationImpl { message } => message.to_string(), + }) + } +} + +pub struct SelectionContext<'cx, 'tcx> { + pub infcx: &'cx InferCtxt<'tcx>, + + /// Freshener used specifically for entries on the obligation + /// stack. This ensures that all entries on the stack at one time + /// will have the same set of placeholder entries, which is + /// important for checking for trait bounds that recursively + /// require themselves. + freshener: TypeFreshener<'cx, 'tcx>, + + /// If `intercrate` is set, we remember predicates which were + /// considered ambiguous because of impls potentially added in other crates. + /// This is used in coherence to give improved diagnostics. + /// We don't do his until we detect a coherence error because it can + /// lead to false overflow results (#47139) and because always + /// computing it may negatively impact performance. + intercrate_ambiguity_causes: Option<FxIndexSet<IntercrateAmbiguityCause<'tcx>>>, + + /// The mode that trait queries run in, which informs our error handling + /// policy. In essence, canonicalized queries need their errors propagated + /// rather than immediately reported because we do not have accurate spans. + query_mode: TraitQueryMode, + + treat_inductive_cycle: TreatInductiveCycleAs, +} + +// A stack that walks back up the stack frame. +struct TraitObligationStack<'prev, 'tcx> { + obligation: &'prev PolyTraitObligation<'tcx>, + + /// The trait predicate from `obligation` but "freshened" with the + /// selection-context's freshener. Used to check for recursion. + fresh_trait_pred: ty::PolyTraitPredicate<'tcx>, + + /// Starts out equal to `depth` -- if, during evaluation, we + /// encounter a cycle, then we will set this flag to the minimum + /// depth of that cycle for all participants in the cycle. These + /// participants will then forego caching their results. This is + /// not the most efficient solution, but it addresses #60010. The + /// problem we are trying to prevent: + /// + /// - If you have `A: AutoTrait` requires `B: AutoTrait` and `C: NonAutoTrait` + /// - `B: AutoTrait` requires `A: AutoTrait` (coinductive cycle, ok) + /// - `C: NonAutoTrait` requires `A: AutoTrait` (non-coinductive cycle, not ok) + /// + /// you don't want to cache that `B: AutoTrait` or `A: AutoTrait` + /// is `EvaluatedToOk`; this is because they were only considered + /// ok on the premise that if `A: AutoTrait` held, but we indeed + /// encountered a problem (later on) with `A: AutoTrait`. So we + /// currently set a flag on the stack node for `B: AutoTrait` (as + /// well as the second instance of `A: AutoTrait`) to suppress + /// caching. + /// + /// This is a simple, targeted fix. A more-performant fix requires + /// deeper changes, but would permit more caching: we could + /// basically defer caching until we have fully evaluated the + /// tree, and then cache the entire tree at once. In any case, the + /// performance impact here shouldn't be so horrible: every time + /// this is hit, we do cache at least one trait, so we only + /// evaluate each member of a cycle up to N times, where N is the + /// length of the cycle. This means the performance impact is + /// bounded and we shouldn't have any terrible worst-cases. + reached_depth: Cell<usize>, + + previous: TraitObligationStackList<'prev, 'tcx>, + + /// The number of parent frames plus one (thus, the topmost frame has depth 1). + depth: usize, + + /// The depth-first number of this node in the search graph -- a + /// pre-order index. Basically, a freshly incremented counter. + dfn: usize, +} + +struct SelectionCandidateSet<'tcx> { + /// A list of candidates that definitely apply to the current + /// obligation (meaning: types unify). + vec: Vec<SelectionCandidate<'tcx>>, + + /// If `true`, then there were candidates that might or might + /// not have applied, but we couldn't tell. This occurs when some + /// of the input types are type variables, in which case there are + /// various "builtin" rules that might or might not trigger. + ambiguous: bool, +} + +#[derive(PartialEq, Eq, Debug, Clone)] +struct EvaluatedCandidate<'tcx> { + candidate: SelectionCandidate<'tcx>, + evaluation: EvaluationResult, +} + +/// When does the builtin impl for `T: Trait` apply? +#[derive(Debug)] +enum BuiltinImplConditions<'tcx> { + /// The impl is conditional on `T1, T2, ...: Trait`. + Where(ty::Binder<'tcx, Vec<Ty<'tcx>>>), + /// There is no built-in impl. There may be some other + /// candidate (a where-clause or user-defined impl). + None, + /// It is unknown whether there is an impl. + Ambiguous, +} + +#[derive(Copy, Clone)] +pub enum TreatInductiveCycleAs { + /// This is the previous behavior, where `Recur` represents an inductive + /// cycle that is known not to hold. This is not forwards-compatible with + /// coinduction, and will be deprecated. This is the default behavior + /// of the old trait solver due to back-compat reasons. + Recur, + /// This is the behavior of the new trait solver, where inductive cycles + /// are treated as ambiguous and possibly holding. + Ambig, +} + +impl From<TreatInductiveCycleAs> for EvaluationResult { + fn from(treat: TreatInductiveCycleAs) -> EvaluationResult { + match treat { + TreatInductiveCycleAs::Ambig => EvaluatedToAmbigStackDependent, + TreatInductiveCycleAs::Recur => EvaluatedToErrStackDependent, + } + } +} + +impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { + pub fn new(infcx: &'cx InferCtxt<'tcx>) -> SelectionContext<'cx, 'tcx> { + SelectionContext { + infcx, + freshener: infcx.freshener(), + intercrate_ambiguity_causes: None, + query_mode: TraitQueryMode::Standard, + treat_inductive_cycle: TreatInductiveCycleAs::Recur, + } + } + + // Sets the `TreatInductiveCycleAs` mode temporarily in the selection context + pub fn with_treat_inductive_cycle_as<T>( + &mut self, + treat_inductive_cycle: TreatInductiveCycleAs, + f: impl FnOnce(&mut Self) -> T, + ) -> T { + // Should be executed in a context where caching is disabled, + // otherwise the cache is poisoned with the temporary result. + assert!(self.is_intercrate()); + let treat_inductive_cycle = + std::mem::replace(&mut self.treat_inductive_cycle, treat_inductive_cycle); + let value = f(self); + self.treat_inductive_cycle = treat_inductive_cycle; + value + } + + pub fn with_query_mode( + infcx: &'cx InferCtxt<'tcx>, + query_mode: TraitQueryMode, + ) -> SelectionContext<'cx, 'tcx> { + debug!(?query_mode, "with_query_mode"); + SelectionContext { query_mode, ..SelectionContext::new(infcx) } + } + + /// Enables tracking of intercrate ambiguity causes. See + /// the documentation of [`Self::intercrate_ambiguity_causes`] for more. + pub fn enable_tracking_intercrate_ambiguity_causes(&mut self) { + assert!(self.is_intercrate()); + assert!(self.intercrate_ambiguity_causes.is_none()); + self.intercrate_ambiguity_causes = Some(FxIndexSet::default()); + debug!("selcx: enable_tracking_intercrate_ambiguity_causes"); + } + + /// Gets the intercrate ambiguity causes collected since tracking + /// was enabled and disables tracking at the same time. If + /// tracking is not enabled, just returns an empty vector. + pub fn take_intercrate_ambiguity_causes( + &mut self, + ) -> FxIndexSet<IntercrateAmbiguityCause<'tcx>> { + assert!(self.is_intercrate()); + self.intercrate_ambiguity_causes.take().unwrap_or_default() + } + + pub fn tcx(&self) -> TyCtxt<'tcx> { + self.infcx.tcx + } + + pub fn is_intercrate(&self) -> bool { + self.infcx.intercrate + } + + /////////////////////////////////////////////////////////////////////////// + // Selection + // + // The selection phase tries to identify *how* an obligation will + // be resolved. For example, it will identify which impl or + // parameter bound is to be used. The process can be inconclusive + // if the self type in the obligation is not fully inferred. Selection + // can result in an error in one of two ways: + // + // 1. If no applicable impl or parameter bound can be found. + // 2. If the output type parameters in the obligation do not match + // those specified by the impl/bound. For example, if the obligation + // is `Vec<Foo>: Iterable<Bar>`, but the impl specifies + // `impl<T> Iterable<T> for Vec<T>`, than an error would result. + + /// 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 poly_select( + &mut self, + 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 + // earlier. + assert!(self.query_mode == TraitQueryMode::Canonical); + return Err(SelectionError::Overflow(OverflowError::Canonical)); + } + Err(e) => { + return Err(e); + } + Ok(None) => { + return Ok(None); + } + Ok(Some(candidate)) => candidate, + }; + + match self.confirm_candidate(obligation, candidate) { + Err(SelectionError::Overflow(OverflowError::Canonical)) => { + assert!(self.query_mode == TraitQueryMode::Canonical); + Err(SelectionError::Overflow(OverflowError::Canonical)) + } + Err(e) => Err(e), + Ok(candidate) => Ok(Some(candidate)), + } + } + + 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()); + + let pec = &ProvisionalEvaluationCache::default(); + let stack = self.push_stack(TraitObligationStackList::empty(pec), obligation); + + self.candidate_from_obligation(&stack) + } + + #[instrument(level = "debug", skip(self), ret)] + fn candidate_from_obligation<'o>( + &mut self, + stack: &TraitObligationStack<'o, 'tcx>, + ) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> { + 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)?; + + // Check the cache. Note that we freshen the trait-ref + // separately rather than using `stack.fresh_trait_ref` -- + // this is because we want the unbound variables to be + // replaced with fresh types starting from index 0. + let cache_fresh_trait_pred = self.infcx.freshen(stack.obligation.predicate); + debug!(?cache_fresh_trait_pred); + debug_assert!(!stack.obligation.predicate.has_escaping_bound_vars()); + + if let Some(c) = + self.check_candidate_cache(stack.obligation.param_env, cache_fresh_trait_pred) + { + debug!("CACHE HIT"); + return c; + } + + // If no match, compute result and insert into cache. + // + // FIXME(nikomatsakis) -- this cache is not taking into + // account cycles that may have occurred in forming the + // candidate. I don't know of any specific problems that + // result but it seems awfully suspicious. + let (candidate, dep_node) = + self.in_task(|this| this.candidate_from_obligation_no_cache(stack)); + + debug!("CACHE MISS"); + self.insert_candidate_cache( + stack.obligation.param_env, + cache_fresh_trait_pred, + dep_node, + candidate.clone(), + ); + candidate + } + + fn candidate_from_obligation_no_cache<'o>( + &mut self, + stack: &TraitObligationStack<'o, 'tcx>, + ) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> { + if let Err(conflict) = self.is_knowable(stack) { + debug!("coherence stage: not knowable"); + if self.intercrate_ambiguity_causes.is_some() { + debug!("evaluate_stack: intercrate_ambiguity_causes is some"); + // Heuristics: show the diagnostics when there are no candidates in crate. + if let Ok(candidate_set) = self.assemble_candidates(stack) { + let mut no_candidates_apply = true; + + for c in candidate_set.vec.iter() { + if self.evaluate_candidate(stack, c)?.may_apply() { + no_candidates_apply = false; + break; + } + } + + if !candidate_set.ambiguous && no_candidates_apply { + let trait_ref = 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 self_ty = self_ty.has_concrete_skeleton().then(|| self_ty); + let cause = if let Conflict::Upstream = conflict { + IntercrateAmbiguityCause::UpstreamCrateUpdate { trait_ref, self_ty } + } else { + IntercrateAmbiguityCause::DownstreamCrate { trait_ref, self_ty } + }; + debug!(?cause, "evaluate_stack: pushing cause"); + self.intercrate_ambiguity_causes.as_mut().unwrap().insert(cause); + } + } + } + } + return Ok(None); + } + + let candidate_set = self.assemble_candidates(stack)?; + + if candidate_set.ambiguous { + debug!("candidate set contains ambig"); + return Ok(None); + } + + let candidates = candidate_set.vec; + + debug!(?stack, ?candidates, "assembled {} candidates", candidates.len()); + + // At this point, we know that each of the entries in the + // candidate set is *individually* applicable. Now we have to + // figure out if they contain mutual incompatibilities. This + // frequently arises if we have an unconstrained input type -- + // for example, we are looking for `$0: Eq` where `$0` is some + // unconstrained type variable. In that case, we'll get a + // candidate which assumes $0 == int, one that assumes `$0 == + // usize`, etc. This spells an ambiguity. + + let mut candidates = self.filter_impls(candidates, stack.obligation); + + // If there is more than one candidate, first winnow them down + // by considering extra conditions (nested obligations and so + // forth). We don't winnow if there is exactly one + // candidate. This is a relatively minor distinction but it + // can lead to better inference and error-reporting. An + // example would be if there was an impl: + // + // impl<T:Clone> Vec<T> { fn push_clone(...) { ... } } + // + // and we were to see some code `foo.push_clone()` where `boo` + // is a `Vec<Bar>` and `Bar` does not implement `Clone`. If + // we were to winnow, we'd wind up with zero candidates. + // Instead, we select the right impl now but report "`Bar` does + // not implement `Clone`". + if candidates.len() == 1 { + return self.filter_reservation_impls(candidates.pop().unwrap(), stack.obligation); + } + + // Winnow, but record the exact outcome of evaluation, which + // is needed for specialization. Propagate overflow if it occurs. + let mut candidates = candidates + .into_iter() + .map(|c| match self.evaluate_candidate(stack, &c) { + Ok(eval) if eval.may_apply() => { + Ok(Some(EvaluatedCandidate { candidate: c, evaluation: eval })) + } + Ok(_) => Ok(None), + Err(OverflowError::Canonical) => Err(Overflow(OverflowError::Canonical)), + Err(OverflowError::ErrorReporting) => Err(ErrorReporting), + Err(OverflowError::Error(e)) => Err(Overflow(OverflowError::Error(e))), + }) + .flat_map(Result::transpose) + .collect::<Result<Vec<_>, _>>()?; + + debug!(?stack, ?candidates, "winnowed to {} candidates", candidates.len()); + + let has_non_region_infer = stack.obligation.predicate.has_non_region_infer(); + + // If there are STILL multiple candidates, we can further + // reduce the list by dropping duplicates -- including + // resolving specializations. + if candidates.len() > 1 { + let mut i = 0; + while i < candidates.len() { + let should_drop_i = (0..candidates.len()).filter(|&j| i != j).any(|j| { + self.candidate_should_be_dropped_in_favor_of( + &candidates[i], + &candidates[j], + has_non_region_infer, + ) == DropVictim::Yes + }); + if should_drop_i { + debug!(candidate = ?candidates[i], "Dropping candidate #{}/{}", i, candidates.len()); + candidates.swap_remove(i); + } else { + debug!(candidate = ?candidates[i], "Retaining candidate #{}/{}", i, candidates.len()); + i += 1; + + // If there are *STILL* multiple candidates, give up + // and report ambiguity. + if i > 1 { + debug!("multiple matches, ambig"); + return Ok(None); + } + } + } + } + + // If there are *NO* candidates, then there are no impls -- + // that we know of, anyway. Note that in the case where there + // are unbound type variables within the obligation, it might + // be the case that you could still satisfy the obligation + // from another crate by instantiating the type variables with + // a type from another crate that does have an impl. This case + // is checked for in `evaluate_stack` (and hence users + // who might care about this case, like coherence, should use + // that function). + if candidates.is_empty() { + // If there's an error type, 'downgrade' our result from + // `Err(Unimplemented)` to `Ok(None)`. This helps us avoid + // emitting additional spurious errors, since we're guaranteed + // to have emitted at least one. + if stack.obligation.predicate.references_error() { + debug!(?stack.obligation.predicate, "found error type in predicate, treating as ambiguous"); + return Ok(None); + } + return Err(Unimplemented); + } + + // Just one candidate left. + self.filter_reservation_impls(candidates.pop().unwrap().candidate, stack.obligation) + } + + /////////////////////////////////////////////////////////////////////////// + // EVALUATION + // + // Tests whether an obligation can be selected or whether an impl + // can be applied to particular types. It skips the "confirmation" + // step and hence completely ignores output type parameters. + // + // 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 + /// 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 = 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 { + result = result.max(EvaluatedToAmbig); + } + Ok(result) + }) + } + + fn evaluation_probe( + &mut self, + 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(outer_universe, Some(snapshot)) { + Ok(()) => {} + Err(_) => return Ok(EvaluatedToErr), + } + + if self.infcx.opaque_types_added_in_snapshot(snapshot) { + return Ok(result.max(EvaluatedToOkModuloOpaqueTypes)); + } + + if self.infcx.region_constraints_added_in_snapshot(snapshot) { + Ok(result.max(EvaluatedToOkModuloRegions)) + } else { + Ok(result) + } + }) + } + + /// Evaluates the predicates in `predicates` recursively. Note that + /// this applies projections in the predicates, and therefore + /// is run within an inference probe. + #[instrument(skip(self, stack), level = "debug")] + fn evaluate_predicates_recursively<'o, I>( + &mut self, + stack: TraitObligationStackList<'o, 'tcx>, + predicates: I, + ) -> Result<EvaluationResult, OverflowError> + where + I: IntoIterator<Item = PredicateObligation<'tcx>> + std::fmt::Debug, + { + 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) + } + + #[instrument( + level = "debug", + skip(self, previous_stack), + fields(previous_stack = ?previous_stack.head()) + ret, + )] + fn evaluate_predicate_recursively<'o>( + &mut self, + previous_stack: TraitObligationStackList<'o, 'tcx>, + obligation: PredicateObligation<'tcx>, + ) -> Result<EvaluationResult, OverflowError> { + 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. + match previous_stack.head() { + Some(h) => self.check_recursion_limit(&obligation, h.obligation)?, + None => self.check_recursion_limit(&obligation, &obligation)?, + } + + ensure_sufficient_stack(|| { + let bound_predicate = obligation.predicate.kind(); + match bound_predicate.skip_binder() { + 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); + self.evaluate_trait_predicate_recursively(previous_stack, obligation) + } + + ty::PredicateKind::Subtype(p) => { + let p = bound_predicate.rebind(p); + // Does this code ever run? + match self.infcx.subtype_predicate(&obligation.cause, obligation.param_env, p) { + Ok(Ok(InferOk { obligations, .. })) => { + self.evaluate_predicates_recursively(previous_stack, obligations) + } + Ok(Err(_)) => Ok(EvaluatedToErr), + Err(..) => Ok(EvaluatedToAmbig), + } + } + + ty::PredicateKind::Coerce(p) => { + let p = bound_predicate.rebind(p); + // Does this code ever run? + match self.infcx.coerce_predicate(&obligation.cause, obligation.param_env, p) { + Ok(Ok(InferOk { obligations, .. })) => { + self.evaluate_predicates_recursively(previous_stack, obligations) + } + Ok(Err(_)) => Ok(EvaluatedToErr), + Err(..) => Ok(EvaluatedToAmbig), + } + } + + 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 + // evaluated `WellFormed(X)`. Otherwise, we would run into + // a "natural" overflow error. + // + // Now, the next question is whether we need to do anything + // special with caching. Considering the following tree: + // - `WF(Foo<T>)` + // - `Bar<T>: Send` + // - `WF(Foo<T>)` + // - `Foo<T>: Trait` + // In this case, the innermost `WF(Foo<T>)` should return + // `EvaluatedToOk`, since it's coinductive. Then if + // `Bar<T>: Send` is resolved to `EvaluatedToOk`, it can be + // inserted into a cache (because without thinking about `WF` + // goals, it isn't in a cycle). If `Foo<T>: Trait` later doesn't + // hold, then `Bar<T>: Send` shouldn't hold. Therefore, we + // *do* need to keep track of coinductive cycles. + + let cache = previous_stack.cache; + let dfn = cache.next_dfn(); + + for stack_arg in previous_stack.cache.wf_args.borrow().iter().rev() { + if stack_arg.0 != arg { + continue; + } + debug!("WellFormed({:?}) on stack", arg); + if let Some(stack) = previous_stack.head { + // Okay, let's imagine we have two different stacks: + // `T: NonAutoTrait -> WF(T) -> T: NonAutoTrait` + // `WF(T) -> T: NonAutoTrait -> WF(T)` + // Because of this, we need to check that all + // predicates between the WF goals are coinductive. + // Otherwise, we can say that `T: NonAutoTrait` is + // true. + // Let's imagine we have a predicate stack like + // `Foo: Bar -> WF(T) -> T: NonAutoTrait -> T: Auto` + // depth ^1 ^2 ^3 + // and the current predicate is `WF(T)`. `wf_args` + // would contain `(T, 1)`. We want to check all + // trait predicates greater than `1`. The previous + // stack would be `T: Auto`. + let cycle = stack.iter().take_while(|s| s.depth > stack_arg.1); + let tcx = self.tcx(); + let cycle = + cycle.map(|stack| stack.obligation.predicate.to_predicate(tcx)); + if self.coinductive_match(cycle) { + stack.update_reached_depth(stack_arg.1); + return Ok(EvaluatedToOk); + } else { + return Ok(self.treat_inductive_cycle.into()); + } + } + return Ok(EvaluatedToOk); + } + + match wf::obligations( + self.infcx, + obligation.param_env, + obligation.cause.body_id, + obligation.recursion_depth + 1, + arg, + obligation.cause.span, + ) { + Some(obligations) => { + cache.wf_args.borrow_mut().push((arg, previous_stack.depth())); + let result = + self.evaluate_predicates_recursively(previous_stack, obligations); + cache.wf_args.borrow_mut().pop(); + + let result = result?; + + if !result.must_apply_modulo_regions() { + cache.on_failure(dfn); + } + + cache.on_completion(dfn); + + Ok(result) + } + None => Ok(EvaluatedToAmbig), + } + } + + 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() + || pred.0.has_bound_regions() + || pred.0.has_non_region_infer() + || pred.0.has_non_region_infer() + { + Ok(EvaluatedToOkModuloRegions) + } else { + Ok(EvaluatedToOk) + } + } + + ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(..)) => { + // We do not consider region relationships when evaluating trait matches. + Ok(EvaluatedToOkModuloRegions) + } + + ty::PredicateKind::ObjectSafe(trait_def_id) => { + if self.tcx().check_is_object_safe(trait_def_id) { + Ok(EvaluatedToOk) + } else { + Ok(EvaluatedToErr) + } + } + + 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) { + ProjectAndUnifyResult::Holds(mut subobligations) => { + 'compute_res: { + // If we've previously marked this projection as 'complete', then + // use the final cached result (either `EvaluatedToOk` or + // `EvaluatedToOkModuloRegions`), and skip re-evaluating the + // sub-obligations. + if let Some(key) = + ProjectionCacheKey::from_poly_projection_predicate(self, data) + { + if let Some(cached_res) = self + .infcx + .inner + .borrow_mut() + .projection_cache() + .is_complete(key) + { + break 'compute_res Ok(cached_res); + } + } + + // Need to explicitly set the depth of nested goals here as + // projection obligations can cycle by themselves and in + // `evaluate_predicates_recursively` we only add the depth + // for parent trait goals because only these get added to the + // `TraitObligationStackList`. + for subobligation in subobligations.iter_mut() { + subobligation.set_depth_from_parent(obligation.recursion_depth); + } + let res = self.evaluate_predicates_recursively( + previous_stack, + subobligations, + ); + if let Ok(eval_rslt) = res + && (eval_rslt == EvaluatedToOk + || eval_rslt == EvaluatedToOkModuloRegions) + && let Some(key) = + ProjectionCacheKey::from_poly_projection_predicate( + self, data, + ) + { + // If the result is something that we can cache, then mark this + // entry as 'complete'. This will allow us to skip evaluating the + // subobligations at all the next time we evaluate the projection + // predicate. + self.infcx + .inner + .borrow_mut() + .projection_cache() + .complete(key, eval_rslt); + } + res + } + } + ProjectAndUnifyResult::FailedNormalization => Ok(EvaluatedToAmbig), + ProjectAndUnifyResult::Recursive => Ok(self.treat_inductive_cycle.into()), + ProjectAndUnifyResult::MismatchedProjectionTypes(_) => Ok(EvaluatedToErr), + } + } + + ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(uv)) => { + match const_evaluatable::is_const_evaluatable( + self.infcx, + uv, + obligation.param_env, + obligation.cause.span, + ) { + Ok(()) => Ok(EvaluatedToOk), + Err(NotConstEvaluatable::MentionsInfer) => Ok(EvaluatedToAmbig), + Err(NotConstEvaluatable::MentionsParam) => Ok(EvaluatedToErr), + Err(_) => Ok(EvaluatedToErr), + } + } + + ty::PredicateKind::ConstEquate(c1, c2) => { + let tcx = self.tcx(); + assert!( + tcx.features().generic_const_exprs, + "`ConstEquate` without a feature gate: {c1:?} {c2:?}", + ); + + { + let c1 = tcx.expand_abstract_consts(c1); + let c2 = tcx.expand_abstract_consts(c2); + debug!( + "evaluate_predicate_recursively: equating consts:\nc1= {:?}\nc2= {:?}", + c1, c2 + ); + + use rustc_hir::def::DefKind; + use ty::Unevaluated; + match (c1.kind(), c2.kind()) { + (Unevaluated(a), Unevaluated(b)) + if a.def == b.def && tcx.def_kind(a.def) == DefKind::AssocConst => + { + if let Ok(InferOk { obligations, value: () }) = self + .infcx + .at(&obligation.cause, obligation.param_env) + .trace(c1, c2) + .eq(DefineOpaqueTypes::No, a.args, b.args) + { + return self.evaluate_predicates_recursively( + previous_stack, + obligations, + ); + } + } + (_, Unevaluated(_)) | (Unevaluated(_), _) => (), + (_, _) => { + if let Ok(InferOk { obligations, value: () }) = self + .infcx + .at(&obligation.cause, obligation.param_env) + .eq(DefineOpaqueTypes::No, c1, c2) + { + return self.evaluate_predicates_recursively( + previous_stack, + obligations, + ); + } + } + } + } + + let evaluate = |c: ty::Const<'tcx>| { + if let ty::ConstKind::Unevaluated(unevaluated) = c.kind() { + match self.infcx.try_const_eval_resolve( + obligation.param_env, + unevaluated, + c.ty(), + Some(obligation.cause.span), + ) { + Ok(val) => Ok(val), + Err(e) => Err(e), + } + } else { + Ok(c) + } + }; + + match (evaluate(c1), evaluate(c2)) { + (Ok(c1), Ok(c2)) => { + match self.infcx.at(&obligation.cause, obligation.param_env).eq( + DefineOpaqueTypes::No, + c1, + c2, + ) { + Ok(inf_ok) => self.evaluate_predicates_recursively( + previous_stack, + inf_ok.into_obligations(), + ), + Err(_) => Ok(EvaluatedToErr), + } + } + (Err(ErrorHandled::Reported(..)), _) + | (_, Err(ErrorHandled::Reported(..))) => Ok(EvaluatedToErr), + (Err(ErrorHandled::TooGeneric(..)), _) + | (_, Err(ErrorHandled::TooGeneric(..))) => { + if c1.has_non_region_infer() || c2.has_non_region_infer() { + Ok(EvaluatedToAmbig) + } else { + // Two different constants using generic parameters ~> error. + Ok(EvaluatedToErr) + } + } + } + } + ty::PredicateKind::NormalizesTo(..) => { + bug!("NormalizesTo is only used by the new solver") + } + ty::PredicateKind::AliasRelate(..) => { + bug!("AliasRelate is only used by the new solver") + } + ty::PredicateKind::Ambiguous => Ok(EvaluatedToAmbig), + ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, ty)) => { + match self.infcx.at(&obligation.cause, obligation.param_env).eq( + DefineOpaqueTypes::No, + ct.ty(), + ty, + ) { + Ok(inf_ok) => self.evaluate_predicates_recursively( + previous_stack, + inf_ok.into_obligations(), + ), + Err(_) => Ok(EvaluatedToErr), + } + } + } + }) + } + + #[instrument(skip(self, previous_stack), level = "debug", ret)] + fn evaluate_trait_predicate_recursively<'o>( + &mut self, + previous_stack: TraitObligationStackList<'o, 'tcx>, + mut obligation: PolyTraitObligation<'tcx>, + ) -> Result<EvaluationResult, OverflowError> { + if !self.is_intercrate() + && obligation.is_global() + && obligation.param_env.caller_bounds().iter().all(|bound| bound.has_param()) + { + // If a param env has no global bounds, global obligations do not + // depend on its particular value in order to work, so we can clear + // out the param env and get better caching. + debug!("in global"); + obligation.param_env = obligation.param_env.without_caller_bounds(); + } + + let stack = self.push_stack(previous_stack, &obligation); + let fresh_trait_pred = stack.fresh_trait_pred; + let param_env = obligation.param_env; + + debug!(?fresh_trait_pred); + + // If a trait predicate is in the (local or global) evaluation cache, + // then we know it holds without cycles. + if let Some(result) = self.check_evaluation_cache(param_env, fresh_trait_pred) { + debug!("CACHE HIT"); + return Ok(result); + } + + if let Some(result) = stack.cache().get_provisional(fresh_trait_pred) { + debug!("PROVISIONAL CACHE HIT"); + stack.update_reached_depth(result.reached_depth); + return Ok(result.result); + } + + // Check if this is a match for something already on the + // stack. If so, we don't want to insert the result into the + // main cache (it is cycle dependent) nor the provisional + // cache (which is meant for things that have completed but + // for a "backedge" -- this result *is* the backedge). + if let Some(cycle_result) = self.check_evaluation_cycle(&stack) { + return Ok(cycle_result); + } + + let (result, dep_node) = self.in_task(|this| { + let mut result = this.evaluate_stack(&stack)?; + + // fix issue #103563, we don't normalize + // nested obligations which produced by `TraitDef` candidate + // (i.e. using bounds on assoc items as assumptions). + // because we don't have enough information to + // normalize these obligations before evaluating. + // so we will try to normalize the obligation and evaluate again. + // we will replace it with new solver in the future. + if EvaluationResult::EvaluatedToErr == result + && fresh_trait_pred.has_projections() + && fresh_trait_pred.is_global() + { + let mut nested_obligations = Vec::new(); + let predicate = try_normalize_with_depth_to( + this, + param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + obligation.predicate, + &mut nested_obligations, + ); + if predicate != obligation.predicate { + let mut nested_result = EvaluationResult::EvaluatedToOk; + for obligation in nested_obligations { + nested_result = cmp::max( + this.evaluate_predicate_recursively(previous_stack, obligation)?, + nested_result, + ); + } + + if nested_result.must_apply_modulo_regions() { + let obligation = obligation.with(this.tcx(), predicate); + result = cmp::max( + nested_result, + this.evaluate_trait_predicate_recursively(previous_stack, obligation)?, + ); + } + } + } + + Ok::<_, OverflowError>(result) + }); + + let result = result?; + + if !result.must_apply_modulo_regions() { + stack.cache().on_failure(stack.dfn); + } + + let reached_depth = stack.reached_depth.get(); + if reached_depth >= stack.depth { + debug!("CACHE MISS"); + self.insert_evaluation_cache(param_env, fresh_trait_pred, dep_node, result); + stack.cache().on_completion(stack.dfn); + } else { + debug!("PROVISIONAL"); + debug!( + "caching provisionally because {:?} \ + is a cycle participant (at depth {}, reached depth {})", + fresh_trait_pred, stack.depth, reached_depth, + ); + + stack.cache().insert_provisional(stack.dfn, reached_depth, fresh_trait_pred, result); + } + + Ok(result) + } + + /// If there is any previous entry on the stack that precisely + /// matches this obligation, then we can assume that the + /// obligation is satisfied for now (still all other conditions + /// must be met of course). One obvious case this comes up is + /// marker traits like `Send`. Think of a linked list: + /// + /// struct List<T> { data: T, next: Option<Box<List<T>>> } + /// + /// `Box<List<T>>` will be `Send` if `T` is `Send` and + /// `Option<Box<List<T>>>` is `Send`, and in turn + /// `Option<Box<List<T>>>` is `Send` if `Box<List<T>>` is + /// `Send`. + /// + /// Note that we do this comparison using the `fresh_trait_ref` + /// fields. Because these have all been freshened using + /// `self.freshener`, we can be sure that (a) this will not + /// affect the inferencer state and (b) that if we see two + /// fresh regions with the same index, they refer to the same + /// unbound type variable. + fn check_evaluation_cycle( + &mut self, + stack: &TraitObligationStack<'_, 'tcx>, + ) -> Option<EvaluationResult> { + if let Some(cycle_depth) = stack + .iter() + .skip(1) // Skip top-most frame. + .find(|prev| { + stack.obligation.param_env == prev.obligation.param_env + && stack.fresh_trait_pred == prev.fresh_trait_pred + }) + .map(|stack| stack.depth) + { + debug!("evaluate_stack --> recursive at depth {}", cycle_depth); + + // If we have a stack like `A B C D E A`, where the top of + // the stack is the final `A`, then this will iterate over + // `A, E, D, C, B` -- i.e., all the participants apart + // from the cycle head. We mark them as participating in a + // cycle. This suppresses caching for those nodes. See + // `in_cycle` field for more details. + stack.update_reached_depth(cycle_depth); + + // Subtle: when checking for a coinductive cycle, we do + // not compare using the "freshened trait refs" (which + // have erased regions) but rather the fully explicit + // trait refs. This is important because it's only a cycle + // if the regions match exactly. + let cycle = stack.iter().skip(1).take_while(|s| s.depth >= cycle_depth); + let tcx = self.tcx(); + let cycle = cycle.map(|stack| stack.obligation.predicate.to_predicate(tcx)); + if self.coinductive_match(cycle) { + debug!("evaluate_stack --> recursive, coinductive"); + Some(EvaluatedToOk) + } else { + debug!("evaluate_stack --> recursive, inductive"); + Some(self.treat_inductive_cycle.into()) + } + } else { + None + } + } + + fn evaluate_stack<'o>( + &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 + // something from another crate that does provide an impl. + // + // In intra mode, we must still be conservative. The reason is + // that we want to avoid cycles. Imagine an impl like: + // + // impl<T:Eq> Eq for Vec<T> + // + // and a trait reference like `$0 : Eq` where `$0` is an + // unbound variable. When we evaluate this trait-reference, we + // will unify `$0` with `Vec<$1>` (for some fresh variable + // `$1`), on the condition that `$1 : Eq`. We will then wind + // up with many candidates (since that are other `Eq` impls + // that apply) and try to winnow things down. This results in + // a recursive evaluation that `$1 : Eq` -- as you can + // imagine, this is just where we started. To avoid that, we + // check for unbound variables and return an ambiguous (hence possible) + // match if we've seen this trait before. + // + // This suffices to allow chains like `FnMut` implemented in + // terms of `Fn` etc, but we could probably make this more + // precise still. + let unbound_input_types = + stack.fresh_trait_pred.skip_binder().trait_ref.args.types().any(|ty| ty.is_fresh()); + + if unbound_input_types + && stack.iter().skip(1).any(|prev| { + stack.obligation.param_env == prev.obligation.param_env + && self.match_fresh_trait_refs(stack.fresh_trait_pred, prev.fresh_trait_pred) + }) + { + debug!("evaluate_stack --> unbound argument, recursive --> giving up",); + return Ok(EvaluatedToAmbigStackDependent); + } + + match self.candidate_from_obligation(stack) { + Ok(Some(c)) => self.evaluate_candidate(stack, &c), + Ok(None) => Ok(EvaluatedToAmbig), + Err(Overflow(OverflowError::Canonical)) => Err(OverflowError::Canonical), + Err(ErrorReporting) => Err(OverflowError::ErrorReporting), + Err(..) => Ok(EvaluatedToErr), + } + } + + /// For defaulted traits, we use a co-inductive strategy to solve, so + /// that recursion is ok. This routine returns `true` if the top of the + /// stack (`cycle[0]`): + /// + /// - is a defaulted trait, + /// - it also appears in the backtrace at some position `X`, + /// - all the predicates at positions `X..` between `X` and the top are + /// also defaulted traits. + pub(crate) fn coinductive_match<I>(&mut self, mut cycle: I) -> bool + where + I: Iterator<Item = ty::Predicate<'tcx>>, + { + cycle.all(|predicate| predicate.is_coinductive(self.tcx())) + } + + /// Further evaluates `candidate` to decide whether all type parameters match and whether nested + /// obligations are met. Returns whether `candidate` remains viable after this further + /// scrutiny. + #[instrument( + level = "debug", + skip(self, stack), + fields(depth = stack.obligation.recursion_depth), + ret + )] + fn evaluate_candidate<'o>( + &mut self, + stack: &TraitObligationStack<'o, 'tcx>, + candidate: &SelectionCandidate<'tcx>, + ) -> Result<EvaluationResult, OverflowError> { + let mut result = self.evaluation_probe(|this| { + let candidate = (*candidate).clone(); + match this.confirm_candidate(stack.obligation, candidate) { + Ok(selection) => { + debug!(?selection); + this.evaluate_predicates_recursively( + stack.list(), + selection.nested_obligations().into_iter(), + ) + } + Err(..) => Ok(EvaluatedToErr), + } + })?; + + // If we erased any lifetimes, then we want to use + // `EvaluatedToOkModuloRegions` instead of `EvaluatedToOk` + // as your final result. The result will be cached using + // the freshened trait predicate as a key, so we need + // our result to be correct by *any* choice of original lifetimes, + // not just the lifetime choice for this particular (non-erased) + // predicate. + // See issue #80691 + if stack.fresh_trait_pred.has_erased_regions() { + result = result.max(EvaluatedToOkModuloRegions); + } + + Ok(result) + } + + fn check_evaluation_cache( + &self, + param_env: ty::ParamEnv<'tcx>, + trait_pred: ty::PolyTraitPredicate<'tcx>, + ) -> Option<EvaluationResult> { + // Neither the global nor local cache is aware of intercrate + // mode, so don't do any caching. In particular, we might + // re-use the same `InferCtxt` with both an intercrate + // and non-intercrate `SelectionContext` + if self.is_intercrate() { + return None; + } + + let tcx = self.tcx(); + if self.can_use_global_caches(param_env) { + if let Some(res) = tcx.evaluation_cache.get(&(param_env, trait_pred), tcx) { + return Some(res); + } + } + self.infcx.evaluation_cache.get(&(param_env, trait_pred), tcx) + } + + fn insert_evaluation_cache( + &mut self, + param_env: ty::ParamEnv<'tcx>, + trait_pred: ty::PolyTraitPredicate<'tcx>, + dep_node: DepNodeIndex, + result: EvaluationResult, + ) { + // Avoid caching results that depend on more than just the trait-ref + // - the stack can create recursion. + if result.is_stack_dependent() { + return; + } + + // Neither the global nor local cache is aware of intercrate + // mode, so don't do any caching. In particular, we might + // re-use the same `InferCtxt` with both an intercrate + // and non-intercrate `SelectionContext` + if self.is_intercrate() { + return; + } + + if self.can_use_global_caches(param_env) { + if !trait_pred.has_infer() { + debug!(?trait_pred, ?result, "insert_evaluation_cache global"); + // This may overwrite the cache with the same value + // FIXME: Due to #50507 this overwrites the different values + // This should be changed to use HashMapExt::insert_same + // when that is fixed + self.tcx().evaluation_cache.insert((param_env, trait_pred), dep_node, result); + return; + } + } + + debug!(?trait_pred, ?result, "insert_evaluation_cache"); + self.infcx.evaluation_cache.insert((param_env, trait_pred), dep_node, result); + } + + fn check_recursion_depth<T>( + &self, + depth: usize, + error_obligation: &Obligation<'tcx, T>, + ) -> Result<(), OverflowError> + where + T: ToPredicate<'tcx> + Clone, + { + if !self.infcx.tcx.recursion_limit().value_within_limit(depth) { + match self.query_mode { + TraitQueryMode::Standard => { + if let Some(e) = self.infcx.tainted_by_errors() { + return Err(OverflowError::Error(e)); + } + self.infcx.err_ctxt().report_overflow_obligation(error_obligation, true); + } + TraitQueryMode::Canonical => { + return Err(OverflowError::Canonical); + } + } + } + Ok(()) + } + + /// Checks that the recursion limit has not been exceeded. + /// + /// The weird return type of this function allows it to be used with the `try` (`?`) + /// operator within certain functions. + #[inline(always)] + fn check_recursion_limit<T: Display + TypeFoldable<TyCtxt<'tcx>>, V>( + &self, + obligation: &Obligation<'tcx, T>, + error_obligation: &Obligation<'tcx, V>, + ) -> Result<(), OverflowError> + where + V: ToPredicate<'tcx> + Clone, + { + self.check_recursion_depth(obligation.recursion_depth, error_obligation) + } + + fn in_task<OP, R>(&mut self, op: OP) -> (R, DepNodeIndex) + where + OP: FnOnce(&mut Self) -> R, + { + let (result, dep_node) = + self.tcx().dep_graph.with_anon_task(self.tcx(), dep_kinds::TraitSelect, || op(self)); + self.tcx().dep_graph.read_index(dep_node); + (result, dep_node) + } + + /// filter_impls filters candidates that have a positive impl for a negative + /// goal and a negative impl for a positive goal + #[instrument(level = "debug", skip(self, candidates))] + fn filter_impls( + &mut self, + candidates: Vec<SelectionCandidate<'tcx>>, + obligation: &PolyTraitObligation<'tcx>, + ) -> Vec<SelectionCandidate<'tcx>> { + trace!("{candidates:#?}"); + let tcx = self.tcx(); + let mut result = Vec::with_capacity(candidates.len()); + + for candidate in candidates { + if let ImplCandidate(def_id) = candidate { + if ty::ImplPolarity::Reservation == tcx.impl_polarity(def_id) + || obligation.polarity() == tcx.impl_polarity(def_id) + { + result.push(candidate); + } + } else { + result.push(candidate); + } + } + + trace!("{result:#?}"); + result + } + + /// filter_reservation_impls filter reservation impl for any goal as ambiguous + #[instrument(level = "debug", skip(self))] + fn filter_reservation_impls( + &mut self, + candidate: SelectionCandidate<'tcx>, + obligation: &PolyTraitObligation<'tcx>, + ) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> { + let tcx = self.tcx(); + // Treat reservation impls as ambiguity. + if let ImplCandidate(def_id) = candidate { + if let ty::ImplPolarity::Reservation = tcx.impl_polarity(def_id) { + if let Some(intercrate_ambiguity_clauses) = &mut self.intercrate_ambiguity_causes { + let message = tcx + .get_attr(def_id, sym::rustc_reservation_impl) + .and_then(|a| a.value_str()); + if let Some(message) = message { + debug!( + "filter_reservation_impls: \ + reservation impl ambiguity on {:?}", + def_id + ); + intercrate_ambiguity_clauses + .insert(IntercrateAmbiguityCause::ReservationImpl { message }); + } + } + return Ok(None); + } + } + Ok(Some(candidate)) + } + + fn is_knowable<'o>(&mut self, stack: &TraitObligationStack<'o, 'tcx>) -> Result<(), Conflict> { + debug!("is_knowable(intercrate={:?})", self.is_intercrate()); + + if !self.is_intercrate() { + return Ok(()); + } + + let obligation = &stack.obligation; + let predicate = self.infcx.resolve_vars_if_possible(obligation.predicate); + + // Okay to skip binder because of the nature of the + // trait-ref-is-knowable check, which does not care about + // bound regions. + let trait_ref = predicate.skip_binder().trait_ref; + + coherence::trait_ref_is_knowable::<!>(self.tcx(), trait_ref, |ty| Ok(ty)).unwrap() + } + + /// Returns `true` if the global caches can be used. + fn can_use_global_caches(&self, param_env: ty::ParamEnv<'tcx>) -> bool { + // If there are any inference variables in the `ParamEnv`, then we + // always use a cache local to this particular scope. Otherwise, we + // switch to a global cache. + if param_env.has_infer() { + return false; + } + + // Avoid using the master cache during coherence and just rely + // on the local cache. This effectively disables caching + // during coherence. It is really just a simplification to + // avoid us having to fear that coherence results "pollute" + // the master cache. Since coherence executes pretty quickly, + // it's not worth going to more trouble to increase the + // hit-rate, I don't think. + if self.is_intercrate() { + return false; + } + + // Otherwise, we can use the global cache. + true + } + + fn check_candidate_cache( + &mut self, + param_env: ty::ParamEnv<'tcx>, + cache_fresh_trait_pred: ty::PolyTraitPredicate<'tcx>, + ) -> Option<SelectionResult<'tcx, SelectionCandidate<'tcx>>> { + // Neither the global nor local cache is aware of intercrate + // mode, so don't do any caching. In particular, we might + // re-use the same `InferCtxt` with both an intercrate + // and non-intercrate `SelectionContext` + if self.is_intercrate() { + return None; + } + let tcx = self.tcx(); + let pred = cache_fresh_trait_pred.skip_binder(); + + if self.can_use_global_caches(param_env) { + if let Some(res) = tcx.selection_cache.get(&(param_env, pred), tcx) { + return Some(res); + } + } + self.infcx.selection_cache.get(&(param_env, pred), tcx) + } + + /// Determines whether can we safely cache the result + /// of selecting an obligation. This is almost always `true`, + /// except when dealing with certain `ParamCandidate`s. + /// + /// Ordinarily, a `ParamCandidate` will contain no inference variables, + /// since it was usually produced directly from a `DefId`. However, + /// certain cases (currently only librustdoc's blanket impl finder), + /// a `ParamEnv` may be explicitly constructed with inference types. + /// When this is the case, we do *not* want to cache the resulting selection + /// candidate. This is due to the fact that it might not always be possible + /// to equate the obligation's trait ref and the candidate's trait ref, + /// if more constraints end up getting added to an inference variable. + /// + /// Because of this, we always want to re-run the full selection + /// process for our obligation the next time we see it, since + /// we might end up picking a different `SelectionCandidate` (or none at all). + fn can_cache_candidate( + &self, + result: &SelectionResult<'tcx, SelectionCandidate<'tcx>>, + ) -> bool { + // Neither the global nor local cache is aware of intercrate + // mode, so don't do any caching. In particular, we might + // re-use the same `InferCtxt` with both an intercrate + // and non-intercrate `SelectionContext` + if self.is_intercrate() { + return false; + } + match result { + Ok(Some(SelectionCandidate::ParamCandidate(trait_ref))) => !trait_ref.has_infer(), + _ => true, + } + } + + #[instrument(skip(self, param_env, cache_fresh_trait_pred, dep_node), level = "debug")] + fn insert_candidate_cache( + &mut self, + param_env: ty::ParamEnv<'tcx>, + cache_fresh_trait_pred: ty::PolyTraitPredicate<'tcx>, + dep_node: DepNodeIndex, + candidate: SelectionResult<'tcx, SelectionCandidate<'tcx>>, + ) { + let tcx = self.tcx(); + let pred = cache_fresh_trait_pred.skip_binder(); + + if !self.can_cache_candidate(&candidate) { + debug!(?pred, ?candidate, "insert_candidate_cache - candidate is not cacheable"); + return; + } + + if self.can_use_global_caches(param_env) { + if let Err(Overflow(OverflowError::Canonical)) = candidate { + // Don't cache overflow globally; we only produce this in certain modes. + } else if !pred.has_infer() { + if !candidate.has_infer() { + debug!(?pred, ?candidate, "insert_candidate_cache global"); + // This may overwrite the cache with the same value. + tcx.selection_cache.insert((param_env, pred), dep_node, candidate); + return; + } + } + } + + debug!(?pred, ?candidate, "insert_candidate_cache local"); + self.infcx.selection_cache.insert((param_env, pred), dep_node, candidate); + } + + /// Matches a predicate against the bounds of its self type. + /// + /// Given an obligation like `<T as Foo>::Bar: Baz` where the self type is + /// a projection, look at the bounds of `T::Bar`, see if we can find a + /// `Baz` bound. We return indexes into the list returned by + /// `tcx.item_bounds` for any applicable bounds. + #[instrument(level = "debug", skip(self), ret)] + fn match_projection_obligation_against_definition_bounds( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + ) -> smallvec::SmallVec<[usize; 2]> { + let poly_trait_predicate = self.infcx.resolve_vars_if_possible(obligation.predicate); + let placeholder_trait_predicate = + self.infcx.instantiate_binder_with_placeholders(poly_trait_predicate); + debug!(?placeholder_trait_predicate); + + let tcx = self.infcx.tcx; + let (def_id, args) = match *placeholder_trait_predicate.trait_ref.self_ty().kind() { + ty::Alias(ty::Projection | ty::Opaque, ty::AliasTy { def_id, args, .. }) => { + (def_id, args) + } + _ => { + span_bug!( + obligation.cause.span, + "match_projection_obligation_against_definition_bounds() called \ + but self-ty is not a projection: {:?}", + placeholder_trait_predicate.trait_ref.self_ty() + ); + } + }; + let bounds = tcx.item_bounds(def_id).instantiate(tcx, args); + + // The bounds returned by `item_bounds` may contain duplicates after + // normalization, so try to deduplicate when possible to avoid + // unnecessary ambiguity. + let mut distinct_normalized_bounds = FxHashSet::default(); + + bounds + .iter() + .enumerate() + .filter_map(|(idx, bound)| { + let bound_predicate = bound.kind(); + 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( + obligation, + bound, + placeholder_trait_predicate.trait_ref, + ) { + Ok(None) => true, + Ok(Some(normalized_trait)) + if distinct_normalized_bounds.insert(normalized_trait) => + { + true + } + _ => false, + } + }) { + return Some(idx); + } + } + None + }) + .collect() + } + + /// Equates the trait in `obligation` with trait bound. If the two traits + /// can be equated and the normalized trait bound doesn't contain inference + /// variables or placeholders, the normalized bound is returned. + fn match_normalize_trait_ref( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + trait_bound: ty::PolyTraitRef<'tcx>, + placeholder_trait_ref: ty::TraitRef<'tcx>, + ) -> Result<Option<ty::PolyTraitRef<'tcx>>, ()> { + debug_assert!(!placeholder_trait_ref.has_escaping_bound_vars()); + if placeholder_trait_ref.def_id != trait_bound.def_id() { + // Avoid unnecessary normalization + return Err(()); + } + + let Normalized { value: trait_bound, obligations: _ } = ensure_sufficient_stack(|| { + project::normalize_with_depth( + self, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + trait_bound, + ) + }); + self.infcx + .at(&obligation.cause, obligation.param_env) + .sup(DefineOpaqueTypes::No, ty::Binder::dummy(placeholder_trait_ref), trait_bound) + .map(|InferOk { obligations: _, value: () }| { + // This method is called within a probe, so we can't have + // inference variables and placeholders escape. + if !trait_bound.has_infer() && !trait_bound.has_placeholders() { + Some(trait_bound) + } else { + None + } + }) + .map_err(|_| ()) + } + + fn where_clause_may_apply<'o>( + &mut self, + stack: &TraitObligationStack<'o, 'tcx>, + where_clause_trait_ref: ty::PolyTraitRef<'tcx>, + ) -> Result<EvaluationResult, OverflowError> { + self.evaluation_probe(|this| { + match this.match_where_clause_trait_ref(stack.obligation, where_clause_trait_ref) { + Ok(obligations) => this.evaluate_predicates_recursively(stack.list(), obligations), + Err(()) => Ok(EvaluatedToErr), + } + }) + } + + /// Return `Yes` if the obligation's predicate type applies to the env_predicate, and + /// `No` if it does not. Return `Ambiguous` in the case that the projection type is a GAT, + /// and applying this env_predicate constrains any of the obligation's GAT substitutions. + /// + /// This behavior is a somewhat of a hack to prevent over-constraining inference variables + /// in cases like #91762. + pub(super) fn match_projection_projections( + &mut self, + obligation: &ProjectionTyObligation<'tcx>, + env_predicate: PolyProjectionPredicate<'tcx>, + potentially_unnormalized_candidates: bool, + ) -> ProjectionMatchesProjection { + let mut nested_obligations = Vec::new(); + let infer_predicate = self.infcx.instantiate_binder_with_fresh_vars( + obligation.cause.span, + BoundRegionConversionTime::HigherRankedType, + env_predicate, + ); + let infer_projection = if potentially_unnormalized_candidates { + ensure_sufficient_stack(|| { + project::normalize_with_depth_to( + self, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + infer_predicate.projection_ty, + &mut nested_obligations, + ) + }) + } else { + infer_predicate.projection_ty + }; + + let is_match = self + .infcx + .at(&obligation.cause, obligation.param_env) + .sup(DefineOpaqueTypes::No, obligation.predicate, infer_projection) + .is_ok_and(|InferOk { obligations, value: () }| { + self.evaluate_predicates_recursively( + TraitObligationStackList::empty(&ProvisionalEvaluationCache::default()), + nested_obligations.into_iter().chain(obligations), + ) + .is_ok_and(|res| res.may_apply()) + }); + + if is_match { + let generics = self.tcx().generics_of(obligation.predicate.def_id); + // FIXME(generic-associated-types): Addresses aggressive inference in #92917. + // If this type is a GAT, and of the GAT args resolve to something new, + // that means that we must have newly inferred something about the GAT. + // We should give up in that case. + if !generics.params.is_empty() + && obligation.predicate.args[generics.parent_count..] + .iter() + .any(|&p| p.has_non_region_infer() && self.infcx.shallow_resolve(p) != p) + { + ProjectionMatchesProjection::Ambiguous + } else { + ProjectionMatchesProjection::Yes + } + } else { + ProjectionMatchesProjection::No + } + } +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +enum DropVictim { + Yes, + No, +} + +impl DropVictim { + fn drop_if(should_drop: bool) -> DropVictim { + if should_drop { DropVictim::Yes } else { DropVictim::No } + } +} + +/// ## Winnowing +/// +/// Winnowing is the process of attempting to resolve ambiguity by +/// probing further. During the winnowing process, we unify all +/// type variables and then we also attempt to evaluate recursive +/// bounds to see if they are satisfied. +impl<'tcx> SelectionContext<'_, 'tcx> { + /// Returns `DropVictim::Yes` if `victim` should be dropped in favor of + /// `other`. Generally speaking we will drop duplicate + /// candidates and prefer where-clause candidates. + /// + /// See the comment for "SelectionCandidate" for more details. + #[instrument(level = "debug", skip(self))] + fn candidate_should_be_dropped_in_favor_of( + &mut self, + victim: &EvaluatedCandidate<'tcx>, + other: &EvaluatedCandidate<'tcx>, + has_non_region_infer: bool, + ) -> DropVictim { + if victim.candidate == other.candidate { + return DropVictim::Yes; + } + + // Check if a bound would previously have been removed when normalizing + // the param_env so that it can be given the lowest priority. See + // #50825 for the motivation for this. + let is_global = + |cand: &ty::PolyTraitPredicate<'tcx>| cand.is_global() && !cand.has_bound_vars(); + + // (*) Prefer `BuiltinCandidate { has_nested: false }`, `PointeeCandidate`, + // `DiscriminantKindCandidate`, `ConstDestructCandidate` + // to anything else. + // + // This is a fix for #53123 and prevents winnowing from accidentally extending the + // lifetime of a variable. + match (&other.candidate, &victim.candidate) { + // FIXME(@jswrenn): this should probably be more sophisticated + (TransmutabilityCandidate, _) | (_, TransmutabilityCandidate) => DropVictim::No, + + // (*) + (BuiltinCandidate { has_nested: false } | ConstDestructCandidate(_), _) => { + DropVictim::Yes + } + (_, BuiltinCandidate { has_nested: false } | ConstDestructCandidate(_)) => { + DropVictim::No + } + + (ParamCandidate(other), ParamCandidate(victim)) => { + let same_except_bound_vars = other.skip_binder().trait_ref + == victim.skip_binder().trait_ref + && other.skip_binder().polarity == victim.skip_binder().polarity + && !other.skip_binder().trait_ref.has_escaping_bound_vars(); + if same_except_bound_vars { + // See issue #84398. In short, we can generate multiple ParamCandidates which are + // the same except for unused bound vars. Just pick the one with the fewest bound vars + // or the current one if tied (they should both evaluate to the same answer). This is + // probably best characterized as a "hack", since we might prefer to just do our + // best to *not* create essentially duplicate candidates in the first place. + DropVictim::drop_if(other.bound_vars().len() <= victim.bound_vars().len()) + } else { + DropVictim::No + } + } + + // Drop otherwise equivalent non-const fn pointer candidates + (FnPointerCandidate { .. }, FnPointerCandidate { fn_host_effect }) => { + DropVictim::drop_if(*fn_host_effect == self.tcx().consts.true_) + } + + ( + ParamCandidate(ref other_cand), + ImplCandidate(..) + | AutoImplCandidate + | ClosureCandidate { .. } + | CoroutineCandidate + | FutureCandidate + | IteratorCandidate + | AsyncIteratorCandidate + | FnPointerCandidate { .. } + | BuiltinObjectCandidate + | BuiltinUnsizeCandidate + | TraitUpcastingUnsizeCandidate(_) + | BuiltinCandidate { .. } + | TraitAliasCandidate + | ObjectCandidate(_) + | ProjectionCandidate(_), + ) => { + // We have a where clause so don't go around looking + // for impls. Arbitrarily give param candidates priority + // over projection and object candidates. + // + // Global bounds from the where clause should be ignored + // here (see issue #50825). + DropVictim::drop_if(!is_global(other_cand)) + } + (ObjectCandidate(_) | ProjectionCandidate(_), ParamCandidate(ref victim_cand)) => { + // Prefer these to a global where-clause bound + // (see issue #50825). + if is_global(victim_cand) { DropVictim::Yes } else { DropVictim::No } + } + ( + ImplCandidate(_) + | AutoImplCandidate + | ClosureCandidate { .. } + | CoroutineCandidate + | FutureCandidate + | IteratorCandidate + | AsyncIteratorCandidate + | FnPointerCandidate { .. } + | BuiltinObjectCandidate + | BuiltinUnsizeCandidate + | TraitUpcastingUnsizeCandidate(_) + | BuiltinCandidate { has_nested: true } + | TraitAliasCandidate, + ParamCandidate(ref victim_cand), + ) => { + // Prefer these to a global where-clause bound + // (see issue #50825). + DropVictim::drop_if( + is_global(victim_cand) && other.evaluation.must_apply_modulo_regions(), + ) + } + + (ProjectionCandidate(i), ProjectionCandidate(j)) + | (ObjectCandidate(i), ObjectCandidate(j)) => { + // Arbitrarily pick the lower numbered candidate for backwards + // compatibility reasons. Don't let this affect inference. + DropVictim::drop_if(i < j && !has_non_region_infer) + } + (ObjectCandidate(_), ProjectionCandidate(_)) + | (ProjectionCandidate(_), ObjectCandidate(_)) => { + bug!("Have both object and projection candidate") + } + + // Arbitrarily give projection and object candidates priority. + ( + ObjectCandidate(_) | ProjectionCandidate(_), + ImplCandidate(..) + | AutoImplCandidate + | ClosureCandidate { .. } + | CoroutineCandidate + | FutureCandidate + | IteratorCandidate + | AsyncIteratorCandidate + | FnPointerCandidate { .. } + | BuiltinObjectCandidate + | BuiltinUnsizeCandidate + | TraitUpcastingUnsizeCandidate(_) + | BuiltinCandidate { .. } + | TraitAliasCandidate, + ) => DropVictim::Yes, + + ( + ImplCandidate(..) + | AutoImplCandidate + | ClosureCandidate { .. } + | CoroutineCandidate + | FutureCandidate + | IteratorCandidate + | AsyncIteratorCandidate + | FnPointerCandidate { .. } + | BuiltinObjectCandidate + | BuiltinUnsizeCandidate + | TraitUpcastingUnsizeCandidate(_) + | BuiltinCandidate { .. } + | TraitAliasCandidate, + ObjectCandidate(_) | ProjectionCandidate(_), + ) => DropVictim::No, + + (&ImplCandidate(other_def), &ImplCandidate(victim_def)) => { + // See if we can toss out `victim` based on specialization. + // While this requires us to know *for sure* that the `other` impl applies + // we still use modulo regions here. + // + // This is fine as specialization currently assumes that specializing + // impls have to be always applicable, meaning that the only allowed + // region constraints may be constraints also present on the default impl. + let tcx = self.tcx(); + if other.evaluation.must_apply_modulo_regions() { + if tcx.specializes((other_def, victim_def)) { + return DropVictim::Yes; + } + } + + match tcx.impls_are_allowed_to_overlap(other_def, victim_def) { + // For #33140 the impl headers must be exactly equal, the trait must not have + // any associated items and there are no where-clauses. + // + // We can just arbitrarily drop one of the impls. + Some(ty::ImplOverlapKind::Issue33140) => { + assert_eq!(other.evaluation, victim.evaluation); + DropVictim::Yes + } + // For candidates which already reference errors it doesn't really + // matter what we do 🤷 + Some(ty::ImplOverlapKind::Permitted { marker: false }) => { + DropVictim::drop_if(other.evaluation.must_apply_considering_regions()) + } + Some(ty::ImplOverlapKind::Permitted { marker: true }) => { + // Subtle: If the predicate we are evaluating has inference + // variables, do *not* allow discarding candidates due to + // marker trait impls. + // + // Without this restriction, we could end up accidentally + // constraining inference variables based on an arbitrarily + // chosen trait impl. + // + // Imagine we have the following code: + // + // ```rust + // #[marker] trait MyTrait {} + // impl MyTrait for u8 {} + // impl MyTrait for bool {} + // ``` + // + // And we are evaluating the predicate `<_#0t as MyTrait>`. + // + // During selection, we will end up with one candidate for each + // impl of `MyTrait`. If we were to discard one impl in favor + // of the other, we would be left with one candidate, causing + // us to "successfully" select the predicate, unifying + // _#0t with (for example) `u8`. + // + // However, we have no reason to believe that this unification + // is correct - we've essentially just picked an arbitrary + // *possibility* for _#0t, and required that this be the *only* + // possibility. + // + // Eventually, we will either: + // 1) Unify all inference variables in the predicate through + // some other means (e.g. type-checking of a function). We will + // then be in a position to drop marker trait candidates + // without constraining inference variables (since there are + // none left to constrain) + // 2) Be left with some unconstrained inference variables. We + // will then correctly report an inference error, since the + // existence of multiple marker trait impls tells us nothing + // about which one should actually apply. + DropVictim::drop_if( + !has_non_region_infer + && other.evaluation.must_apply_considering_regions(), + ) + } + None => DropVictim::No, + } + } + + (AutoImplCandidate, ImplCandidate(_)) | (ImplCandidate(_), AutoImplCandidate) => { + DropVictim::No + } + + (AutoImplCandidate, _) | (_, AutoImplCandidate) => { + bug!( + "default implementations shouldn't be recorded \ + when there are other global candidates: {:?} {:?}", + other, + victim + ); + } + + // Everything else is ambiguous + ( + ImplCandidate(_) + | ClosureCandidate { .. } + | CoroutineCandidate + | FutureCandidate + | IteratorCandidate + | AsyncIteratorCandidate + | FnPointerCandidate { .. } + | BuiltinObjectCandidate + | BuiltinUnsizeCandidate + | TraitUpcastingUnsizeCandidate(_) + | BuiltinCandidate { has_nested: true } + | TraitAliasCandidate, + ImplCandidate(_) + | ClosureCandidate { .. } + | CoroutineCandidate + | FutureCandidate + | IteratorCandidate + | AsyncIteratorCandidate + | FnPointerCandidate { .. } + | BuiltinObjectCandidate + | BuiltinUnsizeCandidate + | TraitUpcastingUnsizeCandidate(_) + | BuiltinCandidate { has_nested: true } + | TraitAliasCandidate, + ) => DropVictim::No, + } + } +} + +impl<'tcx> SelectionContext<'_, 'tcx> { + fn sized_conditions( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + ) -> BuiltinImplConditions<'tcx> { + use self::BuiltinImplConditions::{Ambiguous, None, Where}; + + // NOTE: binder moved to (*) + let self_ty = self.infcx.shallow_resolve(obligation.predicate.skip_binder().self_ty()); + + match self_ty.kind() { + ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) + | ty::Uint(_) + | ty::Int(_) + | ty::Bool + | ty::Float(_) + | ty::FnDef(..) + | ty::FnPtr(_) + | ty::RawPtr(..) + | ty::Char + | ty::Ref(..) + | ty::Coroutine(..) + | ty::CoroutineWitness(..) + | ty::Array(..) + | ty::Closure(..) + | ty::Never + | ty::Dynamic(_, _, ty::DynStar) + | ty::Error(_) => { + // safe for everything + Where(ty::Binder::dummy(Vec::new())) + } + + ty::Str | ty::Slice(_) | ty::Dynamic(..) | ty::Foreign(..) => None, + + ty::Tuple(tys) => Where( + obligation.predicate.rebind(tys.last().map_or_else(Vec::new, |&last| vec![last])), + ), + + ty::Adt(def, args) => { + let sized_crit = def.sized_constraint(self.tcx()); + // (*) binder moved here + Where( + obligation + .predicate + .rebind(sized_crit.iter_instantiated(self.tcx(), args).collect()), + ) + } + + ty::Alias(..) | ty::Param(_) | ty::Placeholder(..) => None, + ty::Infer(ty::TyVar(_)) => Ambiguous, + + // We can make this an ICE if/once we actually instantiate the trait obligation eagerly. + ty::Bound(..) => None, + + ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { + bug!("asked to assemble builtin bounds of unexpected type: {:?}", self_ty); + } + } + } + + fn copy_clone_conditions( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + ) -> BuiltinImplConditions<'tcx> { + // NOTE: binder moved to (*) + let self_ty = self.infcx.shallow_resolve(obligation.predicate.skip_binder().self_ty()); + + use self::BuiltinImplConditions::{Ambiguous, None, Where}; + + match *self_ty.kind() { + ty::FnDef(..) | ty::FnPtr(_) | ty::Error(_) => Where(ty::Binder::dummy(Vec::new())), + + ty::Uint(_) + | ty::Int(_) + | ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) + | ty::Bool + | ty::Float(_) + | ty::Char + | ty::RawPtr(..) + | ty::Never + | ty::Ref(_, _, hir::Mutability::Not) + | ty::Array(..) => { + // Implementations provided in libcore + None + } + + ty::Dynamic(..) + | ty::Str + | ty::Slice(..) + | ty::Foreign(..) + | ty::Ref(_, _, hir::Mutability::Mut) => None, + + ty::Tuple(tys) => { + // (*) binder moved here + Where(obligation.predicate.rebind(tys.iter().collect())) + } + + ty::Coroutine(coroutine_def_id, args) => { + match self.tcx().coroutine_movability(coroutine_def_id) { + hir::Movability::Static => None, + hir::Movability::Movable => { + if self.tcx().features().coroutine_clone { + let resolved_upvars = + self.infcx.shallow_resolve(args.as_coroutine().tupled_upvars_ty()); + let resolved_witness = + self.infcx.shallow_resolve(args.as_coroutine().witness()); + if resolved_upvars.is_ty_var() || resolved_witness.is_ty_var() { + // Not yet resolved. + Ambiguous + } else { + let all = args + .as_coroutine() + .upvar_tys() + .iter() + .chain([args.as_coroutine().witness()]) + .collect::<Vec<_>>(); + Where(obligation.predicate.rebind(all)) + } + } else { + None + } + } + } + } + + ty::CoroutineWitness(def_id, args) => { + let hidden_types = bind_coroutine_hidden_types_above( + self.infcx, + def_id, + args, + obligation.predicate.bound_vars(), + ); + Where(hidden_types) + } + + ty::Closure(_, args) => { + // (*) binder moved here + let ty = self.infcx.shallow_resolve(args.as_closure().tupled_upvars_ty()); + if let ty::Infer(ty::TyVar(_)) = ty.kind() { + // Not yet resolved. + Ambiguous + } else { + Where(obligation.predicate.rebind(args.as_closure().upvar_tys().to_vec())) + } + } + + ty::Adt(..) | ty::Alias(..) | ty::Param(..) | ty::Placeholder(..) => { + // Fallback to whatever user-defined impls exist in this case. + None + } + + ty::Infer(ty::TyVar(_)) => { + // Unbound type variable. Might or might not have + // applicable impls and so forth, depending on what + // those type variables wind up being bound to. + Ambiguous + } + + // We can make this an ICE if/once we actually instantiate the trait obligation eagerly. + ty::Bound(..) => None, + + ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { + bug!("asked to assemble builtin bounds of unexpected type: {:?}", self_ty); + } + } + } + + /// For default impls, we need to break apart a type into its + /// "constituent types" -- meaning, the types that it contains. + /// + /// Here are some (simple) examples: + /// + /// ```ignore (illustrative) + /// (i32, u32) -> [i32, u32] + /// Foo where struct Foo { x: i32, y: u32 } -> [i32, u32] + /// Bar<i32> where struct Bar<T> { x: T, y: u32 } -> [i32, u32] + /// Zed<i32> where enum Zed { A(T), B(u32) } -> [i32, u32] + /// ``` + #[instrument(level = "debug", skip(self), ret)] + fn constituent_types_for_ty( + &self, + t: ty::Binder<'tcx, Ty<'tcx>>, + ) -> Result<ty::Binder<'tcx, Vec<Ty<'tcx>>>, SelectionError<'tcx>> { + Ok(match *t.skip_binder().kind() { + ty::Uint(_) + | ty::Int(_) + | ty::Bool + | ty::Float(_) + | ty::FnDef(..) + | ty::FnPtr(_) + | ty::Error(_) + | ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) + | ty::Never + | ty::Char => ty::Binder::dummy(Vec::new()), + + // Treat this like `struct str([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::Weak, ..) + | ty::Bound(..) + | ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { + bug!("asked to assemble constituent types of unexpected type: {:?}", t); + } + + ty::RawPtr(ty::TypeAndMut { ty: element_ty, .. }) | ty::Ref(_, element_ty, _) => { + t.rebind(vec![element_ty]) + } + + ty::Array(element_ty, _) | ty::Slice(element_ty) => t.rebind(vec![element_ty]), + + ty::Tuple(tys) => { + // (T1, ..., Tn) -- meets any bound that all of T1...Tn meet + t.rebind(tys.iter().collect()) + } + + ty::Closure(_, args) => { + let ty = self.infcx.shallow_resolve(args.as_closure().tupled_upvars_ty()); + t.rebind(vec![ty]) + } + + ty::Coroutine(_, args) => { + let ty = self.infcx.shallow_resolve(args.as_coroutine().tupled_upvars_ty()); + let witness = args.as_coroutine().witness(); + t.rebind([ty].into_iter().chain(iter::once(witness)).collect()) + } + + ty::CoroutineWitness(def_id, args) => { + bind_coroutine_hidden_types_above(self.infcx, def_id, args, t.bound_vars()) + } + + // For `PhantomData<T>`, we pass `T`. + ty::Adt(def, args) if def.is_phantom_data() => t.rebind(args.types().collect()), + + ty::Adt(def, args) => { + t.rebind(def.all_fields().map(|f| f.ty(self.tcx(), args)).collect()) + } + + ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => { + // We can resolve the `impl Trait` to its concrete type, + // which enforces a DAG between the functions requiring + // the auto trait bounds in question. + match self.tcx().type_of_opaque(def_id) { + Ok(ty) => t.rebind(vec![ty.instantiate(self.tcx(), args)]), + Err(_) => { + return Err(SelectionError::OpaqueTypeAutoTraitLeakageUnknown(def_id)); + } + } + } + }) + } + + fn collect_predicates_for_types( + &mut self, + param_env: ty::ParamEnv<'tcx>, + cause: ObligationCause<'tcx>, + recursion_depth: usize, + trait_def_id: DefId, + types: ty::Binder<'tcx, Vec<Ty<'tcx>>>, + ) -> Vec<PredicateObligation<'tcx>> { + // Because the types were potentially derived from + // higher-ranked obligations they may reference late-bound + // regions. For example, `for<'a> Foo<&'a i32> : Copy` would + // yield a type like `for<'a> &'a i32`. In general, we + // maintain the invariant that we never manipulate bound + // regions, so we have to process these bound regions somehow. + // + // The strategy is to: + // + // 1. Instantiate those regions to placeholder regions (e.g., + // `for<'a> &'a i32` becomes `&0 i32`. + // 2. Produce something like `&'0 i32 : Copy` + // 3. Re-bind the regions back to `for<'a> &'a i32 : Copy` + + types + .as_ref() + .skip_binder() // binder moved -\ + .iter() + .flat_map(|ty| { + let ty: ty::Binder<'tcx, Ty<'tcx>> = types.rebind(*ty); // <----/ + + let placeholder_ty = self.infcx.instantiate_binder_with_placeholders(ty); + let Normalized { value: normalized_ty, mut obligations } = + ensure_sufficient_stack(|| { + project::normalize_with_depth( + self, + param_env, + cause.clone(), + recursion_depth, + placeholder_ty, + ) + }); + + let tcx = self.tcx(); + let trait_ref = if tcx.generics_of(trait_def_id).params.len() == 1 { + ty::TraitRef::new(tcx, trait_def_id, [normalized_ty]) + } else { + // If this is an ill-formed auto/built-in trait, then synthesize + // new error args for the missing generics. + let err_args = ty::GenericArgs::extend_with_error( + tcx, + trait_def_id, + &[normalized_ty.into()], + ); + ty::TraitRef::new(tcx, trait_def_id, err_args) + }; + + let obligation = Obligation::new(self.tcx(), cause.clone(), param_env, trait_ref); + obligations.push(obligation); + obligations + }) + .collect() + } + + /////////////////////////////////////////////////////////////////////////// + // Matching + // + // Matching is a common path used for both evaluation and + // confirmation. It basically unifies types that appear in impls + // and traits. This does affect the surrounding environment; + // therefore, when used during evaluation, match routines must be + // run inside of a `probe()` so that their side-effects are + // contained. + + fn rematch_impl( + &mut self, + impl_def_id: DefId, + obligation: &PolyTraitObligation<'tcx>, + ) -> Normalized<'tcx, GenericArgsRef<'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) { + Ok(args) => args, + Err(()) => { + // FIXME: A rematch may fail when a candidate cache hit occurs + // on the freshened form of the trait predicate, but the match + // fails for some reason that is not captured in the freshened + // cache key. For example, equating an impl trait ref against + // the placeholder trait ref may fail due the Generalizer relation + // raising a CyclicalTy error due to a sub_root_var relation + // for a variable being generalized... + let guar = self.infcx.dcx().span_delayed_bug( + obligation.cause.span, + format!( + "Impl {impl_def_id:?} was matchable against {obligation:?} but now is not" + ), + ); + let value = self.infcx.fresh_args_for_item(obligation.cause.span, impl_def_id); + let err = Ty::new_error(self.tcx(), guar); + let value = value.fold_with(&mut BottomUpFolder { + tcx: self.tcx(), + ty_op: |_| err, + lt_op: |l| l, + ct_op: |c| c, + }); + Normalized { value, obligations: vec![] } + } + } + } + + #[instrument(level = "debug", skip(self), ret)] + fn match_impl( + &mut self, + impl_def_id: DefId, + impl_trait_ref: EarlyBinder<ty::TraitRef<'tcx>>, + obligation: &PolyTraitObligation<'tcx>, + ) -> Result<Normalized<'tcx, GenericArgsRef<'tcx>>, ()> { + let placeholder_obligation = + self.infcx.instantiate_binder_with_placeholders(obligation.predicate); + let placeholder_obligation_trait_ref = placeholder_obligation.trait_ref; + + let impl_args = self.infcx.fresh_args_for_item(obligation.cause.span, impl_def_id); + + let impl_trait_ref = impl_trait_ref.instantiate(self.tcx(), impl_args); + if impl_trait_ref.references_error() { + return Err(()); + } + + debug!(?impl_trait_ref); + + let Normalized { value: impl_trait_ref, obligations: mut nested_obligations } = + ensure_sufficient_stack(|| { + project::normalize_with_depth( + self, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + impl_trait_ref, + ) + }); + + debug!(?impl_trait_ref, ?placeholder_obligation_trait_ref); + + let cause = ObligationCause::new( + obligation.cause.span, + obligation.cause.body_id, + ObligationCauseCode::MatchImpl(obligation.cause.clone(), impl_def_id), + ); + + let InferOk { obligations, .. } = self + .infcx + .at(&cause, obligation.param_env) + .eq(DefineOpaqueTypes::No, placeholder_obligation_trait_ref, impl_trait_ref) + .map_err(|e| { + debug!("match_impl: failed eq_trait_refs due to `{}`", e.to_string(self.tcx())) + })?; + nested_obligations.extend(obligations); + + if !self.is_intercrate() + && self.tcx().impl_polarity(impl_def_id) == ty::ImplPolarity::Reservation + { + debug!("reservation impls only apply in intercrate mode"); + return Err(()); + } + + Ok(Normalized { value: impl_args, obligations: nested_obligations }) + } + + fn match_upcast_principal( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + unnormalized_upcast_principal: ty::PolyTraitRef<'tcx>, + a_data: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>, + b_data: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>, + a_region: ty::Region<'tcx>, + b_region: ty::Region<'tcx>, + ) -> SelectionResult<'tcx, Vec<PredicateObligation<'tcx>>> { + let tcx = self.tcx(); + let mut nested = vec![]; + + let upcast_principal = normalize_with_depth_to( + self, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + unnormalized_upcast_principal, + &mut nested, + ); + + for bound in b_data { + match bound.skip_binder() { + // Check that a_ty's supertrait (upcast_principal) is compatible + // with the target (b_ty). + ty::ExistentialPredicate::Trait(target_principal) => { + nested.extend( + self.infcx + .at(&obligation.cause, obligation.param_env) + .sup( + DefineOpaqueTypes::No, + upcast_principal.map_bound(|trait_ref| { + ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref) + }), + bound.rebind(target_principal), + ) + .map_err(|_| SelectionError::Unimplemented)? + .into_obligations(), + ); + } + // Check that b_ty's projection is satisfied by exactly one of + // a_ty's projections. First, we look through the list to see if + // any match. If not, error. Then, if *more* than one matches, we + // return ambiguity. Otherwise, if exactly one matches, equate + // it with b_ty's projection. + ty::ExistentialPredicate::Projection(target_projection) => { + let target_projection = bound.rebind(target_projection); + let mut matching_projections = + a_data.projection_bounds().filter(|source_projection| { + // Eager normalization means that we can just use can_eq + // here instead of equating and processing obligations. + source_projection.item_def_id() == target_projection.item_def_id() + && self.infcx.can_eq( + obligation.param_env, + *source_projection, + target_projection, + ) + }); + let Some(source_projection) = matching_projections.next() else { + return Err(SelectionError::Unimplemented); + }; + if matching_projections.next().is_some() { + return Ok(None); + } + nested.extend( + self.infcx + .at(&obligation.cause, obligation.param_env) + .sup(DefineOpaqueTypes::No, source_projection, target_projection) + .map_err(|_| SelectionError::Unimplemented)? + .into_obligations(), + ); + } + // Check that b_ty's auto traits are present in a_ty's bounds. + ty::ExistentialPredicate::AutoTrait(def_id) => { + if !a_data.auto_traits().any(|source_def_id| source_def_id == def_id) { + return Err(SelectionError::Unimplemented); + } + } + } + } + + nested.push(Obligation::with_depth( + tcx, + obligation.cause.clone(), + obligation.recursion_depth + 1, + obligation.param_env, + ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region)), + )); + + Ok(Some(nested)) + } + + /// Normalize `where_clause_trait_ref` and try to match it against + /// `obligation`. If successful, return any predicates that + /// result from the normalization. + fn match_where_clause_trait_ref( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + where_clause_trait_ref: ty::PolyTraitRef<'tcx>, + ) -> Result<Vec<PredicateObligation<'tcx>>, ()> { + self.match_poly_trait_ref(obligation, where_clause_trait_ref) + } + + /// Returns `Ok` if `poly_trait_ref` being true implies that the + /// obligation is satisfied. + #[instrument(skip(self), level = "debug")] + fn match_poly_trait_ref( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + poly_trait_ref: ty::PolyTraitRef<'tcx>, + ) -> Result<Vec<PredicateObligation<'tcx>>, ()> { + self.infcx + .at(&obligation.cause, obligation.param_env) + .sup(DefineOpaqueTypes::No, obligation.predicate.to_poly_trait_ref(), poly_trait_ref) + .map(|InferOk { obligations, .. }| obligations) + .map_err(|_| ()) + } + + /////////////////////////////////////////////////////////////////////////// + // Miscellany + + fn match_fresh_trait_refs( + &self, + previous: ty::PolyTraitPredicate<'tcx>, + current: ty::PolyTraitPredicate<'tcx>, + ) -> bool { + let mut matcher = MatchAgainstFreshVars::new(self.tcx()); + matcher.relate(previous, current).is_ok() + } + + fn push_stack<'o>( + &mut self, + previous_stack: TraitObligationStackList<'o, 'tcx>, + obligation: &'o PolyTraitObligation<'tcx>, + ) -> TraitObligationStack<'o, 'tcx> { + let fresh_trait_pred = obligation.predicate.fold_with(&mut self.freshener); + + let dfn = previous_stack.cache.next_dfn(); + let depth = previous_stack.depth() + 1; + TraitObligationStack { + obligation, + fresh_trait_pred, + reached_depth: Cell::new(depth), + previous: previous_stack, + dfn, + depth, + } + } + + #[instrument(skip(self), level = "debug")] + fn closure_trait_ref_unnormalized( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + args: GenericArgsRef<'tcx>, + fn_host_effect: ty::Const<'tcx>, + ) -> ty::PolyTraitRef<'tcx> { + let closure_sig = args.as_closure().sig(); + + debug!(?closure_sig); + + // NOTE: The self-type is an unboxed closure type and hence is + // in fact unparameterized (or at least does not reference any + // regions bound in the obligation). + let self_ty = obligation + .predicate + .self_ty() + .no_bound_vars() + .expect("unboxed closure type should not capture bound vars from the predicate"); + + closure_trait_ref_and_return_type( + self.tcx(), + obligation.predicate.def_id(), + self_ty, + closure_sig, + util::TupleArgumentsFlag::No, + fn_host_effect, + ) + .map_bound(|(trait_ref, _)| trait_ref) + } + + /// Returns the obligations that are implied by instantiating an + /// impl or trait. The obligations are substituted and fully + /// normalized. This is used when confirming an impl or default + /// impl. + #[instrument(level = "debug", skip(self, cause, param_env))] + fn impl_or_trait_obligations( + &mut self, + cause: &ObligationCause<'tcx>, + recursion_depth: usize, + param_env: ty::ParamEnv<'tcx>, + def_id: DefId, // of impl or trait + args: GenericArgsRef<'tcx>, // for impl or trait + parent_trait_pred: ty::Binder<'tcx, ty::TraitPredicate<'tcx>>, + ) -> Vec<PredicateObligation<'tcx>> { + let tcx = self.tcx(); + + // To allow for one-pass evaluation of the nested obligation, + // each predicate must be preceded by the obligations required + // to normalize it. + // for example, if we have: + // impl<U: Iterator<Item: Copy>, V: Iterator<Item = U>> Foo for V + // the impl will have the following predicates: + // <V as Iterator>::Item = U, + // U: Iterator, U: Sized, + // V: Iterator, V: Sized, + // <U as Iterator>::Item: Copy + // When we substitute, say, `V => IntoIter<u32>, U => $0`, the last + // obligation will normalize to `<$0 as Iterator>::Item = $1` and + // `$1: Copy`, so we must ensure the obligations are emitted in + // that order. + let predicates = tcx.predicates_of(def_id); + assert_eq!(predicates.parent, None); + let predicates = predicates.instantiate_own(tcx, args); + let mut obligations = Vec::with_capacity(predicates.len()); + for (index, (predicate, span)) in predicates.into_iter().enumerate() { + let cause = + if Some(parent_trait_pred.def_id()) == tcx.lang_items().coerce_unsized_trait() { + cause.clone() + } else { + cause.clone().derived_cause(parent_trait_pred, |derived| { + ImplDerivedObligation(Box::new(ImplDerivedObligationCause { + derived, + impl_or_alias_def_id: def_id, + impl_def_predicate_index: Some(index), + span, + })) + }) + }; + let clause = normalize_with_depth_to( + self, + param_env, + cause.clone(), + recursion_depth, + predicate, + &mut obligations, + ); + obligations.push(Obligation { + cause, + recursion_depth, + param_env, + predicate: clause.as_predicate(), + }); + } + + obligations + } +} + +impl<'o, 'tcx> TraitObligationStack<'o, 'tcx> { + fn list(&'o self) -> TraitObligationStackList<'o, 'tcx> { + TraitObligationStackList::with(self) + } + + fn cache(&self) -> &'o ProvisionalEvaluationCache<'tcx> { + self.previous.cache + } + + fn iter(&'o self) -> TraitObligationStackList<'o, 'tcx> { + self.list() + } + + /// Indicates that attempting to evaluate this stack entry + /// required accessing something from the stack at depth `reached_depth`. + fn update_reached_depth(&self, reached_depth: usize) { + assert!( + self.depth >= reached_depth, + "invoked `update_reached_depth` with something under this stack: \ + self.depth={} reached_depth={}", + self.depth, + reached_depth, + ); + debug!(reached_depth, "update_reached_depth"); + let mut p = self; + while reached_depth < p.depth { + debug!(?p.fresh_trait_pred, "update_reached_depth: marking as cycle participant"); + p.reached_depth.set(p.reached_depth.get().min(reached_depth)); + p = p.previous.head.unwrap(); + } + } +} + +/// The "provisional evaluation cache" is used to store intermediate cache results +/// when solving auto traits. Auto traits are unusual in that they can support +/// cycles. So, for example, a "proof tree" like this would be ok: +/// +/// - `Foo<T>: Send` :- +/// - `Bar<T>: Send` :- +/// - `Foo<T>: Send` -- cycle, but ok +/// - `Baz<T>: Send` +/// +/// Here, to prove `Foo<T>: Send`, we have to prove `Bar<T>: Send` and +/// `Baz<T>: Send`. Proving `Bar<T>: Send` in turn required `Foo<T>: Send`. +/// For non-auto traits, this cycle would be an error, but for auto traits (because +/// they are coinductive) it is considered ok. +/// +/// However, there is a complication: at the point where we have +/// "proven" `Bar<T>: Send`, we have in fact only proven it +/// *provisionally*. In particular, we proved that `Bar<T>: Send` +/// *under the assumption* that `Foo<T>: Send`. But what if we later +/// find out this assumption is wrong? Specifically, we could +/// encounter some kind of error proving `Baz<T>: Send`. In that case, +/// `Bar<T>: Send` didn't turn out to be true. +/// +/// In Issue #60010, we found a bug in rustc where it would cache +/// these intermediate results. This was fixed in #60444 by disabling +/// *all* caching for things involved in a cycle -- in our example, +/// that would mean we don't cache that `Bar<T>: Send`. But this led +/// to large slowdowns. +/// +/// Specifically, imagine this scenario, where proving `Baz<T>: Send` +/// first requires proving `Bar<T>: Send` (which is true: +/// +/// - `Foo<T>: Send` :- +/// - `Bar<T>: Send` :- +/// - `Foo<T>: Send` -- cycle, but ok +/// - `Baz<T>: Send` +/// - `Bar<T>: Send` -- would be nice for this to be a cache hit! +/// - `*const T: Send` -- but what if we later encounter an error? +/// +/// The *provisional evaluation cache* resolves this issue. It stores +/// cache results that we've proven but which were involved in a cycle +/// in some way. We track the minimal stack depth (i.e., the +/// farthest from the top of the stack) that we are dependent on. +/// The idea is that the cache results within are all valid -- so long as +/// none of the nodes in between the current node and the node at that minimum +/// depth result in an error (in which case the cached results are just thrown away). +/// +/// During evaluation, we consult this provisional cache and rely on +/// it. Accessing a cached value is considered equivalent to accessing +/// a result at `reached_depth`, so it marks the *current* solution as +/// provisional as well. If an error is encountered, we toss out any +/// provisional results added from the subtree that encountered the +/// error. When we pop the node at `reached_depth` from the stack, we +/// can commit all the things that remain in the provisional cache. +struct ProvisionalEvaluationCache<'tcx> { + /// next "depth first number" to issue -- just a counter + dfn: Cell<usize>, + + /// Map from cache key to the provisionally evaluated thing. + /// The cache entries contain the result but also the DFN in which they + /// were added. The DFN is used to clear out values on failure. + /// + /// Imagine we have a stack like: + /// + /// - `A B C` and we add a cache for the result of C (DFN 2) + /// - Then we have a stack `A B D` where `D` has DFN 3 + /// - We try to solve D by evaluating E: `A B D E` (DFN 4) + /// - `E` generates various cache entries which have cyclic dependencies on `B` + /// - `A B D E F` and so forth + /// - the DFN of `F` for example would be 5 + /// - then we determine that `E` is in error -- we will then clear + /// all cache values whose DFN is >= 4 -- in this case, that + /// means the cached value for `F`. + map: RefCell<FxIndexMap<ty::PolyTraitPredicate<'tcx>, ProvisionalEvaluation>>, + + /// The stack of args that we assume to be true because a `WF(arg)` predicate + /// is on the stack above (and because of wellformedness is coinductive). + /// In an "ideal" world, this would share a stack with trait predicates in + /// `TraitObligationStack`. However, trait predicates are *much* hotter than + /// `WellFormed` predicates, and it's very likely that the additional matches + /// will have a perf effect. The value here is the well-formed `GenericArg` + /// and the depth of the trait predicate *above* that well-formed predicate. + wf_args: RefCell<Vec<(ty::GenericArg<'tcx>, usize)>>, +} + +/// A cache value for the provisional cache: contains the depth-first +/// number (DFN) and result. +#[derive(Copy, Clone, Debug)] +struct ProvisionalEvaluation { + from_dfn: usize, + reached_depth: usize, + result: EvaluationResult, +} + +impl<'tcx> Default for ProvisionalEvaluationCache<'tcx> { + fn default() -> Self { + Self { dfn: Cell::new(0), map: Default::default(), wf_args: Default::default() } + } +} + +impl<'tcx> ProvisionalEvaluationCache<'tcx> { + /// Get the next DFN in sequence (basically a counter). + fn next_dfn(&self) -> usize { + let result = self.dfn.get(); + self.dfn.set(result + 1); + result + } + + /// Check the provisional cache for any result for + /// `fresh_trait_ref`. If there is a hit, then you must consider + /// it an access to the stack slots at depth + /// `reached_depth` (from the returned value). + fn get_provisional( + &self, + fresh_trait_pred: ty::PolyTraitPredicate<'tcx>, + ) -> Option<ProvisionalEvaluation> { + debug!( + ?fresh_trait_pred, + "get_provisional = {:#?}", + self.map.borrow().get(&fresh_trait_pred), + ); + Some(*self.map.borrow().get(&fresh_trait_pred)?) + } + + /// Insert a provisional result into the cache. The result came + /// from the node with the given DFN. It accessed a minimum depth + /// of `reached_depth` to compute. It evaluated `fresh_trait_pred` + /// and resulted in `result`. + fn insert_provisional( + &self, + from_dfn: usize, + reached_depth: usize, + fresh_trait_pred: ty::PolyTraitPredicate<'tcx>, + result: EvaluationResult, + ) { + debug!(?from_dfn, ?fresh_trait_pred, ?result, "insert_provisional"); + + let mut map = self.map.borrow_mut(); + + // Subtle: when we complete working on the DFN `from_dfn`, anything + // that remains in the provisional cache must be dependent on some older + // stack entry than `from_dfn`. We have to update their depth with our transitive + // depth in that case or else it would be referring to some popped note. + // + // Example: + // A (reached depth 0) + // ... + // B // depth 1 -- reached depth = 0 + // C // depth 2 -- reached depth = 1 (should be 0) + // B + // A // depth 0 + // D (reached depth 1) + // C (cache -- reached depth = 2) + for (_k, v) in &mut *map { + if v.from_dfn >= from_dfn { + v.reached_depth = reached_depth.min(v.reached_depth); + } + } + + map.insert(fresh_trait_pred, ProvisionalEvaluation { from_dfn, reached_depth, result }); + } + + /// Invoked when the node with dfn `dfn` does not get a successful + /// result. This will clear out any provisional cache entries + /// that were added since `dfn` was created. This is because the + /// provisional entries are things which must assume that the + /// things on the stack at the time of their creation succeeded -- + /// since the failing node is presently at the top of the stack, + /// these provisional entries must either depend on it or some + /// ancestor of it. + fn on_failure(&self, dfn: usize) { + debug!(?dfn, "on_failure"); + self.map.borrow_mut().retain(|key, eval| { + if !eval.from_dfn >= dfn { + debug!("on_failure: removing {:?}", key); + false + } else { + true + } + }); + } + + /// Invoked when the node at depth `depth` completed without + /// depending on anything higher in the stack (if that completion + /// was a failure, then `on_failure` should have been invoked + /// already). + /// + /// Note that we may still have provisional cache items remaining + /// in the cache when this is done. For example, if there is a + /// cycle: + /// + /// * A depends on... + /// * B depends on A + /// * C depends on... + /// * D depends on C + /// * ... + /// + /// Then as we complete the C node we will have a provisional cache + /// with results for A, B, C, and D. This method would clear out + /// the C and D results, but leave A and B provisional. + /// + /// This is determined based on the DFN: we remove any provisional + /// results created since `dfn` started (e.g., in our example, dfn + /// would be 2, representing the C node, and hence we would + /// remove the result for D, which has DFN 3, but not the results for + /// A and B, which have DFNs 0 and 1 respectively). + /// + /// Note that we *do not* attempt to cache these cycle participants + /// in the evaluation cache. Doing so would require carefully computing + /// the correct `DepNode` to store in the cache entry: + /// cycle participants may implicitly depend on query results + /// related to other participants in the cycle, due to our logic + /// which examines the evaluation stack. + /// + /// We used to try to perform this caching, + /// but it lead to multiple incremental compilation ICEs + /// (see #92987 and #96319), and was very hard to understand. + /// Fortunately, removing the caching didn't seem to + /// have a performance impact in practice. + fn on_completion(&self, dfn: usize) { + debug!(?dfn, "on_completion"); + self.map.borrow_mut().retain(|fresh_trait_pred, eval| { + if eval.from_dfn >= dfn { + debug!(?fresh_trait_pred, ?eval, "on_completion"); + return false; + } + true + }); + } +} + +#[derive(Copy, Clone)] +struct TraitObligationStackList<'o, 'tcx> { + cache: &'o ProvisionalEvaluationCache<'tcx>, + head: Option<&'o TraitObligationStack<'o, 'tcx>>, +} + +impl<'o, 'tcx> TraitObligationStackList<'o, 'tcx> { + fn empty(cache: &'o ProvisionalEvaluationCache<'tcx>) -> TraitObligationStackList<'o, 'tcx> { + TraitObligationStackList { cache, head: None } + } + + fn with(r: &'o TraitObligationStack<'o, 'tcx>) -> TraitObligationStackList<'o, 'tcx> { + TraitObligationStackList { cache: r.cache(), head: Some(r) } + } + + fn head(&self) -> Option<&'o TraitObligationStack<'o, 'tcx>> { + self.head + } + + fn depth(&self) -> usize { + if let Some(head) = self.head { head.depth } else { 0 } + } +} + +impl<'o, 'tcx> Iterator for TraitObligationStackList<'o, 'tcx> { + type Item = &'o TraitObligationStack<'o, 'tcx>; + + fn next(&mut self) -> Option<&'o TraitObligationStack<'o, 'tcx>> { + let o = self.head?; + *self = o.previous; + Some(o) + } +} + +impl<'o, 'tcx> fmt::Debug for TraitObligationStack<'o, 'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "TraitObligationStack({:?})", self.obligation) + } +} + +pub enum ProjectionMatchesProjection { + Yes, + Ambiguous, + No, +} + +/// Replace all regions inside the coroutine interior with late bound regions. +/// Note that each region slot in the types gets a new fresh late bound region, which means that +/// none of the regions inside relate to any other, even if typeck had previously found constraints +/// that would cause them to be related. +#[instrument(level = "trace", skip(infcx), ret)] +fn bind_coroutine_hidden_types_above<'tcx>( + infcx: &InferCtxt<'tcx>, + def_id: DefId, + args: ty::GenericArgsRef<'tcx>, + bound_vars: &ty::List<ty::BoundVariableKind>, +) -> ty::Binder<'tcx, Vec<Ty<'tcx>>> { + let tcx = infcx.tcx; + let mut seen_tys = FxHashSet::default(); + + let considering_regions = infcx.considering_regions; + + let num_bound_variables = bound_vars.len() as u32; + let mut counter = num_bound_variables; + + let hidden_types: Vec<_> = tcx + .coroutine_hidden_types(def_id) + // Deduplicate tys to avoid repeated work. + .filter(|bty| seen_tys.insert(*bty)) + .map(|mut bty| { + // Only remap erased regions if we use them. + if considering_regions { + bty = bty.map_bound(|ty| { + tcx.fold_regions(ty, |r, current_depth| match r.kind() { + ty::ReErased => { + let br = ty::BoundRegion { + var: ty::BoundVar::from_u32(counter), + kind: ty::BrAnon, + }; + counter += 1; + ty::Region::new_bound(tcx, current_depth, br) + } + r => bug!("unexpected region: {r:?}"), + }) + }) + } + + bty.instantiate(tcx, args) + }) + .collect(); + let bound_vars = + tcx.mk_bound_variable_kinds_from_iter(bound_vars.iter().chain( + (num_bound_variables..counter).map(|_| ty::BoundVariableKind::Region(ty::BrAnon)), + )); + ty::Binder::bind_with_vars(hidden_types, bound_vars) +} diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs new file mode 100644 index 00000000000..d43ab0c8e85 --- /dev/null +++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs @@ -0,0 +1,514 @@ +//! Logic and data structures related to impl specialization, explained in +//! greater detail below. +//! +//! At the moment, this implementation support only the simple "chain" rule: +//! If any two impls overlap, one must be a strict subset of the other. +//! +//! See the [rustc dev guide] for a bit more detail on how specialization +//! fits together with the rest of the trait machinery. +//! +//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/specialization.html + +pub mod specialization_graph; +use rustc_infer::infer::DefineOpaqueTypes; +use specialization_graph::GraphExt; + +use crate::errors::NegativePositiveConflict; +use crate::infer::{InferCtxt, InferOk, TyCtxtInferExt}; +use crate::traits::select::IntercrateAmbiguityCause; +use crate::traits::{ + self, coherence, FutureCompatOverlapErrorKind, ObligationCause, ObligationCtxt, +}; +use rustc_data_structures::fx::FxIndexSet; +use rustc_errors::{error_code, DelayDm, Diagnostic}; +use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_middle::ty::{self, ImplSubject, Ty, TyCtxt, TypeVisitableExt}; +use rustc_middle::ty::{GenericArgs, GenericArgsRef}; +use rustc_session::lint::builtin::COHERENCE_LEAK_CHECK; +use rustc_session::lint::builtin::ORDER_DEPENDENT_TRAIT_OBJECTS; +use rustc_span::{Span, DUMMY_SP}; + +use super::util; +use super::SelectionContext; + +/// Information pertinent to an overlapping impl error. +#[derive(Debug)] +pub struct OverlapError<'tcx> { + pub with_impl: DefId, + pub trait_ref: ty::TraitRef<'tcx>, + pub self_ty: Option<Ty<'tcx>>, + pub intercrate_ambiguity_causes: FxIndexSet<IntercrateAmbiguityCause<'tcx>>, + pub involves_placeholder: bool, +} + +/// Given a subst for the requested impl, translate it to a subst +/// appropriate for the actual item definition (whether it be in that impl, +/// a parent impl, or the trait). +/// +/// When we have selected one impl, but are actually using item definitions from +/// a parent impl providing a default, we need a way to translate between the +/// type parameters of the two impls. Here the `source_impl` is the one we've +/// selected, and `source_args` is a substitution of its generics. +/// And `target_node` is the impl/trait we're actually going to get the +/// definition from. The resulting substitution will map from `target_node`'s +/// generics to `source_impl`'s generics as instantiated by `source_subst`. +/// +/// For example, consider the following scenario: +/// +/// ```ignore (illustrative) +/// trait Foo { ... } +/// impl<T, U> Foo for (T, U) { ... } // target impl +/// impl<V> Foo for (V, V) { ... } // source impl +/// ``` +/// +/// Suppose we have selected "source impl" with `V` instantiated with `u32`. +/// This function will produce a substitution with `T` and `U` both mapping to `u32`. +/// +/// where-clauses add some trickiness here, because they can be used to "define" +/// an argument indirectly: +/// +/// ```ignore (illustrative) +/// impl<'a, I, T: 'a> Iterator for Cloned<I> +/// where I: Iterator<Item = &'a T>, T: Clone +/// ``` +/// +/// In a case like this, the substitution for `T` is determined indirectly, +/// through associated type projection. We deal with such cases by using +/// *fulfillment* to relate the two impls, requiring that all projections are +/// resolved. +pub fn translate_args<'tcx>( + infcx: &InferCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + source_impl: DefId, + source_args: GenericArgsRef<'tcx>, + target_node: specialization_graph::Node, +) -> GenericArgsRef<'tcx> { + translate_args_with_cause(infcx, param_env, source_impl, source_args, target_node, |_, _| { + ObligationCause::dummy() + }) +} + +/// Like [translate_args], but obligations from the parent implementation +/// are registered with the provided `ObligationCause`. +/// +/// This is for reporting *region* errors from those bounds. Type errors should +/// not happen because the specialization graph already checks for those, and +/// will result in an ICE. +pub fn translate_args_with_cause<'tcx>( + infcx: &InferCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + source_impl: DefId, + source_args: GenericArgsRef<'tcx>, + target_node: specialization_graph::Node, + cause: impl Fn(usize, Span) -> ObligationCause<'tcx>, +) -> GenericArgsRef<'tcx> { + debug!( + "translate_args({:?}, {:?}, {:?}, {:?})", + param_env, source_impl, source_args, target_node + ); + let source_trait_ref = + infcx.tcx.impl_trait_ref(source_impl).unwrap().instantiate(infcx.tcx, source_args); + + // translate the Self and Param parts of the substitution, since those + // vary across impls + let target_args = match target_node { + specialization_graph::Node::Impl(target_impl) => { + // no need to translate if we're targeting the impl we started with + if source_impl == target_impl { + return source_args; + } + + fulfill_implication(infcx, param_env, source_trait_ref, source_impl, target_impl, cause) + .unwrap_or_else(|()| { + bug!( + "When translating substitutions from {source_impl:?} to {target_impl:?}, \ + the expected specialization failed to hold" + ) + }) + } + specialization_graph::Node::Trait(..) => source_trait_ref.args, + }; + + // directly inherent the method generics, since those do not vary across impls + source_args.rebase_onto(infcx.tcx, source_impl, target_args) +} + +/// Is `impl1` a specialization of `impl2`? +/// +/// Specialization is determined by the sets of types to which the impls apply; +/// `impl1` specializes `impl2` if it applies to a subset of the types `impl2` applies +/// to. +#[instrument(skip(tcx), level = "debug")] +pub(super) fn specializes(tcx: TyCtxt<'_>, (impl1_def_id, impl2_def_id): (DefId, DefId)) -> bool { + // The feature gate should prevent introducing new specializations, but not + // taking advantage of upstream ones. + let features = tcx.features(); + let specialization_enabled = features.specialization || features.min_specialization; + if !specialization_enabled && (impl1_def_id.is_local() || impl2_def_id.is_local()) { + return false; + } + + // We determine whether there's a subset relationship by: + // + // - replacing bound vars with placeholders in impl1, + // - assuming the where clauses for impl1, + // - instantiating impl2 with fresh inference variables, + // - unifying, + // - attempting to prove the where clauses for impl2 + // + // The last three steps are encapsulated in `fulfill_implication`. + // + // See RFC 1210 for more details and justification. + + // Currently we do not allow e.g., a negative impl to specialize a positive one + if tcx.impl_polarity(impl1_def_id) != tcx.impl_polarity(impl2_def_id) { + return false; + } + + // create a parameter environment corresponding to a (placeholder) instantiation of impl1 + let penv = tcx.param_env(impl1_def_id); + let impl1_trait_ref = tcx.impl_trait_ref(impl1_def_id).unwrap().instantiate_identity(); + + // Create an infcx, taking the predicates of impl1 as assumptions: + let infcx = tcx.infer_ctxt().build(); + + // Attempt to prove that impl2 applies, given all of the above. + fulfill_implication(&infcx, penv, impl1_trait_ref, impl1_def_id, impl2_def_id, |_, _| { + ObligationCause::dummy() + }) + .is_ok() +} + +/// Attempt to fulfill all obligations of `target_impl` after unification with +/// `source_trait_ref`. If successful, returns a substitution for *all* the +/// generics of `target_impl`, including both those needed to unify with +/// `source_trait_ref` and those whose identity is determined via a where +/// clause in the impl. +fn fulfill_implication<'tcx>( + infcx: &InferCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + source_trait_ref: ty::TraitRef<'tcx>, + source_impl: DefId, + target_impl: DefId, + error_cause: impl Fn(usize, Span) -> ObligationCause<'tcx>, +) -> Result<GenericArgsRef<'tcx>, ()> { + debug!( + "fulfill_implication({:?}, trait_ref={:?} |- {:?} applies)", + param_env, source_trait_ref, target_impl + ); + + let source_trait_ref = + match traits::fully_normalize(infcx, ObligationCause::dummy(), param_env, source_trait_ref) + { + Ok(source_trait_ref) => source_trait_ref, + Err(_errors) => { + infcx.dcx().span_delayed_bug( + infcx.tcx.def_span(source_impl), + format!("failed to fully normalize {source_trait_ref}"), + ); + source_trait_ref + } + }; + + let source_trait = ImplSubject::Trait(source_trait_ref); + + let selcx = &mut SelectionContext::new(infcx); + let target_args = infcx.fresh_args_for_item(DUMMY_SP, target_impl); + let (target_trait, obligations) = + util::impl_subject_and_oblig(selcx, param_env, target_impl, target_args, error_cause); + + // do the impls unify? If not, no specialization. + let Ok(InferOk { obligations: more_obligations, .. }) = infcx + .at(&ObligationCause::dummy(), param_env) + .eq(DefineOpaqueTypes::No, source_trait, target_trait) + else { + debug!("fulfill_implication: {:?} does not unify with {:?}", source_trait, target_trait); + return Err(()); + }; + + // Needs to be `in_snapshot` because this function is used to rebase + // substitutions, which may happen inside of a select within a probe. + let ocx = ObligationCtxt::new(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)); + + let errors = ocx.select_all_or_error(); + if !errors.is_empty() { + // no dice! + debug!( + "fulfill_implication: for impls on {:?} and {:?}, \ + could not fulfill: {:?} given {:?}", + source_trait, + target_trait, + errors, + param_env.caller_bounds() + ); + return Err(()); + } + + debug!("fulfill_implication: an impl for {:?} specializes {:?}", source_trait, target_trait); + + // Now resolve the *substitution* we built for the target earlier, replacing + // the inference variables inside with whatever we got from fulfillment. + Ok(infcx.resolve_vars_if_possible(target_args)) +} + +/// Query provider for `specialization_graph_of`. +pub(super) fn specialization_graph_provider( + tcx: TyCtxt<'_>, + trait_id: DefId, +) -> specialization_graph::Graph { + let mut sg = specialization_graph::Graph::new(); + let overlap_mode = specialization_graph::OverlapMode::get(tcx, trait_id); + + let mut trait_impls: Vec<_> = tcx.all_impls(trait_id).collect(); + + // The coherence checking implementation seems to rely on impls being + // iterated over (roughly) in definition order, so we are sorting by + // negated `CrateNum` (so remote definitions are visited first) and then + // by a flattened version of the `DefIndex`. + trait_impls + .sort_unstable_by_key(|def_id| (-(def_id.krate.as_u32() as i64), def_id.index.index())); + + for impl_def_id in trait_impls { + if let Some(impl_def_id) = impl_def_id.as_local() { + // This is where impl overlap checking happens: + let insert_result = sg.insert(tcx, impl_def_id.to_def_id(), overlap_mode); + // Report error if there was one. + let (overlap, used_to_be_allowed) = match insert_result { + Err(overlap) => (Some(overlap), None), + Ok(Some(overlap)) => (Some(overlap.error), Some(overlap.kind)), + Ok(None) => (None, None), + }; + + if let Some(overlap) = overlap { + report_overlap_conflict(tcx, overlap, impl_def_id, used_to_be_allowed, &mut sg); + } + } else { + let parent = tcx.impl_parent(impl_def_id).unwrap_or(trait_id); + sg.record_impl_from_cstore(tcx, parent, impl_def_id) + } + } + + sg +} + +// This function is only used when +// encountering errors and inlining +// it negatively impacts perf. +#[cold] +#[inline(never)] +fn report_overlap_conflict<'tcx>( + tcx: TyCtxt<'tcx>, + overlap: OverlapError<'tcx>, + impl_def_id: LocalDefId, + used_to_be_allowed: Option<FutureCompatOverlapErrorKind>, + sg: &mut specialization_graph::Graph, +) { + let impl_polarity = tcx.impl_polarity(impl_def_id.to_def_id()); + let other_polarity = tcx.impl_polarity(overlap.with_impl); + match (impl_polarity, other_polarity) { + (ty::ImplPolarity::Negative, ty::ImplPolarity::Positive) => { + report_negative_positive_conflict( + tcx, + &overlap, + impl_def_id, + impl_def_id.to_def_id(), + overlap.with_impl, + sg, + ); + } + + (ty::ImplPolarity::Positive, ty::ImplPolarity::Negative) => { + report_negative_positive_conflict( + tcx, + &overlap, + impl_def_id, + overlap.with_impl, + impl_def_id.to_def_id(), + sg, + ); + } + + _ => { + report_conflicting_impls(tcx, overlap, impl_def_id, used_to_be_allowed, sg); + } + } +} + +fn report_negative_positive_conflict<'tcx>( + tcx: TyCtxt<'tcx>, + overlap: &OverlapError<'tcx>, + local_impl_def_id: LocalDefId, + negative_impl_def_id: DefId, + positive_impl_def_id: DefId, + sg: &mut specialization_graph::Graph, +) { + let mut err = tcx.dcx().create_err(NegativePositiveConflict { + impl_span: tcx.def_span(local_impl_def_id), + trait_desc: overlap.trait_ref, + self_ty: overlap.self_ty, + negative_impl_span: tcx.span_of_impl(negative_impl_def_id), + positive_impl_span: tcx.span_of_impl(positive_impl_def_id), + }); + sg.has_errored = Some(err.emit()); +} + +fn report_conflicting_impls<'tcx>( + tcx: TyCtxt<'tcx>, + overlap: OverlapError<'tcx>, + impl_def_id: LocalDefId, + used_to_be_allowed: Option<FutureCompatOverlapErrorKind>, + sg: &mut specialization_graph::Graph, +) { + let impl_span = tcx.def_span(impl_def_id); + + // Work to be done after we've built the DiagnosticBuilder. We have to define it + // now because the struct_lint methods don't return back the DiagnosticBuilder + // that's passed in. + fn decorate<'tcx>( + tcx: TyCtxt<'tcx>, + overlap: &OverlapError<'tcx>, + impl_span: Span, + err: &mut Diagnostic, + ) { + if (overlap.trait_ref, overlap.self_ty).references_error() { + err.downgrade_to_delayed_bug(); + } + + match tcx.span_of_impl(overlap.with_impl) { + Ok(span) => { + err.span_label(span, "first implementation here"); + + err.span_label( + impl_span, + format!( + "conflicting implementation{}", + overlap.self_ty.map_or_else(String::new, |ty| format!(" for `{ty}`")) + ), + ); + } + Err(cname) => { + let msg = match to_pretty_impl_header(tcx, overlap.with_impl) { + Some(s) => { + format!("conflicting implementation in crate `{cname}`:\n- {s}") + } + None => format!("conflicting implementation in crate `{cname}`"), + }; + err.note(msg); + } + } + + for cause in &overlap.intercrate_ambiguity_causes { + cause.add_intercrate_ambiguity_hint(err); + } + + if overlap.involves_placeholder { + coherence::add_placeholder_note(err); + } + } + + let msg = DelayDm(|| { + format!( + "conflicting implementations of trait `{}`{}{}", + overlap.trait_ref.print_trait_sugared(), + overlap.self_ty.map_or_else(String::new, |ty| format!(" for type `{ty}`")), + match used_to_be_allowed { + Some(FutureCompatOverlapErrorKind::Issue33140) => ": (E0119)", + _ => "", + } + ) + }); + + match used_to_be_allowed { + None => { + let reported = if overlap.with_impl.is_local() + || tcx.orphan_check_impl(impl_def_id).is_ok() + { + let mut err = tcx.dcx().struct_span_err(impl_span, msg); + err.code(error_code!(E0119)); + decorate(tcx, &overlap, impl_span, &mut err); + Some(err.emit()) + } else { + Some( + tcx.dcx() + .span_delayed_bug(impl_span, "impl should have failed the orphan check"), + ) + }; + sg.has_errored = reported; + } + Some(kind) => { + let lint = match kind { + FutureCompatOverlapErrorKind::Issue33140 => ORDER_DEPENDENT_TRAIT_OBJECTS, + FutureCompatOverlapErrorKind::LeakCheck => COHERENCE_LEAK_CHECK, + }; + tcx.struct_span_lint_hir( + lint, + tcx.local_def_id_to_hir_id(impl_def_id), + impl_span, + msg, + |err| { + decorate(tcx, &overlap, impl_span, err); + }, + ); + } + }; +} + +/// Recovers the "impl X for Y" signature from `impl_def_id` and returns it as a +/// string. +pub(crate) fn to_pretty_impl_header(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Option<String> { + use std::fmt::Write; + + let trait_ref = tcx.impl_trait_ref(impl_def_id)?.instantiate_identity(); + let mut w = "impl".to_owned(); + + let args = GenericArgs::identity_for_item(tcx, impl_def_id); + + // FIXME: Currently only handles ?Sized. + // Needs to support ?Move and ?DynSized when they are implemented. + let mut types_without_default_bounds = FxIndexSet::default(); + let sized_trait = tcx.lang_items().sized_trait(); + + let arg_names = args.iter().map(|k| k.to_string()).filter(|k| k != "'_").collect::<Vec<_>>(); + if !arg_names.is_empty() { + types_without_default_bounds.extend(args.types()); + w.push('<'); + w.push_str(&arg_names.join(", ")); + w.push('>'); + } + + write!( + w, + " {} for {}", + trait_ref.print_only_trait_path(), + tcx.type_of(impl_def_id).instantiate_identity() + ) + .unwrap(); + + // The predicates will contain default bounds like `T: Sized`. We need to + // remove these bounds, and add `T: ?Sized` to any untouched type parameters. + let predicates = tcx.predicates_of(impl_def_id).predicates; + let mut pretty_predicates = + Vec::with_capacity(predicates.len() + types_without_default_bounds.len()); + + for (p, _) in predicates { + 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; + } + } + pretty_predicates.push(p.to_string()); + } + + pretty_predicates.extend(types_without_default_bounds.iter().map(|ty| format!("{ty}: ?Sized"))); + + if !pretty_predicates.is_empty() { + write!(w, "\n where {}", pretty_predicates.join(", ")).unwrap(); + } + + w.push(';'); + Some(w) +} diff --git a/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs b/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs new file mode 100644 index 00000000000..e9a592bdee7 --- /dev/null +++ b/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs @@ -0,0 +1,431 @@ +use super::OverlapError; + +use crate::traits; +use rustc_errors::ErrorGuaranteed; +use rustc_hir::def_id::DefId; +use rustc_middle::ty::fast_reject::{self, SimplifiedType, TreatParams}; +use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt}; + +pub use rustc_middle::traits::specialization_graph::*; + +#[derive(Copy, Clone, Debug)] +pub enum FutureCompatOverlapErrorKind { + Issue33140, + LeakCheck, +} + +#[derive(Debug)] +pub struct FutureCompatOverlapError<'tcx> { + pub error: OverlapError<'tcx>, + pub kind: FutureCompatOverlapErrorKind, +} + +/// The result of attempting to insert an impl into a group of children. +#[derive(Debug)] +enum Inserted<'tcx> { + /// The impl was inserted as a new child in this group of children. + BecameNewSibling(Option<FutureCompatOverlapError<'tcx>>), + + /// The impl should replace existing impls [X1, ..], because the impl specializes X1, X2, etc. + ReplaceChildren(Vec<DefId>), + + /// The impl is a specialization of an existing child. + ShouldRecurseOn(DefId), +} + +trait ChildrenExt<'tcx> { + fn insert_blindly(&mut self, tcx: TyCtxt<'tcx>, impl_def_id: DefId); + fn remove_existing(&mut self, tcx: TyCtxt<'tcx>, impl_def_id: DefId); + + fn insert( + &mut self, + tcx: TyCtxt<'tcx>, + impl_def_id: DefId, + simplified_self: Option<SimplifiedType>, + overlap_mode: OverlapMode, + ) -> Result<Inserted<'tcx>, OverlapError<'tcx>>; +} + +impl<'tcx> ChildrenExt<'tcx> for Children { + /// Insert an impl into this set of children without comparing to any existing impls. + fn insert_blindly(&mut self, tcx: TyCtxt<'tcx>, impl_def_id: DefId) { + let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap().skip_binder(); + if let Some(st) = + fast_reject::simplify_type(tcx, trait_ref.self_ty(), TreatParams::AsCandidateKey) + { + debug!("insert_blindly: impl_def_id={:?} st={:?}", impl_def_id, st); + self.non_blanket_impls.entry(st).or_default().push(impl_def_id) + } else { + debug!("insert_blindly: impl_def_id={:?} st=None", impl_def_id); + self.blanket_impls.push(impl_def_id) + } + } + + /// Removes an impl from this set of children. Used when replacing + /// an impl with a parent. The impl must be present in the list of + /// children already. + fn remove_existing(&mut self, tcx: TyCtxt<'tcx>, impl_def_id: DefId) { + let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap().skip_binder(); + let vec: &mut Vec<DefId>; + if let Some(st) = + fast_reject::simplify_type(tcx, trait_ref.self_ty(), TreatParams::AsCandidateKey) + { + debug!("remove_existing: impl_def_id={:?} st={:?}", impl_def_id, st); + vec = self.non_blanket_impls.get_mut(&st).unwrap(); + } else { + debug!("remove_existing: impl_def_id={:?} st=None", impl_def_id); + vec = &mut self.blanket_impls; + } + + let index = vec.iter().position(|d| *d == impl_def_id).unwrap(); + vec.remove(index); + } + + /// Attempt to insert an impl into this set of children, while comparing for + /// specialization relationships. + #[instrument(level = "debug", skip(self, tcx), ret)] + fn insert( + &mut self, + tcx: TyCtxt<'tcx>, + impl_def_id: DefId, + simplified_self: Option<SimplifiedType>, + overlap_mode: OverlapMode, + ) -> Result<Inserted<'tcx>, OverlapError<'tcx>> { + let mut last_lint = None; + let mut replace_children = Vec::new(); + + let possible_siblings = match simplified_self { + Some(st) => PotentialSiblings::Filtered(filtered_children(self, st)), + None => PotentialSiblings::Unfiltered(iter_children(self)), + }; + + for possible_sibling in possible_siblings { + debug!(?possible_sibling); + + let create_overlap_error = |overlap: traits::coherence::OverlapResult<'tcx>| { + let trait_ref = overlap.impl_header.trait_ref.unwrap(); + let self_ty = trait_ref.self_ty(); + + OverlapError { + with_impl: possible_sibling, + trait_ref, + // Only report the `Self` type if it has at least + // some outer concrete shell; otherwise, it's + // not adding much information. + self_ty: self_ty.has_concrete_skeleton().then_some(self_ty), + intercrate_ambiguity_causes: overlap.intercrate_ambiguity_causes, + involves_placeholder: overlap.involves_placeholder, + } + }; + + let report_overlap_error = |overlap: traits::coherence::OverlapResult<'tcx>, + last_lint: &mut _| { + // Found overlap, but no specialization; error out or report future-compat warning. + + // Do we *still* get overlap if we disable the future-incompatible modes? + let should_err = traits::overlapping_impls( + tcx, + possible_sibling, + impl_def_id, + traits::SkipLeakCheck::default(), + overlap_mode, + ) + .is_some(); + + let error = create_overlap_error(overlap); + + if should_err { + Err(error) + } else { + *last_lint = Some(FutureCompatOverlapError { + error, + kind: FutureCompatOverlapErrorKind::LeakCheck, + }); + + Ok((false, false)) + } + }; + + let last_lint_mut = &mut last_lint; + let (le, ge) = traits::overlapping_impls( + tcx, + possible_sibling, + impl_def_id, + traits::SkipLeakCheck::Yes, + overlap_mode, + ) + .map_or(Ok((false, false)), |overlap| { + if let Some(overlap_kind) = + tcx.impls_are_allowed_to_overlap(impl_def_id, possible_sibling) + { + match overlap_kind { + ty::ImplOverlapKind::Permitted { marker: _ } => {} + ty::ImplOverlapKind::Issue33140 => { + *last_lint_mut = Some(FutureCompatOverlapError { + error: create_overlap_error(overlap), + kind: FutureCompatOverlapErrorKind::Issue33140, + }); + } + } + + return Ok((false, false)); + } + + let le = tcx.specializes((impl_def_id, possible_sibling)); + let ge = tcx.specializes((possible_sibling, impl_def_id)); + + if le == ge { report_overlap_error(overlap, last_lint_mut) } else { Ok((le, ge)) } + })?; + + if le && !ge { + debug!( + "descending as child of TraitRef {:?}", + tcx.impl_trait_ref(possible_sibling).unwrap().instantiate_identity() + ); + + // The impl specializes `possible_sibling`. + return Ok(Inserted::ShouldRecurseOn(possible_sibling)); + } else if ge && !le { + debug!( + "placing as parent of TraitRef {:?}", + tcx.impl_trait_ref(possible_sibling).unwrap().instantiate_identity() + ); + + replace_children.push(possible_sibling); + } else { + // Either there's no overlap, or the overlap was already reported by + // `overlap_error`. + } + } + + if !replace_children.is_empty() { + return Ok(Inserted::ReplaceChildren(replace_children)); + } + + // No overlap with any potential siblings, so add as a new sibling. + debug!("placing as new sibling"); + self.insert_blindly(tcx, impl_def_id); + Ok(Inserted::BecameNewSibling(last_lint)) + } +} + +fn iter_children(children: &mut Children) -> impl Iterator<Item = DefId> + '_ { + let nonblanket = children.non_blanket_impls.iter().flat_map(|(_, v)| v.iter()); + children.blanket_impls.iter().chain(nonblanket).cloned() +} + +fn filtered_children( + children: &mut Children, + st: SimplifiedType, +) -> impl Iterator<Item = DefId> + '_ { + let nonblanket = children.non_blanket_impls.entry(st).or_default().iter(); + children.blanket_impls.iter().chain(nonblanket).cloned() +} + +// A custom iterator used by Children::insert +enum PotentialSiblings<I, J> +where + I: Iterator<Item = DefId>, + J: Iterator<Item = DefId>, +{ + Unfiltered(I), + Filtered(J), +} + +impl<I, J> Iterator for PotentialSiblings<I, J> +where + I: Iterator<Item = DefId>, + J: Iterator<Item = DefId>, +{ + type Item = DefId; + + fn next(&mut self) -> Option<Self::Item> { + match *self { + PotentialSiblings::Unfiltered(ref mut iter) => iter.next(), + PotentialSiblings::Filtered(ref mut iter) => iter.next(), + } + } +} + +pub trait GraphExt<'tcx> { + /// Insert a local impl into the specialization graph. If an existing impl + /// conflicts with it (has overlap, but neither specializes the other), + /// information about the area of overlap is returned in the `Err`. + fn insert( + &mut self, + tcx: TyCtxt<'tcx>, + impl_def_id: DefId, + overlap_mode: OverlapMode, + ) -> Result<Option<FutureCompatOverlapError<'tcx>>, OverlapError<'tcx>>; + + /// Insert cached metadata mapping from a child impl back to its parent. + fn record_impl_from_cstore(&mut self, tcx: TyCtxt<'tcx>, parent: DefId, child: DefId); +} + +impl<'tcx> GraphExt<'tcx> for Graph { + /// Insert a local impl into the specialization graph. If an existing impl + /// conflicts with it (has overlap, but neither specializes the other), + /// information about the area of overlap is returned in the `Err`. + fn insert( + &mut self, + tcx: TyCtxt<'tcx>, + impl_def_id: DefId, + overlap_mode: OverlapMode, + ) -> Result<Option<FutureCompatOverlapError<'tcx>>, OverlapError<'tcx>> { + assert!(impl_def_id.is_local()); + + // FIXME: use `EarlyBinder` in `self.children` + let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap().skip_binder(); + let trait_def_id = trait_ref.def_id; + + debug!( + "insert({:?}): inserting TraitRef {:?} into specialization graph", + impl_def_id, trait_ref + ); + + // If the reference itself contains an earlier error (e.g., due to a + // resolution failure), then we just insert the impl at the top level of + // the graph and claim that there's no overlap (in order to suppress + // bogus errors). + if trait_ref.references_error() { + debug!( + "insert: inserting dummy node for erroneous TraitRef {:?}, \ + impl_def_id={:?}, trait_def_id={:?}", + trait_ref, impl_def_id, trait_def_id + ); + + self.parent.insert(impl_def_id, trait_def_id); + self.children.entry(trait_def_id).or_default().insert_blindly(tcx, impl_def_id); + return Ok(None); + } + + let mut parent = trait_def_id; + let mut last_lint = None; + let simplified = + fast_reject::simplify_type(tcx, trait_ref.self_ty(), TreatParams::AsCandidateKey); + + // Descend the specialization tree, where `parent` is the current parent node. + loop { + use self::Inserted::*; + + let insert_result = self.children.entry(parent).or_default().insert( + tcx, + impl_def_id, + simplified, + overlap_mode, + )?; + + match insert_result { + BecameNewSibling(opt_lint) => { + last_lint = opt_lint; + break; + } + ReplaceChildren(grand_children_to_be) => { + // We currently have + // + // P + // | + // G + // + // and we are inserting the impl N. We want to make it: + // + // P + // | + // N + // | + // G + + // Adjust P's list of children: remove G and then add N. + { + let siblings = self.children.get_mut(&parent).unwrap(); + for &grand_child_to_be in &grand_children_to_be { + siblings.remove_existing(tcx, grand_child_to_be); + } + siblings.insert_blindly(tcx, impl_def_id); + } + + // Set G's parent to N and N's parent to P. + for &grand_child_to_be in &grand_children_to_be { + self.parent.insert(grand_child_to_be, impl_def_id); + } + self.parent.insert(impl_def_id, parent); + + // Add G as N's child. + for &grand_child_to_be in &grand_children_to_be { + self.children + .entry(impl_def_id) + .or_default() + .insert_blindly(tcx, grand_child_to_be); + } + break; + } + ShouldRecurseOn(new_parent) => { + parent = new_parent; + } + } + } + + self.parent.insert(impl_def_id, parent); + Ok(last_lint) + } + + /// Insert cached metadata mapping from a child impl back to its parent. + fn record_impl_from_cstore(&mut self, tcx: TyCtxt<'tcx>, parent: DefId, child: DefId) { + if self.parent.insert(child, parent).is_some() { + bug!( + "When recording an impl from the crate store, information about its parent \ + was already present." + ); + } + + self.children.entry(parent).or_default().insert_blindly(tcx, child); + } +} + +/// Locate the definition of an associated type in the specialization hierarchy, +/// starting from the given impl. +pub(crate) fn assoc_def( + tcx: TyCtxt<'_>, + impl_def_id: DefId, + assoc_def_id: DefId, +) -> Result<LeafDef, ErrorGuaranteed> { + let trait_def_id = tcx.trait_id_of_impl(impl_def_id).unwrap(); + let trait_def = tcx.trait_def(trait_def_id); + + // This function may be called while we are still building the + // specialization graph that is queried below (via TraitDef::ancestors()), + // so, in order to avoid unnecessary infinite recursion, we manually look + // for the associated item at the given impl. + // If there is no such item in that impl, this function will fail with a + // cycle error if the specialization graph is currently being built. + if let Some(&impl_item_id) = tcx.impl_item_implementor_ids(impl_def_id).get(&assoc_def_id) { + let item = tcx.associated_item(impl_item_id); + let impl_node = Node::Impl(impl_def_id); + return Ok(LeafDef { + item, + defining_node: impl_node, + finalizing_node: if item.defaultness(tcx).is_default() { + None + } else { + Some(impl_node) + }, + }); + } + + let ancestors = trait_def.ancestors(tcx, impl_def_id)?; + if let Some(assoc_item) = ancestors.leaf_def(tcx, assoc_def_id) { + Ok(assoc_item) + } else { + // This is saying that neither the trait nor + // the impl contain a definition for this + // associated type. Normally this situation + // could only arise through a compiler bug -- + // if the user wrote a bad item name, it + // should have failed in astconv. + bug!( + "No associated type `{}` for {}", + tcx.item_name(assoc_def_id), + tcx.def_path_str(impl_def_id) + ) + } +} diff --git a/compiler/rustc_trait_selection/src/traits/structural_match.rs b/compiler/rustc_trait_selection/src/traits/structural_match.rs new file mode 100644 index 00000000000..868a8a3e8b8 --- /dev/null +++ b/compiler/rustc_trait_selection/src/traits/structural_match.rs @@ -0,0 +1,174 @@ +use rustc_data_structures::fx::FxHashSet; +use rustc_hir as hir; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor}; +use rustc_span::Span; +use std::ops::ControlFlow; + +/// This method traverses the structure of `ty`, trying to find an +/// instance of an ADT (i.e. struct or enum) that doesn't implement +/// the structural-match traits, or a generic type parameter +/// (which cannot be determined to be structural-match). +/// +/// The "structure of a type" includes all components that would be +/// considered when doing a pattern match on a constant of that +/// type. +/// +/// * This means this method descends into fields of structs/enums, +/// and also descends into the inner type `T` of `&T` and `&mut T` +/// +/// * The traversal doesn't dereference unsafe pointers (`*const T`, +/// `*mut T`), and it does not visit the type arguments of an +/// instantiated generic like `PhantomData<T>`. +/// +/// The reason we do this search is Rust currently require all ADTs +/// reachable from a constant's type to implement the +/// structural-match traits, which essentially say that +/// the implementation of `PartialEq::eq` behaves *equivalently* to a +/// comparison against the unfolded structure. +/// +/// For more background on why Rust has this requirement, and issues +/// that arose when the requirement was not enforced completely, see +/// Rust RFC 1445, rust-lang/rust#61188, and rust-lang/rust#62307. +pub fn search_for_structural_match_violation<'tcx>( + span: Span, + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, +) -> Option<Ty<'tcx>> { + 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 +/// find instances of ADTs (specifically structs or enums) that do not implement +/// the structural-match traits (`StructuralPartialEq` and `StructuralEq`). +struct Search<'tcx> { + span: Span, + + tcx: TyCtxt<'tcx>, + + /// Tracks ADTs previously encountered during search, so that + /// we will not recur on them again. + seen: FxHashSet<hir::def_id::DefId>, +} + +impl<'tcx> Search<'tcx> { + fn type_marked_structural(&self, adt_ty: Ty<'tcx>) -> bool { + adt_ty.is_structural_eq_shallow(self.tcx) + } +} + +impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for Search<'tcx> { + type BreakTy = Ty<'tcx>; + + fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { + debug!("Search visiting ty: {:?}", ty); + + let (adt_def, args) = match *ty.kind() { + ty::Adt(adt_def, args) => (adt_def, args), + ty::Param(_) => { + return ControlFlow::Break(ty); + } + ty::Dynamic(..) => { + return ControlFlow::Break(ty); + } + ty::Foreign(_) => { + return ControlFlow::Break(ty); + } + ty::Alias(..) => { + return ControlFlow::Break(ty); + } + ty::Closure(..) => { + return ControlFlow::Break(ty); + } + ty::Coroutine(..) | ty::CoroutineWitness(..) => { + return ControlFlow::Break(ty); + } + ty::FnDef(..) => { + // Types of formals and return in `fn(_) -> _` are also irrelevant; + // so we do not recur into them via `super_visit_with` + return ControlFlow::Continue(()); + } + ty::Array(_, n) + if { n.try_eval_target_usize(self.tcx, ty::ParamEnv::reveal_all()) == Some(0) } => + { + // rust-lang/rust#62336: ignore type of contents + // for empty array. + return ControlFlow::Continue(()); + } + ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Str | ty::Never => { + // These primitive types are always structural match. + // + // `Never` is kind of special here, but as it is not inhabitable, this should be fine. + return ControlFlow::Continue(()); + } + + ty::FnPtr(..) => { + return ControlFlow::Continue(()); + } + + ty::RawPtr(..) => { + // 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(_) => { + return ControlFlow::Continue(()); + } + + ty::Array(..) | ty::Slice(_) | ty::Ref(..) | ty::Tuple(..) => { + // First check all contained types and then tell the caller to continue searching. + return ty.super_visit_with(self); + } + ty::Infer(_) | ty::Placeholder(_) | ty::Bound(..) => { + bug!("unexpected type during structural-match checking: {:?}", ty); + } + ty::Error(_) => { + self.tcx.dcx().span_delayed_bug(self.span, "ty::Error in structural-match check"); + // We still want to check other types after encountering an error, + // as this may still emit relevant errors. + return ControlFlow::Continue(()); + } + }; + + if !self.seen.insert(adt_def.did()) { + debug!("Search already seen adt_def: {:?}", adt_def); + return ControlFlow::Continue(()); + } + + if !self.type_marked_structural(ty) { + debug!("Search found ty: {:?}", ty); + return ControlFlow::Break(ty); + } + + // structural-match does not care about the + // instantiation of the generics in an ADT (it + // instead looks directly at its fields outside + // this match), so we skip super_visit_with. + // + // (Must not recur on args for `PhantomData<T>` cf + // rust-lang/rust#55028 and rust-lang/rust#55837; but also + // want to skip args when only uses of generic are + // behind unsafe pointers `*const T`/`*mut T`.) + + // even though we skip super_visit_with, we must recur on + // fields of ADT. + let tcx = self.tcx; + adt_def.all_fields().map(|field| field.ty(tcx, args)).try_for_each(|field_ty| { + let ty = self.tcx.normalize_erasing_regions(ty::ParamEnv::empty(), field_ty); + debug!("structural-match ADT: field_ty={:?}, ty={:?}", field_ty, ty); + ty.visit_with(self) + }) + } +} diff --git a/compiler/rustc_trait_selection/src/traits/structural_normalize.rs b/compiler/rustc_trait_selection/src/traits/structural_normalize.rs new file mode 100644 index 00000000000..e0f9fdc3827 --- /dev/null +++ b/compiler/rustc_trait_selection/src/traits/structural_normalize.rs @@ -0,0 +1,60 @@ +use rustc_infer::infer::at::At; +use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_infer::traits::{FulfillmentError, TraitEngine}; +use rustc_middle::ty::{self, Ty}; + +use crate::traits::{NormalizeExt, Obligation}; + +pub trait StructurallyNormalizeExt<'tcx> { + fn structurally_normalize( + &self, + ty: Ty<'tcx>, + fulfill_cx: &mut dyn TraitEngine<'tcx>, + ) -> Result<Ty<'tcx>, Vec<FulfillmentError<'tcx>>>; +} + +impl<'tcx> StructurallyNormalizeExt<'tcx> for At<'_, 'tcx> { + fn structurally_normalize( + &self, + ty: Ty<'tcx>, + fulfill_cx: &mut dyn TraitEngine<'tcx>, + ) -> Result<Ty<'tcx>, Vec<FulfillmentError<'tcx>>> { + assert!(!ty.is_ty_var(), "should have resolved vars before calling"); + + if self.infcx.next_trait_solver() { + // FIXME(-Znext-solver): Should we resolve opaques here? + let ty::Alias(ty::Projection | ty::Inherent | ty::Weak, _) = *ty.kind() else { + return Ok(ty); + }; + + let new_infer_ty = self.infcx.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::NormalizeProjectionType, + span: self.cause.span, + }); + + // We simply emit an `alias-eq` goal here, since that will take care of + // normalizing the LHS of the projection until it is a rigid projection + // (or a not-yet-defined opaque in scope). + let obligation = Obligation::new( + self.infcx.tcx, + self.cause.clone(), + self.param_env, + ty::PredicateKind::AliasRelate( + ty.into(), + new_infer_ty.into(), + ty::AliasRelationDirection::Equate, + ), + ); + + fulfill_cx.register_predicate_obligation(self.infcx, obligation); + let errors = fulfill_cx.select_where_possible(self.infcx); + if !errors.is_empty() { + return Err(errors); + } + + Ok(self.infcx.resolve_vars_if_possible(new_infer_ty)) + } else { + Ok(self.normalize(ty).into_value_registering_obligations(self.infcx, fulfill_cx)) + } + } +} diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs new file mode 100644 index 00000000000..19eae93df9c --- /dev/null +++ b/compiler/rustc_trait_selection/src/traits/util.rs @@ -0,0 +1,384 @@ +use super::NormalizeExt; +use super::{ObligationCause, PredicateObligation, SelectionContext}; +use rustc_data_structures::fx::FxHashSet; +use rustc_errors::Diagnostic; +use rustc_hir::def_id::DefId; +use rustc_infer::infer::InferOk; +use rustc_middle::ty::GenericArgsRef; +use rustc_middle::ty::{self, ImplSubject, ToPredicate, Ty, TyCtxt, TypeVisitableExt}; +use rustc_span::Span; +use smallvec::SmallVec; + +pub use rustc_infer::traits::util::*; + +/////////////////////////////////////////////////////////////////////////// +// `TraitAliasExpander` iterator +/////////////////////////////////////////////////////////////////////////// + +/// "Trait alias expansion" is the process of expanding a sequence of trait +/// references into another sequence by transitively following all trait +/// aliases. e.g. If you have bounds like `Foo + Send`, a trait alias +/// `trait Foo = Bar + Sync;`, and another trait alias +/// `trait Bar = Read + Write`, then the bounds would expand to +/// `Read + Write + Sync + Send`. +/// Expansion is done via a DFS (depth-first search), and the `visited` field +/// is used to avoid cycles. +pub struct TraitAliasExpander<'tcx> { + tcx: TyCtxt<'tcx>, + stack: Vec<TraitAliasExpansionInfo<'tcx>>, +} + +/// Stores information about the expansion of a trait via a path of zero or more trait aliases. +#[derive(Debug, Clone)] +pub struct TraitAliasExpansionInfo<'tcx> { + pub path: SmallVec<[(ty::PolyTraitRef<'tcx>, Span); 4]>, +} + +impl<'tcx> TraitAliasExpansionInfo<'tcx> { + fn new(trait_ref: ty::PolyTraitRef<'tcx>, span: Span) -> Self { + Self { path: smallvec![(trait_ref, span)] } + } + + /// 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: &'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) { + diag.span_label(*sp, format!("referenced here ({use_desc})")); + } + } + if self.top().1 != self.bottom().1 { + // When the trait object is in a return type these two spans match, we don't want + // redundant labels. + diag.span_label( + self.bottom().1, + format!("trait alias used in trait object type ({use_desc})"), + ); + } + } + + pub fn trait_ref(&self) -> ty::PolyTraitRef<'tcx> { + self.top().0 + } + + pub fn top(&self) -> &(ty::PolyTraitRef<'tcx>, Span) { + self.path.last().unwrap() + } + + pub fn bottom(&self) -> &(ty::PolyTraitRef<'tcx>, Span) { + self.path.first().unwrap() + } + + fn clone_and_push(&self, trait_ref: ty::PolyTraitRef<'tcx>, span: Span) -> Self { + let mut path = self.path.clone(); + path.push((trait_ref, span)); + + Self { path } + } +} + +pub fn expand_trait_aliases<'tcx>( + tcx: TyCtxt<'tcx>, + trait_refs: impl Iterator<Item = (ty::PolyTraitRef<'tcx>, Span)>, +) -> TraitAliasExpander<'tcx> { + let items: Vec<_> = + trait_refs.map(|(trait_ref, span)| TraitAliasExpansionInfo::new(trait_ref, span)).collect(); + TraitAliasExpander { tcx, stack: items } +} + +impl<'tcx> TraitAliasExpander<'tcx> { + /// If `item` is a trait alias and its predicate has not yet been visited, then expands `item` + /// to the definition, pushes the resulting expansion onto `self.stack`, and returns `false`. + /// Otherwise, immediately returns `true` if `item` is a regular trait, or `false` if it is a + /// trait alias. + /// The return value indicates whether `item` should be yielded to the user. + fn expand(&mut self, item: &TraitAliasExpansionInfo<'tcx>) -> bool { + let tcx = self.tcx; + let trait_ref = item.trait_ref(); + let pred = trait_ref.to_predicate(tcx); + + debug!("expand_trait_aliases: trait_ref={:?}", trait_ref); + + // Don't recurse if this bound is not a trait alias. + let is_alias = tcx.is_trait_alias(trait_ref.def_id()); + if !is_alias { + return true; + } + + // Don't recurse if this trait alias is already on the stack for the DFS search. + let anon_pred = anonymize_predicate(tcx, pred); + if item + .path + .iter() + .rev() + .skip(1) + .any(|&(tr, _)| anonymize_predicate(tcx, tr.to_predicate(tcx)) == anon_pred) + { + return false; + } + + // Get components of trait alias. + let predicates = tcx.implied_predicates_of(trait_ref.def_id()); + debug!(?predicates); + + let items = predicates.predicates.iter().rev().filter_map(|(pred, span)| { + pred.subst_supertrait(tcx, &trait_ref) + .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<_>>()); + + self.stack.extend(items); + + false + } +} + +impl<'tcx> Iterator for TraitAliasExpander<'tcx> { + type Item = TraitAliasExpansionInfo<'tcx>; + + fn size_hint(&self) -> (usize, Option<usize>) { + (self.stack.len(), None) + } + + fn next(&mut self) -> Option<TraitAliasExpansionInfo<'tcx>> { + while let Some(item) = self.stack.pop() { + if self.expand(&item) { + return Some(item); + } + } + None + } +} + +/////////////////////////////////////////////////////////////////////////// +// Iterator over def-IDs of supertraits +/////////////////////////////////////////////////////////////////////////// + +pub struct SupertraitDefIds<'tcx> { + tcx: TyCtxt<'tcx>, + stack: Vec<DefId>, + visited: FxHashSet<DefId>, +} + +pub fn supertrait_def_ids(tcx: TyCtxt<'_>, trait_def_id: DefId) -> SupertraitDefIds<'_> { + SupertraitDefIds { + tcx, + stack: vec![trait_def_id], + visited: Some(trait_def_id).into_iter().collect(), + } +} + +impl Iterator for SupertraitDefIds<'_> { + type Item = DefId; + + fn next(&mut self) -> Option<DefId> { + let def_id = self.stack.pop()?; + let predicates = self.tcx.super_predicates_of(def_id); + let visited = &mut self.visited; + self.stack.extend( + predicates + .predicates + .iter() + .filter_map(|(pred, _)| pred.as_trait_clause()) + .map(|trait_ref| trait_ref.def_id()) + .filter(|&super_def_id| visited.insert(super_def_id)), + ); + Some(def_id) + } +} + +/////////////////////////////////////////////////////////////////////////// +// Other +/////////////////////////////////////////////////////////////////////////// + +/// Instantiate all bound parameters of the impl subject with the given args, +/// returning the resulting subject and all obligations that arise. +/// The obligations are closed under normalization. +pub fn impl_subject_and_oblig<'a, 'tcx>( + selcx: &mut SelectionContext<'a, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + impl_def_id: DefId, + impl_args: GenericArgsRef<'tcx>, + cause: impl Fn(usize, Span) -> ObligationCause<'tcx>, +) -> (ImplSubject<'tcx>, impl Iterator<Item = PredicateObligation<'tcx>>) { + let subject = selcx.tcx().impl_subject(impl_def_id); + let subject = subject.instantiate(selcx.tcx(), impl_args); + + let InferOk { value: subject, obligations: normalization_obligations1 } = + selcx.infcx.at(&ObligationCause::dummy(), param_env).normalize(subject); + + let predicates = selcx.tcx().predicates_of(impl_def_id); + let predicates = predicates.instantiate(selcx.tcx(), impl_args); + let InferOk { value: predicates, obligations: normalization_obligations2 } = + selcx.infcx.at(&ObligationCause::dummy(), param_env).normalize(predicates); + let impl_obligations = super::predicates_for_generics(cause, param_env, predicates); + + let impl_obligations = + impl_obligations.chain(normalization_obligations1).chain(normalization_obligations2); + + (subject, impl_obligations) +} + +/// Casts a trait reference into a reference to one of its super +/// traits; returns `None` if `target_trait_def_id` is not a +/// supertrait. +pub fn upcast_choices<'tcx>( + tcx: TyCtxt<'tcx>, + source_trait_ref: ty::PolyTraitRef<'tcx>, + target_trait_def_id: DefId, +) -> Vec<ty::PolyTraitRef<'tcx>> { + if source_trait_ref.def_id() == target_trait_def_id { + return vec![source_trait_ref]; // Shortcut the most common case. + } + + supertraits(tcx, source_trait_ref).filter(|r| r.def_id() == target_trait_def_id).collect() +} + +/// Given an upcast trait object described by `object`, returns the +/// index of the method `method_def_id` (which should be part of +/// `object.upcast_trait_ref`) within the vtable for `object`. +pub fn get_vtable_index_of_object_method<'tcx>( + tcx: TyCtxt<'tcx>, + vtable_base: usize, + 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(tcx.parent(method_def_id)) + .iter() + .copied() + .position(|def_id| def_id == method_def_id) + .map(|index| vtable_base + index) +} + +pub fn closure_trait_ref_and_return_type<'tcx>( + tcx: TyCtxt<'tcx>, + fn_trait_def_id: DefId, + self_ty: Ty<'tcx>, + sig: ty::PolyFnSig<'tcx>, + tuple_arguments: TupleArgumentsFlag, + fn_host_effect: ty::Const<'tcx>, +) -> ty::Binder<'tcx, (ty::TraitRef<'tcx>, Ty<'tcx>)> { + assert!(!self_ty.has_escaping_bound_vars()); + let arguments_tuple = match tuple_arguments { + TupleArgumentsFlag::No => sig.skip_binder().inputs()[0], + TupleArgumentsFlag::Yes => Ty::new_tup(tcx, sig.skip_binder().inputs()), + }; + let trait_ref = if tcx.generics_of(fn_trait_def_id).host_effect_index.is_some() { + ty::TraitRef::new( + tcx, + fn_trait_def_id, + [ + ty::GenericArg::from(self_ty), + ty::GenericArg::from(arguments_tuple), + ty::GenericArg::from(fn_host_effect), + ], + ) + } else { + ty::TraitRef::new(tcx, fn_trait_def_id, [self_ty, arguments_tuple]) + }; + sig.map_bound(|sig| (trait_ref, sig.output())) +} + +pub fn coroutine_trait_ref_and_outputs<'tcx>( + tcx: TyCtxt<'tcx>, + fn_trait_def_id: DefId, + self_ty: Ty<'tcx>, + sig: ty::GenSig<'tcx>, +) -> (ty::TraitRef<'tcx>, Ty<'tcx>, Ty<'tcx>) { + assert!(!self_ty.has_escaping_bound_vars()); + let trait_ref = ty::TraitRef::new(tcx, fn_trait_def_id, [self_ty, sig.resume_ty]); + (trait_ref, sig.yield_ty, sig.return_ty) +} + +pub fn future_trait_ref_and_outputs<'tcx>( + tcx: TyCtxt<'tcx>, + fn_trait_def_id: DefId, + self_ty: Ty<'tcx>, + sig: ty::GenSig<'tcx>, +) -> (ty::TraitRef<'tcx>, Ty<'tcx>) { + assert!(!self_ty.has_escaping_bound_vars()); + let trait_ref = ty::TraitRef::new(tcx, fn_trait_def_id, [self_ty]); + (trait_ref, sig.return_ty) +} + +pub fn iterator_trait_ref_and_outputs<'tcx>( + tcx: TyCtxt<'tcx>, + iterator_def_id: DefId, + self_ty: Ty<'tcx>, + sig: ty::GenSig<'tcx>, +) -> (ty::TraitRef<'tcx>, Ty<'tcx>) { + assert!(!self_ty.has_escaping_bound_vars()); + let trait_ref = ty::TraitRef::new(tcx, iterator_def_id, [self_ty]); + (trait_ref, sig.yield_ty) +} + +pub fn async_iterator_trait_ref_and_outputs<'tcx>( + tcx: TyCtxt<'tcx>, + async_iterator_def_id: DefId, + self_ty: Ty<'tcx>, + sig: ty::GenSig<'tcx>, +) -> (ty::TraitRef<'tcx>, Ty<'tcx>) { + assert!(!self_ty.has_escaping_bound_vars()); + let trait_ref = ty::TraitRef::new(tcx, async_iterator_def_id, [self_ty]); + (trait_ref, sig.yield_ty) +} + +pub fn impl_item_is_final(tcx: TyCtxt<'_>, assoc_item: &ty::AssocItem) -> bool { + assoc_item.defaultness(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 args lists +pub fn check_args_compatible<'tcx>( + tcx: TyCtxt<'tcx>, + assoc_item: ty::AssocItem, + args: ty::GenericArgsRef<'tcx>, +) -> bool { + fn check_args_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_args_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 args (RPITIT) args + let args = &args[0..generics.count().min(args.len())]; + check_args_compatible_inner(tcx, generics, args) +} diff --git a/compiler/rustc_trait_selection/src/traits/vtable.rs b/compiler/rustc_trait_selection/src/traits/vtable.rs new file mode 100644 index 00000000000..d39583b5c7d --- /dev/null +++ b/compiler/rustc_trait_selection/src/traits/vtable.rs @@ -0,0 +1,408 @@ +use crate::errors::DumpVTableEntries; +use crate::traits::{impossible_predicates, is_vtable_safe_method}; +use rustc_hir::def_id::DefId; +use rustc_hir::lang_items::LangItem; +use rustc_infer::traits::util::PredicateSet; +use rustc_infer::traits::ImplSource; +use rustc_middle::query::Providers; +use rustc_middle::traits::BuiltinImplSource; +use rustc_middle::ty::visit::TypeVisitableExt; +use rustc_middle::ty::GenericArgs; +use rustc_middle::ty::{self, GenericParamDefKind, ToPredicate, Ty, TyCtxt, VtblEntry}; +use rustc_span::{sym, Span}; +use smallvec::SmallVec; + +use std::fmt::Debug; +use std::ops::ControlFlow; + +#[derive(Clone, Debug)] +pub enum VtblSegment<'tcx> { + MetadataDSA, + TraitOwnEntries { trait_ref: ty::PolyTraitRef<'tcx>, emit_vptr: bool }, +} + +/// Prepare the segments for a vtable +pub fn prepare_vtable_segments<'tcx, T>( + tcx: TyCtxt<'tcx>, + trait_ref: ty::PolyTraitRef<'tcx>, + segment_visitor: impl FnMut(VtblSegment<'tcx>) -> ControlFlow<T>, +) -> Option<T> { + prepare_vtable_segments_inner(tcx, trait_ref, segment_visitor).break_value() +} + +/// Helper for [`prepare_vtable_segments`] that returns `ControlFlow`, +/// such that we can use `?` in the body. +fn prepare_vtable_segments_inner<'tcx, T>( + tcx: TyCtxt<'tcx>, + trait_ref: ty::PolyTraitRef<'tcx>, + mut segment_visitor: impl FnMut(VtblSegment<'tcx>) -> ControlFlow<T>, +) -> ControlFlow<T> { + // The following constraints holds for the final arrangement. + // 1. The whole virtual table of the first direct super trait is included as the + // the prefix. If this trait doesn't have any super traits, then this step + // consists of the dsa metadata. + // 2. Then comes the proper pointer metadata(vptr) and all own methods for all + // other super traits except those already included as part of the first + // direct super trait virtual table. + // 3. finally, the own methods of this trait. + + // This has the advantage that trait upcasting to the first direct super trait on each level + // is zero cost, and to another trait includes only replacing the pointer with one level indirection, + // while not using too much extra memory. + + // For a single inheritance relationship like this, + // D --> C --> B --> A + // The resulting vtable will consists of these segments: + // DSA, A, B, C, D + + // For a multiple inheritance relationship like this, + // D --> C --> A + // \-> B + // The resulting vtable will consists of these segments: + // DSA, A, B, B-vptr, C, D + + // For a diamond inheritance relationship like this, + // D --> B --> A + // \-> C -/ + // The resulting vtable will consists of these segments: + // DSA, A, B, C, C-vptr, D + + // For a more complex inheritance relationship like this: + // O --> G --> C --> A + // \ \ \-> B + // | |-> F --> D + // | \-> E + // |-> N --> J --> H + // \ \-> I + // |-> M --> K + // \-> L + // The resulting vtable will consists of these segments: + // DSA, A, B, B-vptr, C, D, D-vptr, E, E-vptr, F, F-vptr, G, + // H, H-vptr, I, I-vptr, J, J-vptr, K, K-vptr, L, L-vptr, M, M-vptr, + // N, N-vptr, O + + // emit dsa segment first. + segment_visitor(VtblSegment::MetadataDSA)?; + + let mut emit_vptr_on_new_entry = false; + let mut visited = PredicateSet::new(tcx); + let predicate = trait_ref.to_predicate(tcx); + let mut stack: SmallVec<[(ty::PolyTraitRef<'tcx>, _, _); 5]> = + smallvec![(trait_ref, emit_vptr_on_new_entry, maybe_iter(None))]; + visited.insert(predicate); + + // the main traversal loop: + // basically we want to cut the inheritance directed graph into a few non-overlapping slices of nodes + // such that each node is emitted after all its descendants have been emitted. + // so we convert the directed graph into a tree by skipping all previously visited nodes using a visited set. + // this is done on the fly. + // Each loop run emits a slice - it starts by find a "childless" unvisited node, backtracking upwards, and it + // stops after it finds a node that has a next-sibling node. + // This next-sibling node will used as the starting point of next slice. + + // Example: + // For a diamond inheritance relationship like this, + // D#1 --> B#0 --> A#0 + // \-> C#1 -/ + + // Starting point 0 stack [D] + // Loop run #0: Stack after diving in is [D B A], A is "childless" + // after this point, all newly visited nodes won't have a vtable that equals to a prefix of this one. + // Loop run #0: Emitting the slice [B A] (in reverse order), B has a next-sibling node, so this slice stops here. + // Loop run #0: Stack after exiting out is [D C], C is the next starting point. + // Loop run #1: Stack after diving in is [D C], C is "childless", since its child A is skipped(already emitted). + // Loop run #1: Emitting the slice [D C] (in reverse order). No one has a next-sibling node. + // Loop run #1: Stack after exiting out is []. Now the function exits. + + 'outer: loop { + // dive deeper into the stack, recording the path + 'diving_in: loop { + let &(inner_most_trait_ref, _, _) = stack.last().unwrap(); + + let mut direct_super_traits_iter = tcx + .super_predicates_of(inner_most_trait_ref.def_id()) + .predicates + .into_iter() + .filter_map(move |(pred, _)| { + pred.subst_supertrait(tcx, &inner_most_trait_ref).as_trait_clause() + }); + + // Find an unvisited supertrait + match direct_super_traits_iter + .find(|&super_trait| visited.insert(super_trait.to_predicate(tcx))) + { + // Push it to the stack for the next iteration of 'diving_in to pick up + Some(unvisited_super_trait) => { + // We're throwing away potential constness of super traits here. + // FIXME: handle ~const super traits + let next_super_trait = unvisited_super_trait.map_bound(|t| t.trait_ref); + stack.push(( + next_super_trait, + emit_vptr_on_new_entry, + maybe_iter(Some(direct_super_traits_iter)), + )) + } + + // There are no more unvisited direct super traits, dive-in finished + None => break 'diving_in, + } + } + + // emit innermost item, move to next sibling and stop there if possible, otherwise jump to outer level. + while let Some((inner_most_trait_ref, emit_vptr, mut siblings)) = stack.pop() { + segment_visitor(VtblSegment::TraitOwnEntries { + trait_ref: inner_most_trait_ref, + emit_vptr: emit_vptr && !tcx.sess.opts.unstable_opts.no_trait_vptr, + })?; + + // If we've emitted (fed to `segment_visitor`) a trait that has methods present in the vtable, + // we'll need to emit vptrs from now on. + if !emit_vptr_on_new_entry + && has_own_existential_vtable_entries(tcx, inner_most_trait_ref.def_id()) + { + emit_vptr_on_new_entry = true; + } + + if let Some(next_inner_most_trait_ref) = + siblings.find(|&sibling| visited.insert(sibling.to_predicate(tcx))) + { + // We're throwing away potential constness of super traits here. + // FIXME: handle ~const super traits + let next_inner_most_trait_ref = + next_inner_most_trait_ref.map_bound(|t| t.trait_ref); + + stack.push((next_inner_most_trait_ref, emit_vptr_on_new_entry, siblings)); + + // just pushed a new trait onto the stack, so we need to go through its super traits + continue 'outer; + } + } + + // the stack is empty, all done + return ControlFlow::Continue(()); + } +} + +/// Turns option of iterator into an iterator (this is just flatten) +fn maybe_iter<I: Iterator>(i: Option<I>) -> impl Iterator<Item = I::Item> { + // Flatten is bad perf-vise, we could probably implement a special case here that is better + i.into_iter().flatten() +} + +fn dump_vtable_entries<'tcx>( + tcx: TyCtxt<'tcx>, + sp: Span, + trait_ref: ty::PolyTraitRef<'tcx>, + entries: &[VtblEntry<'tcx>], +) { + tcx.dcx().emit_err(DumpVTableEntries { span: sp, trait_ref, entries: format!("{entries:#?}") }); +} + +fn has_own_existential_vtable_entries(tcx: TyCtxt<'_>, trait_def_id: DefId) -> bool { + own_existential_vtable_entries_iter(tcx, trait_def_id).next().is_some() +} + +fn own_existential_vtable_entries(tcx: TyCtxt<'_>, trait_def_id: DefId) -> &[DefId] { + tcx.arena.alloc_from_iter(own_existential_vtable_entries_iter(tcx, trait_def_id)) +} + +fn own_existential_vtable_entries_iter( + tcx: TyCtxt<'_>, + trait_def_id: DefId, +) -> impl Iterator<Item = DefId> + '_ { + let trait_methods = tcx + .associated_items(trait_def_id) + .in_definition_order() + .filter(|item| item.kind == ty::AssocKind::Fn); + + // Now list each method's DefId (for within its trait). + let own_entries = trait_methods.filter_map(move |&trait_method| { + debug!("own_existential_vtable_entry: trait_method={:?}", trait_method); + let def_id = trait_method.def_id; + + // Some methods cannot be called on an object; skip those. + if !is_vtable_safe_method(tcx, trait_def_id, trait_method) { + debug!("own_existential_vtable_entry: not vtable safe"); + return None; + } + + Some(def_id) + }); + + own_entries +} + +/// Given a trait `trait_ref`, iterates the vtable entries +/// that come from `trait_ref`, including its supertraits. +fn vtable_entries<'tcx>( + tcx: TyCtxt<'tcx>, + trait_ref: ty::PolyTraitRef<'tcx>, +) -> &'tcx [VtblEntry<'tcx>] { + debug!("vtable_entries({:?})", trait_ref); + + let mut entries = vec![]; + + let vtable_segment_callback = |segment| -> ControlFlow<()> { + match segment { + VtblSegment::MetadataDSA => { + entries.extend(TyCtxt::COMMON_VTABLE_ENTRIES); + } + VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => { + let existential_trait_ref = trait_ref + .map_bound(|trait_ref| ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref)); + + // Lookup the shape of vtable for the trait. + let own_existential_entries = + tcx.own_existential_vtable_entries(existential_trait_ref.def_id()); + + let own_entries = own_existential_entries.iter().copied().map(|def_id| { + debug!("vtable_entries: trait_method={:?}", def_id); + + // The method may have some early-bound lifetimes; add regions for those. + let args = trait_ref.map_bound(|trait_ref| { + GenericArgs::for_item(tcx, def_id, |param, _| match param.kind { + GenericParamDefKind::Lifetime => tcx.lifetimes.re_erased.into(), + GenericParamDefKind::Type { .. } + | GenericParamDefKind::Const { .. } => { + trait_ref.args[param.index as usize] + } + }) + }); + + // The trait type may have higher-ranked lifetimes in it; + // erase them if they appear, so that we get the type + // at some particular call site. + let args = + tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), args); + + // It's possible that the method relies on where-clauses that + // do not hold for this particular set of type parameters. + // Note that this method could then never be called, so we + // do not want to try and codegen it, in that case (see #23435). + let predicates = tcx.predicates_of(def_id).instantiate_own(tcx, args); + if impossible_predicates( + tcx, + predicates.map(|(predicate, _)| predicate).collect(), + ) { + debug!("vtable_entries: predicates do not hold"); + return VtblEntry::Vacant; + } + + let instance = ty::Instance::resolve_for_vtable( + tcx, + ty::ParamEnv::reveal_all(), + def_id, + args, + ) + .expect("resolution failed during building vtable representation"); + VtblEntry::Method(instance) + }); + + entries.extend(own_entries); + + if emit_vptr { + entries.push(VtblEntry::TraitVPtr(trait_ref)); + } + } + } + + ControlFlow::Continue(()) + }; + + let _ = prepare_vtable_segments(tcx, trait_ref, vtable_segment_callback); + + if tcx.has_attr(trait_ref.def_id(), sym::rustc_dump_vtable) { + let sp = tcx.def_span(trait_ref.def_id()); + dump_vtable_entries(tcx, sp, trait_ref, &entries); + } + + tcx.arena.alloc_from_iter(entries) +} + +/// Find slot base for trait methods within vtable entries of another trait +pub(super) fn vtable_trait_first_method_offset<'tcx>( + tcx: TyCtxt<'tcx>, + key: ( + ty::PolyTraitRef<'tcx>, // trait_to_be_found + ty::PolyTraitRef<'tcx>, // trait_owning_vtable + ), +) -> usize { + let (trait_to_be_found, trait_owning_vtable) = key; + + // #90177 + let trait_to_be_found_erased = tcx.erase_regions(trait_to_be_found); + + let vtable_segment_callback = { + let mut vtable_base = 0; + + move |segment| { + match segment { + VtblSegment::MetadataDSA => { + vtable_base += TyCtxt::COMMON_VTABLE_ENTRIES.len(); + } + VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => { + if tcx.erase_regions(trait_ref) == trait_to_be_found_erased { + return ControlFlow::Break(vtable_base); + } + vtable_base += count_own_vtable_entries(tcx, trait_ref); + if emit_vptr { + vtable_base += 1; + } + } + } + ControlFlow::Continue(()) + } + }; + + if let Some(vtable_base) = + prepare_vtable_segments(tcx, trait_owning_vtable, vtable_segment_callback) + { + vtable_base + } else { + bug!("Failed to find info for expected trait in vtable"); + } +} + +/// Find slot offset for trait vptr within vtable entries of another trait +pub(crate) fn vtable_trait_upcasting_coercion_new_vptr_slot<'tcx>( + tcx: TyCtxt<'tcx>, + key: ( + Ty<'tcx>, // trait object type whose trait owning vtable + Ty<'tcx>, // trait object for supertrait + ), +) -> Option<usize> { + let (source, target) = key; + assert!(matches!(&source.kind(), &ty::Dynamic(..)) && !source.has_infer()); + assert!(matches!(&target.kind(), &ty::Dynamic(..)) && !target.has_infer()); + + // this has been typecked-before, so diagnostics is not really needed. + let unsize_trait_did = tcx.require_lang_item(LangItem::Unsize, None); + + let trait_ref = ty::TraitRef::new(tcx, unsize_trait_did, [source, target]); + + match tcx.codegen_select_candidate((ty::ParamEnv::reveal_all(), trait_ref)) { + Ok(ImplSource::Builtin(BuiltinImplSource::TraitUpcasting { vtable_vptr_slot }, _)) => { + *vtable_vptr_slot + } + otherwise => bug!("expected TraitUpcasting candidate, got {otherwise:?}"), + } +} + +/// Given a trait `trait_ref`, returns the number of vtable entries +/// that come from `trait_ref`, excluding its supertraits. Used in +/// computing the vtable base for an upcast trait of a trait object. +pub(crate) fn count_own_vtable_entries<'tcx>( + tcx: TyCtxt<'tcx>, + trait_ref: ty::PolyTraitRef<'tcx>, +) -> usize { + tcx.own_existential_vtable_entries(trait_ref.def_id()).len() +} + +pub(super) fn provide(providers: &mut Providers) { + *providers = Providers { + own_existential_vtable_entries, + vtable_entries, + vtable_trait_upcasting_coercion_new_vptr_slot, + ..*providers + }; +} diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs new file mode 100644 index 00000000000..0f8d9c6bf4b --- /dev/null +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -0,0 +1,977 @@ +use crate::infer::InferCtxt; +use crate::traits; +use rustc_hir as hir; +use rustc_hir::lang_items::LangItem; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; +use rustc_middle::ty::{GenericArg, GenericArgKind, GenericArgsRef}; +use rustc_span::def_id::{DefId, LocalDefId, CRATE_DEF_ID}; +use rustc_span::{Span, DUMMY_SP}; + +use std::iter; +/// Returns the set of obligations needed to make `arg` well-formed. +/// If `arg` contains unresolved inference variables, this may include +/// further WF obligations. 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". +pub fn obligations<'tcx>( + infcx: &InferCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + body_id: LocalDefId, + recursion_depth: usize, + arg: GenericArg<'tcx>, + span: Span, +) -> Option<Vec<traits::PredicateObligation<'tcx>>> { + // Handle the "livelock" case (see comment above) by bailing out if necessary. + let arg = match arg.unpack() { + GenericArgKind::Type(ty) => { + match ty.kind() { + ty::Infer(ty::TyVar(_)) => { + let resolved_ty = infcx.shallow_resolve(ty); + if resolved_ty == ty { + // No progress, bail out to prevent "livelock". + return None; + } else { + resolved_ty + } + } + _ => ty, + } + .into() + } + GenericArgKind::Const(ct) => { + match ct.kind() { + ty::ConstKind::Infer(_) => { + let resolved = infcx.shallow_resolve(ct); + if resolved == ct { + // No progress. + return None; + } else { + resolved + } + } + _ => ct, + } + .into() + } + // There is nothing we have to do for lifetimes. + GenericArgKind::Lifetime(..) => return Some(Vec::new()), + }; + + let mut wf = + WfPredicates { infcx, param_env, body_id, span, out: vec![], recursion_depth, item: None }; + wf.compute(arg); + debug!("wf::obligations({:?}, body_id={:?}) = {:?}", arg, body_id, wf.out); + + let result = wf.normalize(infcx); + debug!("wf::obligations({:?}, body_id={:?}) ~~> {:?}", arg, body_id, result); + Some(result) +} + +/// Compute the predicates that are required for a type to be well-formed. +/// +/// This is only intended to be used in the new solver, since it does not +/// take into account recursion depth or proper error-reporting spans. +pub fn unnormalized_obligations<'tcx>( + infcx: &InferCtxt<'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![]); + } + + let mut wf = WfPredicates { + infcx, + param_env, + body_id: CRATE_DEF_ID, + span: DUMMY_SP, + out: vec![], + recursion_depth: 0, + item: None, + }; + wf.compute(arg); + Some(wf.out) +} + +/// Returns the obligations that make this trait reference +/// well-formed. For example, if there is a trait `Set` defined like +/// `trait Set<K: Eq>`, then the trait bound `Foo: Set<Bar>` is WF +/// if `Bar: Eq`. +pub fn trait_obligations<'tcx>( + infcx: &InferCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + body_id: LocalDefId, + trait_pred: ty::TraitPredicate<'tcx>, + span: Span, + item: &'tcx hir::Item<'tcx>, +) -> Vec<traits::PredicateObligation<'tcx>> { + let mut wf = WfPredicates { + infcx, + param_env, + body_id, + span, + out: vec![], + recursion_depth: 0, + item: Some(item), + }; + wf.compute_trait_pred(trait_pred, Elaborate::All); + debug!(obligations = ?wf.out); + wf.normalize(infcx) +} + +/// Returns the requirements for `clause` to be well-formed. +/// +/// For example, if there is a trait `Set` defined like +/// `trait Set<K: Eq>`, then the trait bound `Foo: Set<Bar>` is WF +/// if `Bar: Eq`. +#[instrument(skip(infcx), ret)] +pub fn clause_obligations<'tcx>( + infcx: &InferCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + body_id: LocalDefId, + clause: ty::Clause<'tcx>, + span: Span, +) -> Vec<traits::PredicateObligation<'tcx>> { + let mut wf = WfPredicates { + infcx, + param_env, + body_id, + span, + out: vec![], + recursion_depth: 0, + item: None, + }; + + // It's ok to skip the binder here because wf code is prepared for it + match clause.kind().skip_binder() { + ty::ClauseKind::Trait(t) => { + wf.compute_trait_pred(t, Elaborate::None); + } + ty::ClauseKind::RegionOutlives(..) => {} + ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(ty, _reg)) => { + wf.compute(ty.into()); + } + 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::ClauseKind::ConstArgHasType(ct, ty) => { + wf.compute(ct.into()); + wf.compute(ty.into()); + } + ty::ClauseKind::WellFormed(arg) => { + wf.compute(arg); + } + + ty::ClauseKind::ConstEvaluatable(ct) => { + wf.compute(ct.into()); + } + } + + wf.normalize(infcx) +} + +struct WfPredicates<'a, 'tcx> { + infcx: &'a InferCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + body_id: LocalDefId, + span: Span, + out: Vec<traits::PredicateObligation<'tcx>>, + recursion_depth: usize, + item: Option<&'tcx hir::Item<'tcx>>, +} + +/// Controls whether we "elaborate" supertraits and so forth on the WF +/// predicates. This is a kind of hack to address #43784. The +/// underlying problem in that issue was a trait structure like: +/// +/// ```ignore (illustrative) +/// trait Foo: Copy { } +/// trait Bar: Foo { } +/// impl<T: Bar> Foo for T { } +/// impl<T> Bar for T { } +/// ``` +/// +/// Here, in the `Foo` impl, we will check that `T: Copy` holds -- but +/// we decide that this is true because `T: Bar` is in the +/// where-clauses (and we can elaborate that to include `T: +/// Copy`). This wouldn't be a problem, except that when we check the +/// `Bar` impl, we decide that `T: Foo` must hold because of the `Foo` +/// impl. And so nowhere did we check that `T: Copy` holds! +/// +/// To resolve this, we elaborate the WF requirements that must be +/// proven when checking impls. This means that (e.g.) the `impl Bar +/// for T` will be forced to prove not only that `T: Foo` but also `T: +/// Copy` (which it won't be able to do, because there is no `Copy` +/// impl for `T`). +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +enum Elaborate { + All, + None, +} + +fn extend_cause_with_original_assoc_item_obligation<'tcx>( + tcx: TyCtxt<'tcx>, + trait_ref: ty::TraitRef<'tcx>, + item: Option<&hir::Item<'tcx>>, + cause: &mut traits::ObligationCause<'tcx>, + pred: ty::Predicate<'tcx>, +) { + debug!( + "extended_cause_with_original_assoc_item_obligation {:?} {:?} {:?} {:?}", + trait_ref, item, cause, pred + ); + let (items, impl_def_id) = match item { + Some(hir::Item { kind: hir::ItemKind::Impl(impl_), owner_id, .. }) => { + (impl_.items, *owner_id) + } + _ => return, + }; + let fix_span = + |impl_item_ref: &hir::ImplItemRef| match tcx.hir().impl_item(impl_item_ref.id).kind { + hir::ImplItemKind::Const(ty, _) | hir::ImplItemKind::Type(ty) => ty.span, + _ => impl_item_ref.span, + }; + + // It is fine to skip the binder as we don't care about regions here. + match pred.kind().skip_binder() { + 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 + // `tests/ui/associated-types/point-at-type-on-obligation-failure.rs` and + // `traits-assoc-type-in-supertrait-bad.rs`. + if let Some(ty::Alias(ty::Projection, projection_ty)) = + proj.term.ty().map(|ty| ty.kind()) + && let Some(&impl_item_id) = + tcx.impl_item_implementor_ids(impl_def_id).get(&projection_ty.def_id) + && let Some(impl_item_span) = items + .iter() + .find(|item| item.id.owner_id.to_def_id() == impl_item_id) + .map(fix_span) + { + cause.span = impl_item_span; + } + } + 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); + if let ty::Alias(ty::Projection, ty::AliasTy { def_id, .. }) = *pred.self_ty().kind() + && let Some(&impl_item_id) = tcx.impl_item_implementor_ids(impl_def_id).get(&def_id) + && let Some(impl_item_span) = items + .iter() + .find(|item| item.id.owner_id.to_def_id() == impl_item_id) + .map(fix_span) + { + cause.span = impl_item_span; + } + } + _ => {} + } +} + +impl<'a, 'tcx> WfPredicates<'a, 'tcx> { + fn tcx(&self) -> TyCtxt<'tcx> { + self.infcx.tcx + } + + fn cause(&self, code: traits::ObligationCauseCode<'tcx>) -> traits::ObligationCause<'tcx> { + traits::ObligationCause::new(self.span, self.body_id, code) + } + + 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()); + for mut obligation in self.out { + assert!(!obligation.has_escaping_bound_vars()); + let mut selcx = traits::SelectionContext::new(infcx); + // Don't normalize the whole obligation, the param env is either + // already normalized, or we're currently normalizing the + // param_env. Either way we should only normalize the predicate. + let normalized_predicate = traits::project::normalize_with_depth_to( + &mut selcx, + param_env, + cause.clone(), + self.recursion_depth, + obligation.predicate, + &mut obligations, + ); + obligation.predicate = normalized_predicate; + obligations.push(obligation); + } + obligations + } + + /// Pushes the obligations required for `trait_ref` to be WF into `self.out`. + fn compute_trait_pred(&mut self, trait_pred: ty::TraitPredicate<'tcx>, elaborate: Elaborate) { + let tcx = self.tcx(); + let trait_ref = trait_pred.trait_ref; + + // Negative trait predicates don't require supertraits to hold, just + // that their args are WF. + if trait_pred.polarity == ty::ImplPolarity::Negative { + self.compute_negative_trait_pred(trait_ref); + return; + } + + // if the trait predicate is not const, the wf obligations should not be const as well. + let obligations = self.nominal_obligations(trait_ref.def_id, trait_ref.args); + + debug!("compute_trait_pred obligations {:?}", obligations); + let param_env = self.param_env; + let depth = self.recursion_depth; + + let item = self.item; + + let extend = |traits::PredicateObligation { predicate, mut cause, .. }| { + if let Some(parent_trait_pred) = predicate.to_opt_poly_trait_pred() { + cause = cause.derived_cause( + parent_trait_pred, + traits::ObligationCauseCode::DerivedObligation, + ); + } + extend_cause_with_original_assoc_item_obligation( + tcx, trait_ref, item, &mut cause, predicate, + ); + traits::Obligation::with_depth(tcx, cause, depth, param_env, predicate) + }; + + if let Elaborate::All = elaborate { + let implied_obligations = traits::util::elaborate(tcx, obligations); + let implied_obligations = implied_obligations.map(extend); + self.out.extend(implied_obligations); + } else { + self.out.extend(obligations); + } + + self.out.extend( + trait_ref + .args + .iter() + .enumerate() + .filter(|(_, arg)| { + matches!(arg.unpack(), GenericArgKind::Type(..) | GenericArgKind::Const(..)) + }) + .filter(|(_, arg)| !arg.has_escaping_bound_vars()) + .map(|(i, arg)| { + let mut cause = traits::ObligationCause::misc(self.span, self.body_id); + // The first subst is the self ty - use the correct span for it. + if i == 0 { + if let Some(hir::ItemKind::Impl(hir::Impl { self_ty, .. })) = + item.map(|i| &i.kind) + { + cause.span = self_ty.span; + } + } + traits::Obligation::with_depth( + tcx, + cause, + depth, + param_env, + ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed( + arg, + ))), + ) + }), + ); + } + + // Compute the obligations that are required for `trait_ref` to be WF, + // given that it is a *negative* trait predicate. + fn compute_negative_trait_pred(&mut self, trait_ref: ty::TraitRef<'tcx>) { + for arg in trait_ref.args { + self.compute(arg); + } + } + + /// Pushes the obligations required for `trait_ref::Item` to be WF + /// into `self.out`. + fn compute_projection(&mut self, data: ty::AliasTy<'tcx>) { + // A projection is well-formed if + // + // (a) its predicates hold (*) + // (b) its args are wf + // + // (*) The predicates of an associated type include the predicates of + // the trait that it's contained in. For example, given + // + // trait A<T>: Clone { + // type X where T: Copy; + // } + // + // The predicates of `<() as A<i32>>::X` are: + // [ + // `(): Sized` + // `(): Clone` + // `(): A<i32>` + // `i32: Sized` + // `i32: Clone` + // `i32: Copy` + // ] + let obligations = self.nominal_obligations(data.def_id, data.args); + self.out.extend(obligations); + + self.compute_projection_args(data.args); + } + + fn compute_inherent_projection(&mut self, data: ty::AliasTy<'tcx>) { + // An inherent projection is well-formed if + // + // (a) its predicates hold (*) + // (b) its args are wf + // + // (*) The predicates of an inherent associated type include the + // predicates of the impl that it's contained in. + + if !data.self_ty().has_escaping_bound_vars() { + // FIXME(inherent_associated_types): Should this happen inside of a snapshot? + // FIXME(inherent_associated_types): This is incompatible with the new solver and lazy norm! + let args = traits::project::compute_inherent_assoc_ty_args( + &mut traits::SelectionContext::new(self.infcx), + self.param_env, + data, + self.cause(traits::WellFormed(None)), + self.recursion_depth, + &mut self.out, + ); + let obligations = self.nominal_obligations(data.def_id, args); + self.out.extend(obligations); + } + + self.compute_projection_args(data.args); + } + + fn compute_projection_args(&mut self, args: GenericArgsRef<'tcx>) { + let tcx = self.tcx(); + let cause = self.cause(traits::WellFormed(None)); + let param_env = self.param_env; + let depth = self.recursion_depth; + + self.out.extend( + args.iter() + .filter(|arg| { + matches!(arg.unpack(), GenericArgKind::Type(..) | GenericArgKind::Const(..)) + }) + .filter(|arg| !arg.has_escaping_bound_vars()) + .map(|arg| { + traits::Obligation::with_depth( + tcx, + cause.clone(), + depth, + param_env, + ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed( + arg, + ))), + ) + }), + ); + } + + fn require_sized(&mut self, subty: Ty<'tcx>, cause: traits::ObligationCauseCode<'tcx>) { + if !subty.has_escaping_bound_vars() { + let cause = self.cause(cause); + let trait_ref = + ty::TraitRef::from_lang_item(self.tcx(), LangItem::Sized, cause.span, [subty]); + self.out.push(traits::Obligation::with_depth( + self.tcx(), + cause, + self.recursion_depth, + self.param_env, + ty::Binder::dummy(trait_ref), + )); + } + } + + /// Pushes all the predicates needed to validate that `ty` is WF into `out`. + #[instrument(level = "debug", skip(self))] + fn compute(&mut self, arg: GenericArg<'tcx>) { + let mut walker = arg.walk(); + let param_env = self.param_env; + let depth = self.recursion_depth; + while let Some(arg) = walker.next() { + debug!(?arg, ?self.out); + let ty = match arg.unpack() { + GenericArgKind::Type(ty) => ty, + + // No WF constraints for lifetimes being present, any outlives + // obligations are handled by the parent (e.g. `ty::Ref`). + GenericArgKind::Lifetime(_) => continue, + + GenericArgKind::Const(ct) => { + match ct.kind() { + ty::ConstKind::Unevaluated(uv) => { + if !ct.has_escaping_bound_vars() { + let obligations = self.nominal_obligations(uv.def, uv.args); + self.out.extend(obligations); + + 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(), + cause, + self.recursion_depth, + self.param_env, + predicate, + )); + } + } + ty::ConstKind::Infer(_) => { + let cause = self.cause(traits::WellFormed(None)); + + self.out.push(traits::Obligation::with_depth( + self.tcx(), + cause, + self.recursion_depth, + self.param_env, + ty::Binder::dummy(ty::PredicateKind::Clause( + ty::ClauseKind::WellFormed(ct.into()), + )), + )); + } + ty::ConstKind::Expr(_) => { + // FIXME(generic_const_exprs): this doesn't verify that given `Expr(N + 1)` the + // trait bound `typeof(N): Add<typeof(1)>` holds. This is currently unnecessary + // as `ConstKind::Expr` is only produced via normalization of `ConstKind::Unevaluated` + // which means that the `DefId` would have been typeck'd elsewhere. However in + // the future we may allow directly lowering to `ConstKind::Expr` in which case + // we would not be proving bounds we should. + + let predicate = ty::Binder::dummy(ty::PredicateKind::Clause( + ty::ClauseKind::ConstEvaluatable(ct), + )); + let cause = self.cause(traits::WellFormed(None)); + self.out.push(traits::Obligation::with_depth( + self.tcx(), + cause, + self.recursion_depth, + self.param_env, + predicate, + )); + } + + ty::ConstKind::Error(_) + | ty::ConstKind::Param(_) + | ty::ConstKind::Bound(..) + | ty::ConstKind::Placeholder(..) => { + // These variants are trivially WF, so nothing to do here. + } + ty::ConstKind::Value(..) => { + // FIXME: Enforce that values are structurally-matchable. + } + } + continue; + } + }; + + debug!("wf bounds for ty={:?} ty.kind={:#?}", ty, ty.kind()); + + match *ty.kind() { + ty::Bool + | ty::Char + | ty::Int(..) + | ty::Uint(..) + | ty::Float(..) + | ty::Error(_) + | ty::Str + | ty::CoroutineWitness(..) + | ty::Never + | ty::Param(_) + | ty::Bound(..) + | ty::Placeholder(..) + | ty::Foreign(..) => { + // WfScalar, WfParameter, etc + } + + // Can only infer to `ty::Int(_) | ty::Uint(_)`. + ty::Infer(ty::IntVar(_)) => {} + + // Can only infer to `ty::Float(_)`. + ty::Infer(ty::FloatVar(_)) => {} + + ty::Slice(subty) => { + self.require_sized(subty, traits::SliceOrArrayElem); + } + + ty::Array(subty, _) => { + self.require_sized(subty, traits::SliceOrArrayElem); + // Note that we handle the len is implicitly checked while walking `arg`. + } + + ty::Tuple(tys) => { + if let Some((_last, rest)) = tys.split_last() { + for &elem in rest { + self.require_sized(elem, traits::TupleElem); + } + } + } + + ty::RawPtr(_) => { + // Simple cases that are WF if their type args are WF. + } + + ty::Alias(ty::Projection, data) => { + walker.skip_current_subtree(); // Subtree handled by compute_projection. + self.compute_projection(data); + } + ty::Alias(ty::Inherent, data) => { + walker.skip_current_subtree(); // Subtree handled by compute_inherent_projection. + self.compute_inherent_projection(data); + } + + ty::Adt(def, args) => { + // WfNominalType + let obligations = self.nominal_obligations(def.did(), args); + self.out.extend(obligations); + } + + ty::FnDef(did, args) => { + let obligations = self.nominal_obligations(did, args); + self.out.extend(obligations); + } + + ty::Ref(r, rty, _) => { + // WfReference + if !r.has_escaping_bound_vars() && !rty.has_escaping_bound_vars() { + let cause = self.cause(traits::ReferenceOutlivesReferent(ty)); + self.out.push(traits::Obligation::with_depth( + self.tcx(), + cause, + depth, + param_env, + ty::Binder::dummy(ty::PredicateKind::Clause( + ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(rty, r)), + )), + )); + } + } + + ty::Coroutine(did, args, ..) => { + // Walk ALL the types in the coroutine: this will + // include the upvar types as well as the yield + // type. Note that this is mildly distinct from + // the closure case, where we have to be careful + // about the signature of the closure. We don't + // have the problem of implied bounds here since + // coroutines don't take arguments. + let obligations = self.nominal_obligations(did, args); + self.out.extend(obligations); + } + + ty::Closure(did, args) => { + // Only check the upvar types for WF, not the rest + // of the types within. This is needed because we + // capture the signature and it may not be WF + // without the implied bounds. Consider a closure + // like `|x: &'a T|` -- it may be that `T: 'a` is + // not known to hold in the creator's context (and + // indeed the closure may not be invoked by its + // creator, but rather turned to someone who *can* + // verify that). + // + // The special treatment of closures here really + // ought not to be necessary either; the problem + // is related to #25860 -- there is no way for us + // to express a fn type complete with the implied + // bounds that it is assuming. I think in reality + // the WF rules around fn are a bit messed up, and + // that is the rot problem: `fn(&'a T)` should + // probably always be WF, because it should be + // shorthand for something like `where(T: 'a) { + // fn(&'a T) }`, as discussed in #25860. + walker.skip_current_subtree(); // subtree handled below + // FIXME(eddyb) add the type to `walker` instead of recursing. + self.compute(args.as_closure().tupled_upvars_ty().into()); + // Note that we cannot skip the generic types + // types. Normally, within the fn + // body where they are created, the generics will + // always be WF, and outside of that fn body we + // are not directly inspecting closure types + // anyway, except via auto trait matching (which + // only inspects the upvar types). + // But when a closure is part of a type-alias-impl-trait + // then the function that created the defining site may + // have had more bounds available than the type alias + // specifies. This may cause us to have a closure in the + // hidden type that is not actually well formed and + // can cause compiler crashes when the user abuses unsafe + // code to procure such a closure. + // See tests/ui/type-alias-impl-trait/wf_check_closures.rs + let obligations = self.nominal_obligations(did, args); + self.out.extend(obligations); + } + + ty::FnPtr(_) => { + // let the loop iterate into the argument/return + // types appearing in the fn signature + } + + ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => { + // All of the requirements on type parameters + // have already been checked for `impl Trait` in + // return position. We do need to check type-alias-impl-trait though. + if self.tcx().is_type_alias_impl_trait(def_id) { + let obligations = self.nominal_obligations(def_id, args); + self.out.extend(obligations); + } + } + + ty::Alias(ty::Weak, ty::AliasTy { def_id, args, .. }) => { + let obligations = self.nominal_obligations(def_id, args); + self.out.extend(obligations); + } + + ty::Dynamic(data, r, _) => { + // WfObject + // + // Here, we defer WF checking due to higher-ranked + // regions. This is perhaps not ideal. + self.from_object_ty(ty, data, r); + + // FIXME(#27579) RFC also considers adding trait + // obligations that don't refer to Self and + // checking those + + let defer_to_coercion = self.tcx().features().object_safe_for_dispatch; + + if !defer_to_coercion { + if let Some(principal) = data.principal_def_id() { + self.out.push(traits::Obligation::with_depth( + self.tcx(), + self.cause(traits::WellFormed(None)), + depth, + param_env, + ty::Binder::dummy(ty::PredicateKind::ObjectSafe(principal)), + )); + } + } + } + + // Inference variables are the complicated case, since we don't + // know what type they are. We do two things: + // + // 1. Check if they have been resolved, and if so proceed with + // THAT type. + // 2. If not, we've at least simplified things (e.g., we went + // from `Vec<$0>: WF` to `$0: WF`), so we can + // register a pending obligation and keep + // moving. (Goal is that an "inductive hypothesis" + // is satisfied to ensure termination.) + // See also the comment on `fn obligations`, describing "livelock" + // prevention, which happens before this can be reached. + ty::Infer(_) => { + let cause = self.cause(traits::WellFormed(None)); + self.out.push(traits::Obligation::with_depth( + self.tcx(), + cause, + self.recursion_depth, + param_env, + ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed( + ty.into(), + ))), + )); + } + } + + debug!(?self.out); + } + } + + #[instrument(level = "debug", skip(self))] + fn nominal_obligations( + &mut self, + def_id: DefId, + args: GenericArgsRef<'tcx>, + ) -> Vec<traits::PredicateObligation<'tcx>> { + let predicates = self.tcx().predicates_of(def_id); + let mut origins = vec![def_id; predicates.predicates.len()]; + let mut head = predicates; + while let Some(parent) = head.parent { + head = self.tcx().predicates_of(parent); + origins.extend(iter::repeat(parent).take(head.predicates.len())); + } + + let predicates = predicates.instantiate(self.tcx(), args); + trace!("{:#?}", predicates); + debug_assert_eq!(predicates.predicates.len(), origins.len()); + + iter::zip(predicates, origins.into_iter().rev()) + .map(|((pred, span), origin_def_id)| { + let code = if span.is_dummy() { + traits::ItemObligation(origin_def_id) + } else { + traits::BindingObligation(origin_def_id, span) + }; + let cause = self.cause(code); + traits::Obligation::with_depth( + self.tcx(), + cause, + self.recursion_depth, + self.param_env, + pred, + ) + }) + .filter(|pred| !pred.has_escaping_bound_vars()) + .collect() + } + + fn from_object_ty( + &mut self, + ty: Ty<'tcx>, + data: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>, + region: ty::Region<'tcx>, + ) { + // Imagine a type like this: + // + // trait Foo { } + // trait Bar<'c> : 'c { } + // + // &'b (Foo+'c+Bar<'d>) + // ^ + // + // In this case, the following relationships must hold: + // + // 'b <= 'c + // 'd <= 'c + // + // The first conditions is due to the normal region pointer + // rules, which say that a reference cannot outlive its + // referent. + // + // The final condition may be a bit surprising. In particular, + // you may expect that it would have been `'c <= 'd`, since + // usually lifetimes of outer things are conservative + // approximations for inner things. However, it works somewhat + // differently with trait objects: here the idea is that if the + // user specifies a region bound (`'c`, in this case) it is the + // "master bound" that *implies* that bounds from other traits are + // all met. (Remember that *all bounds* in a type like + // `Foo+Bar+Zed` must be met, not just one, hence if we write + // `Foo<'x>+Bar<'y>`, we know that the type outlives *both* 'x and + // 'y.) + // + // Note: in fact we only permit builtin traits, not `Bar<'d>`, I + // am looking forward to the future here. + if !data.has_escaping_bound_vars() && !region.has_escaping_bound_vars() { + let implicit_bounds = object_region_bounds(self.tcx(), data); + + let explicit_bound = region; + + self.out.reserve(implicit_bounds.len()); + for implicit_bound in implicit_bounds { + let cause = self.cause(traits::ObjectTypeBound(ty, explicit_bound)); + let outlives = + ty::Binder::dummy(ty::OutlivesPredicate(explicit_bound, implicit_bound)); + self.out.push(traits::Obligation::with_depth( + self.tcx(), + cause, + self.recursion_depth, + self.param_env, + outlives, + )); + } + } + } +} + +/// Given an object type like `SomeTrait + Send`, computes the lifetime +/// bounds that must hold on the elided self type. These are derived +/// from the declarations of `SomeTrait`, `Send`, and friends -- if +/// they declare `trait SomeTrait : 'static`, for example, then +/// `'static` would appear in the list. The hard work is done by +/// `infer::required_region_bounds`, see that for more information. +pub fn object_region_bounds<'tcx>( + tcx: TyCtxt<'tcx>, + existential_predicates: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>, +) -> Vec<ty::Region<'tcx>> { + let predicates = existential_predicates.iter().filter_map(|predicate| { + if let ty::ExistentialPredicate::Projection(_) = predicate.skip_binder() { + None + } else { + Some(predicate.with_self_ty(tcx, tcx.types.trait_object_dummy_self)) + } + }); + + required_region_bounds(tcx, tcx.types.trait_object_dummy_self, predicates) +} + +/// Given a set of predicates that apply to an object type, returns +/// the region bounds that the (erased) `Self` type must +/// outlive. Precisely *because* the `Self` type is erased, the +/// parameter `erased_self_ty` must be supplied to indicate what type +/// has been used to represent `Self` in the predicates +/// themselves. This should really be a unique type; `FreshTy(0)` is a +/// popular choice. +/// +/// N.B., in some cases, particularly around higher-ranked bounds, +/// this function returns a kind of conservative approximation. +/// That is, all regions returned by this function are definitely +/// required, but there may be other region bounds that are not +/// returned, as well as requirements like `for<'a> T: 'a`. +/// +/// Requires that trait definitions have been processed so that we can +/// elaborate predicates and walk supertraits. +#[instrument(skip(tcx, predicates), level = "debug", ret)] +pub(crate) fn required_region_bounds<'tcx>( + tcx: TyCtxt<'tcx>, + erased_self_ty: Ty<'tcx>, + predicates: impl Iterator<Item = ty::Clause<'tcx>>, +) -> Vec<ty::Region<'tcx>> { + assert!(!erased_self_ty.has_escaping_bound_vars()); + + traits::elaborate(tcx, predicates) + .filter_map(|pred| { + debug!(?pred); + match pred.kind().skip_binder() { + 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 + // higher-ranked bound like that as 'static, + // though at present the code in `fulfill.rs` + // considers such bounds to be unsatisfiable, so + // it's kind of a moot point since you could never + // construct such an object, but this seems + // correct even if that code changes). + if t == &erased_self_ty && !r.has_escaping_bound_vars() { + Some(*r) + } else { + None + } + } + ty::ClauseKind::Trait(_) + | ty::ClauseKind::RegionOutlives(_) + | ty::ClauseKind::Projection(_) + | ty::ClauseKind::ConstArgHasType(_, _) + | ty::ClauseKind::WellFormed(_) + | ty::ClauseKind::ConstEvaluatable(_) => None, + } + }) + .collect() +} |
