Skip to content

Commit

Permalink
Rollup merge of rust-lang#135900 - compiler-errors:derive-wf, r=lcnr
Browse files Browse the repository at this point in the history
Manually walk into WF obligations in `BestObligation` proof tree visitor

When we encounter a `WellFormed` obligation in the `BestObligation` proof tree visitor, ignore the proof tree and call `wf::unnormalized_obligations` to derive well-formed obligations with the correct cause codes. This is to avoid having to replicate the somewhat delicate logic that `wf.rs` does to set up its obligation causes... Don't see a better way to do this.

vibes?? r? lcnr
  • Loading branch information
Zalathar authored Jan 29, 2025
2 parents 5317161 + 5f76f1a commit 64fc3bf
Show file tree
Hide file tree
Showing 19 changed files with 647 additions and 579 deletions.
9 changes: 5 additions & 4 deletions compiler/rustc_trait_selection/src/solve/delegate.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::ops::Deref;

use rustc_data_structures::fx::FxHashSet;
use rustc_hir::def_id::DefId;
use rustc_hir::def_id::{CRATE_DEF_ID, DefId};
use rustc_infer::infer::canonical::query_response::make_query_region_constraints;
use rustc_infer::infer::canonical::{
Canonical, CanonicalExt as _, CanonicalQueryInput, CanonicalVarInfo, CanonicalVarValues,
Expand Down Expand Up @@ -98,9 +98,10 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate<
param_env: ty::ParamEnv<'tcx>,
arg: ty::GenericArg<'tcx>,
) -> Option<Vec<Goal<'tcx, ty::Predicate<'tcx>>>> {
crate::traits::wf::unnormalized_obligations(&self.0, param_env, arg).map(|obligations| {
obligations.into_iter().map(|obligation| obligation.into()).collect()
})
crate::traits::wf::unnormalized_obligations(&self.0, param_env, arg, DUMMY_SP, CRATE_DEF_ID)
.map(|obligations| {
obligations.into_iter().map(|obligation| obligation.into()).collect()
})
}

fn clone_opaque_types_for_query_response(&self) -> Vec<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)> {
Expand Down
496 changes: 6 additions & 490 deletions compiler/rustc_trait_selection/src/solve/fulfill.rs

Large diffs are not rendered by default.

527 changes: 527 additions & 0 deletions compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs

Large diffs are not rendered by default.

82 changes: 46 additions & 36 deletions compiler/rustc_trait_selection/src/solve/inspect/analyse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,47 +194,57 @@ impl<'a, 'tcx> InspectCandidate<'a, 'tcx> {

let goals = instantiated_goals
.into_iter()
.map(|(source, 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(span).into(),
ty::TermKind::Const(_) => infcx.next_const_var(span).into(),
};
let goal =
goal.with(infcx.tcx, ty::NormalizesTo { alias, term: unconstrained_term });
// We have to use a `probe` here as evaluating a `NormalizesTo` can constrain the
// expected term. This means that candidates which only fail due to nested goals
// and which normalize to a different term then the final result could ICE: when
// building their proof tree, the expected term was unconstrained, but when
// instantiating the candidate it is already constrained to the result of another
// candidate.
let proof_tree = infcx
.probe(|_| infcx.evaluate_root_goal_raw(goal, GenerateProofTree::Yes).1);
InspectGoal::new(
infcx,
self.goal.depth + 1,
proof_tree.unwrap(),
Some(NormalizesToTermHack { term, unconstrained_term }),
source,
)
}
_ => {
// We're using a probe here as evaluating a goal could constrain
// inference variables by choosing one candidate. If we then recurse
// into another candidate who ends up with different inference
// constraints, we get an ICE if we already applied the constraints
// from the chosen candidate.
let proof_tree = infcx
.probe(|_| infcx.evaluate_root_goal(goal, GenerateProofTree::Yes).1)
.unwrap();
InspectGoal::new(infcx, self.goal.depth + 1, proof_tree, None, source)
}
})
.map(|(source, goal)| self.instantiate_proof_tree_for_nested_goal(source, goal, span))
.collect();

(goals, opt_impl_args)
}

