Skip to content

Commit

Permalink
Use liveness visitor when considering regions in opaque hidden types
Browse files Browse the repository at this point in the history
  • Loading branch information
compiler-errors committed Oct 2, 2023
1 parent 56ec828 commit fe7cfaa
Show file tree
Hide file tree
Showing 6 changed files with 208 additions and 189 deletions.
107 changes: 11 additions & 96 deletions compiler/rustc_borrowck/src/type_check/liveness/trace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,13 @@ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
use rustc_index::bit_set::HybridBitSet;
use rustc_index::interval::IntervalSet;
use rustc_infer::infer::canonical::QueryRegionConstraints;
use rustc_infer::infer::outlives::test_type_match;
use rustc_infer::infer::region_constraints::VerifyIfEq;
use rustc_infer::infer::outlives::for_liveness::FreeRegionsVisitor;
use rustc_middle::mir::{BasicBlock, Body, ConstraintCategory, Local, Location};
use rustc_middle::traits::query::DropckOutlivesResult;
use rustc_middle::ty::{
self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
};
use rustc_middle::ty::{Ty, TyCtxt, TypeVisitable, TypeVisitableExt};
use rustc_span::DUMMY_SP;
use rustc_trait_selection::traits::query::type_op::outlives::DropckOutlives;
use rustc_trait_selection::traits::query::type_op::{TypeOp, TypeOpOutput};
use std::ops::ControlFlow;
use std::rc::Rc;

use rustc_mir_dataflow::impls::MaybeInitializedPlaces;
Expand Down Expand Up @@ -559,99 +555,18 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> {
"make_all_regions_live: live_at={}",
values::location_set_str(elements, live_at.iter()),
);

struct MakeAllRegionsLive<'a, 'b, 'tcx> {
typeck: &'b mut TypeChecker<'a, 'tcx>,
live_at: &'b IntervalSet<PointIndex>,
}
impl<'tcx> MakeAllRegionsLive<'_, '_, 'tcx> {
/// We can prove that an alias is live two ways:
/// 1. All the components are live.
/// 2. There is a known outlives bound or where-clause, and that
/// region is live.
/// We search through the item bounds and where clauses for
/// either `'static` or a unique outlives region, and if one is
/// found, we just need to prove that that region is still live.
/// If one is not found, then we continue to walk through the alias.
fn make_alias_live(&mut self, t: Ty<'tcx>) -> ControlFlow<!> {
let ty::Alias(_kind, alias_ty) = t.kind() else {
bug!("`make_alias_live` only takes alias types");
};
let tcx = self.typeck.infcx.tcx;
let param_env = self.typeck.param_env;
let outlives_bounds: Vec<_> = tcx
.item_bounds(alias_ty.def_id)
.iter_instantiated(tcx, alias_ty.args)
.filter_map(|clause| {
if let Some(outlives) = clause.as_type_outlives_clause()
&& outlives.skip_binder().0 == t
{
Some(outlives.skip_binder().1)
} else {
None
}
})
.chain(param_env.caller_bounds().iter().filter_map(|clause| {
let outlives = clause.as_type_outlives_clause()?;
if let Some(outlives) = outlives.no_bound_vars()
&& outlives.0 == t
{
Some(outlives.1)
} else {
test_type_match::extract_verify_if_eq(
tcx,
param_env,
&outlives.map_bound(|ty::OutlivesPredicate(ty, bound)| {
VerifyIfEq { ty, bound }
}),
t,
)
}
}))
.collect();
// If we find `'static`, then we know the alias doesn't capture *any* regions.
// Otherwise, all of the outlives regions should be equal -- if they're not,
// we don't really know how to proceed, so we continue recursing through the
// alias.
if outlives_bounds.contains(&tcx.lifetimes.re_static) {
ControlFlow::Continue(())
} else if let Some(r) = outlives_bounds.first()
&& outlives_bounds[1..].iter().all(|other_r| other_r == r)
{
r.visit_with(self)
} else {
t.super_visit_with(self)
}
}
}
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for MakeAllRegionsLive<'_, '_, 'tcx> {
type BreakTy = !;

fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
if r.is_late_bound() {
return ControlFlow::Continue(());
}
let live_region_vid =
self.typeck.borrowck_context.universal_regions.to_region_vid(r);
self.typeck
value.visit_with(&mut FreeRegionsVisitor {
tcx: typeck.tcx(),
param_env: typeck.param_env,
op: |r| {
let live_region_vid = typeck.borrowck_context.universal_regions.to_region_vid(r);
typeck
.borrowck_context
.constraints
.liveness_constraints
.add_elements(live_region_vid, self.live_at);
ControlFlow::Continue(())
}

fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
if !t.has_free_regions() {
ControlFlow::Continue(())
} else if let ty::Alias(..) = t.kind() {
self.make_alias_live(t)
} else {
t.super_visit_with(self)
}
}
}
value.visit_with(&mut MakeAllRegionsLive { typeck, live_at });
.add_elements(live_region_vid, live_at);
},
});
}

