Skip to content

Commit a156ca8

Browse files
compiler-errorslcnr
authored andcommitted
incompletely prefer opaque type bounds when self type bottoms out in infer
1 parent d6692fb commit a156ca8

File tree

14 files changed

+339
-13
lines changed

14 files changed

+339
-13
lines changed

compiler/rustc_middle/src/ty/context.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -651,7 +651,11 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
651651
| ty::Bound(_, _) => bug!("unexpected self type: {self_ty}"),
652652
}
653653

654-
let trait_impls = tcx.trait_impls_of(trait_def_id);
654+
#[allow(rustc::usage_of_type_ir_traits)]
655+
self.for_each_blanket_impl(trait_def_id, f)
656+
}
657+
fn for_each_blanket_impl(self, trait_def_id: DefId, mut f: impl FnMut(DefId)) {
658+
let trait_impls = self.trait_impls_of(trait_def_id);
655659
for &impl_def_id in trait_impls.blanket_impls() {
656660
f(impl_def_id);
657661
}

compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs

Lines changed: 124 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@ use rustc_type_ir::lang_items::SolverTraitLangItem;
1111
use rustc_type_ir::search_graph::CandidateHeadUsages;
1212
use rustc_type_ir::solve::SizedTraitKind;
1313
use rustc_type_ir::{
14-
self as ty, Interner, TypeFlags, TypeFoldable, TypeSuperVisitable, TypeVisitable,
15-
TypeVisitableExt as _, TypeVisitor, TypingMode, Upcast as _, elaborate,
14+
self as ty, Interner, TypeFlags, TypeFoldable, TypeFolder, TypeSuperFoldable,
15+
TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode, Upcast,
16+
elaborate,
1617
};
1718
use tracing::{debug, instrument};
1819

@@ -187,6 +188,7 @@ where
187188
ecx: &mut EvalCtxt<'_, D>,
188189
goal: Goal<I, Self>,
189190
impl_def_id: I::ImplId,
191+
finalize: impl FnOnce(&mut EvalCtxt<'_, D>, Certainty) -> QueryResult<I>,
190192
) -> Result<Candidate<I>, NoSolution>;
191193

192194
/// If the predicate contained an error, we want to avoid emitting unnecessary trait
@@ -365,6 +367,15 @@ pub(super) enum AssembleCandidatesFrom {
365367
EnvAndBounds,
366368
}
367369

370+
impl AssembleCandidatesFrom {
371+
fn should_assemble_impl_candidates(&self) -> bool {
372+
match self {
373+
AssembleCandidatesFrom::All => true,
374+
AssembleCandidatesFrom::EnvAndBounds => false,
375+
}
376+
}
377+
}
378+
368379
/// This is currently used to track the [CandidateHeadUsages] of all failed `ParamEnv`
369380
/// candidates. This is then used to ignore their head usages in case there's another
370381
/// always applicable `ParamEnv` candidate. Look at how `param_env_head_usages` is
@@ -397,14 +408,15 @@ where
397408
return (candidates, failed_candidate_info);
398409
};
399410

411+
let goal: Goal<I, G> = goal
412+
.with(self.cx(), goal.predicate.with_replaced_self_ty(self.cx(), normalized_self_ty));
413+
400414
if normalized_self_ty.is_ty_var() {
401415
debug!("self type has been normalized to infer");
402-
candidates.extend(self.forced_ambiguity(MaybeCause::Ambiguity));
416+
self.try_assemble_bounds_via_registered_opaque(goal, assemble_from, &mut candidates);
403417
return (candidates, failed_candidate_info);
404418
}
405419

406-
let goal: Goal<I, G> = goal
407-
.with(self.cx(), goal.predicate.with_replaced_self_ty(self.cx(), normalized_self_ty));
408420
// Vars that show up in the rest of the goal substs may have been constrained by
409421
// normalizing the self type as well, since type variables are not uniquified.
410422
let goal = self.resolve_vars_if_possible(goal);
@@ -485,7 +497,9 @@ where
485497
return;
486498
}
487499

