Skip to content

Commit

Permalink
Auto merge of #119101 - compiler-errors:outlives, r=lcnr
Browse files Browse the repository at this point in the history
Normalize region obligation in lexical region resolution with next-gen solver

This normalizes region obligations when we `resolve_regions`, since they may be unnormalized with deferred projection equality.

It's pretty hard to add tests that exercise this without also triggering MIR borrowck errors (because we don't normalize there yet). I've added one test with two revisions that should test that we both 1. normalize region obligations in the param env, and 2. normalize registered region obligations during lexical region resolution.
  • Loading branch information
bors committed Jan 30, 2024
2 parents f3d71c9 + 720d7a7 commit cb4d9a1
Show file tree
Hide file tree
Showing 25 changed files with 211 additions and 70 deletions.
14 changes: 9 additions & 5 deletions compiler/rustc_borrowck/src/type_check/constraint_conversion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,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,
category: ConstraintCategory<'tcx>,
Expand All @@ -47,7 +47,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,
category: ConstraintCategory<'tcx>,
Expand All @@ -59,7 +59,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
universal_regions,
region_bound_pairs,
implicit_region_bound,
param_env,
known_type_outlives_obligations,
locations,
span,
category,
Expand Down Expand Up @@ -136,7 +136,11 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {

// Extract out various useful fields we'll need below.
let ConstraintConversion {
tcx, region_bound_pairs, implicit_region_bound, param_env, ..
tcx,
region_bound_pairs,
implicit_region_bound,
known_type_outlives_obligations,
..
} = *self;

let ty::OutlivesPredicate(k1, r2) = predicate;
Expand All @@ -157,7 +161,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
tcx,
region_bound_pairs,
Some(implicit_region_bound),
param_env,
known_type_outlives_obligations,
)
.type_must_outlive(origin, t1, r2, constraint_category);
}
Expand Down
10 changes: 8 additions & 2 deletions compiler/rustc_borrowck/src/type_check/free_region_relations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,19 +45,22 @@ type NormalizedInputsAndOutput<'tcx> = Vec<Ty<'tcx>>;
pub(crate) struct CreateResult<'tcx> {
pub(crate) universal_region_relations: Frozen<UniversalRegionRelations<'tcx>>,
pub(crate) region_bound_pairs: RegionBoundPairs<'tcx>,
pub(crate) known_type_outlives_obligations: &'tcx [ty::PolyTypeOutlivesPredicate<'tcx>],
pub(crate) normalized_inputs_and_output: NormalizedInputsAndOutput<'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 @@ -175,6 +178,7 @@ 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 All @@ -200,7 +204,8 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
let defining_ty_def_id = self.universal_regions.defining_ty.def_id().expect_local();
let span = tcx.def_span(defining_ty_def_id);

// Insert the facts we know from the predicates. Why? Why not.
// Insert the `'a: 'b` we know from the predicates.
// This does not consider the type-outlives.
let param_env = self.param_env;
self.add_outlives_bounds(outlives::explicit_outlives_bounds(param_env));

