Skip to content

Commit aac91f7

Browse files
authored
Rollup merge of #131699 - compiler-errors:better-errors-for-projections, r=lcnr
Try to improve error messages involving aliases in the solver 1. Treat aliases as rigid only if it may not be defined and it's well formed (i.e. for projections, its trait goal is satisfied). 2. Record goals that are related to alias normalization under a new `GoalKind`, so we can look into them in the `BestObligation` visitor. 3. Try to deduplicate errors due to self types of goals that are un-normalizable aliases. r? lcnr
2 parents 2560453 + f956dc2 commit aac91f7

34 files changed

+324
-187
lines changed

Diff for: compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs

+20
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use derive_where::derive_where;
66
use rustc_type_ir::fold::TypeFoldable;
77
use rustc_type_ir::inherent::*;
88
use rustc_type_ir::lang_items::TraitSolverLangItem;
9+
use rustc_type_ir::solve::inspect;
910
use rustc_type_ir::visit::TypeVisitableExt as _;
1011
use rustc_type_ir::{self as ty, Interner, Upcast as _, elaborate};
1112
use tracing::{debug, instrument};
@@ -288,6 +289,25 @@ where
288289
let Ok(normalized_self_ty) =
289290
self.structurally_normalize_ty(goal.param_env, goal.predicate.self_ty())
290291
else {
292+
// FIXME: We register a fake candidate when normalization fails so that
293+
// we can point at the reason for *why*. I'm tempted to say that this
294+
// is the wrong way to do this, though.
295+
let result =
296+
self.probe(|&result| inspect::ProbeKind::RigidAlias { result }).enter(|this| {
297+
let normalized_ty = this.next_ty_infer();
298+
let alias_relate_goal = Goal::new(
299+
this.cx(),
300+
goal.param_env,
301+
ty::PredicateKind::AliasRelate(
302+
goal.predicate.self_ty().into(),
303+
normalized_ty.into(),
304+
ty::AliasRelationDirection::Equate,
305+
),
306+
);
307+
this.add_goal(GoalSource::AliasWellFormed, alias_relate_goal);
308+
this.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
309+
});
310+
assert_eq!(result, Err(NoSolution));
291311
return vec![];
292312
};
293313

Diff for: compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -983,7 +983,7 @@ where
983983
hidden_ty,
984984
&mut goals,
985985
);
986-
self.add_goals(GoalSource::Misc, goals);
986+
self.add_goals(GoalSource::AliasWellFormed, goals);
987987
}
988988

989989
// Do something for each opaque/hidden pair defined with `def_id` in the

Diff for: compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs

+58-5
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use crate::solve::assembly::{self, Candidate};
1515
use crate::solve::inspect::ProbeKind;
1616
use crate::solve::{
1717
BuiltinImplSource, CandidateSource, Certainty, EvalCtxt, Goal, GoalSource, MaybeCause,
18-
NoSolution, QueryResult,
18+
NoSolution, QueryResult, Reveal,
1919
};
2020

2121
impl<D, I> EvalCtxt<'_, D>
@@ -37,10 +37,61 @@ where
3737
match normalize_result {
3838
Ok(res) => Ok(res),
3939
Err(NoSolution) => {
40-
let Goal { param_env, predicate: NormalizesTo { alias, term } } = goal;
41-
self.relate_rigid_alias_non_alias(param_env, alias, ty::Invariant, term)?;
42-
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
40+
self.probe(|&result| ProbeKind::RigidAlias { result }).enter(|this| {
41+
let Goal { param_env, predicate: NormalizesTo { alias, term } } = goal;
42+
this.add_rigid_constraints(param_env, alias)?;
43+
this.relate_rigid_alias_non_alias(param_env, alias, ty::Invariant, term)?;
44+
this.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
45+
})
46+
}
47+
}
48+
}
49+
50+
/// Register any obligations that are used to validate that an alias should be
51+
/// treated as rigid.
52+
///
53+
/// An alias may be considered rigid if it fails normalization, but we also don't
54+
/// want to consider aliases that are not well-formed to be rigid simply because
55+
/// they fail normalization.
56+
///
57+
/// For example, some `<T as Trait>::Assoc` where `T: Trait` does not hold, or an
58+
/// opaque type whose hidden type doesn't actually satisfy the opaque item bounds.
59+
fn add_rigid_constraints(
60+
&mut self,
61+
param_env: I::ParamEnv,
62+
rigid_alias: ty::AliasTerm<I>,
63+
) -> Result<(), NoSolution> {
64+
let cx = self.cx();
65+
match rigid_alias.kind(cx) {
66+
// Projections are rigid only if their trait ref holds,
67+
// and the GAT where-clauses hold.
68+
ty::AliasTermKind::ProjectionTy | ty::AliasTermKind::ProjectionConst => {
69+
let trait_ref = rigid_alias.trait_ref(cx);
70+
self.add_goal(GoalSource::AliasWellFormed, Goal::new(cx, param_env, trait_ref));
71+
Ok(())
72+
}
73+
ty::AliasTermKind::OpaqueTy => {
74+
match param_env.reveal() {
75+
// In user-facing mode, paques are only rigid if we may not define it.
76+
Reveal::UserFacing => {
77+
if rigid_alias
78+
.def_id
79+
.as_local()
80+
.is_some_and(|def_id| self.can_define_opaque_ty(def_id))
81+
{
82+
Err(NoSolution)
83+
} else {
84+
Ok(())
85+
}
86+
}
87+
// Opaques are never rigid in reveal-all mode.
88+
Reveal::All => Err(NoSolution),
89+
}
4390
}
91+
// FIXME(generic_const_exprs): we would need to support generic consts here
92+
ty::AliasTermKind::UnevaluatedConst => Err(NoSolution),
93+
// Inherent and weak types are never rigid. This type must not be well-formed.
94+
ty::AliasTermKind::WeakTy | ty::AliasTermKind::InherentTy => Err(NoSolution),
4495
}
4596
}
4697