fn compute_drop_data(
Expand Down
98 changes: 5 additions & 93 deletions compiler/rustc_infer/src/infer/opaque_types.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use super::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use super::{DefineOpaqueTypes, InferResult};
use crate::errors::OpaqueHiddenTypeDiag;
use crate::infer::outlives::for_liveness::FreeRegionsVisitor;
use crate::infer::{InferCtxt, InferOk};
use crate::traits::{self, PredicateObligation};
use hir::def_id::{DefId, LocalDefId};
Expand All @@ -13,11 +14,10 @@ use rustc_middle::ty::error::{ExpectedFound, TypeError};
use rustc_middle::ty::fold::BottomUpFolder;
use rustc_middle::ty::GenericArgKind;
use rustc_middle::ty::{
self, OpaqueHiddenType, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable,
TypeVisitable, TypeVisitableExt, TypeVisitor,
self, OpaqueHiddenType, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable, TypeVisitable,
TypeVisitableExt,
};
use rustc_span::Span;
use std::ops::ControlFlow;

mod table;

Expand Down Expand Up @@ -380,8 +380,9 @@ impl<'tcx> InferCtxt<'tcx> {
.collect(),
);

concrete_ty.visit_with(&mut ConstrainOpaqueTypeRegionVisitor {
concrete_ty.visit_with(&mut FreeRegionsVisitor {
tcx: self.tcx,
param_env,
op: |r| self.member_constraint(opaque_type_key, span, concrete_ty, r, &choice_regions),
});
}
Expand Down Expand Up @@ -415,95 +416,6 @@ impl<'tcx> InferCtxt<'tcx> {
}
}

/// Visitor that requires that (almost) all regions in the type visited outlive
/// `least_region`. We cannot use `push_outlives_components` because regions in
/// closure signatures are not included in their outlives components. We need to
/// ensure all regions outlive the given bound so that we don't end up with,
/// say, `ReVar` appearing in a return type and causing ICEs when other
/// functions end up with region constraints involving regions from other
/// functions.
///
/// We also cannot use `for_each_free_region` because for closures it includes
/// the regions parameters from the enclosing item.
///
/// We ignore any type parameters because impl trait values are assumed to
/// capture all the in-scope type parameters.
pub struct ConstrainOpaqueTypeRegionVisitor<'tcx, OP: FnMut(ty::Region<'tcx>)> {
pub tcx: TyCtxt<'tcx>,
pub op: OP,
}

impl<'tcx, OP> TypeVisitor<TyCtxt<'tcx>> for ConstrainOpaqueTypeRegionVisitor<'tcx, OP>
where
OP: FnMut(ty::Region<'tcx>),
{
fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(
&mut self,
t: &ty::Binder<'tcx, T>,
) -> ControlFlow<Self::BreakTy> {
t.super_visit_with(self);
ControlFlow::Continue(())
}

fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
match *r {
// ignore bound regions, keep visiting
ty::ReLateBound(_, _) => ControlFlow::Continue(()),
_ => {
(self.op)(r);
ControlFlow::Continue(())
}
}
}

fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
// We're only interested in types involving regions
if !ty.flags().intersects(ty::TypeFlags::HAS_FREE_REGIONS) {
return ControlFlow::Continue(());
}

match ty.kind() {
ty::Closure(_, ref args) => {
// Skip lifetime parameters of the enclosing item(s)

for upvar in args.as_closure().upvar_tys() {
upvar.visit_with(self);
}
args.as_closure().sig_as_fn_ptr_ty().visit_with(self);
}

ty::Generator(_, ref args, _) => {
// Skip lifetime parameters of the enclosing item(s)
// Also skip the witness type, because that has no free regions.

for upvar in args.as_generator().upvar_tys() {
upvar.visit_with(self);
}
args.as_generator().return_ty().visit_with(self);
args.as_generator().yield_ty().visit_with(self);
args.as_generator().resume_ty().visit_with(self);
}

ty::Alias(ty::Opaque, ty::AliasTy { def_id, ref args, .. }) => {
// Skip lifetime parameters that are not captures.
let variances = self.tcx.variances_of(*def_id);

for (v, s) in std::iter::zip(variances, args.iter()) {
if *v != ty::Variance::Bivariant {
s.visit_with(self);
}
}
}

_ => {
ty.super_visit_with(self);
}
}

ControlFlow::Continue(())
}
}

pub enum UseKind {
DefiningUse,
OpaqueUse,
Expand Down
Loading

0 comments on commit fe7cfaa

Please sign in to comment.