Skip to content

Commit f69d672

Browse files
committed
generalize: handle occurs check failure in aliases
1 parent 2d0ec17 commit f69d672

File tree

5 files changed

+124
-33
lines changed

5 files changed

+124
-33
lines changed

compiler/rustc_infer/src/infer/combine.rs

+46-14
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,10 @@ impl<'tcx> InferCtxt<'tcx> {
334334
ty::Variance::Invariant,
335335
)?;
336336

337+
// FIXME(generic_const_exprs): Occurs check failures for unevaluated
338+
// constants and generic expressions are not yet handled correctly.
339+
let value = value.may_be_infer();
340+
337341
self.inner.borrow_mut().const_unification_table().union_value(
338342
target_vid,
339343
ConstVarValue {
@@ -445,7 +449,7 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
445449
// `'?2` and `?3` are fresh region/type inference
446450
// variables. (Down below, we will relate `a_ty <: b_ty`,
447451
// adding constraints like `'x: '?2` and `?1 <: ?3`.)
448-
let Generalization { value: b_ty, needs_wf } = generalize::generalize(
452+
let Generalization { value, needs_wf } = generalize::generalize(
449453
self.infcx,
450454
&mut CombineDelegate {
451455
infcx: self.infcx,
@@ -457,7 +461,7 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
457461
ambient_variance,
458462
)?;
459463

460-
debug!(?b_ty);
464+
let b_ty = value.may_be_infer(); // we handle this further down.
461465
self.infcx.inner.borrow_mut().type_variables().instantiate(b_vid, b_ty);
462466

463467
if needs_wf {
@@ -477,19 +481,47 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
477481
// relations wind up attributed to the same spans. We need
478482
// to associate causes/spans with each of the relations in
479483
// the stack to get this right.
480-
match ambient_variance {
481-
ty::Variance::Invariant => self.equate(a_is_expected).relate(a_ty, b_ty),
482-
ty::Variance::Covariant => self.sub(a_is_expected).relate(a_ty, b_ty),
483-
ty::Variance::Contravariant => self.sub(a_is_expected).relate_with_variance(
484-
ty::Contravariant,
485-
ty::VarianceDiagInfo::default(),
486-
a_ty,
487-
b_ty,
488-
),
489-
ty::Variance::Bivariant => {
490-
unreachable!("no code should be generalizing bivariantly (currently)")
484+
if b_ty.is_ty_var() {
485+
// This happens for cases like `<?0 as Trait>::Assoc == ?0`.
486+
// We can't instantiate `?0` here as that would result in a
487+
// cyclic type. We instead delay the unification in case
488+
// the alias can be normalized to something which does not
489+
// mention `?0`.
490+
491+
// FIXME(-Ztrait-solver=next): replace this with `AliasRelate`
492+
let &ty::Alias(kind, data) = a_ty.kind() else {
493+
bug!("generalization should only result in infer vars for aliases");
494+
};
495+
if !self.infcx.next_trait_solver() {
496+
// The old solver only accepts projection predicates for associated types.
497+
match kind {
498+
ty::AliasKind::Projection => {}
499+
ty::AliasKind::Inherent | ty::AliasKind::Weak | ty::AliasKind::Opaque => {
500+
return Err(TypeError::CyclicTy(a_ty));
501+
}
502+
}
491503
}
492-
}?;
504+
self.obligations.push(Obligation::new(
505+
self.tcx(),
506+
self.trace.cause.clone(),
507+
self.param_env,
508+
ty::ProjectionPredicate { projection_ty: data, term: b_ty.into() },
509+
))
510+
} else {
511+
match ambient_variance {
512+
ty::Variance::Invariant => self.equate(a_is_expected).relate(a_ty, b_ty),
513+
ty::Variance::Covariant => self.sub(a_is_expected).relate(a_ty, b_ty),
514+
ty::Variance::Contravariant => self.sub(a_is_expected).relate_with_variance(
515+
ty::Contravariant,
516+
ty::VarianceDiagInfo::default(),
517+
a_ty,
518+
b_ty,
519+
),
520+
ty::Variance::Bivariant => {
521+
unreachable!("no code should be generalizing bivariantly (currently)")
522+
}
523+
}?;
524+
}
493525

494526
Ok(())
495527
}

compiler/rustc_infer/src/infer/generalize.rs

+68-8
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
1+
use std::mem;
2+
13
use rustc_data_structures::sso::SsoHashMap;
24
use rustc_hir::def_id::DefId;
35
use rustc_middle::infer::unify_key::{ConstVarValue, ConstVariableValue};
46
use rustc_middle::ty::error::TypeError;
57
use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation};
6-
use rustc_middle::ty::{self, InferConst, Term, Ty, TyCtxt, TypeVisitableExt};
8+
use rustc_middle::ty::visit::MaxUniverse;
9+
use rustc_middle::ty::{self, InferConst, Term, Ty, TyCtxt, TypeVisitable, TypeVisitableExt};
710
use rustc_span::Span;
811

912
use crate::infer::nll_relate::TypeRelatingDelegate;
10-
use crate::infer::type_variable::TypeVariableValue;
13+
use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind, TypeVariableValue};
1114
use crate::infer::{InferCtxt, RegionVariableOrigin};
1215

1316
/// Attempts to generalize `term` for the type variable `for_vid`.
@@ -38,27 +41,30 @@ pub(super) fn generalize<'tcx, D: GeneralizerDelegate<'tcx>, T: Into<Term<'tcx>>
3841
root_vid,
3942
for_universe,
4043
root_term: term.into(),
44+
in_alias: false,
4145
needs_wf: false,
4246
cache: Default::default(),
4347
};
4448

4549
assert!(!term.has_escaping_bound_vars());
4650
let value = generalizer.relate(term, term)?;
4751
let needs_wf = generalizer.needs_wf;
48-
Ok(Generalization { value, needs_wf })
52+
Ok(Generalization { value: HandleProjection(value), needs_wf })
4953
}
5054

5155
/// Abstracts the handling of region vars between HIR and MIR/NLL typechecking
5256
/// in the generalizer code.
53-
pub trait GeneralizerDelegate<'tcx> {
57+
pub(super) trait GeneralizerDelegate<'tcx> {
5458
fn param_env(&self) -> ty::ParamEnv<'tcx>;
5559

5660
fn forbid_inference_vars() -> bool;
5761

62+
fn span(&self) -> Span;
63+
5864
fn generalize_region(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx>;
5965
}
6066

61-
pub struct CombineDelegate<'cx, 'tcx> {
67+
pub(super) struct CombineDelegate<'cx, 'tcx> {
6268
pub infcx: &'cx InferCtxt<'tcx>,
6369
pub param_env: ty::ParamEnv<'tcx>,
6470
pub span: Span,
@@ -73,6 +79,10 @@ impl<'tcx> GeneralizerDelegate<'tcx> for CombineDelegate<'_, 'tcx> {
7379
false
7480
}
7581

82+
fn span(&self) -> Span {
83+
self.span
84+
}
85+
7686
fn generalize_region(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx> {
7787
// FIXME: This is non-ideal because we don't give a
7888
// very descriptive origin for this region variable.
@@ -93,6 +103,10 @@ where
93103
<Self as TypeRelatingDelegate<'tcx>>::forbid_inference_vars()
94104
}
95105

106+
fn span(&self) -> Span {
107+
<Self as TypeRelatingDelegate<'tcx>>::span(&self)
108+
}
109+
96110
fn generalize_region(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx> {
97111
<Self as TypeRelatingDelegate<'tcx>>::generalize_existential(self, universe)
98112
}
@@ -139,6 +153,12 @@ struct Generalizer<'me, 'tcx, D> {
139153

140154
cache: SsoHashMap<Ty<'tcx>, Ty<'tcx>>,
141155

156+
/// This is set once we're generalizing the arguments of an alias. In case
157+
/// we encounter an occurs check failure we generalize the alias to an
158+
/// inference variable instead of erroring. This is necessary to avoid
159+
/// incorrect errors when relating `?0` with `<?0 as Trait>::Assoc`.
160+
in_alias: bool,
161+
142162
/// See the field `needs_wf` in `Generalization`.
143163
needs_wf: bool,
144164
}
@@ -309,6 +329,38 @@ where
309329
}
310330
}
311331

332+
ty::Alias(kind, data) => {
333+
let is_nested_alias = mem::replace(&mut self.in_alias, true);
334+
let result = match self.relate(data, data) {
335+
Ok(data) => Ok(Ty::new_alias(self.tcx(), kind, data)),
336+
Err(e) => {
337+
if is_nested_alias {
338+
return Err(e);
339+
} else {
340+
let mut visitor = MaxUniverse::new();
341+
t.visit_with(&mut visitor);
342+
let infer_replacement_is_complete =
343+
self.for_universe.can_name(visitor.max_universe())
344+
&& !t.has_escaping_bound_vars();
345+
if !infer_replacement_is_complete {
346+
warn!("incomplete generalization of an alias type: {t:?}");
347+
}
348+
349+
debug!("generalization failure in alias");
350+
Ok(self.infcx.next_ty_var_in_universe(
351+
TypeVariableOrigin {
352+
kind: TypeVariableOriginKind::MiscVariable,
353+
span: self.delegate.span(),
354+
},
355+
self.for_universe,
356+
))
357+
}
358+
}
359+
};
360+
self.in_alias = is_nested_alias;
361+
result
362+
}
363+
312364
_ => relate::structurally_relate_tys(self, t, t),
313365
}?;
314366

@@ -452,12 +504,20 @@ where
452504
}
453505
}
454506

