Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rollup of 2 pull requests #124590

Merged
merged 5 commits into from
May 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -754,6 +754,7 @@ symbols! {
enable,
encode,
end,
enumerate_method,
env,
env_CFG_RELEASE: env!("CFG_RELEASE"),
eprint_macro,
Expand Down
170 changes: 120 additions & 50 deletions compiler/rustc_trait_selection/src/solve/inspect/analyse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use rustc_ast_ir::visit::VisitorResult;
use rustc_infer::infer::resolve::EagerResolver;
use rustc_infer::infer::type_variable::TypeVariableOrigin;
use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk};
use rustc_infer::traits::{TraitEngine, TraitEngineExt};
use rustc_macros::extension;
use rustc_middle::infer::unify_key::ConstVariableOrigin;
use rustc_middle::traits::query::NoSolution;
Expand All @@ -22,9 +23,10 @@ use rustc_middle::traits::solve::{Certainty, Goal};
use rustc_middle::traits::ObligationCause;
use rustc_middle::ty;
use rustc_middle::ty::TypeFoldable;
use rustc_span::Span;
use rustc_span::{Span, DUMMY_SP};

use crate::solve::eval_ctxt::canonical;
use crate::solve::FulfillmentCtxt;
use crate::solve::{EvalCtxt, GoalEvaluationKind, GoalSource};
use crate::solve::{GenerateProofTree, InferCtxtEvalExt};

Expand All @@ -37,7 +39,54 @@ pub struct InspectGoal<'a, 'tcx> {
depth: usize,
orig_values: Vec<ty::GenericArg<'tcx>>,
goal: Goal<'tcx, ty::Predicate<'tcx>>,
evaluation: inspect::CanonicalGoalEvaluation<'tcx>,
result: Result<Certainty, NoSolution>,
evaluation_kind: inspect::CanonicalGoalEvaluationKind<'tcx>,
normalizes_to_term_hack: Option<NormalizesToTermHack<'tcx>>,
}

/// The expected term of a `NormalizesTo` goal gets replaced
/// with an unconstrained inference variable when computing
/// `NormalizesTo` goals and we return the nested goals to the
/// caller, who also equates the actual term with the expected.
///
/// This is an implementation detail of the trait solver and
/// not something we want to leak to users. We therefore
/// treat `NormalizesTo` goals as if they apply the expected
/// type at the end of each candidate.
#[derive(Copy, Clone)]
struct NormalizesToTermHack<'tcx> {
term: ty::Term<'tcx>,
unconstrained_term: ty::Term<'tcx>,
}

impl<'tcx> NormalizesToTermHack<'tcx> {
/// Relate the `term` with the new `unconstrained_term` created
/// when computing the proof tree for this `NormalizesTo` goals.
/// This handles nested obligations.
fn constrain(
self,
infcx: &InferCtxt<'tcx>,
span: Span,
param_env: ty::ParamEnv<'tcx>,
) -> Result<Certainty, NoSolution> {
infcx
.at(&ObligationCause::dummy_with_span(span), param_env)
.eq(DefineOpaqueTypes::Yes, self.term, self.unconstrained_term)
.map_err(|_| NoSolution)
.and_then(|InferOk { value: (), obligations }| {
let mut fulfill_cx = FulfillmentCtxt::new(infcx);
fulfill_cx.register_predicate_obligations(infcx, obligations);
if fulfill_cx.select_where_possible(infcx).is_empty() {
if fulfill_cx.pending_obligations().is_empty() {
Ok(Certainty::Yes)
} else {
Ok(Certainty::AMBIGUOUS)
}
} else {
Err(NoSolution)
}
})
}
}

