Skip to content

Commit 35c8a37

Browse files
committed
handle reservation impls, track impl source
1 parent 97043c2 commit 35c8a37

File tree

9 files changed

+88
-27
lines changed

9 files changed

+88
-27
lines changed

compiler/rustc_trait_selection/src/solve/assembly/mod.rs

+3-5
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ pub(super) trait GoalKind<'tcx>:
110110
ecx: &mut EvalCtxt<'_, 'tcx>,
111111
goal: Goal<'tcx, Self>,
112112
impl_def_id: DefId,
113-
) -> QueryResult<'tcx>;
113+
) -> Result<Candidate<'tcx>, NoSolution>;
114114

115115
/// If the predicate contained an error, we want to avoid emitting unnecessary trait
116116
/// errors but still want to emit errors for other trait goals. We have some special
@@ -400,8 +400,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
400400
if let Some(impls_for_type) = trait_impls.non_blanket_impls().get(&simp) {
401401
for &impl_def_id in impls_for_type {
402402
match G::consider_impl_candidate(self, goal, impl_def_id) {
403-
Ok(result) => candidates
404-
.push(Candidate { source: CandidateSource::Impl(impl_def_id), result }),
403+
Ok(candidate) => candidates.push(candidate),
405404
Err(NoSolution) => (),
406405
}
407406
}
@@ -519,8 +518,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
519518
let trait_impls = tcx.trait_impls_of(goal.predicate.trait_def_id(tcx));
520519
for &impl_def_id in trait_impls.blanket_impls() {
521520
match G::consider_impl_candidate(self, goal, impl_def_id) {
522-
Ok(result) => candidates
523-
.push(Candidate { source: CandidateSource::Impl(impl_def_id), result }),
521+
Ok(candidate) => candidates.push(candidate),
524522
Err(NoSolution) => (),
525523
}
526524
}

compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs

+34-14
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
1+
use crate::solve::assembly::Candidate;
2+
13
use super::EvalCtxt;
2-
use rustc_middle::traits::solve::{inspect, CandidateSource, QueryResult};
4+
use rustc_middle::traits::{
5+
query::NoSolution,
6+
solve::{inspect, CandidateSource, QueryResult},
7+
};
38
use std::marker::PhantomData;
49

510
pub(in crate::solve) struct ProbeCtxt<'me, 'a, 'tcx, F, T> {
@@ -36,6 +41,23 @@ where
3641
}
3742
}
3843

44+
pub(in crate::solve) struct TraitProbeCtxt<'me, 'a, 'tcx, F> {
45+
cx: ProbeCtxt<'me, 'a, 'tcx, F, QueryResult<'tcx>>,
46+
source: CandidateSource,
47+
}
48+
49+
impl<'tcx, F> TraitProbeCtxt<'_, '_, 'tcx, F>
50+
where
51+
F: FnOnce(&QueryResult<'tcx>) -> inspect::ProbeKind<'tcx>,
52+
{
53+
pub(in crate::solve) fn enter(
54+
self,
55+
f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> QueryResult<'tcx>,
56+
) -> Result<Candidate<'tcx>, NoSolution> {
57+
self.cx.enter(|ecx| f(ecx)).map(|result| Candidate { source: self.source, result })
58+
}
59+
}
60+
3961
impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
4062
/// `probe_kind` is only called when proof tree building is enabled so it can be
4163
/// as expensive as necessary to output the desired information.
@@ -69,20 +91,18 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
6991
pub(in crate::solve) fn probe_trait_candidate(
7092
&mut self,
7193
source: CandidateSource,
72-
) -> ProbeCtxt<
73-
'_,
74-
'a,
75-
'tcx,
76-
impl FnOnce(&QueryResult<'tcx>) -> inspect::ProbeKind<'tcx>,
77-
QueryResult<'tcx>,
78-
> {
79-
ProbeCtxt {
80-
ecx: self,
81-
probe_kind: move |result: &QueryResult<'tcx>| inspect::ProbeKind::TraitCandidate {
82-
source,
83-
result: *result,
94+
) -> TraitProbeCtxt<'_, 'a, 'tcx, impl FnOnce(&QueryResult<'tcx>) -> inspect::ProbeKind<'tcx>>
95+
{
96+
TraitProbeCtxt {
97+
cx: ProbeCtxt {
98+
ecx: self,
99+
probe_kind: move |result: &QueryResult<'tcx>| inspect::ProbeKind::TraitCandidate {
100+
source,
101+
result: *result,
102+
},
103+
_result: PhantomData,
84104
},
85-
_result: PhantomData,
105+
source,
86106
}
87107
}
88108
}

compiler/rustc_trait_selection/src/solve/project_goals/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::traits::{check_args_compatible, specialization_graph};
22

3-
use super::assembly::{self, structural_traits};
3+
use super::assembly::{self, structural_traits, Candidate};
44
use super::EvalCtxt;
55
use rustc_hir::def::DefKind;
66
use rustc_hir::def_id::DefId;
@@ -154,7 +154,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
154154
ecx: &mut EvalCtxt<'_, 'tcx>,
155155
goal: Goal<'tcx, ProjectionPredicate<'tcx>>,
156156
impl_def_id: DefId,
157-
) -> QueryResult<'tcx> {
157+
) -> Result<Candidate<'tcx>, NoSolution> {
158158
let tcx = ecx.tcx();
159159

160160
let goal_trait_ref = goal.predicate.projection_ty.trait_ref(tcx);