488-
match G::consider_impl_candidate(self, goal, impl_def_id) {
500+
match G::consider_impl_candidate(self, goal, impl_def_id, |ecx, certainty| {
501+
ecx.evaluate_added_goals_and_make_canonical_response(certainty)
502+
}) {
489503
Ok(candidate) => candidates.push(candidate),
490504
Err(NoSolution) => (),
491505
}
@@ -943,6 +957,110 @@ where
943957
}
944958
}
945959

960+
/// If the self type is the hidden type of an opaque, try to assemble
961+
/// candidates for it by consider its item bounds and by using blanket
962+
/// impls. This is used to incompletely guide type inference when handling
963+
/// non-defining uses in the defining scope.
964+
///
965+
/// We otherwise just fail fail with ambiguity. Even if we're using an
966+
/// opaque type item bound or a blank impls, we still force its certainty
967+
/// to be `Maybe` so that we properly prove this goal later.
968+
///
969+
/// See <https://github.com/rust-lang/trait-system-refactor-initiative/issues/182>
970+
/// for why this is necessary.
971+
fn try_assemble_bounds_via_registered_opaque<G: GoalKind<D>>(
972+
&mut self,
973+
goal: Goal<I, G>,
974+
assemble_from: AssembleCandidatesFrom,
975+
candidates: &mut Vec<Candidate<I>>,
976+
) {
977+
let self_ty = goal.predicate.self_ty();
978+
let mut is_hidden_type_of_alias = false;
979+
for alias_ty in self.find_sup_as_registered_opaque(self_ty) {
980+
debug!("self ty is sub unified with {alias_ty:?}");
981+
is_hidden_type_of_alias = true;
982+
983+
struct ReplaceOpaque<I: Interner> {
984+
cx: I,
985+
alias_ty: ty::AliasTy<I>,
986+
self_ty: I::Ty,
987+
}
988+
impl<I: Interner> TypeFolder<I> for ReplaceOpaque<I> {
989+
fn cx(&self) -> I {
990+
self.cx
991+
}
992+
fn fold_ty(&mut self, ty: I::Ty) -> I::Ty {
993+
if let ty::Alias(ty::Opaque, alias_ty) = ty.kind() {
994+
if alias_ty == self.alias_ty {
995+
return self.self_ty;
996+
}
997+
}
998+
ty.super_fold_with(self)
999+
}
1000+
}
1001+
1002+
// We look at all item-bounds of the opaque, replacing the
1003+
// opaque with the current self type before considering
1004+
// them as a candidate. Imagine e've got `?x: Trait<?y>`
1005+
// and `?x` has been sub-unified with the hidden type of
1006+
// `impl Trait<u32>`, We take the item bound `opaque: Trait<u32>`
1007+
// and replace all occurrences of `opaque` with `?x`. This results
1008+
// in a `?x: Trait<u32>` alias-bound candidate.
1009+
for item_bound in self
1010+
.cx()
1011+
.item_self_bounds(alias_ty.def_id)
1012+
.iter_instantiated(self.cx(), alias_ty.args)
1013+
{
1014+
let assumption =
1015+
item_bound.fold_with(&mut ReplaceOpaque { cx: self.cx(), alias_ty, self_ty });
1016+
// We want to reprove this goal once we've inferred the hidden type,
1017+
// so we force the certainty to `Maybe`.
1018+
candidates.extend(G::probe_and_match_goal_against_assumption(
1019+
self,
1020+
CandidateSource::AliasBound,
1021+
goal,
1022+
assumption,
1023+
|ecx| {
1024+
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
1025+
},
1026+
));
1027+
}
1028+
}
1029+
1030+
// We also need to consider blanket impls for not-yet-defined opaque types.
1031+
//
1032+
// See tests/ui/impl-trait/non-defining-uses/use-blanket-impl.rs for an example.
1033+
if is_hidden_type_of_alias && assemble_from.should_assemble_impl_candidates() {
1034+
let cx = self.cx();
1035+
cx.for_each_blanket_impl(goal.predicate.trait_def_id(cx), |impl_def_id| {
1036+
// For every `default impl`, there's always a non-default `impl`
1037+
// that will *also* apply. There's no reason to register a candidate
1038+
// for this impl, since it is *not* proof that the trait goal holds.
1039+
if cx.impl_is_default(impl_def_id) {
1040+
return;
1041+
}
1042+
1043+
// We force the certainty of impl candidates to be `Maybe`.
1044+
match G::consider_impl_candidate(self, goal, impl_def_id, |ecx, certainty| {
1045+
if ecx.shallow_resolve(self_ty).is_ty_var() {
1046+
ecx.evaluate_added_goals_and_make_canonical_response(
1047+
certainty.and(Certainty::AMBIGUOUS),
1048+
)
1049+
} else {
1050+
Err(NoSolution)
1051+
}
1052+
}) {
1053+
Ok(candidate) => candidates.push(candidate),
1054+
Err(NoSolution) => (),
1055+
}
1056+
});
1057+
}
1058+
1059+
if candidates.is_empty() {
1060+
candidates.extend(self.forced_ambiguity(MaybeCause::Ambiguity));
1061+
}
1062+
}
1063+
9461064
/// Assemble and merge candidates for goals which are related to an underlying trait
9471065
/// goal. Right now, this is normalizes-to and host effect goals.
9481066
///