pub fn instantiate_proof_tree_for_nested_goal(
&self,
source: GoalSource,
goal: Goal<'tcx, ty::Predicate<'tcx>>,
span: Span,
) -> InspectGoal<'a, 'tcx> {
let infcx = self.goal.infcx;
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(span).into(),
ty::TermKind::Const(_) => infcx.next_const_var(span).into(),
};
let goal =
goal.with(infcx.tcx, ty::NormalizesTo { alias, term: unconstrained_term });
// We have to use a `probe` here as evaluating a `NormalizesTo` can constrain the
// expected term. This means that candidates which only fail due to nested goals
// and which normalize to a different term then the final result could ICE: when
// building their proof tree, the expected term was unconstrained, but when
// instantiating the candidate it is already constrained to the result of another
// candidate.
let proof_tree =
infcx.probe(|_| infcx.evaluate_root_goal_raw(goal, GenerateProofTree::Yes).1);
InspectGoal::new(
infcx,
self.goal.depth + 1,
proof_tree.unwrap(),
Some(NormalizesToTermHack { term, unconstrained_term }),
source,
)
}
_ => {
// We're using a probe here as evaluating a goal could constrain
// inference variables by choosing one candidate. If we then recurse
// into another candidate who ends up with different inference
// constraints, we get an ICE if we already applied the constraints
// from the chosen candidate.
let proof_tree = infcx
.probe(|_| infcx.evaluate_root_goal(goal, GenerateProofTree::Yes).1)
.unwrap();
InspectGoal::new(infcx, self.goal.depth + 1, proof_tree, None, source)
}
}
}

/// Visit all nested goals of this candidate, rolling back
/// all inference constraints.
pub fn visit_nested_in_probe<V: ProofTreeVisitor<'tcx>>(&self, visitor: &mut V) -> V::Result {
Expand Down
7 changes: 1 addition & 6 deletions compiler/rustc_trait_selection/src/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ use crate::infer::{InferCtxt, TyCtxtInferExt};
use crate::regions::InferCtxtRegionExt;
use crate::traits::query::evaluate_obligation::InferCtxtExt as _;

#[derive(Debug)]
pub struct FulfillmentError<'tcx> {
pub obligation: PredicateObligation<'tcx>,
pub code: FulfillmentErrorCode<'tcx>,
Expand Down Expand Up @@ -107,12 +108,6 @@ impl<'tcx> FulfillmentError<'tcx> {
}
}

impl<'tcx> Debug for FulfillmentError<'tcx> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "FulfillmentError({:?},{:?})", self.obligation, self.code)
}
}