@@ -124,6 +175,7 @@ where
124175
ecx.instantiate_normalizes_to_term(goal, assumption_projection_pred.term);
125176

126177
// Add GAT where clauses from the trait's definition
178+
// FIXME: We don't need these, since these are the type's own WF obligations.
127179
ecx.add_goals(
128180
GoalSource::Misc,
129181
cx.own_predicates_of(goal.predicate.def_id())
@@ -179,7 +231,8 @@ where
179231
.map(|pred| goal.with(cx, pred));
180232
ecx.add_goals(GoalSource::ImplWhereBound, where_clause_bounds);
181233

182-
// Add GAT where clauses from the trait's definition
234+
// Add GAT where clauses from the trait's definition.
235+
// FIXME: We don't need these, since these are the type's own WF obligations.
183236
ecx.add_goals(
184237
GoalSource::Misc,
185238
cx.own_predicates_of(goal.predicate.def_id())

Diff for: compiler/rustc_trait_selection/src/solve/fulfill.rs

+16-9
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use rustc_middle::bug;
1414
use rustc_middle::ty::error::{ExpectedFound, TypeError};
1515
use rustc_middle::ty::{self, TyCtxt};
1616
use rustc_next_trait_solver::solve::{GenerateProofTree, HasChanged, SolverDelegateEvalExt as _};
17-
use tracing::instrument;
17+
use tracing::{instrument, trace};
1818

1919
use super::Certainty;
2020
use super::delegate::SolverDelegate;
@@ -401,6 +401,7 @@ impl<'tcx> BestObligation<'tcx> {
401401
nested_goal.source(),
402402
GoalSource::ImplWhereBound
403403
| GoalSource::InstantiateHigherRanked
404+
| GoalSource::AliasWellFormed
404405
) && match self.consider_ambiguities {
405406
true => {
406407
matches!(
@@ -415,6 +416,13 @@ impl<'tcx> BestObligation<'tcx> {
415416
})
416417
});
417418
}
419+
420+
// Prefer a non-rigid candidate if there is one.
421+
if candidates.len() > 1 {
422+
candidates.retain(|candidate| {
423+
!matches!(candidate.kind(), inspect::ProbeKind::RigidAlias { .. })
424+
});
425+
}
418426
}
419427
}
420428

@@ -429,8 +437,11 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> {
429437
self.obligation.cause.span
430438
}
431439

440+
#[instrument(level = "trace", skip(self, goal), fields(goal = ?goal.goal()))]
432441
fn visit_goal(&mut self, goal: &inspect::InspectGoal<'_, 'tcx>) -> Self::Result {
433442
let candidates = self.non_trivial_candidates(goal);
443+
trace!(candidates = ?candidates.iter().map(|c| c.kind()).collect::<Vec<_>>());
444+
434445
let [candidate] = candidates.as_slice() else {
435446
return ControlFlow::Break(self.obligation.clone());
436447
};
@@ -464,17 +475,13 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> {
464475
polarity: ty::PredicatePolarity::Positive,
465476
}))
466477
}
467-
ty::PredicateKind::Clause(
468-
ty::ClauseKind::WellFormed(_) | ty::ClauseKind::Projection(..),
469-
)
470-
| ty::PredicateKind::AliasRelate(..) => ChildMode::PassThrough,
471-
_ => {
472-
return ControlFlow::Break(self.obligation.clone());
473-
}
478+
_ => ChildMode::PassThrough,
474479
};
475480

