Normalize type outlives obligations in NLL

This commit is contained in:
Michael Goulet 2024-01-30 21:50:05 +00:00
parent 7576b77e50
commit 7d1fda7b40
5 changed files with 158 additions and 52 deletions

View file

@ -5,10 +5,15 @@
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,
@ -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,
@ -47,6 +53,7 @@ pub(crate) fn new(
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,
@ -59,6 +66,7 @@ pub(crate) fn new(
universal_regions,
region_bound_pairs,
implicit_region_bound,
param_env,
known_type_outlives_obligations,
locations,
span,
@ -137,36 +145,83 @@ fn convert(
// 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(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);
}
}
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(_) => {}
}
}
// 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!(),
}
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);
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!(),
}
}

View file

@ -8,8 +8,11 @@
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;
@ -52,7 +55,6 @@ 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>,
@ -60,7 +62,6 @@ pub(crate) fn create<'tcx>(
UniversalRegionRelationsBuilder {
infcx,
param_env,
known_type_outlives_obligations,
implicit_region_bound,
constraints,
universal_regions: universal_regions.clone(),
@ -178,7 +179,6 @@ pub(crate) fn known_outlives(&self) -> impl Iterator<Item = (RegionVid, RegionVi
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>,
@ -222,6 +222,35 @@ pub(crate) fn create(mut self) -> CreateResult<'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
@ -239,7 +268,6 @@ pub(crate) fn create(mut self) -> CreateResult<'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.
@ -304,7 +332,19 @@ pub(crate) fn create(mut self) -> CreateResult<'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 {
@ -313,30 +353,12 @@ pub(crate) fn create(mut self) -> CreateResult<'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

View file

@ -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,
@ -1136,6 +1132,7 @@ fn push_region_constraints(
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),
@ -2740,6 +2737,7 @@ fn prove_closure_bounds(
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.

View file

@ -0,0 +1,18 @@
// check-pass
// compile-flags: -Znext-solver
trait Mirror {
type Assoc;
}
impl<T> Mirror for T {
type Assoc = T;
}
fn is_static<T: 'static>() {}
fn test<T>() where <T as Mirror>::Assoc: 'static {
is_static::<T>();
}
fn main() {}

View file

@ -0,0 +1,13 @@
// check-pass
trait Tr<'a> {
type Assoc;
}
fn outlives<'o, T: 'o>() {}
fn foo<'a, 'b, T: Tr<'a, Assoc = ()>>() {
outlives::<'b, T::Assoc>();
}
fn main() {}