compiler/rustc_next_trait_solver/src/solve/effect_goals.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ where
124124
ecx: &mut EvalCtxt<'_, D>,
125125
goal: Goal<I, Self>,
126126
impl_def_id: I::ImplId,
127+
finalize: impl FnOnce(&mut EvalCtxt<'_, D>, Certainty) -> QueryResult<I>,
127128
) -> Result<Candidate<I>, NoSolution> {
128129
let cx = ecx.cx();
129130

@@ -175,7 +176,7 @@ where
175176
});
176177
ecx.add_goals(GoalSource::ImplWhereBound, const_conditions);
177178

178-
ecx.evaluate_added_goals_and_make_canonical_response(certainty)
179+
finalize(ecx, certainty)
179180
})
180181
}
181182

compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1060,6 +1060,10 @@ where
10601060
self.delegate.resolve_vars_if_possible(value)
10611061
}
10621062

1063+
pub(super) fn shallow_resolve(&self, ty: I::Ty) -> I::Ty {
1064+
self.delegate.shallow_resolve(ty)
1065+
}
1066+
10631067
pub(super) fn eager_resolve_region(&self, r: I::Region) -> I::Region {
10641068
if let ty::ReVar(vid) = r.kind() {
10651069
self.delegate.opportunistic_resolve_lt_var(vid)
@@ -1176,6 +1180,33 @@ where
11761180
) -> bool {
11771181
may_use_unstable_feature(&**self.delegate, param_env, symbol)
11781182
}
1183+
1184+
pub(crate) fn find_sup_as_registered_opaque(
1185+
&self,
1186+
self_ty: I::Ty,
1187+
) -> impl Iterator<Item = ty::AliasTy<I>> + use<'a, D, I> {
1188+
let delegate = self.delegate;
1189+
delegate
1190+
.clone_opaque_types_lookup_table()
1191+
.into_iter()
1192+
.chain(delegate.clone_duplicate_opaque_types())
1193+
.filter_map(move |(key, hidden_ty)| {
1194+
if let ty::Infer(ty::TyVar(self_vid)) = self_ty.kind() {
1195+
if let ty::Infer(ty::TyVar(hidden_vid)) = hidden_ty.kind() {
1196+
if delegate.sub_unification_table_root_var(self_vid)
1197+
== delegate.sub_unification_table_root_var(hidden_vid)
1198+
{
1199+
return Some(ty::AliasTy::new_from_args(
1200+
delegate.cx(),
1201+
key.def_id.into(),
1202+
key.args,
1203+
));
1204+
}
1205+
}
1206+
}
1207+
None
1208+
})
1209+
}
11791210
}
11801211

11811212
/// Eagerly replace aliases with inference variables, emitting `AliasRelate`

compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@ where
194194
ecx: &mut EvalCtxt<'_, D>,
195195
goal: Goal<I, NormalizesTo<I>>,
196196
impl_def_id: I::ImplId,
197+
finalize: impl FnOnce(&mut EvalCtxt<'_, D>, Certainty) -> QueryResult<I>,
197198
) -> Result<Candidate<I>, NoSolution> {
198199
let cx = ecx.cx();
199200

@@ -314,8 +315,7 @@ where
314315
// nested goal for consistency.
315316
ty::TypingMode::Coherence => {
316317
ecx.add_goal(GoalSource::Misc, goal.with(cx, PredicateKind::Ambiguous));
317-
return ecx
318-
.evaluate_added_goals_and_make_canonical_response(Certainty::Yes);
318+
return finalize(ecx, Certainty::Yes);
319319
}
320320
ty::TypingMode::Analysis { .. }
321321
| ty::TypingMode::Borrowck { .. }
@@ -325,8 +325,7 @@ where
325325
goal,
326326
goal.predicate.alias,
327327
);
328-
return ecx
329-
.evaluate_added_goals_and_make_canonical_response(Certainty::Yes);
328+
return finalize(ecx, Certainty::Yes);
330329
}
331330
}
332331
} else {

compiler/rustc_next_trait_solver/src/solve/trait_goals.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ where
5555
ecx: &mut EvalCtxt<'_, D>,
5656
goal: Goal<I, TraitPredicate<I>>,
5757
impl_def_id: I::ImplId,
58+
finalize: impl FnOnce(&mut EvalCtxt<'_, D>, Certainty) -> QueryResult<I>,
5859
) -> Result<Candidate<I>, NoSolution> {
5960
let cx = ecx.cx();
6061

@@ -112,7 +113,7 @@ where
112113
.map(|pred| goal.with(cx, pred)),
113114
);
114115

115-
ecx.evaluate_added_goals_and_make_canonical_response(maximal_certainty)
116+
finalize(ecx, maximal_certainty)
116117
})
117118
}
118119

compiler/rustc_type_ir/src/interner.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,7 @@ pub trait Interner:
347347
self_ty: Self::Ty,
348348
f: impl FnMut(Self::ImplId),
349349
);
350+
fn for_each_blanket_impl(self, trait_def_id: Self::TraitId, f: impl FnMut(Self::ImplId));
350351

351352
fn has_item_definition(self, def_id: Self::DefId) -> bool;
352353

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/avoid-inference-constraints-from-blanket-2.rs:27:18
3+
|
4+
LL | let _: u32 = x;
5+
| --- ^ expected `u32`, found `u64`
6+
| |
7+
| expected due to this
8+
|
9+
help: you can convert a `u64` to a `u32` and panic if the converted value doesn't fit
10+
|
11+
LL | let _: u32 = x.try_into().unwrap();
12+
| ++++++++++++++++++++
13+
14+
error: aborting due to 1 previous error
15+
16+
For more information about this error, try `rustc --explain E0308`.
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//@ revisions: current next
2+
//@[next] compile-flags: -Znext-solver
3+
//@ ignore-compare-mode-next-solver (explicit revisions)
4+
//@[current] check-pass
5+
6+
// Regression test for trait-system-refactor-initiative#205. Avoid
7+
// constraining other impl arguments when applying blanket impls.
8+
9+
// FIXME(-Znext-solver): This currently incompletely constrains the
10+
// argument of `opaque: Trait<?x>` using the blanket impl of trait.
11+
// Ideally we don't do that.
12+
13+
trait Trait<T> {}
14+
15+
impl<T> Trait<u64> for T {}
16+
impl Trait<u32> for u64 {}
17+
18+
fn impls_trait<T: Trait<U>, U>(_: U) -> T {
19+
todo!()
20+
}
21+
22+
fn foo() -> impl Sized {
23+
let x = Default::default();
24+
if false {
25+
return impls_trait::<_, _>(x);
26+
}
27+
let _: u32 = x;
28+
//[next]~^ ERROR mismatched types
29+
1u64
30+
}
31+
fn main() {}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//@ revisions: current next
2+
//@[next] compile-flags: -Znext-solver
3+
//@ ignore-compare-mode-next-solver (explicit revisions)
4+
//@ check-pass
5+
6+
// Regression test for trait-system-refactor-initiative#205. Avoid constraining
7+
// the opaque type when applying blanket impls.
8+
9+
trait Trait<T> {}
10+
11+
impl<T> Trait<T> for T {}
12+
impl Trait<u32> for u64 {}
13+
14+
fn impls_trait<T: Trait<U>, U>() -> T {
15+
todo!()
16+
}
17+
18+
fn foo() -> impl Sized {
19+
if false {
20+
return impls_trait::<_, u32>();
21+
}
22+
1u64
23+
}
24+
fn main() {}

0 commit comments

Comments
 (0)