476481
let mut impl_where_bound_count = 0;
477482
for nested_goal in candidate.instantiate_nested_goals(self.span()) {
483+
trace!(nested_goal = ?(nested_goal.goal(), nested_goal.source(), nested_goal.result()));
484+
478485
let make_obligation = |cause| Obligation {
479486
cause,
480487
param_env: nested_goal.goal().param_env,
@@ -501,7 +508,7 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> {
501508
(_, GoalSource::InstantiateHigherRanked) => {
502509
obligation = self.obligation.clone();
503510
}
504-
(ChildMode::PassThrough, _) => {
511+
(ChildMode::PassThrough, _) | (_, GoalSource::AliasWellFormed) => {
505512
obligation = make_obligation(self.obligation.cause.clone());
506513
}
507514
}

Diff for: compiler/rustc_trait_selection/src/solve/inspect/analyse.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,8 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> {
292292
| inspect::ProbeKind::Root { .. }
293293
| inspect::ProbeKind::TryNormalizeNonRigid { .. }
294294
| inspect::ProbeKind::TraitCandidate { .. }
295-
| inspect::ProbeKind::OpaqueTypeStorageLookup { .. } => {
295+
| inspect::ProbeKind::OpaqueTypeStorageLookup { .. }
296+
| inspect::ProbeKind::RigidAlias { .. } => {
296297
// Nested probes have to prove goals added in their parent
297298
// but do not leak them, so we truncate the added goals
298299
// afterwards.
@@ -316,7 +317,8 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> {
316317
inspect::ProbeKind::Root { result }
317318
| inspect::ProbeKind::TryNormalizeNonRigid { result }
318319
| inspect::ProbeKind::TraitCandidate { source: _, result }
319-
| inspect::ProbeKind::OpaqueTypeStorageLookup { result } => {
320+
| inspect::ProbeKind::OpaqueTypeStorageLookup { result }
321+
| inspect::ProbeKind::RigidAlias { result } => {
320322
// We only add a candidate if `shallow_certainty` was set, which means
321323
// that we ended up calling `evaluate_added_goals_and_make_canonical_response`.
322324
if let Some(shallow_certainty) = shallow_certainty {

Diff for: compiler/rustc_trait_selection/src/solve/select.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,8 @@ fn to_selection<'tcx>(
177177
| ProbeKind::UpcastProjectionCompatibility
178178
| ProbeKind::OpaqueTypeStorageLookup { result: _ }
179179
| ProbeKind::Root { result: _ }
180-
| ProbeKind::ShadowedEnvProbing => {
180+
| ProbeKind::ShadowedEnvProbing
181+
| ProbeKind::RigidAlias { result: _ } => {
181182
span_bug!(span, "didn't expect to assemble trait candidate from {:#?}", cand.kind())
182183
}
183184
})

Diff for: compiler/rustc_type_ir/src/solve/inspect.rs

+2
Original file line numberDiff line numberDiff line change
@@ -135,4 +135,6 @@ pub enum ProbeKind<I: Interner> {
135135
ShadowedEnvProbing,
136136
/// Try to unify an opaque type with an existing key in the storage.
137137
OpaqueTypeStorageLookup { result: QueryResult<I> },
138+
/// Checking that a rigid alias is well-formed.
139+
RigidAlias { result: QueryResult<I> },
138140
}

Diff for: compiler/rustc_type_ir/src/solve/mod.rs

+4
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,10 @@ pub enum GoalSource {
130130
ImplWhereBound,
131131
/// Instantiating a higher-ranked goal and re-proving it.
132132
InstantiateHigherRanked,
133+
/// Predicate required for an alias projection to be well-formed.
134+
/// This is used in two places: projecting to an opaque whose hidden type
135+
/// is already registered in the opaque type storage, and for rigid projections.
136+
AliasWellFormed,
133137
}
134138

135139
#[derive_where(Clone; I: Interner, Goal<I, P>: Clone)]

Diff for: tests/ui/for/issue-20605.next.stderr

+2-9
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,6 @@ help: consider mutably borrowing here
1111
LL | for item in &mut *things { *item = 0 }
1212
| ++++
1313

14-
error[E0614]: type `<dyn Iterator<Item = &'a mut u8> as IntoIterator>::Item` cannot be dereferenced
15-
--> $DIR/issue-20605.rs:6:27
16-
|
17-
LL | for item in *things { *item = 0 }
18-
| ^^^^^
19-
20-
error: aborting due to 2 previous errors
14+
error: aborting due to 1 previous error
2115

22-
Some errors have detailed explanations: E0277, E0614.
23-
For more information about an error, try `rustc --explain E0277`.
16+
For more information about this error, try `rustc --explain E0277`.

Diff for: tests/ui/for/issue-20605.rs

+1-6
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,7 @@
44

55
fn changer<'a>(mut things: Box<dyn Iterator<Item=&'a mut u8>>) {
66
for item in *things { *item = 0 }
7-
//[current]~^ ERROR `dyn Iterator<Item = &'a mut u8>` is not an iterator
8-
//[next]~^^ ERROR `dyn Iterator<Item = &'a mut u8>` is not an iterator
9-
//[next]~| ERROR type `<dyn Iterator<Item = &'a mut u8> as IntoIterator>::Item` cannot be dereferenced
10-
11-
// FIXME(-Znext-solver): these error messages are horrible and have to be
12-
// improved before we stabilize the new solver.
7+
//~^ ERROR `dyn Iterator<Item = &'a mut u8>` is not an iterator
138
}
149

1510
fn main() {}

Diff for: tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.next.stderr

+13-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error[E0283]: type annotations needed
2-
--> $DIR/ambig-hr-projection-issue-93340.rs:16:5
2+
--> $DIR/ambig-hr-projection-issue-93340.rs:17:5
33
|
44
LL | cmp_eq
55
| ^^^^^^ cannot infer type of the type parameter `A` declared on the function `cmp_eq`
@@ -15,6 +15,16 @@ help: consider specifying the generic arguments
1515
LL | cmp_eq::<A, B, O>
1616
| +++++++++++
1717

18-
error: aborting due to 1 previous error
18+
error[E0277]: expected a `Fn(<A as Scalar>::RefType<'_>, <B as Scalar>::RefType<'_>)` closure, found `for<'a, 'b> fn(<O as Scalar>::RefType<'a>, <_ as Scalar>::RefType<'b>) -> O {cmp_eq::<O, _, O>}`
19+
--> $DIR/ambig-hr-projection-issue-93340.rs:14:1
20+
|
21+
LL | / fn build_expression<A: Scalar, B: Scalar, O: Scalar>(
22+
LL | | ) -> impl Fn(A::RefType<'_>, B::RefType<'_>) -> O {
23+
| |_________________________________________________^ expected an `Fn(<A as Scalar>::RefType<'_>, <B as Scalar>::RefType<'_>)` closure, found `for<'a, 'b> fn(<O as Scalar>::RefType<'a>, <_ as Scalar>::RefType<'b>) -> O {cmp_eq::<O, _, O>}`
24+
|
25+
= help: the trait `for<'a, 'b> Fn(<A as Scalar>::RefType<'a>, <B as Scalar>::RefType<'b>)` is not implemented for fn item `for<'a, 'b> fn(<O as Scalar>::RefType<'a>, <_ as Scalar>::RefType<'b>) -> O {cmp_eq::<O, _, O>}`
26+
27+
error: aborting due to 2 previous errors
1928

20-
For more information about this error, try `rustc --explain E0283`.
29+
Some errors have detailed explanations: E0277, E0283.
30+
For more information about an error, try `rustc --explain E0277`.

Diff for: tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.old.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error[E0283]: type annotations needed
2-
--> $DIR/ambig-hr-projection-issue-93340.rs:16:5
2+
--> $DIR/ambig-hr-projection-issue-93340.rs:17:5
33
|
44
LL | cmp_eq
55
| ^^^^^^ cannot infer type of the type parameter `A` declared on the function `cmp_eq`

Diff for: tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.rs

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ fn cmp_eq<'a, 'b, A: Scalar, B: Scalar, O: Scalar>(a: A::RefType<'a>, b: B::RefT
1313

1414
fn build_expression<A: Scalar, B: Scalar, O: Scalar>(
1515
) -> impl Fn(A::RefType<'_>, B::RefType<'_>) -> O {
16+
//[next]~^^ expected a `Fn(<A as Scalar>::RefType<'_>, <B as Scalar>::RefType<'_>)` closure
1617
cmp_eq
1718
//~^ ERROR type annotations needed
1819
}

Diff for: tests/ui/higher-ranked/structually-relate-aliases.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ type Assoc<'a, T> = <T as ToUnit<'a>>::Unit;
1111
impl<T> Overlap<T> for T {}
1212

1313
impl<T> Overlap<for<'a> fn(&'a (), Assoc<'a, T>)> for T {}
14-
//~^ ERROR conflicting implementations of trait `Overlap<for<'a> fn(&'a (), _)>`
14+
//~^ ERROR the trait bound `for<'a> T: ToUnit<'a>` is not satisfied
15+
//~| ERROR the trait bound `for<'a> T: ToUnit<'a>` is not satisfied
1516

1617
fn main() {}
+20-12
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,26 @@
11
WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [?1t, '^0.Named(DefId(0:15 ~ structually_relate_aliases[de75]::{impl#1}::'a), "'a")], def_id: DefId(0:5 ~ structually_relate_aliases[de75]::ToUnit::Unit), .. }
2-
WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [?1t, '^0.Named(DefId(0:15 ~ structually_relate_aliases[de75]::{impl#1}::'a), "'a")], def_id: DefId(0:5 ~ structually_relate_aliases[de75]::ToUnit::Unit), .. }
3-
WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [?1t, '^0.Named(DefId(0:15 ~ structually_relate_aliases[de75]::{impl#1}::'a), "'a")], def_id: DefId(0:5 ~ structually_relate_aliases[de75]::ToUnit::Unit), .. }
4-
WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [?1t, '^0.Named(DefId(0:15 ~ structually_relate_aliases[de75]::{impl#1}::'a), "'a")], def_id: DefId(0:5 ~ structually_relate_aliases[de75]::ToUnit::Unit), .. }
5-
error[E0119]: conflicting implementations of trait `Overlap<for<'a> fn(&'a (), _)>` for type `for<'a> fn(&'a (), _)`
6-
--> $DIR/structually-relate-aliases.rs:13:1
2+
error[E0277]: the trait bound `for<'a> T: ToUnit<'a>` is not satisfied
3+
--> $DIR/structually-relate-aliases.rs:13:36
74
|
8-
LL | impl<T> Overlap<T> for T {}
9-
| ------------------------ first implementation here
10-
LL |
115
LL | impl<T> Overlap<for<'a> fn(&'a (), Assoc<'a, T>)> for T {}
12-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `for<'a> fn(&'a (), _)`
6+
| ^^^^^^^^^^^^ the trait `for<'a> ToUnit<'a>` is not implemented for `T`
7+
|
8+
help: consider restricting type parameter `T`
9+
|
10+
LL | impl<T: for<'a> ToUnit<'a>> Overlap<for<'a> fn(&'a (), Assoc<'a, T>)> for T {}
11+
| ++++++++++++++++++++
12+
13+
error[E0277]: the trait bound `for<'a> T: ToUnit<'a>` is not satisfied
14+
--> $DIR/structually-relate-aliases.rs:13:17
15+
|
16+
LL | impl<T> Overlap<for<'a> fn(&'a (), Assoc<'a, T>)> for T {}
17+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `for<'a> ToUnit<'a>` is not implemented for `T`
18+
|
19+
help: consider restricting type parameter `T`
1320
|
14-
= note: this behavior recently changed as a result of a bug fix; see rust-lang/rust#56105 for details
21+
LL | impl<T: for<'a> ToUnit<'a>> Overlap<for<'a> fn(&'a (), Assoc<'a, T>)> for T {}
22+
| ++++++++++++++++++++
1523

16-
error: aborting due to 1 previous error
24+
error: aborting due to 2 previous errors
1725

18-
For more information about this error, try `rustc --explain E0119`.
26+
For more information about this error, try `rustc --explain E0277`.

Diff for: tests/ui/impl-trait/in-trait/alias-bounds-when-not-wf.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ struct W<T>(T);
1414
// `usize: Foo` doesn't hold. Therefore we ICE, because we don't expect to still
1515
// encounter weak types in `assemble_alias_bound_candidates_recur`.
1616
fn hello(_: W<A<usize>>) {}
17-
//~^ ERROR the size for values of type `A<usize>` cannot be known at compilation time
17+
//~^ ERROR the trait bound `usize: Foo` is not satisfied
18+
//~| ERROR the trait bound `usize: Foo` is not satisfied
19+
//~| ERROR the trait bound `usize: Foo` is not satisfied
1820

1921
fn main() {}

0 commit comments

Comments
 (0)