diff options
Diffstat (limited to 'compiler/rustc_trait_selection/src')
15 files changed, 556 insertions, 348 deletions
diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs index dde9e9c9ac6..c1fb287d63e 100644 --- a/compiler/rustc_trait_selection/src/errors.rs +++ b/compiler/rustc_trait_selection/src/errors.rs @@ -4,7 +4,7 @@ use rustc_errors::{ SubdiagnosticMessage, }; use rustc_macros::Diagnostic; -use rustc_middle::ty::{self, PolyTraitRef, Ty}; +use rustc_middle::ty::{self, ClosureKind, PolyTraitRef, Ty}; use rustc_span::{Span, Symbol}; #[derive(Diagnostic)] @@ -131,3 +131,37 @@ impl AddToDiagnostic for AdjustSignatureBorrow { } } } + +#[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/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs index 1391b51e67f..36194f973b5 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs @@ -281,23 +281,27 @@ pub(super) trait GoalKind<'tcx>: ) -> QueryResult<'tcx>; /// Consider (possibly several) candidates to upcast or unsize a type to another - /// type. - /// - /// The most common forms of unsizing are array to slice, and concrete (Sized) - /// type into a `dyn Trait`. ADTs and Tuples can also have their final field - /// unsized if it's generic. - /// - /// `dyn Trait1` can be unsized to `dyn Trait2` if they are the same trait, or - /// if `Trait2` is a (transitive) supertrait of `Trait2`. + /// 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_builtin_unsize_candidates( + 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> { @@ -312,10 +316,14 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { 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 } @@ -363,10 +371,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { self.assemble_object_bound_candidates(goal, &mut candidates); - self.assemble_coherence_unknowable_candidates(goal, &mut candidates); - self.assemble_candidates_after_normalizing_self_ty(goal, &mut candidates, num_steps); - candidates } @@ -531,6 +536,23 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { } } + 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) => (), + } + } + } + fn assemble_blanket_impl_candidates<G: GoalKind<'tcx>>( &mut self, goal: Goal<'tcx, G>, @@ -611,7 +633,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { // 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_builtin_unsize_candidates(self, goal) { + for (result, source) in G::consider_structural_builtin_unsize_candidates(self, goal) { candidates.push(Candidate { source: CandidateSource::BuiltinImpl(source), result }); } } @@ -827,6 +849,11 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { 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... @@ -877,26 +904,43 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { goal: Goal<'tcx, G>, candidates: &mut Vec<Candidate<'tcx>>, ) { + let tcx = self.tcx(); match self.solver_mode() { SolverMode::Normal => return, - SolverMode::Coherence => { - let trait_ref = goal.predicate.trait_ref(self.tcx()); - match coherence::trait_ref_is_knowable(self.tcx(), trait_ref) { - Ok(()) => {} - Err(_) => match self - .evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) - { - Ok(result) => candidates.push(Candidate { - source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), - result, - }), - // FIXME: This will be reachable at some point if we're in - // `assemble_candidates_after_normalizing_self_ty` and we get a - // universe error. We'll deal with it at this point. - Err(NoSolution) => bug!("coherence candidate resulted in NoSolution"), - }, + SolverMode::Coherence => {} + }; + + let result = self.probe_candidate("coherence unknowable").enter(|ecx| { + let trait_ref = goal.predicate.trait_ref(tcx); + + #[derive(Debug)] + enum FailureKind { + Overflow, + NoSolution(NoSolution), + } + let lazily_normalize_ty = |ty| match ecx.try_normalize_ty(goal.param_env, ty) { + Ok(Some(ty)) => Ok(ty), + Ok(None) => Err(FailureKind::Overflow), + Err(e) => Err(FailureKind::NoSolution(e)), + }; + + match coherence::trait_ref_is_knowable(tcx, trait_ref, lazily_normalize_ty) { + Err(FailureKind::Overflow) => { + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::OVERFLOW) + } + Err(FailureKind::NoSolution(NoSolution)) | 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) => {} } } diff --git a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs index b882ec254e3..c47767101a4 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs @@ -161,14 +161,12 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_copy_clone_trait<'tcx>( ty: Ty<'tcx>, ) -> Result<Vec<Ty<'tcx>>, NoSolution> { match *ty.kind() { - ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) - | ty::FnDef(..) - | ty::FnPtr(_) - | ty::Error(_) => Ok(vec![]), + 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 diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs index 60c49f665a6..5c2cbe39953 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs @@ -388,44 +388,60 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { && is_normalizes_to_hack == IsNormalizesToHack::No && !self.search_graph.in_cycle() { - debug!("rerunning goal to check result is stable"); - self.search_graph.reset_encountered_overflow(encountered_overflow); - let (_orig_values, canonical_goal) = self.canonicalize_goal(goal); - let Ok(new_canonical_response) = EvalCtxt::evaluate_canonical_goal( - self.tcx(), - self.search_graph, - canonical_goal, - // FIXME(-Ztrait-solver=next): we do not track what happens in `evaluate_canonical_goal` - &mut ProofTreeBuilder::new_noop(), - ) else { - bug!( - "goal went from {certainty:?} to error: re-canonicalized goal={canonical_goal:#?} \ - first_response={canonical_response:#?}, - second response was error" - ); - }; - // We only check for modulo regions as we convert all regions in - // the input to new existentials, even if they're expected to be - // `'static` or a placeholder region. - if !new_canonical_response.value.var_values.is_identity_modulo_regions() { - bug!( - "unstable result: re-canonicalized goal={canonical_goal:#?} \ - first_response={canonical_response:#?} \ - second_response={new_canonical_response:#?}" - ); - } - if certainty != new_canonical_response.value.certainty { - bug!( - "unstable certainty: {certainty:#?} re-canonicalized goal={canonical_goal:#?} \ - first_response={canonical_response:#?} \ - second_response={new_canonical_response:#?}" - ); - } + // The nested evaluation has to happen with the original state + // of `encountered_overflow`. + let from_original_evaluation = + self.search_graph.reset_encountered_overflow(encountered_overflow); + self.check_evaluate_goal_stable_result(goal, canonical_goal, canonical_response); + // In case the evaluation was unstable, we manually make sure that this + // debug check does not influence the result of the parent goal. + self.search_graph.reset_encountered_overflow(from_original_evaluation); } Ok((has_changed, certainty, nested_goals)) } + fn check_evaluate_goal_stable_result( + &mut self, + goal: Goal<'tcx, ty::Predicate<'tcx>>, + original_input: CanonicalInput<'tcx>, + original_result: CanonicalResponse<'tcx>, + ) { + let (_orig_values, canonical_goal) = self.canonicalize_goal(goal); + let result = EvalCtxt::evaluate_canonical_goal( + self.tcx(), + self.search_graph, + canonical_goal, + // FIXME(-Ztrait-solver=next): we do not track what happens in `evaluate_canonical_goal` + &mut ProofTreeBuilder::new_noop(), + ); + + macro_rules! fail { + ($msg:expr) => {{ + let msg = $msg; + warn!( + "unstable result: {msg}\n\ + original goal: {original_input:?},\n\ + original result: {original_result:?}\n\ + re-canonicalized goal: {canonical_goal:?}\n\ + second response: {result:?}" + ); + return; + }}; + } + + let Ok(new_canonical_response) = result else { fail!("second response was error") }; + // We only check for modulo regions as we convert all regions in + // the input to new existentials, even if they're expected to be + // `'static` or a placeholder region. + if !new_canonical_response.value.var_values.is_identity_modulo_regions() { + fail!("additional constraints from second response") + } + if original_result.value.certainty != new_canonical_response.value.certainty { + fail!("unstable certainty") + } + } + fn compute_goal(&mut self, goal: Goal<'tcx, ty::Predicate<'tcx>>) -> QueryResult<'tcx> { let Goal { param_env, predicate } = goal; let kind = predicate.kind(); diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs index ca4a4c9510c..42d7a587cac 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs @@ -123,7 +123,7 @@ impl<'tcx> InferCtxtSelectExt<'tcx> for InferCtxt<'tcx> { // 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(ty::BoundConstness::NotConst, nested_obligations))) + Ok(Some(ImplSource::Param(nested_obligations))) } (Certainty::Maybe(_), _) => Ok(None), diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs index 63d4a38119f..75a99f799a2 100644 --- a/compiler/rustc_trait_selection/src/solve/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/mod.rs @@ -283,6 +283,37 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { 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. + fn try_normalize_ty( + &mut self, + param_env: ty::ParamEnv<'tcx>, + mut ty: Ty<'tcx>, + ) -> Result<Option<Ty<'tcx>>, NoSolution> { + for _ in 0..self.local_overflow_limit() { + let ty::Alias(_, projection_ty) = *ty.kind() else { + return Ok(Some(ty)); + }; + + let normalized_ty = self.next_ty_infer(); + let normalizes_to_goal = Goal::new( + self.tcx(), + param_env, + ty::ProjectionPredicate { projection_ty, term: normalized_ty.into() }, + ); + self.add_goal(normalizes_to_goal); + self.try_evaluate_added_goals()?; + ty = self.resolve_vars_if_possible(normalized_ty); + } + + Ok(None) + } } fn response_no_constraints_raw<'tcx>( diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs index e1980f4d7bb..e47e228774e 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -1,4 +1,4 @@ -use crate::traits::specialization_graph; +use crate::traits::{check_args_compatible, specialization_graph}; use super::assembly::{self, structural_traits}; use super::EvalCtxt; @@ -190,11 +190,8 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS); }; - if !assoc_def.item.defaultness(tcx).has_value() { - let guar = tcx.sess.delay_span_bug( - tcx.def_span(assoc_def.item.def_id), - "missing value for assoc item in impl", - ); + let error_response = |ecx: &mut EvalCtxt<'_, 'tcx>, reason| { + let guar = tcx.sess.delay_span_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, @@ -208,7 +205,11 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { }; ecx.eq(goal.param_env, goal.predicate.term, error_term) .expect("expected goal term to be fully unconstrained"); - return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes); + 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: @@ -233,6 +234,13 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { 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()), @@ -497,7 +505,14 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { ) } - fn consider_builtin_unsize_candidates( + 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)> { diff --git a/compiler/rustc_trait_selection/src/solve/search_graph/cache.rs b/compiler/rustc_trait_selection/src/solve/search_graph/cache.rs index 56f126e9157..be48447e27c 100644 --- a/compiler/rustc_trait_selection/src/solve/search_graph/cache.rs +++ b/compiler/rustc_trait_selection/src/solve/search_graph/cache.rs @@ -19,21 +19,25 @@ rustc_index::newtype_index! { #[derive(Debug, Clone)] pub(super) struct ProvisionalEntry<'tcx> { - // In case we have a coinductive cycle, this is the - // the currently least restrictive result of this goal. - pub(super) response: QueryResult<'tcx>, - // In case of a cycle, the position of deepest stack entry involved - // in that cycle. This is monotonically decreasing in the stack as all - // elements between the current stack element in the deepest stack entry - // involved have to also be involved in that cycle. - // - // We can only move entries to the global cache once we're complete done - // with the cycle. If this entry has not been involved in a cycle, - // this is just its own depth. + /// In case we have a coinductive cycle, this is the + /// the current provisional result of this goal. + /// + /// This starts out as `None` for all goals and gets to some + /// when the goal gets popped from the stack or we rerun evaluation + /// for this goal to reach a fixpoint. + pub(super) response: Option<QueryResult<'tcx>>, + /// In case of a cycle, the position of deepest stack entry involved + /// in that cycle. This is monotonically decreasing in the stack as all + /// elements between the current stack element in the deepest stack entry + /// involved have to also be involved in that cycle. + /// + /// We can only move entries to the global cache once we're complete done + /// with the cycle. If this entry has not been involved in a cycle, + /// this is just its own depth. pub(super) depth: StackDepth, - // The goal for this entry. Should always be equal to the corresponding goal - // in the lookup table. + /// The goal for this entry. Should always be equal to the corresponding goal + /// in the lookup table. pub(super) input: CanonicalInput<'tcx>, } @@ -92,7 +96,7 @@ impl<'tcx> ProvisionalCache<'tcx> { self.entries[entry_index].depth } - pub(super) fn provisional_result(&self, entry_index: EntryIndex) -> QueryResult<'tcx> { + pub(super) fn provisional_result(&self, entry_index: EntryIndex) -> Option<QueryResult<'tcx>> { self.entries[entry_index].response } } diff --git a/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs b/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs index 21c8d476902..49ebfa4e6cb 100644 --- a/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs @@ -13,7 +13,7 @@ use rustc_middle::traits::solve::CacheData; use rustc_middle::traits::solve::{CanonicalInput, Certainty, EvaluationCache, QueryResult}; use rustc_middle::ty::TyCtxt; use rustc_session::Limit; -use std::{collections::hash_map::Entry, mem}; +use std::collections::hash_map::Entry; rustc_index::newtype_index! { pub struct StackDepth {} @@ -134,9 +134,13 @@ impl<'tcx> SearchGraph<'tcx> { /// Resets `encountered_overflow` of the current goal. /// /// This should only be used for the check in `evaluate_goal`. - pub(super) fn reset_encountered_overflow(&mut self, encountered_overflow: bool) { - if encountered_overflow { - self.stack.raw.last_mut().unwrap().encountered_overflow = true; + pub(super) fn reset_encountered_overflow(&mut self, encountered_overflow: bool) -> bool { + if let Some(last) = self.stack.raw.last_mut() { + let prev = last.encountered_overflow; + last.encountered_overflow = encountered_overflow; + prev + } else { + false } } @@ -216,8 +220,8 @@ impl<'tcx> SearchGraph<'tcx> { cycle_participants: Default::default(), }; assert_eq!(self.stack.push(entry), depth); - let response = Self::response_no_constraints(tcx, input, Certainty::Yes); - let entry_index = cache.entries.push(ProvisionalEntry { response, depth, input }); + let entry_index = + cache.entries.push(ProvisionalEntry { response: None, depth, input }); v.insert(entry_index); } // We have a nested goal which relies on a goal `root` deeper in the stack. @@ -243,23 +247,31 @@ impl<'tcx> SearchGraph<'tcx> { root.cycle_participants.insert(e.input); } - // NOTE: The goals on the stack aren't the only goals involved in this cycle. - // We can also depend on goals which aren't part of the stack but coinductively - // depend on the stack themselves. We already checked whether all the goals - // between these goals and their root on the stack. This means that as long as - // each goal in a cycle is checked for coinductivity by itself, simply checking - // the stack is enough. - if self.stack.raw[stack_depth.index()..] - .iter() - .all(|g| g.input.value.goal.predicate.is_coinductive(tcx)) - { - // If we're in a coinductive cycle, we have to retry proving the current goal - // until we reach a fixpoint. - self.stack[stack_depth].has_been_used = true; - return cache.provisional_result(entry_index); + // If we're in a cycle, we have to retry proving the current goal + // until we reach a fixpoint. + self.stack[stack_depth].has_been_used = true; + return if let Some(result) = cache.provisional_result(entry_index) { + result } else { - return Self::response_no_constraints(tcx, input, Certainty::OVERFLOW); - } + // If we don't have a provisional result yet, the goal has to + // still be on the stack. + let mut goal_on_stack = false; + let mut is_coinductive = true; + for entry in self.stack.raw[stack_depth.index()..] + .iter() + .skip_while(|entry| entry.input != input) + { + goal_on_stack = true; + is_coinductive &= entry.input.value.goal.predicate.is_coinductive(tcx); + } + debug_assert!(goal_on_stack); + + if is_coinductive { + Self::response_no_constraints(tcx, input, Certainty::Yes) + } else { + Self::response_no_constraints(tcx, input, Certainty::OVERFLOW) + } + }; } } @@ -288,15 +300,18 @@ impl<'tcx> SearchGraph<'tcx> { let provisional_entry_index = *cache.lookup_table.get(&stack_entry.input).unwrap(); let provisional_entry = &mut cache.entries[provisional_entry_index]; - let prev_response = mem::replace(&mut provisional_entry.response, response); - if stack_entry.has_been_used && prev_response != response { - // If so, remove all entries whose result depends on this goal - // from the provisional cache... + if stack_entry.has_been_used + && provisional_entry.response.map_or(true, |r| r != response) + { + // If so, update the provisional result for this goal and remove + // all entries whose result depends on this goal from the provisional + // cache... // - // That's not completely correct, as a nested goal can also + // That's not completely correct, as a nested goal can also only // depend on a goal which is lower in the stack so it doesn't // actually depend on the current goal. This should be fairly // rare and is hopefully not relevant for performance. + provisional_entry.response = Some(response); #[allow(rustc::potential_query_instability)] cache.lookup_table.retain(|_key, index| *index <= provisional_entry_index); cache.entries.truncate(provisional_entry_index.index() + 1); @@ -315,8 +330,8 @@ impl<'tcx> SearchGraph<'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 do not add it to the global - // cache. + // 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. @@ -348,6 +363,8 @@ impl<'tcx> SearchGraph<'tcx> { dep_node, result, ) + } else { + provisional_entry.response = Some(result); } result diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index db80b62d8a2..8685f3100a8 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -423,7 +423,55 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { ecx.evaluate_added_goals_and_make_canonical_response(certainty) } - fn consider_builtin_unsize_candidates( + fn consider_unsize_to_dyn_candidate( + ecx: &mut EvalCtxt<'_, 'tcx>, + goal: Goal<'tcx, Self>, + ) -> QueryResult<'tcx> { + ecx.probe(|_| CandidateKind::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(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(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(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)> { @@ -448,7 +496,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { // We need to normalize the b_ty since it's matched structurally // in the other functions below. let b_ty = match ecx - .normalize_non_self_ty(goal.predicate.trait_ref.args.type_at(1), goal.param_env) + .try_normalize_ty(goal.param_env, goal.predicate.trait_ref.args.type_at(1)) { Ok(Some(b_ty)) => b_ty, Ok(None) => return vec![misc_candidate(ecx, Certainty::OVERFLOW)], @@ -468,11 +516,8 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { goal, a_data, a_region, b_data, b_region, ), - // `T` -> `dyn Trait` unsizing - (_, &ty::Dynamic(b_data, b_region, ty::Dyn)) => result_to_single( - ecx.consider_builtin_unsize_to_dyn(goal, b_data, b_region), - BuiltinImplSource::Misc, - ), + // `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( @@ -552,16 +597,18 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { self.walk_vtable( a_principal.with_self_ty(tcx, a_ty), |ecx, new_a_principal, _, vtable_vptr_slot| { - if let Ok(resp) = 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) - })), - ) { + if let Ok(resp) = ecx.probe_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 })); } @@ -572,43 +619,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { responses } - /// ```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_builtin_unsize_to_dyn( - &mut self, - goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>, - b_data: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>, - b_region: ty::Region<'tcx>, - ) -> QueryResult<'tcx> { - let tcx = self.tcx(); - let Goal { predicate: (a_ty, _b_ty), .. } = goal; - - // 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) - self.add_goals(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() { - self.add_goal(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. - self.add_goal(goal.with(tcx, ty::OutlivesPredicate(a_ty, b_region))); - self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - } - fn consider_builtin_upcast_to_principal( &mut self, goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>, @@ -927,41 +937,4 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { let candidates = self.assemble_and_evaluate_candidates(goal); self.merge_candidates(candidates) } - - /// Normalize a non-self type when it is structually matched on when solving - /// a built-in goal. - /// - /// This is handled already through `assemble_candidates_after_normalizing_self_ty` - /// for the self type, but for other goals, additional normalization of other - /// arguments may be needed to completely implement the semantics of the trait. - /// - /// This is required when structurally matching on any trait argument that is - /// not the self type. - fn normalize_non_self_ty( - &mut self, - mut ty: Ty<'tcx>, - param_env: ty::ParamEnv<'tcx>, - ) -> Result<Option<Ty<'tcx>>, NoSolution> { - if !matches!(ty.kind(), ty::Alias(..)) { - return Ok(Some(ty)); - } - - for _ in 0..self.local_overflow_limit() { - let ty::Alias(_, projection_ty) = *ty.kind() else { - return Ok(Some(ty)); - }; - - let normalized_ty = self.next_ty_infer(); - let normalizes_to_goal = Goal::new( - self.tcx(), - param_env, - ty::ProjectionPredicate { projection_ty, term: normalized_ty.into() }, - ); - self.add_goal(normalizes_to_goal); - self.try_evaluate_added_goals()?; - ty = self.resolve_vars_if_possible(normalized_ty); - } - - Ok(None) - } } diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index 19d5baa30ec..5746781ae35 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -7,7 +7,7 @@ use crate::infer::outlives::env::OutlivesEnvironment; use crate::infer::InferOk; use crate::traits::outlives_bounds::InferCtxtExt as _; -use crate::traits::select::IntercrateAmbiguityCause; +use crate::traits::select::{IntercrateAmbiguityCause, TreatInductiveCycleAs}; use crate::traits::util::impl_subject_and_oblig; use crate::traits::SkipLeakCheck; use crate::traits::{ @@ -24,6 +24,7 @@ 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, TypeVisitor}; +use rustc_session::lint::builtin::COINDUCTIVE_OVERLAP_IN_COHERENCE; use rustc_span::symbol::sym; use rustc_span::DUMMY_SP; use std::fmt::Debug; @@ -151,14 +152,16 @@ fn with_fresh_ty_vars<'cx, 'tcx>( .predicates_of(impl_def_id) .instantiate(tcx, impl_args) .iter() - .map(|(c, _)| c.as_predicate()) + .map(|(c, s)| (c.as_predicate(), s)) .collect(), }; - let InferOk { value: mut header, obligations } = - selcx.infcx.at(&ObligationCause::dummy(), param_env).normalize(header); + let InferOk { value: mut header, obligations } = selcx + .infcx + .at(&ObligationCause::dummy_with_span(tcx.def_span(impl_def_id)), param_env) + .normalize(header); - header.predicates.extend(obligations.into_iter().map(|o| o.predicate)); + header.predicates.extend(obligations.into_iter().map(|o| (o.predicate, o.cause.span))); header } @@ -207,16 +210,76 @@ fn overlap<'tcx>( let equate_obligations = equate_impl_headers(selcx.infcx, &impl1_header, &impl2_header)?; debug!("overlap: unification check succeeded"); - if overlap_mode.use_implicit_negative() - && impl_intersection_has_impossible_obligation( - selcx, - param_env, - &impl1_header, - impl2_header, - equate_obligations, - ) - { - return None; + 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, + param_env, + &impl1_header, + &impl2_header, + &equate_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_only_trait_path(), + 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", + ); + if !failing_obligation.cause.span.is_dummy() { + lint.span_label( + failing_obligation.cause.span, + format!( + "`{}` may be considered to hold in future releases, \ + causing the impls to overlap", + infcx + .resolve_vars_if_possible(failing_obligation.predicate) + ), + ); + } + lint + }, + ); + } + + return None; + } + } } // We toggle the `leak_check` by using `skip_leak_check` when constructing the @@ -284,40 +347,30 @@ fn impl_intersection_has_impossible_obligation<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, param_env: ty::ParamEnv<'tcx>, impl1_header: &ty::ImplHeader<'tcx>, - impl2_header: ty::ImplHeader<'tcx>, - obligations: PredicateObligations<'tcx>, -) -> bool { + impl2_header: &ty::ImplHeader<'tcx>, + obligations: &PredicateObligations<'tcx>, +) -> Option<PredicateObligation<'tcx>> { let infcx = selcx.infcx; - let obligation_guaranteed_to_fail = move |obligation: &PredicateObligation<'tcx>| { - if infcx.next_trait_solver() { - infcx.evaluate_obligation(obligation).map_or(false, |result| !result.may_apply()) - } else { - // We use `evaluate_root_obligation` to correctly track - // intercrate ambiguity clauses. We do not need this in the - // new solver. - selcx.evaluate_root_obligation(obligation).map_or( - false, // Overflow has occurred, and treat the obligation as possibly holding. - |result| !result.may_apply(), - ) - } - }; - - let opt_failing_obligation = [&impl1_header.predicates, &impl2_header.predicates] + [&impl1_header.predicates, &impl2_header.predicates] .into_iter() .flatten() - .map(|&predicate| { - Obligation::new(infcx.tcx, ObligationCause::dummy(), param_env, predicate) + .map(|&(predicate, span)| { + Obligation::new(infcx.tcx, ObligationCause::dummy_with_span(span), param_env, predicate) + }) + .chain(obligations.into_iter().cloned()) + .find(|obligation: &PredicateObligation<'tcx>| { + if infcx.next_trait_solver() { + infcx.evaluate_obligation(obligation).map_or(false, |result| !result.may_apply()) + } else { + // We use `evaluate_root_obligation` to correctly track intercrate + // ambiguity clauses. We cannot use this in the new solver. + selcx.evaluate_root_obligation(obligation).map_or( + false, // Overflow has occurred, and treat the obligation as possibly holding. + |result| !result.may_apply(), + ) + } }) - .chain(obligations) - .find(obligation_guaranteed_to_fail); - - if let Some(failing_obligation) = opt_failing_obligation { - debug!("overlap: obligation unsatisfiable {:?}", failing_obligation); - true - } else { - false - } } /// Check if both impls can be satisfied by a common type by considering whether @@ -452,22 +505,23 @@ fn prove_negated_obligation<'tcx>( /// 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), ret)] -pub fn trait_ref_is_knowable<'tcx>( +#[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>, -) -> Result<(), Conflict> { + mut lazily_normalize_ty: impl FnMut(Ty<'tcx>) -> Result<Ty<'tcx>, E>, +) -> Result<Result<(), Conflict>, E> { if Some(trait_ref.def_id) == tcx.lang_items().fn_ptr_trait() { // The only types implementing `FnPtr` are function pointers, // so if there's no impl of `FnPtr` in the current crate, // then such an impl will never be added in the future. - return Ok(()); + return Ok(Ok(())); } - if orphan_check_trait_ref(trait_ref, InCrate::Remote).is_ok() { + 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 Err(Conflict::Downstream); + return Ok(Err(Conflict::Downstream)); } if trait_ref_is_local_or_fundamental(tcx, trait_ref) { @@ -476,7 +530,7 @@ pub fn trait_ref_is_knowable<'tcx>( // allowed to implement a substitution of this trait ref, which // means impls could only come from dependencies of this crate, // which we already know about. - return Ok(()); + return Ok(Ok(())); } // This is a remote non-fundamental trait, so if another crate @@ -487,10 +541,10 @@ pub fn trait_ref_is_knowable<'tcx>( // 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).is_ok() { - Ok(()) + if orphan_check_trait_ref(trait_ref, InCrate::Local, &mut lazily_normalize_ty)?.is_ok() { + Ok(Ok(())) } else { - Err(Conflict::Upstream) + Ok(Err(Conflict::Upstream)) } } @@ -526,7 +580,7 @@ pub fn orphan_check(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Result<(), OrphanChe return Ok(()); } - orphan_check_trait_ref(trait_ref, InCrate::Local) + orphan_check_trait_ref::<!>(trait_ref, InCrate::Local, |ty| Ok(ty)).unwrap() } /// Checks whether a trait-ref is potentially implementable by a crate. @@ -615,11 +669,12 @@ pub fn orphan_check(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Result<(), OrphanChe /// /// Note that this function is never called for types that have both type /// parameters and inference variables. -#[instrument(level = "trace", ret)] -fn orphan_check_trait_ref<'tcx>( +#[instrument(level = "trace", skip(lazily_normalize_ty), ret)] +fn orphan_check_trait_ref<'tcx, E: Debug>( trait_ref: ty::TraitRef<'tcx>, in_crate: InCrate, -) -> Result<(), OrphanCheckErr<'tcx>> { + 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 {:?}", @@ -627,9 +682,10 @@ fn orphan_check_trait_ref<'tcx>( ); } - let mut checker = OrphanChecker::new(in_crate); - match trait_ref.visit_with(&mut checker) { + 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; @@ -642,34 +698,39 @@ fn orphan_check_trait_ref<'tcx>( } } ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(_)) => Ok(()), - } + }) } -struct OrphanChecker<'tcx> { +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> OrphanChecker<'tcx> { - fn new(in_crate: InCrate) -> Self { +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>> { + 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>> { + fn found_param_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<OrphanCheckEarlyExit<'tcx, E>> { if self.search_first_local_ty { ControlFlow::Continue(()) } else { @@ -685,18 +746,28 @@ impl<'tcx> OrphanChecker<'tcx> { } } -enum OrphanCheckEarlyExit<'tcx> { +enum OrphanCheckEarlyExit<'tcx, E> { + NormalizationFailure(E), ParamTy(Ty<'tcx>), LocalTy(Ty<'tcx>), } -impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for OrphanChecker<'tcx> { - type BreakTy = OrphanCheckEarlyExit<'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 `-Ztrait-solver=next-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 diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index 4d85e2b6089..457d5420ca3 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -7,6 +7,7 @@ use super::{ ObligationCauseCode, ObligationCtxt, OutputTypeParameterMismatch, Overflow, PredicateObligation, SelectionError, TraitNotObjectSafe, }; +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::{self, InferCtxt}; @@ -3110,27 +3111,6 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ) -> UnsatisfiedConst { let unsatisfied_const = UnsatisfiedConst(false); // FIXME(effects) - /* if trait_predicate.is_const_if_const() { - let non_const_predicate = trait_ref.without_const(); - let non_const_obligation = Obligation { - cause: obligation.cause.clone(), - param_env: obligation.param_env, - predicate: non_const_predicate.to_predicate(self.tcx), - recursion_depth: obligation.recursion_depth, - }; - if self.predicate_may_hold(&non_const_obligation) { - unsatisfied_const = UnsatisfiedConst(true); - err.span_note( - span, - format!( - "the trait `{}` is implemented for `{}`, \ - but that implementation is not `const`", - non_const_predicate.print_modifiers_and_trait_path(), - trait_ref.skip_binder().self_ty(), - ), - ); - } - } */ unsatisfied_const } @@ -3142,24 +3122,15 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { kind: ty::ClosureKind, ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { let closure_span = self.tcx.def_span(closure_def_id); - let mut err = struct_span_err!( - self.tcx.sess, - closure_span, - E0525, - "expected a closure that implements the `{}` trait, \ - but this closure only implements `{}`", - kind, - found_kind - ); - err.span_label( + let mut err = ClosureKindMismatch { closure_span, - format!("this closure implements `{found_kind}`, not `{kind}`"), - ); - err.span_label( - obligation.cause.span, - format!("the requirement to implement `{kind}` derives from here"), - ); + 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. @@ -3167,30 +3138,22 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let hir_id = self.tcx.hir().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.span_label( - *span, - format!( - "closure is `FnOnce` because it moves the \ - variable `{}` out of its environment", - ty::place_to_string_for_capture(self.tcx, 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.span_label( - *span, - format!( - "closure is `FnMut` because it mutates the \ - variable `{}` here", - ty::place_to_string_for_capture(self.tcx, place) - ), - ); + err.fn_mut_label = Some(ClosureFnMutLabel { + span: *span, + place: ty::place_to_string_for_capture(self.tcx, &place), + }) } _ => {} } } - err + self.tcx.sess.create_err(err) } fn report_type_parameter_mismatch_cyclic_type_error( diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 2b8571796df..5e075984238 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -2700,7 +2700,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { | ObligationCauseCode::MatchImpl(..) | ObligationCauseCode::ReturnType | ObligationCauseCode::ReturnValue(_) - | ObligationCauseCode::BlockTailExpression(_) + | ObligationCauseCode::BlockTailExpression(..) | ObligationCauseCode::AwaitableExpr(_) | ObligationCauseCode::ForLoopIterator | ObligationCauseCode::QuestionMark @@ -2743,6 +2743,12 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } ObligationCauseCode::BindingObligation(item_def_id, span) | ObligationCauseCode::ExprBindingObligation(item_def_id, span, ..) => { + if self.tcx.is_diagnostic_item(sym::Send, item_def_id) + || self.tcx.lang_items().sync_trait() == Some(item_def_id) + { + return; + } + 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); diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 71a82baeec6..88d03003309 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -59,8 +59,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ParamCandidate(param) => { let obligations = self.confirm_param_candidate(obligation, param.map_bound(|t| t.trait_ref)); - // FIXME(effects) - ImplSource::Param(ty::BoundConstness::NotConst, obligations) + ImplSource::Param(obligations) } ImplCandidate(impl_def_id) => { @@ -72,9 +71,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ImplSource::Builtin(BuiltinImplSource::Misc, data) } - ProjectionCandidate(idx, constness) => { + ProjectionCandidate(idx, _) => { let obligations = self.confirm_projection_candidate(obligation, idx)?; - ImplSource::Param(constness, obligations) + ImplSource::Param(obligations) } ObjectCandidate(idx) => self.confirm_object_candidate(obligation, idx)?, diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index f1bd9f8b71a..19385e2d7f2 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -118,6 +118,8 @@ pub struct SelectionContext<'cx, 'tcx> { /// 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. @@ -198,6 +200,27 @@ enum BuiltinImplConditions<'tcx> { 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 => EvaluatedToUnknown, + TreatInductiveCycleAs::Recur => EvaluatedToRecur, + } + } +} + impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { pub fn new(infcx: &'cx InferCtxt<'tcx>) -> SelectionContext<'cx, 'tcx> { SelectionContext { @@ -205,9 +228,26 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { 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, @@ -719,7 +759,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { stack.update_reached_depth(stack_arg.1); return Ok(EvaluatedToOk); } else { - return Ok(EvaluatedToRecur); + return Ok(self.treat_inductive_cycle.into()); } } return Ok(EvaluatedToOk); @@ -837,7 +877,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } ProjectAndUnifyResult::FailedNormalization => Ok(EvaluatedToAmbig), - ProjectAndUnifyResult::Recursive => Ok(EvaluatedToRecur), + ProjectAndUnifyResult::Recursive => Ok(self.treat_inductive_cycle.into()), ProjectAndUnifyResult::MismatchedProjectionTypes(_) => Ok(EvaluatedToErr), } } @@ -1151,7 +1191,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { Some(EvaluatedToOk) } else { debug!("evaluate_stack --> recursive, inductive"); - Some(EvaluatedToRecur) + Some(self.treat_inductive_cycle.into()) } } else { None @@ -1457,7 +1497,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // bound regions. let trait_ref = predicate.skip_binder().trait_ref; - coherence::trait_ref_is_knowable(self.tcx(), trait_ref) + coherence::trait_ref_is_knowable::<!>(self.tcx(), trait_ref, |ty| Ok(ty)).unwrap() } /// Returns `true` if the global caches can be used. @@ -2118,14 +2158,11 @@ impl<'tcx> SelectionContext<'_, 'tcx> { use self::BuiltinImplConditions::{Ambiguous, None, Where}; match *self_ty.kind() { - ty::Infer(ty::IntVar(_)) - | ty::Infer(ty::FloatVar(_)) - | ty::FnDef(..) - | ty::FnPtr(_) - | ty::Error(_) => Where(ty::Binder::dummy(Vec::new())), + 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 @@ -2394,7 +2431,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { Ok(args) => args, Err(()) => { // FIXME: A rematch may fail when a candidate cache hit occurs - // on thefreshened form of the trait predicate, but the match + // 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 |