507+
#[derive(Debug)]
508+
pub(super) struct HandleProjection<T>(T);
509+
impl<T> HandleProjection<T> {
510+
pub(super) fn may_be_infer(self) -> T {
511+
self.0
512+
}
513+
}
514+
455515
/// Result from a generalization operation. This includes
456516
/// not only the generalized type, but also a bool flag
457517
/// indicating whether further WF checks are needed.
458518
#[derive(Debug)]
459-
pub struct Generalization<T> {
460-
pub value: T,
519+
pub(super) struct Generalization<T> {
520+
pub(super) value: HandleProjection<T>,
461521

462522
/// If true, then the generalized type may not be well-formed,
463523
/// even if the source type is well-formed, so we should add an
@@ -484,5 +544,5 @@ pub struct Generalization<T> {
484544
/// will force the calling code to check that `WF(Foo<?C, ?D>)`
485545
/// holds, which in turn implies that `?C::Item == ?D`. So once
486546
/// `?C` is constrained, that should suffice to restrict `?D`.
487-
pub needs_wf: bool,
547+
pub(super) needs_wf: bool,
488548
}

compiler/rustc_infer/src/infer/nll_relate/mod.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -214,13 +214,18 @@ where
214214
}
215215

216216
fn generalize(&mut self, ty: Ty<'tcx>, for_vid: ty::TyVid) -> RelateResult<'tcx, Ty<'tcx>> {
217-
let Generalization { value: ty, needs_wf: _ } = generalize::generalize(
217+
let Generalization { value, needs_wf: _ } = generalize::generalize(
218218
self.infcx,
219219
&mut self.delegate,
220220
ty,
221221
for_vid,
222222
self.ambient_variance,
223223
)?;
224+
225+
let ty = value.may_be_infer();
226+
if ty.is_ty_var() {
227+
warn!("occurs check failure in MIR typeck");
228+
}
224229
Ok(ty)
225230
}
226231

tests/ui/traits/new-solver/equating-projection-cyclically.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// check-pass
12
// compile-flags: -Ztrait-solver=next
23

34
trait Test {
@@ -22,7 +23,9 @@ fn main() {
2223
let mut x: Inv<_> = Inv(None);
2324
// This ends up equating `Inv<?x>` with `Inv<<?x as Test>::Assoc>`
2425
// which fails the occurs check when generalizing `?x`.
26+
//
27+
// We end up emitting a delayed obligation, causing this to still
28+
// succeed.
2529
x = transform(x);
26-
//~^ ERROR mismatched types
2730
x = Inv::<i32>(None);
2831
}

tests/ui/traits/new-solver/equating-projection-cyclically.stderr

-9
This file was deleted.

0 commit comments

Comments
 (0)