Skip to content

Commit 32a4713

Browse files
committed
Auto merge of rust-lang#124589 - matthiaskrgr:rollup-w11p0ms, r=matthiaskrgr
Rollup of 3 pull requests Successful merges: - rust-lang#124529 (Rewrite select (in the new solver) to use a `ProofTreeVisitor`) - rust-lang#124542 (Add diagnostic item for `std::iter::Iterator::enumerate`) - rust-lang#124566 (fix `NormalizesTo` proof tree issue) r? `@ghost` `@rustbot` modify labels: rollup
2 parents 9ba3d31 + e5b6bb3 commit 32a4713

File tree

8 files changed

+346
-380
lines changed

8 files changed

+346
-380
lines changed

compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -754,6 +754,7 @@ symbols! {
754754
enable,
755755
encode,
756756
end,
757+
enumerate_method,
757758
env,
758759
env_CFG_RELEASE: env!("CFG_RELEASE"),
759760
eprint_macro,

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

+156-328
Large diffs are not rendered by default.

compiler/rustc_trait_selection/src/solve/inspect/analyse.rs

+120-50
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use rustc_ast_ir::visit::VisitorResult;
1414
use rustc_infer::infer::resolve::EagerResolver;
1515
use rustc_infer::infer::type_variable::TypeVariableOrigin;
1616
use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk};
17+
use rustc_infer::traits::{TraitEngine, TraitEngineExt};
1718
use rustc_macros::extension;
1819
use rustc_middle::infer::unify_key::ConstVariableOrigin;
1920
use rustc_middle::traits::query::NoSolution;
@@ -22,9 +23,10 @@ use rustc_middle::traits::solve::{Certainty, Goal};
2223
use rustc_middle::traits::ObligationCause;
2324
use rustc_middle::ty;
2425
use rustc_middle::ty::TypeFoldable;
25-
use rustc_span::Span;
26+
use rustc_span::{Span, DUMMY_SP};
2627

2728
use crate::solve::eval_ctxt::canonical;
29+
use crate::solve::FulfillmentCtxt;
2830
use crate::solve::{EvalCtxt, GoalEvaluationKind, GoalSource};
2931
use crate::solve::{GenerateProofTree, InferCtxtEvalExt};
3032

@@ -37,7 +39,54 @@ pub struct InspectGoal<'a, 'tcx> {
3739
depth: usize,
3840
orig_values: Vec<ty::GenericArg<'tcx>>,
3941
goal: Goal<'tcx, ty::Predicate<'tcx>>,
40-
evaluation: inspect::CanonicalGoalEvaluation<'tcx>,
42+
result: Result<Certainty, NoSolution>,
43+
evaluation_kind: inspect::CanonicalGoalEvaluationKind<'tcx>,
44+
normalizes_to_term_hack: Option<NormalizesToTermHack<'tcx>>,
45+
}
46+
47+
/// The expected term of a `NormalizesTo` goal gets replaced
48+
/// with an unconstrained inference variable when computing
49+
/// `NormalizesTo` goals and we return the nested goals to the
50+
/// caller, who also equates the actual term with the expected.
51+
///
52+
/// This is an implementation detail of the trait solver and
53+
/// not something we want to leak to users. We therefore
54+
/// treat `NormalizesTo` goals as if they apply the expected
55+
/// type at the end of each candidate.
56+
#[derive(Copy, Clone)]
57+
struct NormalizesToTermHack<'tcx> {
58+
term: ty::Term<'tcx>,
59+
unconstrained_term: ty::Term<'tcx>,
60+
}
61+
62+
impl<'tcx> NormalizesToTermHack<'tcx> {
63+
/// Relate the `term` with the new `unconstrained_term` created
64+
/// when computing the proof tree for this `NormalizesTo` goals.
65+
/// This handles nested obligations.
66+
fn constrain(
67+
self,
68+
infcx: &InferCtxt<'tcx>,
69+
span: Span,
70+
param_env: ty::ParamEnv<'tcx>,
71+
) -> Result<Certainty, NoSolution> {
72+
infcx
73+
.at(&ObligationCause::dummy_with_span(span), param_env)
74+
.eq(DefineOpaqueTypes::Yes, self.term, self.unconstrained_term)
75+
.map_err(|_| NoSolution)
76+
.and_then(|InferOk { value: (), obligations }| {
77+
let mut fulfill_cx = FulfillmentCtxt::new(infcx);
78+
fulfill_cx.register_predicate_obligations(infcx, obligations);
79+
if fulfill_cx.select_where_possible(infcx).is_empty() {
80+
if fulfill_cx.pending_obligations().is_empty() {
81+
Ok(Certainty::Yes)
82+
} else {
83+
Ok(Certainty::AMBIGUOUS)
84+
}
85+
} else {
86+
Err(NoSolution)
87+
}
88+
})
89+
}
4190
}
4291

