From 35c8a37a6fce74100f54221ccc17783bad489436 Mon Sep 17 00:00:00 2001 From: lcnr Date: Mon, 20 Nov 2023 15:01:31 +0100 Subject: [PATCH] handle reservation impls, track impl source --- .../src/solve/assembly/mod.rs | 8 ++-- .../src/solve/eval_ctxt/probe.rs | 48 +++++++++++++------ .../src/solve/project_goals/mod.rs | 4 +- .../src/solve/trait_goals.rs | 10 ++-- .../src/traits/coherence.rs | 24 +++++++++- ...ever-from-impl-is-reserved.current.stderr} | 2 +- .../never-from-impl-is-reserved.next.stderr | 14 ++++++ .../never_type/never-from-impl-is-reserved.rs | 3 ++ .../coherence-conflict.next.stderr | 2 + 9 files changed, 88 insertions(+), 27 deletions(-) rename tests/ui/never_type/{never-from-impl-is-reserved.stderr => never-from-impl-is-reserved.current.stderr} (92%) create mode 100644 tests/ui/never_type/never-from-impl-is-reserved.next.stderr diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs index c3abf4bac1d8b..b6d2228d4b8fb 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs @@ -110,7 +110,7 @@ pub(super) trait GoalKind<'tcx>: ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, impl_def_id: DefId, - ) -> QueryResult<'tcx>; + ) -> Result, 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 @@ -400,8 +400,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { 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(result) => candidates - .push(Candidate { source: CandidateSource::Impl(impl_def_id), result }), + Ok(candidate) => candidates.push(candidate), Err(NoSolution) => (), } } @@ -519,8 +518,7 @@ impl<'tcx> EvalCtxt<'_, '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(result) => candidates - .push(Candidate { source: CandidateSource::Impl(impl_def_id), result }), + Ok(candidate) => candidates.push(candidate), Err(NoSolution) => (), } } diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs index 6087b91679095..91fd48807a4d8 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs @@ -1,5 +1,10 @@ +use crate::solve::assembly::Candidate; + use super::EvalCtxt; -use rustc_middle::traits::solve::{inspect, CandidateSource, QueryResult}; +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> { @@ -36,6 +41,23 @@ where } } +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, 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. @@ -69,20 +91,18 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { pub(in crate::solve) fn probe_trait_candidate( &mut self, source: CandidateSource, - ) -> ProbeCtxt< - '_, - 'a, - 'tcx, - impl FnOnce(&QueryResult<'tcx>) -> inspect::ProbeKind<'tcx>, - QueryResult<'tcx>, - > { - ProbeCtxt { - ecx: self, - probe_kind: move |result: &QueryResult<'tcx>| inspect::ProbeKind::TraitCandidate { - source, - result: *result, + ) -> 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, }, - _result: PhantomData, + source, } } } diff --git a/compiler/rustc_trait_selection/src/solve/project_goals/mod.rs b/compiler/rustc_trait_selection/src/solve/project_goals/mod.rs index 7fb550aa3e063..6c29ce492df0e 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals/mod.rs @@ -1,6 +1,6 @@ use crate::traits::{check_args_compatible, specialization_graph}; -use super::assembly::{self, structural_traits}; +use super::assembly::{self, structural_traits, Candidate}; use super::EvalCtxt; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; @@ -154,7 +154,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, ProjectionPredicate<'tcx>>, impl_def_id: DefId, - ) -> QueryResult<'tcx> { + ) -> Result, NoSolution> { let tcx = ecx.tcx(); let goal_trait_ref = goal.predicate.projection_ty.trait_ref(tcx); diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index 0f30b49314b03..a0c065dfa03ba 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -1,12 +1,14 @@ //! Dealing with trait goals, i.e. `T: Trait<'a, U>`. -use super::assembly::{self, structural_traits}; +use super::assembly::{self, structural_traits, Candidate}; use super::{EvalCtxt, 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::{CanonicalResponse, Certainty, Goal, QueryResult}; +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}; @@ -38,7 +40,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, TraitPredicate<'tcx>>, impl_def_id: DefId, - ) -> QueryResult<'tcx> { + ) -> Result, NoSolution> { let tcx = ecx.tcx(); let impl_trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); @@ -63,7 +65,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { }, }; - ecx.probe_misc_candidate("impl").enter(|ecx| { + 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); diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index 362a734818c1e..0d04be91fcb6a 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -25,7 +25,7 @@ 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::{Certainty, Goal}; +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}; @@ -1019,6 +1019,28 @@ impl<'a, 'tcx> ProofTreeVisitor<'tcx> for AmbiguityCausesVisitor<'a> { _ => 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 value = infcx + .tcx + .get_attr(def_id, sym::rustc_reservation_impl) + .and_then(|a| a.value_str()); + if let Some(value) = value { + self.causes.insert(IntercrateAmbiguityCause::ReservationImpl { + message: value.to_string(), + }); + } + } + } + } + + // 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. diff --git a/tests/ui/never_type/never-from-impl-is-reserved.stderr b/tests/ui/never_type/never-from-impl-is-reserved.current.stderr similarity index 92% rename from tests/ui/never_type/never-from-impl-is-reserved.stderr rename to tests/ui/never_type/never-from-impl-is-reserved.current.stderr index 871c512052821..6c71785de2897 100644 --- a/tests/ui/never_type/never-from-impl-is-reserved.stderr +++ b/tests/ui/never_type/never-from-impl-is-reserved.current.stderr @@ -1,5 +1,5 @@ error[E0119]: conflicting implementations of trait `MyTrait` for type `MyFoo` - --> $DIR/never-from-impl-is-reserved.rs:10:1 + --> $DIR/never-from-impl-is-reserved.rs:13:1 | LL | impl MyTrait for MyFoo {} | ---------------------- first implementation here diff --git a/tests/ui/never_type/never-from-impl-is-reserved.next.stderr b/tests/ui/never_type/never-from-impl-is-reserved.next.stderr new file mode 100644 index 0000000000000..6c71785de2897 --- /dev/null +++ b/tests/ui/never_type/never-from-impl-is-reserved.next.stderr @@ -0,0 +1,14 @@ +error[E0119]: conflicting implementations of trait `MyTrait` for type `MyFoo` + --> $DIR/never-from-impl-is-reserved.rs:13:1 + | +LL | impl MyTrait for MyFoo {} + | ---------------------- first implementation here +LL | // This will conflict with the first impl if we impl `for T: From`. +LL | impl MyTrait for T where T: From {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `MyFoo` + | + = note: permitting this impl would forbid us from adding `impl From for T` later; see rust-lang/rust#64715 for details + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0119`. diff --git a/tests/ui/never_type/never-from-impl-is-reserved.rs b/tests/ui/never_type/never-from-impl-is-reserved.rs index 9d16015bdc129..f358e045cf019 100644 --- a/tests/ui/never_type/never-from-impl-is-reserved.rs +++ b/tests/ui/never_type/never-from-impl-is-reserved.rs @@ -1,5 +1,8 @@ // check that the `for T: From` impl is reserved +// revisions: current next +//[next] compile-flags: -Ztrait-solver=next-coherence + #![feature(never_type)] pub struct MyFoo; diff --git a/tests/ui/traits/reservation-impl/coherence-conflict.next.stderr b/tests/ui/traits/reservation-impl/coherence-conflict.next.stderr index e5a3c3f5cc4b7..393350ea3f12a 100644 --- a/tests/ui/traits/reservation-impl/coherence-conflict.next.stderr +++ b/tests/ui/traits/reservation-impl/coherence-conflict.next.stderr @@ -5,6 +5,8 @@ LL | impl OtherTrait for () {} | ---------------------- first implementation here LL | impl OtherTrait for T {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `()` + | + = note: this impl is reserved error: aborting due to previous error