incompletely prefer opaque type bounds when self type bottoms out in infer
This commit is contained in:
parent
0e29865434
commit
cf224ea1fb
18 changed files with 457 additions and 14 deletions
|
|
@ -651,7 +651,11 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
|
|||
| ty::Bound(_, _) => bug!("unexpected self type: {self_ty}"),
|
||||
}
|
||||
|
||||
let trait_impls = tcx.trait_impls_of(trait_def_id);
|
||||
#[allow(rustc::usage_of_type_ir_traits)]
|
||||
self.for_each_blanket_impl(trait_def_id, f)
|
||||
}
|
||||
fn for_each_blanket_impl(self, trait_def_id: DefId, mut f: impl FnMut(DefId)) {
|
||||
let trait_impls = self.trait_impls_of(trait_def_id);
|
||||
for &impl_def_id in trait_impls.blanket_impls() {
|
||||
f(impl_def_id);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,8 +11,9 @@ use rustc_type_ir::lang_items::SolverTraitLangItem;
|
|||
use rustc_type_ir::search_graph::CandidateHeadUsages;
|
||||
use rustc_type_ir::solve::SizedTraitKind;
|
||||
use rustc_type_ir::{
|
||||
self as ty, Interner, TypeFlags, TypeFoldable, TypeSuperVisitable, TypeVisitable,
|
||||
TypeVisitableExt as _, TypeVisitor, TypingMode, Upcast as _, elaborate,
|
||||
self as ty, Interner, TypeFlags, TypeFoldable, TypeFolder, TypeSuperFoldable,
|
||||
TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode, Upcast,
|
||||
elaborate,
|
||||
};
|
||||
use tracing::{debug, instrument};
|
||||
|
||||
|
|
@ -187,6 +188,7 @@ where
|
|||
ecx: &mut EvalCtxt<'_, D>,
|
||||
goal: Goal<I, Self>,
|
||||
impl_def_id: I::ImplId,
|
||||
then: impl FnOnce(&mut EvalCtxt<'_, D>, Certainty) -> QueryResult<I>,
|
||||
) -> Result<Candidate<I>, NoSolution>;
|
||||
|
||||
/// If the predicate contained an error, we want to avoid emitting unnecessary trait
|
||||
|
|
@ -365,6 +367,15 @@ pub(super) enum AssembleCandidatesFrom {
|
|||
EnvAndBounds,
|
||||
}
|
||||
|
||||
impl AssembleCandidatesFrom {
|
||||
fn should_assemble_impl_candidates(&self) -> bool {
|
||||
match self {
|
||||
AssembleCandidatesFrom::All => true,
|
||||
AssembleCandidatesFrom::EnvAndBounds => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This is currently used to track the [CandidateHeadUsages] of all failed `ParamEnv`
|
||||
/// candidates. This is then used to ignore their head usages in case there's another
|
||||
/// always applicable `ParamEnv` candidate. Look at how `param_env_head_usages` is
|
||||
|
|
@ -397,14 +408,15 @@ where
|
|||
return (candidates, failed_candidate_info);
|
||||
};
|
||||
|
||||
let goal: Goal<I, G> = goal
|
||||
.with(self.cx(), goal.predicate.with_replaced_self_ty(self.cx(), normalized_self_ty));
|
||||
|
||||
if normalized_self_ty.is_ty_var() {
|
||||
debug!("self type has been normalized to infer");
|
||||
candidates.extend(self.forced_ambiguity(MaybeCause::Ambiguity));
|
||||
self.try_assemble_bounds_via_registered_opaques(goal, assemble_from, &mut candidates);
|
||||
return (candidates, failed_candidate_info);
|
||||
}
|
||||
|
||||
let goal: Goal<I, G> = goal
|
||||
.with(self.cx(), goal.predicate.with_replaced_self_ty(self.cx(), normalized_self_ty));
|
||||
// Vars that show up in the rest of the goal substs may have been constrained by
|
||||
// normalizing the self type as well, since type variables are not uniquified.
|
||||
let goal = self.resolve_vars_if_possible(goal);
|
||||
|
|
@ -484,8 +496,9 @@ where
|
|||
if cx.impl_is_default(impl_def_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
match G::consider_impl_candidate(self, goal, impl_def_id) {
|
||||
match G::consider_impl_candidate(self, goal, impl_def_id, |ecx, certainty| {
|
||||
ecx.evaluate_added_goals_and_make_canonical_response(certainty)
|
||||
}) {
|
||||
Ok(candidate) => candidates.push(candidate),
|
||||
Err(NoSolution) => (),
|
||||
}
|
||||
|
|
@ -943,6 +956,116 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
/// If the self type is the hidden type of an opaque, try to assemble
|
||||
/// candidates for it by consider its item bounds and by using blanket
|
||||
/// impls. This is used to incompletely guide type inference when handling
|
||||
/// non-defining uses in the defining scope.
|
||||
///
|
||||
/// We otherwise just fail fail with ambiguity. Even if we're using an
|
||||
/// opaque type item bound or a blank impls, we still force its certainty
|
||||
/// to be `Maybe` so that we properly prove this goal later.
|
||||
///
|
||||
/// See <https://github.com/rust-lang/trait-system-refactor-initiative/issues/182>
|
||||
/// for why this is necessary.
|
||||
fn try_assemble_bounds_via_registered_opaques<G: GoalKind<D>>(
|
||||
&mut self,
|
||||
goal: Goal<I, G>,
|
||||
assemble_from: AssembleCandidatesFrom,
|
||||
candidates: &mut Vec<Candidate<I>>,
|
||||
) {
|
||||
let self_ty = goal.predicate.self_ty();
|
||||
// If the self type is sub unified with any opaque type, we
|
||||
// also look at blanket impls for it.
|
||||
let mut assemble_blanket_impls = false;
|
||||
for alias_ty in self.opaques_with_sub_unified_hidden_type(self_ty) {
|
||||
assemble_blanket_impls = true;
|
||||
debug!("self ty is sub unified with {alias_ty:?}");
|
||||
|
||||
struct ReplaceOpaque<I: Interner> {
|
||||
cx: I,
|
||||
alias_ty: ty::AliasTy<I>,
|
||||
self_ty: I::Ty,
|
||||
}
|
||||
impl<I: Interner> TypeFolder<I> for ReplaceOpaque<I> {
|
||||
fn cx(&self) -> I {
|
||||
self.cx
|
||||
}
|
||||
fn fold_ty(&mut self, ty: I::Ty) -> I::Ty {
|
||||
if let ty::Alias(ty::Opaque, alias_ty) = ty.kind() {
|
||||
if alias_ty == self.alias_ty {
|
||||
return self.self_ty;
|
||||
}
|
||||
}
|
||||
ty.super_fold_with(self)
|
||||
}
|
||||
}
|
||||
|
||||
// We look at all item-bounds of the opaque, replacing the
|
||||
// opaque with the current self type before considering
|
||||
// them as a candidate. Imagine e've got `?x: Trait<?y>`
|
||||
// and `?x` has been sub-unified with the hidden type of
|
||||
// `impl Trait<u32>`, We take the item bound `opaque: Trait<u32>`
|
||||
// and replace all occurrences of `opaque` with `?x`. This results
|
||||
// in a `?x: Trait<u32>` alias-bound candidate.
|
||||
for item_bound in self
|
||||
.cx()
|
||||
.item_self_bounds(alias_ty.def_id)
|
||||
.iter_instantiated(self.cx(), alias_ty.args)
|
||||
{
|
||||
let assumption =
|
||||
item_bound.fold_with(&mut ReplaceOpaque { cx: self.cx(), alias_ty, self_ty });
|
||||
candidates.extend(G::probe_and_match_goal_against_assumption(
|
||||
self,
|
||||
CandidateSource::AliasBound,
|
||||
goal,
|
||||
assumption,
|
||||
|ecx| {
|
||||
// We want to reprove this goal once we've inferred the
|
||||
// hidden type, so we force the certainty to `Maybe`.
|
||||
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
|
||||
},
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// We also need to consider blanket impls for not-yet-defined opaque types.
|
||||
//
|
||||
// See tests/ui/impl-trait/non-defining-uses/use-blanket-impl.rs for an example.
|
||||
if assemble_blanket_impls && assemble_from.should_assemble_impl_candidates() {
|
||||
let cx = self.cx();
|
||||
cx.for_each_blanket_impl(goal.predicate.trait_def_id(cx), |impl_def_id| {
|
||||
// For every `default impl`, there's always a non-default `impl`
|
||||
// that will *also* apply. There's no reason to register a candidate
|
||||
// for this impl, since it is *not* proof that the trait goal holds.
|
||||
if cx.impl_is_default(impl_def_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
match G::consider_impl_candidate(self, goal, impl_def_id, |ecx, certainty| {
|
||||
if ecx.shallow_resolve(self_ty).is_ty_var() {
|
||||
// We force the certainty of impl candidates to be `Maybe`.
|
||||
let certainty = certainty.and(Certainty::AMBIGUOUS);
|
||||
ecx.evaluate_added_goals_and_make_canonical_response(certainty)
|
||||
} else {
|
||||
// We don't want to use impls if they constrain the opaque.
|
||||
//
|
||||
// FIXME(trait-system-refactor-initiative#229): This isn't
|
||||
// perfect yet as it still allows us to incorrectly constrain
|
||||
// other inference variables.
|
||||
Err(NoSolution)
|
||||
}
|
||||
}) {
|
||||
Ok(candidate) => candidates.push(candidate),
|
||||
Err(NoSolution) => (),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if candidates.is_empty() {
|
||||
candidates.extend(self.forced_ambiguity(MaybeCause::Ambiguity));
|
||||
}
|
||||
}
|
||||
|
||||
/// Assemble and merge candidates for goals which are related to an underlying trait
|
||||
/// goal. Right now, this is normalizes-to and host effect goals.
|
||||
///
|
||||
|
|
|
|||
|
|
@ -124,6 +124,7 @@ where
|
|||
ecx: &mut EvalCtxt<'_, D>,
|
||||
goal: Goal<I, Self>,
|
||||
impl_def_id: I::ImplId,
|
||||
then: impl FnOnce(&mut EvalCtxt<'_, D>, Certainty) -> QueryResult<I>,
|
||||
) -> Result<Candidate<I>, NoSolution> {
|
||||
let cx = ecx.cx();
|
||||
|
||||
|
|
@ -175,7 +176,7 @@ where
|
|||
});
|
||||
ecx.add_goals(GoalSource::ImplWhereBound, const_conditions);
|
||||
|
||||
ecx.evaluate_added_goals_and_make_canonical_response(certainty)
|
||||
then(ecx, certainty)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1060,6 +1060,10 @@ where
|
|||
self.delegate.resolve_vars_if_possible(value)
|
||||
}
|
||||
|
||||
pub(super) fn shallow_resolve(&self, ty: I::Ty) -> I::Ty {
|
||||
self.delegate.shallow_resolve(ty)
|
||||
}
|
||||
|
||||
pub(super) fn eager_resolve_region(&self, r: I::Region) -> I::Region {
|
||||
if let ty::ReVar(vid) = r.kind() {
|
||||
self.delegate.opportunistic_resolve_lt_var(vid)
|
||||
|
|
@ -1176,6 +1180,33 @@ where
|
|||
) -> bool {
|
||||
may_use_unstable_feature(&**self.delegate, param_env, symbol)
|
||||
}
|
||||
|
||||
pub(crate) fn opaques_with_sub_unified_hidden_type(
|
||||
&self,
|
||||
self_ty: I::Ty,
|
||||
) -> impl Iterator<Item = ty::AliasTy<I>> + use<'a, D, I> {
|
||||
let delegate = self.delegate;
|
||||
delegate
|
||||
.clone_opaque_types_lookup_table()
|
||||
.into_iter()
|
||||
.chain(delegate.clone_duplicate_opaque_types())
|
||||
.filter_map(move |(key, hidden_ty)| {
|
||||
if let ty::Infer(ty::TyVar(self_vid)) = self_ty.kind() {
|
||||
if let ty::Infer(ty::TyVar(hidden_vid)) = hidden_ty.kind() {
|
||||
if delegate.sub_unification_table_root_var(self_vid)
|
||||
== delegate.sub_unification_table_root_var(hidden_vid)
|
||||
{
|
||||
return Some(ty::AliasTy::new_from_args(
|
||||
delegate.cx(),
|
||||
key.def_id.into(),
|
||||
key.args,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Eagerly replace aliases with inference variables, emitting `AliasRelate`
|
||||
|
|
|
|||
|
|
@ -194,6 +194,7 @@ where
|
|||
ecx: &mut EvalCtxt<'_, D>,
|
||||
goal: Goal<I, NormalizesTo<I>>,
|
||||
impl_def_id: I::ImplId,
|
||||
then: impl FnOnce(&mut EvalCtxt<'_, D>, Certainty) -> QueryResult<I>,
|
||||
) -> Result<Candidate<I>, NoSolution> {
|
||||
let cx = ecx.cx();
|
||||
|
||||
|
|
@ -314,8 +315,7 @@ where
|
|||
// nested goal for consistency.
|
||||
ty::TypingMode::Coherence => {
|
||||
ecx.add_goal(GoalSource::Misc, goal.with(cx, PredicateKind::Ambiguous));
|
||||
return ecx
|
||||
.evaluate_added_goals_and_make_canonical_response(Certainty::Yes);
|
||||
return then(ecx, Certainty::Yes);
|
||||
}
|
||||
ty::TypingMode::Analysis { .. }
|
||||
| ty::TypingMode::Borrowck { .. }
|
||||
|
|
@ -325,8 +325,7 @@ where
|
|||
goal,
|
||||
goal.predicate.alias,
|
||||
);
|
||||
return ecx
|
||||
.evaluate_added_goals_and_make_canonical_response(Certainty::Yes);
|
||||
return then(ecx, Certainty::Yes);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ where
|
|||
ecx: &mut EvalCtxt<'_, D>,
|
||||
goal: Goal<I, TraitPredicate<I>>,
|
||||
impl_def_id: I::ImplId,
|
||||
then: impl FnOnce(&mut EvalCtxt<'_, D>, Certainty) -> QueryResult<I>,
|
||||
) -> Result<Candidate<I>, NoSolution> {
|
||||
let cx = ecx.cx();
|
||||
|
||||
|
|
@ -112,7 +113,7 @@ where
|
|||
.map(|pred| goal.with(cx, pred)),
|
||||
);
|
||||
|
||||
ecx.evaluate_added_goals_and_make_canonical_response(maximal_certainty)
|
||||
then(ecx, maximal_certainty)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -347,6 +347,7 @@ pub trait Interner:
|
|||
self_ty: Self::Ty,
|
||||
f: impl FnMut(Self::ImplId),
|
||||
);
|
||||
fn for_each_blanket_impl(self, trait_def_id: Self::TraitId, f: impl FnMut(Self::ImplId));
|
||||
|
||||
fn has_item_definition(self, def_id: Self::DefId) -> bool;
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
error[E0308]: mismatched types
|
||||
--> $DIR/avoid-inference-constraints-from-blanket-2.rs:27:18
|
||||
|
|
||||
LL | let _: u32 = x;
|
||||
| --- ^ expected `u32`, found `u64`
|
||||
| |
|
||||
| expected due to this
|
||||
|
|
||||
help: you can convert a `u64` to a `u32` and panic if the converted value doesn't fit
|
||||
|
|
||||
LL | let _: u32 = x.try_into().unwrap();
|
||||
| ++++++++++++++++++++
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0308`.
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
//@ revisions: current next
|
||||
//@[next] compile-flags: -Znext-solver
|
||||
//@ ignore-compare-mode-next-solver (explicit revisions)
|
||||
//@[current] check-pass
|
||||
|
||||
// Regression test for trait-system-refactor-initiative#205. Avoid
|
||||
// constraining other impl arguments when applying blanket impls.
|
||||
|
||||
// FIXME(-Znext-solver): This currently incompletely constrains the
|
||||
// argument of `opaque: Trait<?x>` using the blanket impl of trait.
|
||||
// Ideally we don't do that.
|
||||
|
||||
trait Trait<T> {}
|
||||
|
||||
impl<T> Trait<u64> for T {}
|
||||
impl Trait<u32> for u64 {}
|
||||
|
||||
fn impls_trait<T: Trait<U>, U>(_: U) -> T {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn foo() -> impl Sized {
|
||||
let x = Default::default();
|
||||
if false {
|
||||
return impls_trait::<_, _>(x);
|
||||
}
|
||||
let _: u32 = x;
|
||||
//[next]~^ ERROR mismatched types
|
||||
1u64
|
||||
}
|
||||
fn main() {}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
//@ compile-flags: -Znext-solver
|
||||
//@ ignore-compare-mode-next-solver (explicit revisions)
|
||||
#![allow(unconditional_recursion)]
|
||||
|
||||
// Regression test for trait-system-refactor-initiative#205. Avoid
|
||||
// constraining other impl arguments when applying blanket impls,
|
||||
// especially if the nested where-bounds of the blanket impl don't
|
||||
// actually apply for the opaque.
|
||||
|
||||
// FIXME(-Znext-solver): This currently incompletely constrains the
|
||||
// argument of `opaque: Trait<?x>` using the blanket impl of trait.
|
||||
// Ideally we don't do that.
|
||||
|
||||
trait Trait<T> {}
|
||||
|
||||
impl<T: Copy> Trait<u32> for T {}
|
||||
impl Trait<u64> for String {}
|
||||
fn impls_trait<T: Trait<U>, U>(_: T) {}
|
||||
|
||||
fn test() -> impl Sized {
|
||||
let x = test();
|
||||
impls_trait(x); //~ ERROR the trait bound `String: Trait<u32>` is not satisfied
|
||||
String::new()
|
||||
}
|
||||
fn main() {}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
error[E0277]: the trait bound `String: Trait<u32>` is not satisfied
|
||||
--> $DIR/avoid-inference-constraints-from-blanket-3.rs:22:5
|
||||
|
|
||||
LL | impls_trait(x);
|
||||
| ^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `String`
|
||||
|
|
||||
= help: the trait `Trait<u32>` is not implemented for `String`
|
||||
but trait `Trait<u64>` is implemented for it
|
||||
= help: for that trait implementation, expected `u64`, found `u32`
|
||||
note: required for `String` to implement `Trait<u32>`
|
||||
--> $DIR/avoid-inference-constraints-from-blanket-3.rs:16:15
|
||||
|
|
||||
LL | impl<T: Copy> Trait<u32> for T {}
|
||||
| ---- ^^^^^^^^^^ ^
|
||||
| |
|
||||
| unsatisfied trait bound introduced here
|
||||
note: required by a bound in `impls_trait`
|
||||
--> $DIR/avoid-inference-constraints-from-blanket-3.rs:18:19
|
||||
|
|
||||
LL | fn impls_trait<T: Trait<U>, U>(_: T) {}
|
||||
| ^^^^^^^^ required by this bound in `impls_trait`
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0277`.
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
//@ revisions: current next
|
||||
//@[next] compile-flags: -Znext-solver
|
||||
//@ ignore-compare-mode-next-solver (explicit revisions)
|
||||
//@ check-pass
|
||||
|
||||
// Regression test for trait-system-refactor-initiative#205. Avoid constraining
|
||||
// the opaque type when applying blanket impls.
|
||||
|
||||
trait Trait<T> {}
|
||||
|
||||
impl<T> Trait<T> for T {}
|
||||
impl Trait<u32> for u64 {}
|
||||
|
||||
fn impls_trait<T: Trait<U>, U>() -> T {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn foo() -> impl Sized {
|
||||
if false {
|
||||
// `opaque: Trait<u32>` shouldn't constrain `opaque` to `u32` via the blanket impl
|
||||
return impls_trait::<_, u32>();
|
||||
}
|
||||
1u64
|
||||
}
|
||||
fn main() {}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
//@ compile-flags: -Znext-solver
|
||||
//@ ignore-compare-mode-next-solver (explicit revisions)
|
||||
//@ check-pass
|
||||
#![allow(unconditional_recursion)]
|
||||
|
||||
// Regression test for trait-system-refactor-initiative#182. If multiple
|
||||
// opaque types result in different item bounds, do not apply them.
|
||||
|
||||
trait Trait<T> {}
|
||||
impl<T, U> Trait<T> for U {}
|
||||
|
||||
fn impls_trait<T: Trait<U>, U>(_: T) -> U {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn overlap<T, U>() -> (impl Trait<T>, impl Trait<U>) {
|
||||
let mut x = overlap::<T, U>().0;
|
||||
x = overlap::<T, U>().1;
|
||||
let u = impls_trait(x);
|
||||
let _: u32 = u;
|
||||
((), ())
|
||||
}
|
||||
fn main() {}
|
||||
37
tests/ui/impl-trait/non-defining-uses/multiple-opaques-ok.rs
Normal file
37
tests/ui/impl-trait/non-defining-uses/multiple-opaques-ok.rs
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
//@ compile-flags: -Znext-solver
|
||||
//@ ignore-compare-mode-next-solver (explicit revisions)
|
||||
//@ check-pass
|
||||
#![allow(unconditional_recursion)]
|
||||
|
||||
// Regression test for trait-system-refactor-initiative#18, making sure
|
||||
// we support being sub unified with more than 1 opaque type.
|
||||
|
||||
trait Id {
|
||||
type This;
|
||||
}
|
||||
impl Id for &'static str {
|
||||
type This = &'static str;
|
||||
}
|
||||
fn to_assoc<T: Id>(x: T) -> <T as Id>::This {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn mirror1() -> (impl Id<This = &'static str>, impl Sized) {
|
||||
let mut opaque = mirror1().0;
|
||||
opaque = mirror1().1;
|
||||
let x = to_assoc(opaque);
|
||||
// `?x` equals both opaques, make sure we still use the applicable
|
||||
// item bound.
|
||||
x.len();
|
||||
(x, x)
|
||||
}
|
||||
fn mirror2() -> (impl Sized, impl Id<This = &'static str>) {
|
||||
let mut opaque = mirror2().0;
|
||||
opaque = mirror2().1;
|
||||
let x = to_assoc(opaque);
|
||||
// `?x` equals both opaques, make sure we still use the applicable
|
||||
// item bound.
|
||||
x.len();
|
||||
(x, x)
|
||||
}
|
||||
fn main() {}
|
||||
27
tests/ui/impl-trait/non-defining-uses/no-rigid-alias.rs
Normal file
27
tests/ui/impl-trait/non-defining-uses/no-rigid-alias.rs
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
//@ compile-flags: -Znext-solver
|
||||
//@ ignore-compare-mode-next-solver (explicit revisions)
|
||||
//@ check-pass
|
||||
#![allow(unconditional_recursion)]
|
||||
|
||||
// Test for trait-system-refactor-initiative#182 making sure
|
||||
// that we don't incorrectly normalize to rigid aliases if the
|
||||
// opaque type only has a trait bound.
|
||||
|
||||
trait Id {
|
||||
type This;
|
||||
}
|
||||
impl<T> Id for Vec<T> {
|
||||
type This = Vec<T>;
|
||||
}
|
||||
fn to_assoc<T: Id>(x: T) -> <T as Id>::This {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn mirror<T>(x: Vec<T>) -> impl Id {
|
||||
let x = to_assoc(mirror(x));
|
||||
// `?x` equals `<opaque::<T> as Id>::This`. We should not infer `?x`
|
||||
// to be a rigid alias here.
|
||||
let _: Vec<u32> = x;
|
||||
x
|
||||
}
|
||||
fn main() {}
|
||||
19
tests/ui/impl-trait/non-defining-uses/use-blanket-impl.rs
Normal file
19
tests/ui/impl-trait/non-defining-uses/use-blanket-impl.rs
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
//@ revisions: current next
|
||||
//@[next] compile-flags: -Znext-solver
|
||||
//@ ignore-compare-mode-next-solver (explicit revisions)
|
||||
//@ check-pass
|
||||
|
||||
// Regression test for trait-system-refactor-initiative#196.
|
||||
fn iterator(b: bool) -> impl Iterator<Item = String> {
|
||||
if b {
|
||||
// We need to eagerly figure out the type of `i` here by using
|
||||
// the `<opaque as IntoIterator>::Item` obligation. This means
|
||||
// we not only have to consider item bounds, but also blanket impls.
|
||||
for i in iterator(false) {
|
||||
i.len();
|
||||
}
|
||||
}
|
||||
|
||||
vec![].into_iter()
|
||||
}
|
||||
fn main() {}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
//@ revisions: current next
|
||||
//@[next] compile-flags: -Znext-solver
|
||||
//@ ignore-compare-mode-next-solver (explicit revisions)
|
||||
//@ check-pass
|
||||
#![allow(unconditional_recursion)]
|
||||
|
||||
// Regression test for trait-system-refactor-initiative#182.
|
||||
|
||||
trait Id {
|
||||
type This;
|
||||
}
|
||||
impl<T> Id for T {
|
||||
type This = T;
|
||||
}
|
||||
fn to_assoc<T>(x: T) -> <T as Id>::This {
|
||||
x
|
||||
}
|
||||
|
||||
fn mirror<T>(x: Vec<T>) -> impl Id<This = Vec<T>> {
|
||||
let x = to_assoc(mirror(x));
|
||||
// `?x` equals `<opaque::<T> as Id>::This`. We need to eagerly infer the
|
||||
// type of `?x` to prevent this method call from resulting in an error.
|
||||
//
|
||||
// We could use both the item bound to normalize to `Vec<T>`, or the
|
||||
// blanket impl to normalize to `opaque::<T>`. We have to go with the
|
||||
// item bound.
|
||||
x.len();
|
||||
x
|
||||
}
|
||||
fn main() {}
|
||||
25
tests/ui/impl-trait/non-defining-uses/use-item-bound.rs
Normal file
25
tests/ui/impl-trait/non-defining-uses/use-item-bound.rs
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
//@ revisions: current next
|
||||
//@[next] compile-flags: -Znext-solver
|
||||
//@ ignore-compare-mode-next-solver (explicit revisions)
|
||||
//@ check-pass
|
||||
#![allow(unconditional_recursion)]
|
||||
// Regression test for trait-system-refactor-initiative#182.
|
||||
|
||||
trait Id {
|
||||
type This;
|
||||
}
|
||||
impl<T> Id for Vec<T> {
|
||||
type This = Vec<T>;
|
||||
}
|
||||
fn to_assoc<T: Id>(x: T) -> <T as Id>::This {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn mirror<T>(x: Vec<T>) -> impl Id<This = Vec<T>> {
|
||||
let x = to_assoc(mirror(x));
|
||||
// `?x` equals `<opaque::<T> as Id>::This`. We need to eagerly infer the
|
||||
// type of `?x` to prevent this method call from resulting in an error.
|
||||
x.len();
|
||||
x
|
||||
}
|
||||
fn main() {}
|
||||
Loading…
Add table
Add a link
Reference in a new issue