#[derive(Clone)]
pub enum FulfillmentErrorCode<'tcx> {
/// Inherently impossible to fulfill; this trait is implemented if and only
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,9 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>(

// From the full set of obligations, just filter down to the region relationships.
for obligation in
wf::unnormalized_obligations(ocx.infcx, param_env, arg).into_iter().flatten()
wf::unnormalized_obligations(ocx.infcx, param_env, arg, DUMMY_SP, CRATE_DEF_ID)
.into_iter()
.flatten()
{
assert!(!obligation.has_escaping_bound_vars());
let Some(pred) = obligation.predicate.kind().no_bound_vars() else {
Expand Down
10 changes: 6 additions & 4 deletions compiler/rustc_trait_selection/src/traits/wf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ use rustc_middle::ty::{
self, GenericArg, GenericArgKind, GenericArgsRef, Ty, TyCtxt, TypeSuperVisitable,
TypeVisitable, TypeVisitableExt, TypeVisitor,
};
use rustc_span::def_id::{CRATE_DEF_ID, DefId, LocalDefId};
use rustc_span::{DUMMY_SP, Span};
use rustc_span::Span;
use rustc_span::def_id::{DefId, LocalDefId};
use tracing::{debug, instrument, trace};

use crate::infer::InferCtxt;
Expand Down Expand Up @@ -89,6 +89,8 @@ pub fn unnormalized_obligations<'tcx>(
infcx: &InferCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
arg: GenericArg<'tcx>,
span: Span,
body_id: LocalDefId,
) -> Option<PredicateObligations<'tcx>> {
debug_assert_eq!(arg, infcx.resolve_vars_if_possible(arg));

Expand All @@ -106,8 +108,8 @@ pub fn unnormalized_obligations<'tcx>(
let mut wf = WfPredicates {
infcx,
param_env,
body_id: CRATE_DEF_ID,
span: DUMMY_SP,
body_id,
span,
out: PredicateObligations::new(),
recursion_depth: 0,
item: None,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ LL | fn main() -> Foo::Bar::<Vec<[u32]>> {}
| ^^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `[u32]`
note: required by an implicit `Sized` bound in `Vec`
--> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL

error: aborting due to 1 previous error

Expand Down
34 changes: 14 additions & 20 deletions tests/ui/const-generics/issues/issue-88119.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -6,35 +6,29 @@ LL | #![feature(const_trait_impl, generic_const_exprs)]
|
= help: remove one of these features

error[E0284]: type annotations needed: cannot normalize `<&T as ConstName>::{constant#0}`
--> $DIR/issue-88119.rs:19:49
error[E0284]: type annotations needed: cannot satisfy `the constant `name_len::<T>()` can be evaluated`
--> $DIR/issue-88119.rs:21:5
|
LL | impl<T: ?Sized + ConstName> const ConstName for &T
| ^^ cannot normalize `<&T as ConstName>::{constant#0}`
LL | [(); name_len::<T>()]:,
| ^^^^^^^^^^^^^^^^^^^^^ cannot satisfy `the constant `name_len::<T>()` can be evaluated`
|
note: required for `&T` to implement `~const ConstName`
--> $DIR/issue-88119.rs:19:35
note: required by a bound in `<&T as ConstName>`
--> $DIR/issue-88119.rs:21:10
|
LL | impl<T: ?Sized + ConstName> const ConstName for &T
| ^^^^^^^^^ ^^
LL | where
LL | [(); name_len::<T>()]:,
| --------------------- unsatisfied trait bound introduced here
| ^^^^^^^^^^^^^^^ required by this bound in `<&T as ConstName>`

error[E0284]: type annotations needed: cannot normalize `<&mut T as ConstName>::{constant#0}`
--> $DIR/issue-88119.rs:26:49
error[E0284]: type annotations needed: cannot satisfy `the constant `name_len::<T>()` can be evaluated`
--> $DIR/issue-88119.rs:28:5
|
LL | impl<T: ?Sized + ConstName> const ConstName for &mut T
| ^^^^^^ cannot normalize `<&mut T as ConstName>::{constant#0}`
LL | [(); name_len::<T>()]:,
| ^^^^^^^^^^^^^^^^^^^^^ cannot satisfy `the constant `name_len::<T>()` can be evaluated`
|
note: required for `&mut T` to implement `~const ConstName`
--> $DIR/issue-88119.rs:26:35
note: required by a bound in `<&mut T as ConstName>`
--> $DIR/issue-88119.rs:28:10
|
LL | impl<T: ?Sized + ConstName> const ConstName for &mut T
| ^^^^^^^^^ ^^^^^^
LL | where
LL | [(); name_len::<T>()]:,
| --------------------- unsatisfied trait bound introduced here
| ^^^^^^^^^^^^^^^ required by this bound in `<&mut T as ConstName>`

error: aborting due to 3 previous errors

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,13 @@ LL | where
LL | T: AsExpression<Self::SqlType>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Foo::check`

error[E0277]: the trait bound `&str: AsExpression<Integer>` is not satisfied
--> $DIR/as_expression.rs:55:15
|
LL | SelectInt.check("bar");
| ^^^^^ the trait `AsExpression<Integer>` is not implemented for `&str`
|
= help: the trait `AsExpression<Integer>` is not implemented for `&str`
but trait `AsExpression<Text>` is implemented for it
= help: for that trait implementation, expected `Text`, found `Integer`

error[E0271]: type mismatch resolving `<SelectInt as Expression>::SqlType == Text`
--> $DIR/as_expression.rs:55:5
|
LL | SelectInt.check("bar");
| ^^^^^^^^^^^^^^^^^^^^^^ expected `Text`, found `Integer`

error: aborting due to 3 previous errors
error: aborting due to 2 previous errors

Some errors have detailed explanations: E0271, E0277.
For more information about an error, try `rustc --explain E0271`.
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ impl<T> Foo for T where T: Expression {}

fn main() {
SelectInt.check("bar");
//~^ ERROR the trait bound `&str: AsExpression<Integer>` is not satisfied
//[next]~| the trait bound `&str: AsExpression<<SelectInt as Expression>::SqlType>` is not satisfied
//[current]~^ ERROR the trait bound `&str: AsExpression<Integer>` is not satisfied
//[next]~^^ the trait bound `&str: AsExpression<<SelectInt as Expression>::SqlType>` is not satisfied
//[next]~| type mismatch
}
5 changes: 5 additions & 0 deletions tests/ui/impl-trait/in-trait/alias-bounds-when-not-wf.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ help: this trait has no implementations, consider adding one
|
LL | trait Foo {}
| ^^^^^^^^^
note: required by a bound in `A`
--> $DIR/alias-bounds-when-not-wf.rs:8:11
|
LL | type A<T: Foo> = T;
| ^^^ required by this bound in `A`

error[E0277]: the trait bound `usize: Foo` is not satisfied
--> $DIR/alias-bounds-when-not-wf.rs:16:10
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
error[E0284]: type annotations needed: cannot normalize `X::{constant#0}`
error[E0284]: type annotations needed: cannot satisfy `the constant `{ || {} }` can be evaluated`
--> $DIR/const-region-infer-to-static-in-binder.rs:4:10
|
LL | struct X<const FN: fn() = { || {} }>;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot normalize `X::{constant#0}`
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot satisfy `the constant `{ || {} }` can be evaluated`

error: using function pointers as const generic parameters is forbidden
--> $DIR/const-region-infer-to-static-in-binder.rs:4:20
Expand Down
7 changes: 4 additions & 3 deletions tests/ui/traits/next-solver/specialization-transmute.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ LL | #![feature(specialization)]

error: cannot normalize `<T as Default>::Id: '_`

error[E0284]: type annotations needed: cannot normalize `<T as Default>::Id`
error[E0282]: type annotations needed
--> $DIR/specialization-transmute.rs:15:23
|
LL | fn intu(&self) -> &Self::Id {
| ^^^^^^^^^ cannot normalize `<T as Default>::Id`
| ^^^^^^^^^ cannot infer type for reference `&<T as Default>::Id`

error[E0284]: type annotations needed: cannot satisfy `<T as Default>::Id normalizes-to T`
--> $DIR/specialization-transmute.rs:17:9
Expand All @@ -36,4 +36,5 @@ LL | fn transmute<T: Default<Id = U>, U: Copy>(t: T) -> U {

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

For more information about this error, try `rustc --explain E0284`.
Some errors have detailed explanations: E0282, E0284.
For more information about an error, try `rustc --explain E0282`.
2 changes: 2 additions & 0 deletions tests/ui/union/union-derive-eq.next.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ LL | union U2 {
LL | a: PartialEqNotEq,
| ^^^^^^^^^^^^^^^^^ the trait `Eq` is not implemented for `PartialEqNotEq`
|
note: required by a bound in `AssertParamIsEq`
--> $SRC_DIR/core/src/cmp.rs:LL:COL
= note: this error originates in the derive macro `Eq` (in Nightly builds, run with -Z macro-backtrace for more info)
help: consider annotating `PartialEqNotEq` with `#[derive(Eq)]`
|
Expand Down
6 changes: 6 additions & 0 deletions tests/ui/wf/wf-normalization-sized.next.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ LL | const _: <[[[[[[u8]]]]]] as WellUnformed>::RequestNormalize = ();
| ^^^^^^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `[[[[[u8]]]]]`
= note: slice and array elements must have `Sized` type

error[E0277]: the size for values of type `[[[[[u8]]]]]` cannot be known at compilation time
--> $DIR/wf-normalization-sized.rs:19:11
Expand All @@ -13,6 +14,7 @@ LL | const _: <[[[[[[u8]]]]]] as WellUnformed>::RequestNormalize = ();
| ^^^^^^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `[[[[[u8]]]]]`
= note: slice and array elements must have `Sized` type
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`

error[E0277]: the size for values of type `str` cannot be known at compilation time
Expand All @@ -22,6 +24,8 @@ LL | const _: <Vec<str> as WellUnformed>::RequestNormalize = ();
| ^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `str`
note: required by an implicit `Sized` bound in `Vec`
--> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL

error[E0277]: the size for values of type `str` cannot be known at compilation time
--> $DIR/wf-normalization-sized.rs:22:11
Expand All @@ -30,6 +34,8 @@ LL | const _: <Vec<str> as WellUnformed>::RequestNormalize = ();
| ^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `str`
note: required by an implicit `Sized` bound in `Vec`
--> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`

error: aborting due to 4 previous errors
Expand Down
5 changes: 5 additions & 0 deletions tests/ui/wf/wf-trait-fn-arg.next.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ error[E0277]: the trait bound `Self: Eq` is not satisfied
LL | fn bar(&self, x: &Bar<Self>);
| ^^^^^^^^^ the trait `Eq` is not implemented for `Self`
|
note: required by a bound in `Bar`
--> $DIR/wf-trait-fn-arg.rs:11:15
|
LL | struct Bar<T: Eq + ?Sized> {
| ^^ required by this bound in `Bar`
help: consider further restricting `Self`
|
LL | fn bar(&self, x: &Bar<Self>) where Self: Eq;
Expand Down
5 changes: 5 additions & 0 deletions tests/ui/wf/wf-trait-fn-ret.next.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ error[E0277]: the trait bound `Self: Eq` is not satisfied
LL | fn bar(&self) -> &Bar<Self>;
| ^^^^^^^^^ the trait `Eq` is not implemented for `Self`
|
note: required by a bound in `Bar`
--> $DIR/wf-trait-fn-ret.rs:10:15
|
LL | struct Bar<T: Eq + ?Sized> {
| ^^ required by this bound in `Bar`
help: consider further restricting `Self`
|
LL | fn bar(&self) -> &Bar<Self> where Self: Eq;
Expand Down
5 changes: 5 additions & 0 deletions tests/ui/wf/wf-trait-fn-where-clause.next.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ error[E0277]: the trait bound `Self: Eq` is not satisfied
LL | Bar<Self>: Copy;
| ^^^^ the trait `Eq` is not implemented for `Self`
|
note: required by a bound in `Bar`
--> $DIR/wf-trait-fn-where-clause.rs:10:15
|
LL | struct Bar<T: Eq + ?Sized> {
| ^^ required by this bound in `Bar`
help: consider further restricting `Self`
|
LL | Bar<Self>: Copy, Self: Eq;
Expand Down

0 comments on commit 64fc3bf

Please sign in to comment.