Skip to content

Commit

Permalink
Auto merge of rust-lang#120513 - compiler-errors:normalize-regions-fo…
Browse files Browse the repository at this point in the history
…r-nll, r=<try>

Normalize type outlives obligations in NLL for new solver

Normalize the type outlives assumptions and obligations in MIR borrowck. This should fix any of the lazy-norm-related MIR borrowck problems.

Also some cleanups from last PR:
1. Normalize obligations in a loop in lexical region resolution
2. Use `deeply_normalize_with_skipped_universes` in lexical resolution since we may have, e.g. `for<'a> Alias<'a>: 'b`.

r? lcnr
  • Loading branch information
bors committed Jan 30, 2024
2 parents cb4d9a1 + 870f77c commit 78d5ab6
Show file tree
Hide file tree
Showing 8 changed files with 188 additions and 68 deletions.
97 changes: 76 additions & 21 deletions compiler/rustc_borrowck/src/type_check/constraint_conversion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,15 @@ use rustc_infer::infer::outlives::obligations::{TypeOutlives, TypeOutlivesDelega
use rustc_infer::infer::region_constraints::{GenericKind, VerifyBound};
use rustc_infer::infer::{self, InferCtxt, SubregionOrigin};
use rustc_middle::mir::{ClosureOutlivesSubject, ClosureRegionRequirements, ConstraintCategory};
use rustc_middle::traits::ObligationCause;
use rustc_middle::ty::GenericArgKind;
use rustc_middle::ty::{self, TyCtxt};
use rustc_middle::ty::{TypeFoldable, TypeVisitableExt};
use rustc_span::{Span, DUMMY_SP};
use rustc_trait_selection::solve::deeply_normalize;
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
use rustc_trait_selection::traits::query::type_op::custom::CustomTypeOp;
use rustc_trait_selection::traits::query::type_op::{TypeOp, TypeOpOutput};

use crate::{
constraints::OutlivesConstraint,
Expand All @@ -33,6 +38,7 @@ pub(crate) struct ConstraintConversion<'a, 'tcx> {
/// our special inference variable there, we would mess that up.
region_bound_pairs: &'a RegionBoundPairs<'tcx>,
implicit_region_bound: ty::Region<'tcx>,
param_env: ty::ParamEnv<'tcx>,
known_type_outlives_obligations: &'tcx [ty::PolyTypeOutlivesPredicate<'tcx>],
locations: Locations,
span: Span,
Expand All @@ -47,6 +53,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
universal_regions: &'a UniversalRegions<'tcx>,
region_bound_pairs: &'a RegionBoundPairs<'tcx>,
implicit_region_bound: ty::Region<'tcx>,
param_env: ty::ParamEnv<'tcx>,
known_type_outlives_obligations: &'tcx [ty::PolyTypeOutlivesPredicate<'tcx>],
locations: Locations,
span: Span,
Expand All @@ -59,6 +66,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
universal_regions,
region_bound_pairs,
implicit_region_bound,
param_env,
known_type_outlives_obligations,
locations,
span,
Expand Down Expand Up @@ -137,36 +145,83 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
// Extract out various useful fields we'll need below.
let ConstraintConversion {
tcx,
infcx,
param_env,
region_bound_pairs,
implicit_region_bound,
known_type_outlives_obligations,
..
} = *self;

let ty::OutlivesPredicate(k1, r2) = predicate;
match k1.unpack() {
GenericArgKind::Lifetime(r1) => {
let r1_vid = self.to_region_vid(r1);
let r2_vid = self.to_region_vid(r2);
self.add_outlives(r1_vid, r2_vid, constraint_category);
}
let mut outlives_predicates = vec![(predicate, constraint_category)];
while let Some((ty::OutlivesPredicate(k1, r2), constraint_category)) =
outlives_predicates.pop()
{
match k1.unpack() {
GenericArgKind::Lifetime(r1) => {
let r1_vid = self.to_region_vid(r1);
let r2_vid = self.to_region_vid(r2);
self.add_outlives(r1_vid, r2_vid, constraint_category);
}

GenericArgKind::Type(t1) => {
// we don't actually use this for anything, but
// the `TypeOutlives` code needs an origin.
let origin = infer::RelateParamBound(DUMMY_SP, t1, None);
GenericArgKind::Type(mut t1) => {
// Normalize the type we receive from a `TypeOutlives` obligation
// in the new trait solver.
if infcx.next_trait_solver() {
let result = CustomTypeOp::new(
|ocx| {
match deeply_normalize(
ocx.infcx.at(
&ObligationCause::dummy_with_span(self.span),
param_env,
),
t1,
) {
Ok(normalized_ty) => {
t1 = normalized_ty;
}
Err(e) => {
infcx.err_ctxt().report_fulfillment_errors(e);
}
}

TypeOutlives::new(
&mut *self,
tcx,
region_bound_pairs,
Some(implicit_region_bound),
known_type_outlives_obligations,
)
.type_must_outlive(origin, t1, r2, constraint_category);
}
Ok(())
},
"normalize type outlives obligation",
)
.fully_perform(infcx, self.span);

match result {
Ok(TypeOpOutput { output: (), constraints, .. }) => {
if let Some(constraints) = constraints {
assert!(
constraints.member_constraints.is_empty(),
"FIXME(-Znext-solver): How do I handle these?"
);
outlives_predicates
.extend(constraints.outlives.iter().copied());
}
}
Err(_) => {}
}
}

GenericArgKind::Const(_) => unreachable!(),
// we don't actually use this for anything, but
// the `TypeOutlives` code needs an origin.
let origin = infer::RelateParamBound(DUMMY_SP, t1, None);

TypeOutlives::new(
&mut *self,
tcx,
region_bound_pairs,
Some(implicit_region_bound),
known_type_outlives_obligations,
)
.type_must_outlive(origin, t1, r2, constraint_category);
}

GenericArgKind::Const(_) => unreachable!(),
}
}
}

Expand Down
72 changes: 47 additions & 25 deletions compiler/rustc_borrowck/src/type_check/free_region_relations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@ use rustc_infer::infer::region_constraints::GenericKind;
use rustc_infer::infer::InferCtxt;
use rustc_middle::mir::ConstraintCategory;
use rustc_middle::traits::query::OutlivesBound;
use rustc_middle::traits::ObligationCause;
use rustc_middle::ty::{self, RegionVid, Ty, TypeVisitableExt};
use rustc_span::{ErrorGuaranteed, Span, DUMMY_SP};
use rustc_span::{ErrorGuaranteed, DUMMY_SP};
use rustc_trait_selection::solve::deeply_normalize_with_skipped_universes;
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
use rustc_trait_selection::traits::query::type_op::{self, TypeOp};
use std::rc::Rc;
use type_op::TypeOpOutput;
Expand Down Expand Up @@ -52,15 +55,13 @@ pub(crate) struct CreateResult<'tcx> {
pub(crate) fn create<'tcx>(
infcx: &InferCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
known_type_outlives_obligations: &'tcx [ty::PolyTypeOutlivesPredicate<'tcx>],
implicit_region_bound: ty::Region<'tcx>,
universal_regions: &Rc<UniversalRegions<'tcx>>,
constraints: &mut MirTypeckRegionConstraints<'tcx>,
) -> CreateResult<'tcx> {
UniversalRegionRelationsBuilder {
infcx,
param_env,
known_type_outlives_obligations,
implicit_region_bound,
constraints,
universal_regions: universal_regions.clone(),
Expand Down Expand Up @@ -178,7 +179,6 @@ impl UniversalRegionRelations<'_> {
struct UniversalRegionRelationsBuilder<'this, 'tcx> {
infcx: &'this InferCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
known_type_outlives_obligations: &'tcx [ty::PolyTypeOutlivesPredicate<'tcx>],
universal_regions: Rc<UniversalRegions<'tcx>>,
implicit_region_bound: ty::Region<'tcx>,
constraints: &'this mut MirTypeckRegionConstraints<'tcx>,
Expand Down Expand Up @@ -222,6 +222,35 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
self.relate_universal_regions(fr, fr_fn_body);
}

