the .eq() in instantiate_normalizes_to_term may fail

This commit is contained in:
khyperia
2026-06-30 13:53:20 +02:00
parent e9df4e03c8
commit 9ccafdc0f8
4 changed files with 78 additions and 17 deletions
@@ -82,7 +82,7 @@ where
},
|ecx| {
ecx.probe(|&result| ProbeKind::RigidAlias { result }).enter(|this| {
this.structurally_instantiate_normalizes_to_term(goal, goal.predicate.alias);
this.structurally_instantiate_normalizes_to_term(goal, goal.predicate.alias)?;
this.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
})
},
@@ -111,13 +111,9 @@ where
Ok(())
}
/// When normalizing an associated item, constrain the expected term to `term`.
/// When normalizing an associated item, constrain the expected term to `value`.
///
/// We know `term` to always be a fully unconstrained inference variable, so
/// `eq` should never fail here. However, in case `term` contains aliases, we
/// emit nested `AliasRelate` goals to structurally normalize the alias.
///
/// Additionally, when `term` is a const, this registers a `ConstArgHasType`
/// Additionally, when `value` is a const, this registers a `ConstArgHasType`
/// goal to ensure that the const value's type matches the declared type of
/// the alias it was normalized from.
///
@@ -140,11 +136,13 @@ where
fn instantiate_normalizes_to_term(
&mut self,
goal: Goal<I, NormalizesTo<I>>,
term: I::Term,
value: I::Term,
) -> Result<(), NoSolutionOrRerunNonErased> {
self.push_const_arg_has_type_goal(goal.param_env, goal.predicate.alias, term)?;
self.eq(goal.param_env, goal.predicate.term, term)
.expect("expected goal term to be fully unconstrained");
self.push_const_arg_has_type_goal(goal.param_env, goal.predicate.alias, value)?;
// While `goal.predicate.term` should always be a fully unconstrained inference variable,
// `eq` can still fail if `value` is not fully normalized, due to `eq` eagerly normalizing,
// and that normalization can fail.
self.eq(goal.param_env, goal.predicate.term, value)?;
Ok(())
}
@@ -154,14 +152,13 @@ where
&mut self,
goal: Goal<I, NormalizesTo<I>>,
term: ty::AliasTerm<I>,
) {
) -> Result<(), NoSolutionOrRerunNonErased> {
self.relate(
goal.param_env,
term.to_term(self.cx(), ty::IsRigid::Yes),
ty::Invariant,
goal.predicate.term,
)
.expect("expected goal term to be fully unconstrained");
}
}
@@ -359,7 +356,7 @@ where
ecx.structurally_instantiate_normalizes_to_term(
goal,
goal.predicate.alias,
);
)?;
return ecx.evaluate_added_goals_and_make_canonical_response(
Certainty::Yes,
);
@@ -394,7 +391,10 @@ where
ecx.add_goal(GoalSource::Misc, goal.with(cx, PredicateKind::Ambiguous))?;
return then(ecx, Certainty::Yes);
} else {
ecx.structurally_instantiate_normalizes_to_term(goal, goal.predicate.alias);
ecx.structurally_instantiate_normalizes_to_term(
goal,
goal.predicate.alias,
)?;
return then(ecx, Certainty::Yes);
}
} else {
@@ -770,7 +770,7 @@ where
this.structurally_instantiate_normalizes_to_term(
goal,
goal.predicate.alias,
);
)?;
this.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
})
});
@@ -1026,7 +1026,7 @@ where
// this impl candidate anyways. It's still a bit scuffed.
ty::Alias(ty::IsRigid::Yes, _) | ty::Param(_) | ty::Placeholder(..) => {
return ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| {
ecx.structurally_instantiate_normalizes_to_term(goal, goal.predicate.alias);
ecx.structurally_instantiate_normalizes_to_term(goal, goal.predicate.alias)?;
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
});
}
@@ -0,0 +1,15 @@
//@ compile-flags: -Znext-solver
//! This test is extremely similar to `nested-rerun-not-erased-in-normalizes-to.rs`, however,
//! instead of the eager normalization failing due to an anon const when in ErasedNotCoherence, this
//! just straight up fails normalization because u32 is not Iterator
#![feature(ptr_metadata)]
struct ThisStructAintValid(<u32 as Iterator>::Item);
//~^ ERROR `u32` is not an iterator
fn main() {
let y: <ThisStructAintValid as std::ptr::Pointee>::Metadata;
//~^ ERROR type mismatch resolving `<ThisStructAintValid as Pointee>::Metadata == _`
}
@@ -0,0 +1,18 @@
error[E0277]: `u32` is not an iterator
--> $DIR/consider_builtin_pointee_candidate-instantiate-result-fail.rs:9:28
|
LL | struct ThisStructAintValid(<u32 as Iterator>::Item);
| ^^^^^^^^^^^^^^^^^^^^^^^ `u32` is not an iterator
|
= help: the trait `Iterator` is not implemented for `u32`
error[E0271]: type mismatch resolving `<ThisStructAintValid as Pointee>::Metadata == _`
--> $DIR/consider_builtin_pointee_candidate-instantiate-result-fail.rs:13:12
|
LL | let y: <ThisStructAintValid as std::ptr::Pointee>::Metadata;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ types differ
error: aborting due to 2 previous errors
Some errors have detailed explanations: E0271, E0277.
For more information about an error, try `rustc --explain E0271`.
@@ -0,0 +1,28 @@
//@ build-pass
//@ compile-flags: -Znext-solver
//! Consider:
//!
//! goal is <StructTailHasAnonConst as Pointee>::Metadata == ?0, in a typing context of
//! ErasedNotCoherence
//!
//! `consider_builtin_pointee_candidate` looks at StructTailHasAnonConst, realizes it's an ADT,
//! fetches the struct tail (which is `S<{ 2 + 2 }>`), and calls `instantiate_normalizes_to_term`
//! with the result of `<Struct<{ 2 + 2 }> as Pointee>::Metadata`
//!
//! `instantiate_normalizes_to_term` `.eq()`s `<Struct<{ 2 + 2 }> as Pointee>::Metadata` and `?0`
//!
//! this eagerly normalizes, which normalizes the anon const, which fails due to ErasedNotCoherence
//!
//! this causes the `.eq()` in `instantiate_normalizes_to_term` to fail, which used to have an
//! unwrap, which ICEd
#![feature(ptr_metadata)]
struct S<const N: usize>;
struct StructTailHasAnonConst(S<{ 2 + 2 }>);
fn main() {
let y: <StructTailHasAnonConst as std::ptr::Pointee>::Metadata;
}