Skip to content

Commit

Permalink
handle reservation impls, track impl source
Browse files Browse the repository at this point in the history
  • Loading branch information
lcnr committed Nov 20, 2023
1 parent 97043c2 commit 35c8a37
Show file tree
Hide file tree
Showing 9 changed files with 88 additions and 27 deletions.
8 changes: 3 additions & 5 deletions compiler/rustc_trait_selection/src/solve/assembly/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ pub(super) trait GoalKind<'tcx>:
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
impl_def_id: DefId,
) -> QueryResult<'tcx>;
) -> 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
Expand Down Expand Up @@ -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) => (),
}
}
Expand Down Expand Up @@ -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) => (),
}
}
Expand Down
48 changes: 34 additions & 14 deletions compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs
Original file line number Diff line number Diff line change
@@ -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> {
Expand Down Expand Up @@ -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<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.
Expand Down Expand Up @@ -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,
}
}
}
4 changes: 2 additions & 2 deletions compiler/rustc_trait_selection/src/solve/project_goals/mod.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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<Candidate<'tcx>, NoSolution> {
let tcx = ecx.tcx();

let goal_trait_ref = goal.predicate.projection_ty.trait_ref(tcx);
Expand Down
10 changes: 6 additions & 4 deletions compiler/rustc_trait_selection/src/solve/trait_goals.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down Expand Up @@ -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<Candidate<'tcx>, NoSolution> {
let tcx = ecx.tcx();

let impl_trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap();
Expand All @@ -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);

Expand Down
24 changes: 23 additions & 1 deletion compiler/rustc_trait_selection/src/traits/coherence.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down
14 changes: 14 additions & 0 deletions tests/ui/never_type/never-from-impl-is-reserved.next.stderr
Original file line number Diff line number Diff line change
@@ -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> T: From<!>`.
LL | impl<T> MyTrait for T where T: From<!> {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `MyFoo`
|
= note: permitting this impl would forbid us from adding `impl<T> 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`.
3 changes: 3 additions & 0 deletions tests/ui/never_type/never-from-impl-is-reserved.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
// check that the `for<T> T: From<!>` impl is reserved

// revisions: current next
//[next] compile-flags: -Ztrait-solver=next-coherence

#![feature(never_type)]

pub struct MyFoo;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ LL | impl OtherTrait for () {}
| ---------------------- first implementation here
LL | impl<T: MyTrait> OtherTrait for T {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `()`
|
= note: this impl is reserved

error: aborting due to previous error

Expand Down

0 comments on commit 35c8a37

Please sign in to comment.