Expand Down Expand Up @@ -308,6 +313,7 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
outlives: self.outlives.freeze(),
inverse_outlives: self.inverse_outlives.freeze(),
}),
known_type_outlives_obligations: self.known_type_outlives_obligations,
region_bound_pairs: self.region_bound_pairs,
normalized_inputs_and_output,
}
Expand All @@ -322,7 +328,7 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
&self.universal_regions,
&self.region_bound_pairs,
self.implicit_region_bound,
self.param_env,
self.known_type_outlives_obligations,
Locations::All(span),
span,
ConstraintCategory::Internal,
Expand Down
13 changes: 11 additions & 2 deletions compiler/rustc_borrowck/src/type_check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,9 +152,14 @@ pub(crate) fn type_check<'mir, 'tcx>(
universal_region_relations,
region_bound_pairs,
normalized_inputs_and_output,
known_type_outlives_obligations,
} = 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 All @@ -176,6 +181,7 @@ pub(crate) fn type_check<'mir, 'tcx>(
body,
param_env,
&region_bound_pairs,
known_type_outlives_obligations,
implicit_region_bound,
&mut borrowck_context,
);
Expand Down Expand Up @@ -850,6 +856,7 @@ struct TypeChecker<'a, 'tcx> {
/// all of the promoted items.
user_type_annotations: &'a CanonicalUserTypeAnnotations<'tcx>,
region_bound_pairs: &'a RegionBoundPairs<'tcx>,
known_type_outlives_obligations: &'tcx [ty::PolyTypeOutlivesPredicate<'tcx>],
implicit_region_bound: ty::Region<'tcx>,
reported_errors: FxIndexSet<(Ty<'tcx>, Span)>,
borrowck_context: &'a mut BorrowCheckContext<'a, 'tcx>,
Expand Down Expand Up @@ -1000,6 +1007,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
body: &'a Body<'tcx>,
param_env: ty::ParamEnv<'tcx>,
region_bound_pairs: &'a RegionBoundPairs<'tcx>,
known_type_outlives_obligations: &'tcx [ty::PolyTypeOutlivesPredicate<'tcx>],
implicit_region_bound: ty::Region<'tcx>,
borrowck_context: &'a mut BorrowCheckContext<'a, 'tcx>,
) -> Self {
Expand All @@ -1010,6 +1018,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
user_type_annotations: &body.user_type_annotations,
param_env,
region_bound_pairs,
known_type_outlives_obligations,
implicit_region_bound,
borrowck_context,
reported_errors: Default::default(),
Expand Down Expand Up @@ -1127,7 +1136,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),
category,
Expand Down Expand Up @@ -2731,7 +2740,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.
ConstraintCategory::Boring, // same as above.
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use rustc_middle::ty::{
};
use rustc_middle::ty::{GenericParamDefKind, TyCtxt};
use rustc_span::Span;
use rustc_trait_selection::regions::InferCtxtRegionExt;
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _;
use rustc_trait_selection::traits::{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use rustc_middle::ty::{
self, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperVisitable, TypeVisitable, TypeVisitor,
};
use rustc_span::Span;
use rustc_trait_selection::regions::InferCtxtRegionExt;
use rustc_trait_selection::traits::{
elaborate, normalize_param_env_or_error, outlives_bounds::InferCtxtExt, ObligationCtxt,
};
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_hir_analysis/src/check/dropck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use rustc_infer::infer::{RegionResolutionError, TyCtxtInferExt};
use rustc_middle::ty::util::CheckRegions;
use rustc_middle::ty::GenericArgsRef;
use rustc_middle::ty::{self, TyCtxt};
use rustc_trait_selection::regions::InferCtxtRegionExt;
use rustc_trait_selection::traits::{self, ObligationCtxt};

use crate::errors;
Expand Down Expand Up @@ -188,6 +189,7 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>(
RegionResolutionError::UpperBoundUniverseConflict(a, _, _, _, b) => {
format!("{b}: {a}", a = ty::Region::new_var(tcx, a))
}
RegionResolutionError::CannotNormalize(..) => unreachable!(),
};
guar = Some(
struct_span_code_err!(
Expand Down
37 changes: 15 additions & 22 deletions compiler/rustc_hir_analysis/src/check/wfcheck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,8 @@ use rustc_hir as hir;
use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId};
use rustc_hir::lang_items::LangItem;
use rustc_hir::ItemKind;
use rustc_infer::infer::outlives::env::{OutlivesEnvironment, RegionBoundPairs};
use rustc_infer::infer::outlives::obligations::TypeOutlives;
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt};
use rustc_middle::mir::ConstraintCategory;
use rustc_middle::query::Providers;
use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::trait_def::TraitSpecializationKind;
Expand All @@ -26,6 +24,7 @@ use rustc_session::parse::feature_err;
use rustc_span::symbol::{sym, Ident, Symbol};
use rustc_span::{Span, DUMMY_SP};
use rustc_target::spec::abi::Abi;
use rustc_trait_selection::regions::InferCtxtRegionExt;
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
use rustc_trait_selection::traits::misc::{
type_allowed_to_implement_const_param_ty, ConstParamTyImplementationError,
Expand Down Expand Up @@ -731,10 +730,12 @@ fn ty_known_to_outlive<'tcx>(
ty: Ty<'tcx>,
region: ty::Region<'tcx>,
) -> bool {
resolve_regions_with_wf_tys(tcx, id, param_env, wf_tys, |infcx, region_bound_pairs| {
let origin = infer::RelateParamBound(DUMMY_SP, ty, None);
let outlives = &mut TypeOutlives::new(infcx, tcx, region_bound_pairs, None, param_env);
outlives.type_must_outlive(origin, ty, region, ConstraintCategory::BoringNoLocation);
test_region_obligations(tcx, id, param_env, wf_tys, |infcx| {
infcx.register_region_obligation(infer::RegionObligation {
sub_region: region,
sup_type: ty,
origin: infer::RelateParamBound(DUMMY_SP, ty, None),
});
})
}

Expand All @@ -748,40 +749,32 @@ fn region_known_to_outlive<'tcx>(
region_a: ty::Region<'tcx>,
region_b: ty::Region<'tcx>,
) -> bool {
resolve_regions_with_wf_tys(tcx, id, param_env, wf_tys, |mut infcx, _| {
use rustc_infer::infer::outlives::obligations::TypeOutlivesDelegate;
let origin = infer::RelateRegionParamBound(DUMMY_SP);
// `region_a: region_b` -> `region_b <= region_a`
infcx.push_sub_region_constraint(
origin,
region_b,
region_a,
ConstraintCategory::BoringNoLocation,
);
test_region_obligations(tcx, id, param_env, wf_tys, |infcx| {
infcx.sub_regions(infer::RelateRegionParamBound(DUMMY_SP), region_b, region_a);
})
}

/// Given a known `param_env` and a set of well formed types, set up an
/// `InferCtxt`, call the passed function (to e.g. set up region constraints
/// to be tested), then resolve region and return errors
fn resolve_regions_with_wf_tys<'tcx>(
fn test_region_obligations<'tcx>(
tcx: TyCtxt<'tcx>,
id: LocalDefId,
param_env: ty::ParamEnv<'tcx>,
wf_tys: &FxIndexSet<Ty<'tcx>>,
add_constraints: impl for<'a> FnOnce(&'a InferCtxt<'tcx>, &'a RegionBoundPairs<'tcx>),
add_constraints: impl FnOnce(&InferCtxt<'tcx>),
) -> bool {
// Unfortunately, we have to use a new `InferCtxt` each call, because
// region constraints get added and solved there and we need to test each
// call individually.
let infcx = tcx.infer_ctxt().build();

add_constraints(&infcx);

let outlives_environment = OutlivesEnvironment::with_bounds(
param_env,
infcx.implied_bounds_tys(param_env, id, wf_tys),
);
let region_bound_pairs = outlives_environment.region_bound_pairs();

add_constraints(&infcx, region_bound_pairs);

let errors = infcx.resolve_regions(&outlives_environment);
debug!(?errors, "errors");
Expand Down
11 changes: 10 additions & 1 deletion compiler/rustc_infer/src/infer/error_reporting/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,13 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {

self.report_placeholder_failure(sup_origin, sub_r, sup_r).emit();
}

RegionResolutionError::CannotNormalize(ty, origin) => {
self.tcx
.dcx()
.struct_span_err(origin.span(), format!("cannot normalize `{ty}`"))
.emit();
}
}
}
}
Expand Down Expand Up @@ -559,7 +566,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
RegionResolutionError::GenericBoundFailure(..) => true,
RegionResolutionError::ConcreteFailure(..)
| RegionResolutionError::SubSupConflict(..)
| RegionResolutionError::UpperBoundUniverseConflict(..) => false,
| RegionResolutionError::UpperBoundUniverseConflict(..)
| RegionResolutionError::CannotNormalize(..) => false,
};

let mut errors = if errors.iter().all(|e| is_bound_failure(e)) {
Expand All @@ -574,6 +582,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
RegionResolutionError::GenericBoundFailure(ref sro, _, _) => sro.span(),
RegionResolutionError::SubSupConflict(_, ref rvo, _, _, _, _, _) => rvo.span(),
RegionResolutionError::UpperBoundUniverseConflict(_, ref rvo, _, _, _) => rvo.span(),
RegionResolutionError::CannotNormalize(_, ref sro) => sro.span(),
});
errors
}
Expand Down
5 changes: 4 additions & 1 deletion compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ pub enum RegionResolutionError<'tcx> {
SubregionOrigin<'tcx>, // cause of the constraint
Region<'tcx>, // the placeholder `'b`
),

CannotNormalize(Ty<'tcx>, SubregionOrigin<'tcx>),
}

impl<'tcx> RegionResolutionError<'tcx> {
Expand All @@ -106,7 +108,8 @@ impl<'tcx> RegionResolutionError<'tcx> {
RegionResolutionError::ConcreteFailure(origin, _, _)
| RegionResolutionError::GenericBoundFailure(origin, _, _)
| RegionResolutionError::SubSupConflict(_, _, origin, _, _, _, _)
| RegionResolutionError::UpperBoundUniverseConflict(_, _, _, origin, _) => origin,
| RegionResolutionError::UpperBoundUniverseConflict(_, _, _, origin, _)
| RegionResolutionError::CannotNormalize(_, origin) => origin,
}
}
}
Expand Down
18 changes: 14 additions & 4 deletions compiler/rustc_infer/src/infer/outlives/mod.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
//! Various code related to computing outlives relations.
use self::env::OutlivesEnvironment;
use super::region_constraints::RegionConstraintData;
use super::{InferCtxt, RegionResolutionError};
use super::{InferCtxt, RegionResolutionError, SubregionOrigin};
use crate::infer::free_regions::RegionRelations;
use crate::infer::lexical_region_resolve;
use rustc_middle::traits::query::OutlivesBound;
use rustc_middle::ty;
use rustc_middle::ty::{self, Ty};

pub mod components;
pub mod env;
Expand Down Expand Up @@ -41,12 +41,22 @@ impl<'tcx> InferCtxt<'tcx> {
/// result. After this, no more unification operations should be
/// done -- or the compiler will panic -- but it is legal to use
/// `resolve_vars_if_possible` as well as `fully_resolve`.
///
/// If you are in a crate that has access to `rustc_trait_selection`,
/// then it's probably better to use `resolve_regions`,
/// which knows how to normalize registered region obligations.
#[must_use]
pub fn resolve_regions(
pub fn resolve_regions_with_normalize(
&self,
outlives_env: &OutlivesEnvironment<'tcx>,
deeply_normalize_ty: impl Fn(Ty<'tcx>, SubregionOrigin<'tcx>) -> Result<Ty<'tcx>, Ty<'tcx>>,
) -> Vec<RegionResolutionError<'tcx>> {
self.process_registered_region_obligations(outlives_env);
match self.process_registered_region_obligations(outlives_env, deeply_normalize_ty) {
Ok(()) => {}
Err((ty, origin)) => {
return vec![RegionResolutionError::CannotNormalize(ty, origin)];
}
};

let (var_infos, data) = {
let mut inner = self.inner.borrow_mut();
Expand Down
Loading

0 comments on commit cb4d9a1

Please sign in to comment.