4392
pub struct InspectCandidate<'a, 'tcx> {
@@ -115,42 +164,47 @@ impl<'a, 'tcx> InspectCandidate<'a, 'tcx> {
115164
self.final_state,
116165
);
117166

167+
if let Some(term_hack) = self.goal.normalizes_to_term_hack {
168+
// FIXME: We ignore the expected term of `NormalizesTo` goals
169+
// when computing the result of its candidates. This is
170+
// scuffed.
171+
let _ = term_hack.constrain(infcx, span, param_env);
172+
}
173+
118174
instantiated_goals
119175
.into_iter()
120-
.map(|goal| {
121-
let proof_tree = match goal.predicate.kind().no_bound_vars() {
122-
Some(ty::PredicateKind::NormalizesTo(ty::NormalizesTo { alias, term })) => {
123-
let unconstrained_term = match term.unpack() {
124-
ty::TermKind::Ty(_) => infcx
125-
.next_ty_var(TypeVariableOrigin { param_def_id: None, span })
126-
.into(),
127-
ty::TermKind::Const(ct) => infcx
128-
.next_const_var(
129-
ct.ty(),
130-
ConstVariableOrigin { param_def_id: None, span },
131-
)
132-
.into(),
133-
};
134-
let goal = goal
135-
.with(infcx.tcx, ty::NormalizesTo { alias, term: unconstrained_term });
136-
let proof_tree =
137-
EvalCtxt::enter_root(infcx, GenerateProofTree::Yes, |ecx| {
138-
ecx.evaluate_goal_raw(
139-
GoalEvaluationKind::Root,
140-
GoalSource::Misc,
141-
goal,
142-
)
143-
})
144-
.1;
145-
let InferOk { value: (), obligations: _ } = infcx
146-
.at(&ObligationCause::dummy_with_span(span), param_env)
147-
.eq(DefineOpaqueTypes::Yes, term, unconstrained_term)
148-
.unwrap();
149-
proof_tree
150-
}
151-
_ => infcx.evaluate_root_goal(goal, GenerateProofTree::Yes).1,
152-
};
153-
InspectGoal::new(infcx, self.goal.depth + 1, proof_tree.unwrap())
176+
.map(|goal| match goal.predicate.kind().no_bound_vars() {
177+
Some(ty::PredicateKind::NormalizesTo(ty::NormalizesTo { alias, term })) => {
178+
let unconstrained_term = match term.unpack() {
179+
ty::TermKind::Ty(_) => infcx
180+
.next_ty_var(TypeVariableOrigin { param_def_id: None, span })
181+
.into(),
182+
ty::TermKind::Const(ct) => infcx
183+
.next_const_var(
184+
ct.ty(),
185+
ConstVariableOrigin { param_def_id: None, span },
186+
)
187+
.into(),
188+
};
189+
let goal =
190+
goal.with(infcx.tcx, ty::NormalizesTo { alias, term: unconstrained_term });
191+
let proof_tree = EvalCtxt::enter_root(infcx, GenerateProofTree::Yes, |ecx| {
192+
ecx.evaluate_goal_raw(GoalEvaluationKind::Root, GoalSource::Misc, goal)
193+
})
194+
.1;
195+
InspectGoal::new(
196+
infcx,
197+
self.goal.depth + 1,
198+
proof_tree.unwrap(),
199+
Some(NormalizesToTermHack { term, unconstrained_term }),
200+
)
201+
}
202+
_ => InspectGoal::new(
203+
infcx,
204+
self.goal.depth + 1,
205+
infcx.evaluate_root_goal(goal, GenerateProofTree::Yes).1.unwrap(),
206+
None,
207+
),
154208
})
155209
.collect()
156210
}
@@ -172,7 +226,7 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> {
172226
}
173227

174228
pub fn result(&self) -> Result<Certainty, NoSolution> {
175-
self.evaluation.result.map(|c| c.value.certainty)
229+
self.result
176230
}
177231

178232
fn candidates_recur(
@@ -229,11 +283,11 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> {
229283

230284
pub fn candidates(&'a self) -> Vec<InspectCandidate<'a, 'tcx>> {
231285
let mut candidates = vec![];
232-
let last_eval_step = match self.evaluation.kind {
286+
let last_eval_step = match self.evaluation_kind {
233287
inspect::CanonicalGoalEvaluationKind::Overflow
234288
| inspect::CanonicalGoalEvaluationKind::CycleInStack
235289
| inspect::CanonicalGoalEvaluationKind::ProvisionalCacheHit => {
236-
warn!("unexpected root evaluation: {:?}", self.evaluation);
290+
warn!("unexpected root evaluation: {:?}", self.evaluation_kind);
237291
return vec![];
238292
}
239293
inspect::CanonicalGoalEvaluationKind::Evaluation { revisions } => {
@@ -262,17 +316,33 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> {
262316
candidates.pop().filter(|_| candidates.is_empty())
263317
}
264318

265-
fn new(infcx: &'a InferCtxt<'tcx>, depth: usize, root: inspect::GoalEvaluation<'tcx>) -> Self {
319+
fn new(
320+
infcx: &'a InferCtxt<'tcx>,
321+
depth: usize,
322+
root: inspect::GoalEvaluation<'tcx>,
323+
normalizes_to_term_hack: Option<NormalizesToTermHack<'tcx>>,
324+
) -> Self {
266325
let inspect::GoalEvaluation { uncanonicalized_goal, kind, evaluation } = root;
267-
match kind {
268-
inspect::GoalEvaluationKind::Root { orig_values } => InspectGoal {
269-
infcx,
270-
depth,
271-
orig_values,
272-
goal: uncanonicalized_goal.fold_with(&mut EagerResolver::new(infcx)),
273-
evaluation,
274-
},
275-
inspect::GoalEvaluationKind::Nested { .. } => unreachable!(),
326+
let inspect::GoalEvaluationKind::Root { orig_values } = kind else { unreachable!() };
327+
328+
let result = evaluation.result.and_then(|ok| {
329+
if let Some(term_hack) = normalizes_to_term_hack {
330+
infcx
331+
.probe(|_| term_hack.constrain(infcx, DUMMY_SP, uncanonicalized_goal.param_env))
332+
.map(|certainty| ok.value.certainty.unify_with(certainty))
333+
} else {
334+
Ok(ok.value.certainty)
335+
}
336+
});
337+
338+
InspectGoal {
339+
infcx,
340+
depth,
341+
orig_values,
342+
goal: uncanonicalized_goal.fold_with(&mut EagerResolver::new(infcx)),
343+
result,
344+
evaluation_kind: evaluation.kind,
345+
normalizes_to_term_hack,
276346
}
277347
}
278348
}
@@ -299,6 +369,6 @@ impl<'tcx> InferCtxt<'tcx> {
299369
) -> V::Result {
300370
let (_, proof_tree) = self.evaluate_root_goal(goal, GenerateProofTree::Yes);
301371
let proof_tree = proof_tree.unwrap();
302-
visitor.visit_goal(&InspectGoal::new(self, 0, proof_tree))
372+
visitor.visit_goal(&InspectGoal::new(self, 0, proof_tree, None))
303373
}
304374
}

library/core/src/iter/traits/iterator.rs

+1
Original file line numberDiff line numberDiff line change
@@ -974,6 +974,7 @@ pub trait Iterator {
974974
#[inline]
975975
#[stable(feature = "rust1", since = "1.0.0")]
976976
#[rustc_do_not_const_check]
977+
#[cfg_attr(not(test), rustc_diagnostic_item = "enumerate_method")]
977978
fn enumerate(self) -> Enumerate<Self>
978979
where
979980
Self: Sized,

tests/ui/specialization/specialization-overlap-projection.stderr tests/ui/specialization/specialization-overlap-projection.current.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes
2-
--> $DIR/specialization-overlap-projection.rs:7:12
2+
--> $DIR/specialization-overlap-projection.rs:10:12
33
|
44
LL | #![feature(specialization)]
55
| ^^^^^^^^^^^^^^
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes
2+
--> $DIR/specialization-overlap-projection.rs:10:12
3+
|
4+
LL | #![feature(specialization)]
5+
| ^^^^^^^^^^^^^^
6+
|
7+
= note: see issue #31844 <https://github.com/rust-lang/rust/issues/31844> for more information
8+
= help: consider using `min_specialization` instead, which is more stable and complete
9+
= note: `#[warn(incomplete_features)]` on by default
10+
11+
error[E0119]: conflicting implementations of trait `Foo` for type `u32`
12+
--> $DIR/specialization-overlap-projection.rs:28:1
13+
|
14+
LL | impl Foo for u32 {}
15+
| ---------------- first implementation here
16+
LL | impl Foo for <u8 as Assoc>::Output {}
17+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `u32`
18+
19+
error[E0119]: conflicting implementations of trait `Foo` for type `u32`
20+
--> $DIR/specialization-overlap-projection.rs:30:1
21+
|
22+
LL | impl Foo for u32 {}
23+
| ---------------- first implementation here
24+
...
25+
LL | impl Foo for <u16 as Assoc>::Output {}
26+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `u32`
27+
28+
error[E0282]: type annotations needed
29+
--> $DIR/specialization-overlap-projection.rs:17:27
30+
|
31+
LL | default type Output = bool;
32+
| ^^^^ cannot infer type for associated type `<T as Assoc>::Output`
33+
34+
error[E0282]: type annotations needed
35+
--> $DIR/specialization-overlap-projection.rs:21:35
36+
|
37+
LL | impl Assoc for u8 { type Output = u8; }
38+
| ^^ cannot infer type for associated type `<u8 as Assoc>::Output`
39+
40+
error[E0282]: type annotations needed
41+
--> $DIR/specialization-overlap-projection.rs:23:36
42+
|
43+
LL | impl Assoc for u16 { type Output = u16; }
44+
| ^^^ cannot infer type for associated type `<u16 as Assoc>::Output`
45+
46+
error: aborting due to 5 previous errors; 1 warning emitted
47+
48+
Some errors have detailed explanations: E0119, E0282.
49+
For more information about an error, try `rustc --explain E0119`.

tests/ui/specialization/specialization-overlap-projection.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
//@ check-pass
1+
//@ revisions: current next
2+
//@ ignore-compare-mode-next-solver (explicit revisions)
3+
//@[next] compile-flags: -Znext-solver
4+
//@[current] check-pass
25

36
// Test that impls on projected self types can resolve overlap, even when the
47
// projections involve specialization, so long as the associated type is
@@ -12,14 +15,19 @@ trait Assoc {
1215

1316
impl<T> Assoc for T {
1417
default type Output = bool;
18+
//[next]~^ ERROR type annotations needed
1519
}
1620

1721
impl Assoc for u8 { type Output = u8; }
22+
//[next]~^ ERROR type annotations needed
1823
impl Assoc for u16 { type Output = u16; }
24+
//[next]~^ ERROR type annotations needed
1925

2026
trait Foo {}
2127
impl Foo for u32 {}
2228
impl Foo for <u8 as Assoc>::Output {}
29+
//[next]~^ ERROR conflicting implementations of trait `Foo` for type `u32`
2330
impl Foo for <u16 as Assoc>::Output {}
31+
//[next]~^ ERROR conflicting implementations of trait `Foo` for type `u32`
2432

2533
fn main() {}
+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
//@ run-pass
2+
//@ revisions: current next
3+
//@ ignore-compare-mode-next-solver (explicit revisions)
4+
//@[next] compile-flags: -Znext-solver
5+
6+
fn main() {
7+
let x: &dyn std::any::Any = &1i32;
8+
assert_eq!(x.type_id(), std::any::TypeId::of::<i32>());
9+
}

0 commit comments

Comments
 (0)