// Normalize the assumptions we use to borrowck the program.
let mut constraints = vec![];
let mut known_type_outlives_obligations = vec![];
for bound in param_env.caller_bounds() {
let Some(outlives) = bound.as_type_outlives_clause() else { continue };
let ty::OutlivesPredicate(mut ty, region) = outlives.skip_binder();

// In the new solver, normalize the type-outlives obligation assumptions.
if self.infcx.next_trait_solver() {
match deeply_normalize_with_skipped_universes(
self.infcx.at(&ObligationCause::misc(span, defining_ty_def_id), self.param_env),
ty,
vec![None; ty.outer_exclusive_binder().as_usize()],
) {
Ok(normalized_ty) => {
ty = normalized_ty;
}
Err(e) => {
self.infcx.err_ctxt().report_fulfillment_errors(e);
}
}
}

known_type_outlives_obligations
.push(outlives.rebind(ty::OutlivesPredicate(ty, region)));
}
let known_type_outlives_obligations =
self.infcx.tcx.arena.alloc_slice(&known_type_outlives_obligations);

let unnormalized_input_output_tys = self
.universal_regions
.unnormalized_input_tys
Expand All @@ -239,7 +268,6 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
// the `relations` is built.
let mut normalized_inputs_and_output =
Vec::with_capacity(self.universal_regions.unnormalized_input_tys.len() + 1);
let mut constraints = vec![];
for ty in unnormalized_input_output_tys {
debug!("build: input_or_output={:?}", ty);
// We add implied bounds from both the unnormalized and normalized ty.
Expand Down Expand Up @@ -304,7 +332,19 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
}