pub struct InspectCandidate<'a, 'tcx> {
Expand Down Expand Up @@ -115,42 +164,47 @@ impl<'a, 'tcx> InspectCandidate<'a, 'tcx> {
self.final_state,
);

if let Some(term_hack) = self.goal.normalizes_to_term_hack {
// FIXME: We ignore the expected term of `NormalizesTo` goals
// when computing the result of its candidates. This is
// scuffed.
let _ = term_hack.constrain(infcx, span, param_env);
}

instantiated_goals
.into_iter()
.map(|goal| {
let proof_tree = match goal.predicate.kind().no_bound_vars() {
Some(ty::PredicateKind::NormalizesTo(ty::NormalizesTo { alias, term })) => {
let unconstrained_term = match term.unpack() {
ty::TermKind::Ty(_) => infcx
.next_ty_var(TypeVariableOrigin { param_def_id: None, span })
.into(),
ty::TermKind::Const(ct) => infcx
.next_const_var(
ct.ty(),
ConstVariableOrigin { param_def_id: None, span },
)
.into(),
};
let goal = goal
.with(infcx.tcx, ty::NormalizesTo { alias, term: unconstrained_term });
let proof_tree =
EvalCtxt::enter_root(infcx, GenerateProofTree::Yes, |ecx| {
ecx.evaluate_goal_raw(
GoalEvaluationKind::Root,
GoalSource::Misc,
goal,
)
})
.1;
let InferOk { value: (), obligations: _ } = infcx
.at(&ObligationCause::dummy_with_span(span), param_env)
.eq(DefineOpaqueTypes::Yes, term, unconstrained_term)
.unwrap();
proof_tree
}
_ => infcx.evaluate_root_goal(goal, GenerateProofTree::Yes).1,
};
InspectGoal::new(infcx, self.goal.depth + 1, proof_tree.unwrap())
.map(|goal| match goal.predicate.kind().no_bound_vars() {
Some(ty::PredicateKind::NormalizesTo(ty::NormalizesTo { alias, term })) => {
let unconstrained_term = match term.unpack() {
ty::TermKind::Ty(_) => infcx
.next_ty_var(TypeVariableOrigin { param_def_id: None, span })
.into(),
ty::TermKind::Const(ct) => infcx
.next_const_var(
ct.ty(),
ConstVariableOrigin { param_def_id: None, span },
)
.into(),
};
let goal =
goal.with(infcx.tcx, ty::NormalizesTo { alias, term: unconstrained_term });
let proof_tree = EvalCtxt::enter_root(infcx, GenerateProofTree::Yes, |ecx| {
ecx.evaluate_goal_raw(GoalEvaluationKind::Root, GoalSource::Misc, goal)
})
.1;
InspectGoal::new(
infcx,
self.goal.depth + 1,
proof_tree.unwrap(),
Some(NormalizesToTermHack { term, unconstrained_term }),
)
}
_ => InspectGoal::new(
infcx,
self.goal.depth + 1,
infcx.evaluate_root_goal(goal, GenerateProofTree::Yes).1.unwrap(),
None,
),
})
.collect()
}
Expand All @@ -172,7 +226,7 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> {
}

pub fn result(&self) -> Result<Certainty, NoSolution> {
self.evaluation.result.map(|c| c.value.certainty)
self.result
}

fn candidates_recur(
Expand Down Expand Up @@ -229,11 +283,11 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> {

pub fn candidates(&'a self) -> Vec<InspectCandidate<'a, 'tcx>> {
let mut candidates = vec![];
let last_eval_step = match self.evaluation.kind {
let last_eval_step = match self.evaluation_kind {
inspect::CanonicalGoalEvaluationKind::Overflow
| inspect::CanonicalGoalEvaluationKind::CycleInStack
| inspect::CanonicalGoalEvaluationKind::ProvisionalCacheHit => {
warn!("unexpected root evaluation: {:?}", self.evaluation);
warn!("unexpected root evaluation: {:?}", self.evaluation_kind);
return vec![];
}
inspect::CanonicalGoalEvaluationKind::Evaluation { revisions } => {
Expand Down Expand Up @@ -262,17 +316,33 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> {
candidates.pop().filter(|_| candidates.is_empty())
}

fn new(infcx: &'a InferCtxt<'tcx>, depth: usize, root: inspect::GoalEvaluation<'tcx>) -> Self {
fn new(
infcx: &'a InferCtxt<'tcx>,
depth: usize,
root: inspect::GoalEvaluation<'tcx>,
normalizes_to_term_hack: Option<NormalizesToTermHack<'tcx>>,
) -> Self {
let inspect::GoalEvaluation { uncanonicalized_goal, kind, evaluation } = root;
match kind {
inspect::GoalEvaluationKind::Root { orig_values } => InspectGoal {
infcx,
depth,
orig_values,
goal: uncanonicalized_goal.fold_with(&mut EagerResolver::new(infcx)),
evaluation,
},
inspect::GoalEvaluationKind::Nested { .. } => unreachable!(),
let inspect::GoalEvaluationKind::Root { orig_values } = kind else { unreachable!() };

let result = evaluation.result.and_then(|ok| {
if let Some(term_hack) = normalizes_to_term_hack {
infcx
.probe(|_| term_hack.constrain(infcx, DUMMY_SP, uncanonicalized_goal.param_env))
.map(|certainty| ok.value.certainty.unify_with(certainty))
} else {
Ok(ok.value.certainty)
}
});

InspectGoal {
infcx,
depth,
orig_values,
goal: uncanonicalized_goal.fold_with(&mut EagerResolver::new(infcx)),
result,
evaluation_kind: evaluation.kind,
normalizes_to_term_hack,
}
}
}
Expand All @@ -299,6 +369,6 @@ impl<'tcx> InferCtxt<'tcx> {
) -> V::Result {
let (_, proof_tree) = self.evaluate_root_goal(goal, GenerateProofTree::Yes);
let proof_tree = proof_tree.unwrap();
visitor.visit_goal(&InspectGoal::new(self, 0, proof_tree))
visitor.visit_goal(&InspectGoal::new(self, 0, proof_tree, None))
}
}
1 change: 1 addition & 0 deletions library/core/src/iter/traits/iterator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -974,6 +974,7 @@ pub trait Iterator {
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_do_not_const_check]
#[cfg_attr(not(test), rustc_diagnostic_item = "enumerate_method")]
fn enumerate(self) -> Enumerate<Self>
where
Self: Sized,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/specialization-overlap-projection.rs:7:12
--> $DIR/specialization-overlap-projection.rs:10:12
|
LL | #![feature(specialization)]
| ^^^^^^^^^^^^^^
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/specialization-overlap-projection.rs:10:12
|
LL | #![feature(specialization)]
| ^^^^^^^^^^^^^^
|
= note: see issue #31844 <https://github.com/rust-lang/rust/issues/31844> for more information
= help: consider using `min_specialization` instead, which is more stable and complete
= note: `#[warn(incomplete_features)]` on by default

error[E0119]: conflicting implementations of trait `Foo` for type `u32`
--> $DIR/specialization-overlap-projection.rs:28:1
|
LL | impl Foo for u32 {}
| ---------------- first implementation here
LL | impl Foo for <u8 as Assoc>::Output {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `u32`

error[E0119]: conflicting implementations of trait `Foo` for type `u32`
--> $DIR/specialization-overlap-projection.rs:30:1
|
LL | impl Foo for u32 {}
| ---------------- first implementation here
...
LL | impl Foo for <u16 as Assoc>::Output {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `u32`

error[E0282]: type annotations needed
--> $DIR/specialization-overlap-projection.rs:17:27
|
LL | default type Output = bool;
| ^^^^ cannot infer type for associated type `<T as Assoc>::Output`

error[E0282]: type annotations needed
--> $DIR/specialization-overlap-projection.rs:21:35
|
LL | impl Assoc for u8 { type Output = u8; }
| ^^ cannot infer type for associated type `<u8 as Assoc>::Output`

error[E0282]: type annotations needed
--> $DIR/specialization-overlap-projection.rs:23:36
|
LL | impl Assoc for u16 { type Output = u16; }
| ^^^ cannot infer type for associated type `<u16 as Assoc>::Output`

error: aborting due to 5 previous errors; 1 warning emitted

Some errors have detailed explanations: E0119, E0282.
For more information about an error, try `rustc --explain E0119`.
10 changes: 9 additions & 1 deletion tests/ui/specialization/specialization-overlap-projection.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
//@ check-pass
//@ revisions: current next
//@ ignore-compare-mode-next-solver (explicit revisions)
//@[next] compile-flags: -Znext-solver
//@[current] check-pass

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

impl<T> Assoc for T {
default type Output = bool;
//[next]~^ ERROR type annotations needed
}

impl Assoc for u8 { type Output = u8; }
//[next]~^ ERROR type annotations needed
impl Assoc for u16 { type Output = u16; }
//[next]~^ ERROR type annotations needed

trait Foo {}
impl Foo for u32 {}
impl Foo for <u8 as Assoc>::Output {}
//[next]~^ ERROR conflicting implementations of trait `Foo` for type `u32`
impl Foo for <u16 as Assoc>::Output {}
//[next]~^ ERROR conflicting implementations of trait `Foo` for type `u32`

fn main() {}
Loading