compiler/rustc_trait_selection/src/solve/trait_goals.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
//! Dealing with trait goals, i.e. `T: Trait<'a, U>`.
22
3-
use super::assembly::{self, structural_traits};
3+
use super::assembly::{self, structural_traits, Candidate};
44
use super::{EvalCtxt, SolverMode};
55
use rustc_hir::def_id::DefId;
66
use rustc_hir::{LangItem, Movability};
77
use rustc_infer::traits::query::NoSolution;
88
use rustc_middle::traits::solve::inspect::ProbeKind;
9-
use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, QueryResult};
9+
use rustc_middle::traits::solve::{
10+
CandidateSource, CanonicalResponse, Certainty, Goal, QueryResult,
11+
};
1012
use rustc_middle::traits::{BuiltinImplSource, Reveal};
1113
use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams, TreatProjections};
1214
use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt};
@@ -38,7 +40,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
3840
ecx: &mut EvalCtxt<'_, 'tcx>,
3941
goal: Goal<'tcx, TraitPredicate<'tcx>>,
4042
impl_def_id: DefId,
41-
) -> QueryResult<'tcx> {
43+
) -> Result<Candidate<'tcx>, NoSolution> {
4244
let tcx = ecx.tcx();
4345

4446
let impl_trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap();
@@ -63,7 +65,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
6365
},
6466
};
6567

66-
ecx.probe_misc_candidate("impl").enter(|ecx| {
68+
ecx.probe_trait_candidate(CandidateSource::Impl(impl_def_id)).enter(|ecx| {
6769
let impl_args = ecx.fresh_args_for_item(impl_def_id);
6870
let impl_trait_ref = impl_trait_ref.instantiate(tcx, impl_args);
6971

compiler/rustc_trait_selection/src/traits/coherence.rs

+23-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use rustc_hir::def_id::{DefId, LOCAL_CRATE};
2525
use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, TyCtxtInferExt};
2626
use rustc_infer::traits::{util, TraitEngine};
2727
use rustc_middle::traits::query::NoSolution;
28-
use rustc_middle::traits::solve::{Certainty, Goal};
28+
use rustc_middle::traits::solve::{CandidateSource, Certainty, Goal};
2929
use rustc_middle::traits::specialization_graph::OverlapMode;
3030
use rustc_middle::traits::DefiningAnchor;
3131
use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
@@ -1019,6 +1019,28 @@ impl<'a, 'tcx> ProofTreeVisitor<'tcx> for AmbiguityCausesVisitor<'a> {
10191019
_ => return ControlFlow::Continue(()),
10201020
};
10211021

1022+
// Add ambiguity causes for reservation impls.
1023+
for cand in goal.candidates() {
1024+
if let inspect::ProbeKind::TraitCandidate {
1025+
source: CandidateSource::Impl(def_id),
1026+
result: Ok(_),
1027+
} = cand.kind()
1028+
{
1029+
if let ty::ImplPolarity::Reservation = infcx.tcx.impl_polarity(def_id) {
1030+
let value = infcx
1031+
.tcx
1032+
.get_attr(def_id, sym::rustc_reservation_impl)
1033+
.and_then(|a| a.value_str());
1034+
if let Some(value) = value {
1035+
self.causes.insert(IntercrateAmbiguityCause::ReservationImpl {
1036+
message: value.to_string(),
1037+
});
1038+
}
1039+
}
1040+
}
1041+
}
1042+
1043+
// Add ambiguity causes for unknowable goals.
10221044
let mut ambiguity_cause = None;
10231045
for cand in goal.candidates() {
10241046
// FIXME: boiiii, using string comparisions here sure is scuffed.

tests/ui/never_type/never-from-impl-is-reserved.stderr renamed to tests/ui/never_type/never-from-impl-is-reserved.current.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error[E0119]: conflicting implementations of trait `MyTrait` for type `MyFoo`
2-
--> $DIR/never-from-impl-is-reserved.rs:10:1
2+
--> $DIR/never-from-impl-is-reserved.rs:13:1
33
|
44
LL | impl MyTrait for MyFoo {}
55
| ---------------------- first implementation here
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error[E0119]: conflicting implementations of trait `MyTrait` for type `MyFoo`
2+
--> $DIR/never-from-impl-is-reserved.rs:13:1
3+
|
4+
LL | impl MyTrait for MyFoo {}
5+
| ---------------------- first implementation here
6+
LL | // This will conflict with the first impl if we impl `for<T> T: From<!>`.
7+
LL | impl<T> MyTrait for T where T: From<!> {}
8+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `MyFoo`
9+
|
10+
= note: permitting this impl would forbid us from adding `impl<T> From<!> for T` later; see rust-lang/rust#64715 for details
11+
12+
error: aborting due to previous error
13+
14+
For more information about this error, try `rustc --explain E0119`.

tests/ui/never_type/never-from-impl-is-reserved.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
// check that the `for<T> T: From<!>` impl is reserved
22

3+
// revisions: current next
4+
//[next] compile-flags: -Ztrait-solver=next-coherence
5+
36
#![feature(never_type)]
47

58
pub struct MyFoo;

tests/ui/traits/reservation-impl/coherence-conflict.next.stderr

+2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ LL | impl OtherTrait for () {}
55
| ---------------------- first implementation here
66
LL | impl<T: MyTrait> OtherTrait for T {}
77
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `()`
8+
|
9+
= note: this impl is reserved
810

911
error: aborting due to previous error
1012

0 commit comments

Comments
 (0)