Skip to content
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
229 changes: 229 additions & 0 deletions compiler/rustc_borrowck/src/compute_rename_later.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
// TODO: add description later

use rustc_infer::infer::canonical::Canonical;
use rustc_infer::infer::{TyCtxtInferExt, TypeOutlivesConstraint, canonical};
use rustc_infer::traits::ObligationCause;
use rustc_infer::traits::query::OutlivesBound;
use rustc_middle::query::Providers;
use rustc_middle::ty::{RegionExt, TyCtxt};
use rustc_span::sym;
use rustc_trait_selection::traits::ObligationCtxt;
use rustc_trait_selection::traits::query::type_op::implied_outlives_bounds::{
compute_implied_outlives_bounds_inner_new, implied_bounds_from_components,
};
use smallvec::smallvec;

use crate::hir::def::DefKind;
use crate::ty::outlives::push_outlives_components;
use crate::ty::solve::NoSolution;
use crate::ty::{CanonicalVarValues, GenericArg, GenericArgKind, GenericArgs, Region};
use crate::universal_regions::{
compute_inputs_and_output_non_nll, defining_ty_non_nll,
for_each_late_bound_region_in_recursive_scope,
};
use crate::{
LocalDefId, ParamEnv, RegionVariableOrigin, SmallVec, Ty, TypingMode, debug, fold_regions, ty,
};

pub(crate) fn provide(p: &mut Providers) {
*p = Providers { compute_outlives_bounds_rename, ..*p };
}

fn compute_outlives_bounds_rename<'tcx>(
tcx: TyCtxt<'tcx>,
mir_def: LocalDefId,
) -> Result<
&'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, Vec<OutlivesBound<'tcx>>>>,
NoSolution,
> {
debug!("enter compute_outlives_bounds_rename");
let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis());
let ocx = ObligationCtxt::new(&infcx);
let param_env = ParamEnv::empty();
let defining_ty = defining_ty_non_nll(&infcx, mir_def);
let defining_ty_def_id = defining_ty.def_id().expect_local();
let mut late_bound_region: Vec<Region<'_>> = vec![];

let unnormalized_input_output_tys =
compute_inputs_and_output_non_nll(&infcx, mir_def, defining_ty);
debug!("first unnormalized input and output ty is {:?}", unnormalized_input_output_tys);

// Collect late bound region before instantiating the binder. There is difference between collecting region like this
// and just collect it after `liberate_late_bound_region`. With this, we can make sure unused late bound region is
// collected too.
for (idx, bound_var) in unnormalized_input_output_tys.bound_vars().iter().enumerate() {
if let ty::BoundVariableKind::Region(kind) = bound_var {
let kind = ty::LateParamRegionKind::from_bound(ty::BoundVar::from_usize(idx), kind);
let region = ty::Region::new_late_param(infcx.tcx, mir_def.to_def_id(), kind);
debug!(?region);
late_bound_region.push(region);
}
}

debug!("late bound region is {:?}", late_bound_region);

let unnormalized_input_output_tys = tcx
.liberate_late_bound_regions(defining_ty_def_id.to_def_id(), unnormalized_input_output_tys);

debug!("second unnormalized input and output ty is {:?}", unnormalized_input_output_tys);

let span = tcx.def_span(defining_ty_def_id);
let mut outlives_bounds: Vec<OutlivesBound<'tcx>> = vec![];
let mut norm_sig_tys: Vec<Ty<'_>> = vec![];

for ty in unnormalized_input_output_tys {
debug!("the ty for input and output are {:?}", ty);

// Replace erased regions with fresh region variables.
let ty = fold_regions(tcx, ty, |re, _dbi| match re.kind() {
ty::ReErased => infcx.next_region_var(RegionVariableOrigin::Misc(span)),
_ => re,
});

// We add implied bounds from both the unnormalized and normalized ty.
// See issue #87748
if let Ok(bounds) = compute_implied_outlives_bounds_inner_new(&ocx, param_env, ty, span) {
outlives_bounds.extend(bounds);
}

let Ok(norm_ty) = ocx.deeply_normalize(
&ObligationCause::dummy_with_span(span),
param_env,
ty::Unnormalized::new_wip(ty),
) else {
// Even if deeply normalize returns no solution, we still need to store the ty for canonicalization later.
norm_sig_tys.push(ty);
continue;
};

// Currently `implied_outlives_bounds` will normalize the provided
// `Ty`, despite this it's still important to normalize the ty ourselves
// as normalization may introduce new region variables (#136547).
//
// If we do not add implied bounds for the type involving these new
// region variables then we'll wind up with the normalized form of
// the signature having not-wf types due to unsatisfied region
// constraints.
//
// Note: we need this in examples like
// ```
// trait Foo {
// type Bar;
// fn foo(&self) -> &Self::Bar;
// }
// impl Foo for () {
// type Bar = ();
// fn foo(&self) -> &() {}
// }
// ```
// Both &Self::Bar and &() are WF
if ty != norm_ty {
if let Ok(bounds) =
compute_implied_outlives_bounds_inner_new(&ocx, param_env, norm_ty, span)
{
outlives_bounds.extend(bounds);
}
}
debug!("normalized ty is {:?}", norm_ty);

norm_sig_tys.push(norm_ty);
}