for c in constraints {
self.push_region_constraints(c, span);
constraint_conversion::ConstraintConversion::new(
self.infcx,
&self.universal_regions,
&self.region_bound_pairs,
self.implicit_region_bound,
param_env,
known_type_outlives_obligations,
Locations::All(span),
span,
ConstraintCategory::Internal,
self.constraints,
)
.convert_all(c);
}

CreateResult {
Expand All @@ -313,30 +353,12 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
outlives: self.outlives.freeze(),
inverse_outlives: self.inverse_outlives.freeze(),
}),
known_type_outlives_obligations: self.known_type_outlives_obligations,
known_type_outlives_obligations,
region_bound_pairs: self.region_bound_pairs,
normalized_inputs_and_output,
}
}

#[instrument(skip(self, data), level = "debug")]
fn push_region_constraints(&mut self, data: &QueryRegionConstraints<'tcx>, span: Span) {
debug!("constraints generated: {:#?}", data);

constraint_conversion::ConstraintConversion::new(
self.infcx,
&self.universal_regions,
&self.region_bound_pairs,
self.implicit_region_bound,
self.known_type_outlives_obligations,
Locations::All(span),
span,
ConstraintCategory::Internal,
self.constraints,
)
.convert_all(data);
}

/// Update the type of a single local, which should represent
/// either the return type of the MIR or one of its arguments. At
/// the same time, compute and add any implied bounds that come
Expand Down
6 changes: 2 additions & 4 deletions compiler/rustc_borrowck/src/type_check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,10 +156,6 @@ pub(crate) fn type_check<'mir, 'tcx>(
} = free_region_relations::create(
infcx,
param_env,
// FIXME(-Znext-solver): These are unnormalized. Normalize them.
infcx.tcx.arena.alloc_from_iter(
param_env.caller_bounds().iter().filter_map(|clause| clause.as_type_outlives_clause()),
),
implicit_region_bound,
universal_regions,
&mut constraints,
Expand Down Expand Up @@ -1136,6 +1132,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
self.borrowck_context.universal_regions,
self.region_bound_pairs,
self.implicit_region_bound,
self.param_env,
self.known_type_outlives_obligations,
locations,
locations.span(self.body),
Expand Down Expand Up @@ -2740,6 +2737,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
self.borrowck_context.universal_regions,
self.region_bound_pairs,
self.implicit_region_bound,
self.param_env,
self.known_type_outlives_obligations,
locations,
DUMMY_SP, // irrelevant; will be overridden.
Expand Down
38 changes: 22 additions & 16 deletions compiler/rustc_infer/src/infer/outlives/obligations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,22 +153,28 @@ impl<'tcx> InferCtxt<'tcx> {
.try_collect()
.map_err(|e| (e, SubregionOrigin::AscribeUserTypeProvePredicate(DUMMY_SP)))?;

let my_region_obligations = self.take_registered_region_obligations();

for RegionObligation { sup_type, sub_region, origin } in my_region_obligations {
let sup_type =
deeply_normalize_ty(sup_type, origin.clone()).map_err(|e| (e, origin.clone()))?;
debug!(?sup_type, ?sub_region, ?origin);

let outlives = &mut TypeOutlives::new(
self,
self.tcx,
outlives_env.region_bound_pairs(),
None,
&normalized_caller_bounds,
);
let category = origin.to_constraint_category();
outlives.type_must_outlive(origin, sup_type, sub_region, category);
// Must loop since the process of normalizing may itself register region obligations.
loop {
let my_region_obligations = self.take_registered_region_obligations();
if my_region_obligations.is_empty() {
break;
}

for RegionObligation { sup_type, sub_region, origin } in my_region_obligations {
let sup_type = deeply_normalize_ty(sup_type, origin.clone())
.map_err(|e| (e, origin.clone()))?;
debug!(?sup_type, ?sub_region, ?origin);

let outlives = &mut TypeOutlives::new(
self,
self.tcx,
outlives_env.region_bound_pairs(),
None,
&normalized_caller_bounds,
);
let category = origin.to_constraint_category();
outlives.type_must_outlive(origin, sup_type, sub_region, category);
}
}

Ok(())
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_trait_selection/src/regions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,13 @@ impl<'tcx> InferCtxtRegionExt<'tcx> for InferCtxt<'tcx> {
let ty = self.resolve_vars_if_possible(ty);

if self.next_trait_solver() {
crate::solve::deeply_normalize(
crate::solve::deeply_normalize_with_skipped_universes(
self.at(
&ObligationCause::dummy_with_span(origin.span()),
outlives_env.param_env,
),
ty,
vec![None; ty.outer_exclusive_binder().as_usize()],
)
.map_err(|_| ty)
} else {
Expand Down
9 changes: 8 additions & 1 deletion tests/ui/traits/next-solver/normalize-region-obligations.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// revisions: normalize_param_env normalize_obligation
// revisions: normalize_param_env normalize_obligation hrtb
// check-pass
// compile-flags: -Znext-solver

Expand All @@ -7,16 +7,23 @@ trait Foo {
type Gat<'a> where <Self as Mirror>::Assoc: 'a;
#[cfg(normalize_obligation)]
type Gat<'a> where Self: 'a;
#[cfg(hrtb)]
type Gat<'b> where for<'a> <Self as MirrorRegion<'a>>::Assoc: 'b;
}

trait Mirror { type Assoc: ?Sized; }
impl<T: ?Sized> Mirror for T { type Assoc = T; }

trait MirrorRegion<'a> { type Assoc: ?Sized; }
impl<'a, T> MirrorRegion<'a> for T { type Assoc = T; }

impl<T> Foo for T {
#[cfg(normalize_param_env)]
type Gat<'a> = i32 where T: 'a;
#[cfg(normalize_obligation)]
type Gat<'a> = i32 where <T as Mirror>::Assoc: 'a;
#[cfg(hrtb)]
type Gat<'b> = i32 where Self: 'b;
}

fn main() {}
Loading

0 comments on commit 78d5ab6

Please sign in to comment.