Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support higher-ranked regions in opaque type inference #100503

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 46 additions & 10 deletions compiler/rustc_borrowck/src/region_infer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,9 +167,25 @@ pub(crate) struct RegionDefinition<'tcx> {

/// If this is 'static or an early-bound region, then this is
/// `Some(X)` where `X` is the name of the region.
///
/// Use the method `Self::external_name` for more flexibility.
pub(crate) external_name: Option<ty::Region<'tcx>>,
}

impl<'tcx> RegionDefinition<'tcx> {
/// Gets the name of a universal region (including placeholders).
/// Returns `None` if this is an existential variable.
pub fn external_name(&self, tcx: TyCtxt<'tcx>) -> Option<ty::Region<'tcx>> {
match self.origin {
NllRegionVariableOrigin::FreeRegion => self.external_name,
NllRegionVariableOrigin::Placeholder(placeholder) => {
Some(tcx.mk_region(ty::RePlaceholder(placeholder)))
}
NllRegionVariableOrigin::Existential { .. } => None,
}
}
}

/// N.B., the variants in `Cause` are intentionally ordered. Lower
/// values are preferred when it comes to error messages. Do not
/// reorder willy nilly.
Expand Down Expand Up @@ -712,16 +728,28 @@ impl<'tcx> RegionInferenceContext<'tcx> {
*c_r = self.scc_representatives[scc];
}

// The 'member region' in a member constraint is part of the
// hidden type, which must be in the root universe. Therefore,
// it cannot have any placeholders in its value.
assert!(self.scc_universes[scc] == ty::UniverseIndex::ROOT);
debug_assert!(
self.scc_values.placeholders_contained_in(scc).next().is_none(),
"scc {:?} in a member constraint has placeholder value: {:?}",
scc,
self.scc_values.region_value_str(scc),
);
// The 'member region' may have a placeholder region in its value.
// Consider the inner opaque type `impl Sized` in:
// `fn test() -> impl for<'a> Trait<'a, Ty = impl Sized + 'a>`.
// Here choice_regions = ['static, Placeholder('a, U1)].
if self.scc_universes[scc] != ty::UniverseIndex::ROOT {
let Some(&choice) = choice_regions
.iter()
.find(|&&choice| self.eval_equal(choice, self.scc_representatives[scc]))
else {
debug!("failed higher-ranked member constraint");
return false;
};

self.member_constraints_applied.push(AppliedMemberConstraint {
member_region_scc: scc,
min_choice: choice,
member_constraint_index,
});

debug!(?choice, "higher-ranked");
return true;
}

// The existing value for `scc` is a lower-bound. This will
// consist of some set `{P} + {LB}` of points `{P}` and
Expand Down Expand Up @@ -1360,6 +1388,14 @@ impl<'tcx> RegionInferenceContext<'tcx> {
return self.eval_outlives(sup_region, self.universal_regions.fr_static);
}

// Check `sup_region` contains all the placeholder regions in `sub_region`.
if !self.scc_values.contains_placeholders(sup_region_scc, sub_region_scc) {
debug!(
"eval_outlives: returning false because sub region contains a placeholder region not present in super"
);
return false;
}

// Both the `sub_region` and `sup_region` consist of the union
// of some number of universal regions (along with the union
// of various points in the CFG; ignore those points for
Expand Down
73 changes: 52 additions & 21 deletions compiler/rustc_borrowck/src/region_infer/opaque_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use rustc_data_structures::vec_map::VecMap;
use rustc_hir::def_id::LocalDefId;
use rustc_hir::OpaqueTyOrigin;
use rustc_infer::infer::error_reporting::unexpected_hidden_region_diagnostic;
use rustc_infer::infer::opaque_types::OpaqueTypeMap;
use rustc_infer::infer::TyCtxtInferExt as _;
use rustc_infer::infer::{DefiningAnchor, InferCtxt};
use rustc_infer::traits::{Obligation, ObligationCause, TraitEngine};
Expand Down Expand Up @@ -62,23 +63,31 @@ impl<'tcx> RegionInferenceContext<'tcx> {
pub(crate) fn infer_opaque_types(
&self,
infcx: &InferCtxt<'_, 'tcx>,
opaque_ty_decls: VecMap<OpaqueTypeKey<'tcx>, (OpaqueHiddenType<'tcx>, OpaqueTyOrigin)>,
opaque_ty_decls: OpaqueTypeMap<'tcx>,
) -> VecMap<LocalDefId, OpaqueHiddenType<'tcx>> {
let mut result: VecMap<LocalDefId, OpaqueHiddenType<'tcx>> = VecMap::new();
for (opaque_type_key, (concrete_type, origin)) in opaque_ty_decls {
for (opaque_type_key, decl) in opaque_ty_decls {
let substs = opaque_type_key.substs;
debug!(?concrete_type, ?substs);
debug!(?decl.hidden_type, ?substs);

let mut subst_regions = vec![self.universal_regions.fr_static];
let universal_substs = infcx.tcx.fold_regions(substs, |region, _| {
if let ty::RePlaceholder(..) = region.kind() {
// Higher kinded regions don't need remapping, they don't refer to anything outside of this the substs.
return region;
let (vid, scc) = match region.kind() {
ty::ReVar(vid) => (vid, self.constraint_sccs.scc(vid)),
_ => bug!("expected nll var"),
};
trace!(?vid, ?scc);

// Special handling of higher-ranked regions. These appear in the substs of the
// inner opaque type `impl Sized` in:
// `fn test() -> impl for<'a> Trait<'a, Ty = impl Sized + 'a>`
if self.scc_universes[scc] != ty::UniverseIndex::ROOT {
subst_regions.push(vid);
return self.definitions[vid]
.external_name(infcx.tcx)
.expect("higher-ranked existential region found in opaque type substs");
}
let vid = self.to_region_vid(region);
trace!(?vid);
let scc = self.constraint_sccs.scc(vid);
trace!(?scc);

match self.scc_values.universal_regions_outlived_by(scc).find_map(|lb| {
self.eval_equal(vid, lb).then_some(self.definitions[lb].external_name?)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this (and anywhere else) always be calling the external_name function?

}) {
Expand All @@ -90,7 +99,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
None => {
subst_regions.push(vid);
infcx.tcx.sess.delay_span_bug(
concrete_type.span,
decl.hidden_type.span,
"opaque type with non-universal region substs",
);
infcx.tcx.lifetimes.re_static
Expand All @@ -102,13 +111,13 @@ impl<'tcx> RegionInferenceContext<'tcx> {
subst_regions.dedup();

let universal_concrete_type =
infcx.tcx.fold_regions(concrete_type, |region, _| match *region {
infcx.tcx.fold_regions(decl.hidden_type, |region, _| match *region {
ty::ReVar(vid) => subst_regions
.iter()
.find(|ur_vid| self.eval_equal(vid, **ur_vid))
.and_then(|ur_vid| self.definitions[*ur_vid].external_name)
.and_then(|ur_vid| self.definitions[*ur_vid].external_name(infcx.tcx))
.unwrap_or(infcx.tcx.lifetimes.re_root_empty),
_ => region,
_ => bug!("expected nll var"),
});

debug!(?universal_concrete_type, ?universal_substs);
Expand All @@ -118,7 +127,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
let ty = infcx.infer_opaque_definition_from_instantiation(
opaque_type_key,
universal_concrete_type,
origin,
decl.origin,
);
// Sometimes two opaque types are the same only after we remap the generic parameters
// back to the opaque type definition. E.g. we may have `OpaqueType<X, Y>` mapped to `(X, Y)`
Expand All @@ -128,19 +137,19 @@ impl<'tcx> RegionInferenceContext<'tcx> {
if prev.ty != ty {
if !ty.references_error() {
prev.report_mismatch(
&OpaqueHiddenType { ty, span: concrete_type.span },
&OpaqueHiddenType { ty, span: decl.hidden_type.span },
infcx.tcx,
);
}
prev.ty = infcx.tcx.ty_error();
}
// Pick a better span if there is one.
// FIXME(oli-obk): collect multiple spans for better diagnostics down the road.
prev.span = prev.span.substitute_dummy(concrete_type.span);
prev.span = prev.span.substitute_dummy(decl.hidden_type.span);
} else {
result.insert(
opaque_type_key.def_id,
OpaqueHiddenType { ty, span: concrete_type.span },
OpaqueHiddenType { ty, span: decl.hidden_type.span },
);
}
}
Expand All @@ -159,6 +168,25 @@ impl<'tcx> RegionInferenceContext<'tcx> {
{
tcx.fold_regions(ty, |region, _| match *region {
ty::ReVar(vid) => {
let scc = self.constraint_sccs.scc(vid);

// Special handling of higher-ranked regions.
if self.scc_universes[scc] != ty::UniverseIndex::ROOT {
// If the region contains a single placeholder then they're equal.
if let Some((0, placeholder)) =
self.scc_values.placeholders_contained_in(scc).enumerate().last()
{
// HACK: we convert named placeholders to free regions for better errors.
// Otherwise, this is incorrect.
if let bound_region @ ty::BrNamed(scope, _) = placeholder.name {
return tcx
.mk_region(ty::ReFree(ty::FreeRegion { scope, bound_region }));
}
}
// Fallback: this will produce a cryptic error message.
return region;
}

// Find something that we can name
let upper_bound = self.approx_universal_upper_bound(vid);
let upper_bound = &self.definitions[upper_bound];
Expand Down Expand Up @@ -384,7 +412,7 @@ fn check_opaque_type_parameter_valid(
return false;
}
GenericArgKind::Lifetime(lt) => {
matches!(*lt, ty::ReEarlyBound(_) | ty::ReFree(_))
matches!(*lt, ty::ReEarlyBound(_) | ty::ReFree(_) | ty::RePlaceholder(_))
}
GenericArgKind::Const(ct) => matches!(ct.kind(), ty::ConstKind::Param(_)),
};
Expand Down Expand Up @@ -494,9 +522,12 @@ impl<'tcx> TypeFolder<'tcx> for ReverseMapper<'tcx> {
ty::ReErased => return r,

// The regions that we expect from borrow checking.
ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReEmpty(ty::UniverseIndex::ROOT) => {}
ty::ReEarlyBound(_)
| ty::ReFree(_)
| ty::RePlaceholder(_)
| ty::ReEmpty(ty::UniverseIndex::ROOT) => {}

ty::ReEmpty(_) | ty::RePlaceholder(_) | ty::ReVar(_) => {
ty::ReEmpty(_) | ty::ReVar(_) => {
// All of the regions in the type should either have been
// erased by writeback, or mapped back to named regions by
// borrow checking.
Expand Down
16 changes: 16 additions & 0 deletions compiler/rustc_borrowck/src/region_infer/values.rs
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,22 @@ impl<N: Idx> RegionValues<N> {
}
}

/// Returns `true` if `sup_region` contains all the placeholder elements that
/// `sub_region` contains.
pub(crate) fn contains_placeholders(&self, sup_region: N, sub_region: N) -> bool {
if let Some(sub_row) = self.placeholders.row(sub_region) {
if let Some(sup_row) = self.placeholders.row(sup_region) {
sup_row.superset(sub_row)
} else {
// sup row is empty, so sub row must be empty
sub_row.is_empty()
}
} else {
// sub row is empty, always true
true
}
}

/// Returns the locations contained within a given region `r`.
pub(crate) fn locations_outlived_by<'a>(&'a self, r: N) -> impl Iterator<Item = Location> + 'a {
self.points.row(r).into_iter().flat_map(move |set| {
Expand Down
Loading