// Add implied bounds from impl header.
// We don't use `assumed_wf_types` to source the entire set of implied bounds for
// a few reasons:
// - `DefiningTy` for closure has the `&'env Self` type while `assumed_wf_types` doesn't
// - We compute implied bounds from the unnormalized types in the `DefiningTy` but do not
// do so for types in impl headers
// - We must compute the normalized signature and then compute implied bounds from that
// in order to connect any unconstrained region vars created during normalization to
// the types of the locals corresponding to the inputs and outputs of the item. (#136547)
if matches!(tcx.def_kind(defining_ty_def_id), DefKind::AssocFn | DefKind::AssocConst { .. }) {
for &(ty, _) in tcx.assumed_wf_types(tcx.local_parent(defining_ty_def_id)) {
// Replace erased regions with fresh region variables.
let ty = fold_regions(tcx, ty, |re, _dbi| match re.kind() {
ty::ReErased => infcx.next_region_var(RegionVariableOrigin::Misc(span)),
_ => re,
});

let Ok(norm_ty) = ocx.deeply_normalize(
&ObligationCause::dummy_with_span(span),
param_env,
ty::Unnormalized::new_wip(ty),
) else {
continue;
};

// We currently add implied bounds from the normalized ty only.
// This is more conservative and matches wfcheck behavior.
if let Ok(bounds) =
compute_implied_outlives_bounds_inner_new(&ocx, param_env, norm_ty, span)
{
outlives_bounds.extend(bounds);
}
}
}
// Take outlives constraints from fn sig for bevy.
// Previously, we only collect outlives obligations for `ParamSet<T>` or
// `&ParamSet<T>`, now we collect outlives obligations for the entire function
// signature.
if !tcx.sess.opts.unstable_opts.no_implied_bounds_compat
&& tcx.crate_name(mir_def.to_def_id().krate) == sym::bevy_ecs
{
for TypeOutlivesConstraint { sup_type, sub_region, .. } in
infcx.clone_registered_region_obligations()
{
let mut components = smallvec![];
push_outlives_components(tcx, sup_type, &mut components);
outlives_bounds.extend(implied_bounds_from_components(tcx, sub_region, components));
}
}

// Get early bound params.
let typeck_root_def_id = tcx.typeck_root_def_id(mir_def.to_def_id());

let early_bound_params = GenericArgs::identity_for_item(tcx, typeck_root_def_id);
let mut early_bound_region: SmallVec<[GenericArg<'_>; 8]> = Default::default();
let mut early_bound_non_region: SmallVec<[GenericArg<'_>; 8]> = Default::default();

debug!("early bound params are {:?}", early_bound_params);
// Separate early bound region and non-region.
for param in early_bound_params {
match param.kind() {
GenericArgKind::Lifetime(_) => early_bound_region.push(param),
_ => early_bound_non_region.push(param),
}
}

let mut var_value: SmallVec<[GenericArg<'_>; 8]> = early_bound_non_region;

for param in early_bound_region {
var_value.push(param);
}

// Collect late bound region for closure, coroutine, or inline-const.
if mir_def.to_def_id() != typeck_root_def_id {
for_each_late_bound_region_in_recursive_scope(tcx, tcx.local_parent(mir_def), |r| {
debug!("the late bound region for closure is {:?}", r);
var_value.push(GenericArg::from(r));
});
}

// var value = [generic param, early bound region, late bound region, sig tys]
// Make generic param goes before region to match with the call site in free_region_relations.

for region in &late_bound_region {
var_value.push(GenericArg::from(*region))
}

for ty in &norm_sig_tys {
var_value.push(GenericArg::from(*ty))
}

let var_values: CanonicalVarValues<TyCtxt<'_>> =
CanonicalVarValues { var_values: tcx.mk_args(var_value.as_slice()) };

debug!("var value in query is {:?}", var_values);

ocx.make_canonicalized_query_response(var_values, outlives_bounds)
}
4 changes: 4 additions & 0 deletions compiler/rustc_borrowck/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ use crate::type_check::{Locations, MirTypeckRegionConstraints, MirTypeckResults}

mod borrow_set;
mod borrowck_errors;
mod compute_rename_later;
mod constraints;
mod dataflow;
mod def_use;
Expand Down Expand Up @@ -107,6 +108,7 @@ impl<'tcx> TyCtxtConsts<'tcx> {

pub fn provide(providers: &mut Providers) {
*providers = Providers { mir_borrowck, ..*providers };
compute_rename_later::provide(providers);
}

/// Provider for `query mir_borrowck`. Unlike `typeck`, this must
Expand Down Expand Up @@ -351,6 +353,7 @@ fn borrowck_collect_region_constraints<'tcx>(
let mut polonius_facts =
(polonius_input || PoloniusFacts::enabled(infcx.tcx)).then_some(PoloniusFacts::default());

// TODO: figure out if I can reuse the same defId from somewhere instead of passing it.
// Run the MIR type-checker.
let MirTypeckResults {
constraints,
Expand All @@ -361,6 +364,7 @@ fn borrowck_collect_region_constraints<'tcx>(
polonius_context,
} = type_check::type_check(
root_cx,
def,
&infcx,
body,
&promoted,
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_borrowck/src/region_infer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -719,6 +719,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
// avoid potential non-determinism we approximate this by requiring
// T: '1 and T: '2.
for upper_bound in non_local_ub {
debug!("the current upper bound is {:?}", upper_bound);
debug_assert!(self.universal_regions().is_universal_region(upper_bound));
debug_assert!(!self.universal_regions().is_local_free_region(upper_bound));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,9 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
// Create the predicates.
let backup = (self.category, self.span, self.from_closure);
self.from_closure = true;
debug!("closure mapping is {:?}", closure_mapping);
for outlives_requirement in &closure_requirements.outlives_requirements {
debug!("outlive requirement for closure is {:?}", outlives_requirement);
let outlived_region = closure_mapping[outlives_requirement.outlived_free_region];
let subject = match outlives_requirement.subject {
ClosureOutlivesSubject::Region(re) => closure_mapping[re].into(),
Expand Down
Loading
Loading