Change outlives clause checking algorithm

This commit is contained in:
jackh726 2021-10-16 02:29:59 -04:00
parent a8c44d344b
commit 82148cdc66
5 changed files with 138 additions and 107 deletions

View File

@ -1255,16 +1255,16 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
self.tainted_by_errors_flag.set(true)
}
/// Process the region constraints and report any errors that
/// Process the region constraints and return any any errors that
/// 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`.
pub fn resolve_regions_and_report_errors(
pub fn resolve_regions(
&self,
region_context: DefId,
outlives_env: &OutlivesEnvironment<'tcx>,
mode: RegionckMode,
) {
) -> Vec<RegionResolutionError<'tcx>> {
let (var_infos, data) = {
let mut inner = self.inner.borrow_mut();
let inner = &mut *inner;
@ -1290,6 +1290,21 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
let old_value = self.lexical_region_resolutions.replace(Some(lexical_region_resolutions));
assert!(old_value.is_none());
errors
}
/// Process the region constraints and report any errors that
/// 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`.
pub fn resolve_regions_and_report_errors(
&self,
region_context: DefId,
outlives_env: &OutlivesEnvironment<'tcx>,
mode: RegionckMode,
) {
let errors = self.resolve_regions(region_context, outlives_env, mode);
if !self.is_tainted_by_errors() {
// As a heuristic, just skip reporting region errors
// altogether if other errors have been reported while

View File

@ -104,7 +104,7 @@ macro_rules! ignore_err {
};
}
trait OutlivesEnvironmentExt<'tcx> {
pub(crate) trait OutlivesEnvironmentExt<'tcx> {
fn add_implied_bounds(
&mut self,
infcx: &InferCtxt<'a, 'tcx>,

View File

@ -1,7 +1,7 @@
use crate::check::regionck::OutlivesEnvironmentExt;
use crate::check::{FnCtxt, Inherited};
use crate::constrained_generic_params::{identify_constrained_generic_params, Parameter};
use crate::traits::query::type_op::{self, TypeOp, TypeOpOutput};
use rustc_ast as ast;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder};
@ -12,7 +12,10 @@ use rustc_hir::intravisit::Visitor;
use rustc_hir::itemlikevisit::ParItemLikeVisitor;
use rustc_hir::lang_items::LangItem;
use rustc_hir::ItemKind;
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
use rustc_infer::infer::outlives::obligations::TypeOutlives;
use rustc_infer::infer::TyCtxtInferExt;
use rustc_infer::infer::{self, RegionckMode, SubregionOrigin};
use rustc_middle::hir::map as hir_map;
use rustc_middle::ty::subst::{GenericArgKind, InternalSubsts, Subst};
use rustc_middle::ty::trait_def::TraitSpecializationKind;
@ -22,7 +25,7 @@ use rustc_middle::ty::{
};
use rustc_session::parse::feature_err;
use rustc_span::symbol::{sym, Ident, Symbol};
use rustc_span::Span;
use rustc_span::{Span, DUMMY_SP};
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode, WellFormedLoc};
@ -279,84 +282,104 @@ fn check_gat_where_clauses(
return;
}
let associated_items: &ty::AssocItems<'_> = tcx.associated_items(encl_trait_def_id);
let mut clauses = FxHashSet::default();
// For every function in this trait...
for item in
associated_items.in_definition_order().filter(|item| matches!(item.kind, ty::AssocKind::Fn))
{
tcx.infer_ctxt().enter(|infcx| {
let sig: ty::Binder<'_, ty::FnSig<'_>> = tcx.fn_sig(item.def_id);
let sig = infcx.replace_bound_vars_with_placeholders(sig);
// Find out what regions are passed as GAT substs
let mut visitor = GATSubstCollector {
tcx,
gat: trait_item.def_id.to_def_id(),
regions: FxHashSet::default(),
_types: FxHashSet::default(),
};
sig.output().visit_with(&mut visitor);
// If there are none, then it nothing to do
if visitor.regions.is_empty() {
return;
}
let mut clauses = FxHashSet::default();
// Otherwise, find the clauses required from implied bounds
for input in sig.inputs() {
// For a given input type, find the implied bounds
let TypeOpOutput { output: bounds, .. } = match ty::ParamEnv::empty()
.and(type_op::implied_outlives_bounds::ImpliedOutlivesBounds { ty: input })
.fully_perform(&infcx)
{
Ok(o) => o,
Err(_) => continue,
};
debug!(?bounds);
for bound in bounds {
match bound {
traits::query::OutlivesBound::RegionSubParam(r, p) => {
// If the implied bound is a `RegionSubParam` and
// the region is used a GAT subst...
for idx in visitor
.regions
.iter()
.filter(|(proj_r, _)| proj_r == &r)
.map(|r| r.1)
{
// Then create a clause that is required on the GAT
let param_r = tcx.mk_region(ty::RegionKind::ReEarlyBound(
ty::EarlyBoundRegion {
def_id: generics.params[idx].def_id,
index: idx as u32,
name: generics.params[idx].name,
},
));
let clause = ty::PredicateKind::TypeOutlives(
ty::OutlivesPredicate(tcx.mk_ty(ty::Param(p)), param_r),
);
let clause = tcx.mk_predicate(ty::Binder::dummy(clause));
clauses.insert(clause);
}
}
_ => {}
}
}
}
// If there are any missing clauses, emit an error
debug!(?clauses);
if !clauses.is_empty() {
let written_predicates: ty::GenericPredicates<'_> =
tcx.predicates_of(trait_item.def_id);
for clause in clauses {
let found =
written_predicates.predicates.iter().find(|p| p.0 == clause).is_some();
debug!(?clause, ?found);
let mut error = tcx.sess.struct_span_err(
trait_item.generics.span,
&format!("Missing bound: {}", clause),
let id = hir::HirId::make_owner(item.def_id.expect_local());
let span = DUMMY_SP;
let param_env = tcx.param_env(item.def_id.expect_local());
let sig = tcx.fn_sig(item.def_id);
let sig = tcx.liberate_late_bound_regions(item.def_id, sig);
let mut visitor = GATSubstCollector {
tcx,
gat: trait_item.def_id.to_def_id(),
regions: FxHashSet::default(),
types: FxHashSet::default(),
};
sig.output().visit_with(&mut visitor);
let mut wf_tys = FxHashSet::default();
wf_tys.extend(sig.inputs());
// FIXME: normalize and add normalized inputs?
for (region, region_idx) in &visitor.regions {
for (ty, ty_idx) in &visitor.types {
tcx.infer_ctxt().enter(|infcx| {
let mut outlives_environment = OutlivesEnvironment::new(param_env);
outlives_environment.add_implied_bounds(&infcx, wf_tys.clone(), id, span);
outlives_environment.save_implied_bounds(id);
let region_bound_pairs =
outlives_environment.region_bound_pairs_map().get(&id).unwrap();
let cause =
ObligationCause::new(DUMMY_SP, id, ObligationCauseCode::MiscObligation);
let sup_type = *ty;
let sub_region = region;
let origin = SubregionOrigin::from_obligation_cause(&cause, || {
infer::RelateParamBound(cause.span, sup_type, None)
});
let outlives = &mut TypeOutlives::new(
&infcx,
tcx,
&region_bound_pairs,
Some(tcx.lifetimes.re_root_empty),
param_env,
);
error.emit();
}
outlives.type_must_outlive(origin, sup_type, sub_region);
let errors = infcx.resolve_regions(
trait_item.def_id.to_def_id(),
&outlives_environment,
RegionckMode::default(),
);
debug!(?errors, "errors");
if errors.is_empty() {
debug!(?ty_idx, ?region_idx);
debug!("required clause: {} must outlive {}", ty, region);
let ty_param = generics.param_at(*ty_idx, tcx);
let ty_param = tcx.mk_ty(ty::Param(ty::ParamTy {
index: ty_param.index,
name: ty_param.name,
}));
let region_param = generics.param_at(*region_idx, tcx);
// Then create a clause that is required on the GAT
let region_param =
tcx.mk_region(ty::RegionKind::ReEarlyBound(ty::EarlyBoundRegion {
def_id: region_param.def_id,
index: region_param.index,
name: region_param.name,
}));
let clause = ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(
ty_param,
region_param,
));
let clause = tcx.mk_predicate(ty::Binder::dummy(clause));
clauses.insert(clause);
}
});
}
})
}
}
// If there are any missing clauses, emit an error
debug!(?clauses);
if !clauses.is_empty() {
let written_predicates: ty::GenericPredicates<'_> = tcx.predicates_of(trait_item.def_id);
for clause in clauses {
let found = written_predicates.predicates.iter().find(|p| p.0 == clause).is_some();
debug!(?clause, ?found);
let mut error = tcx
.sess
.struct_span_err(trait_item.generics.span, &format!("Missing bound: {}", clause));
error.emit();
}
}
}
@ -366,7 +389,7 @@ struct GATSubstCollector<'tcx> {
// Which region appears and which parameter index its subsituted for
regions: FxHashSet<(ty::Region<'tcx>, usize)>,
// Which params appears and which parameter index its subsituted for
_types: FxHashSet<(Ty<'tcx>, usize)>,
types: FxHashSet<(Ty<'tcx>, usize)>,
}
impl<'tcx> TypeVisitor<'tcx> for GATSubstCollector<'tcx> {
@ -375,13 +398,20 @@ impl<'tcx> TypeVisitor<'tcx> for GATSubstCollector<'tcx> {
fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
match t.kind() {
ty::Projection(p) if p.item_def_id == self.gat => {
let (_, substs) = p.trait_ref_and_own_substs(self.tcx);
self.regions.extend(substs.iter().enumerate().filter_map(|(idx, subst)| {
for (idx, subst) in p.substs.iter().enumerate() {
match subst.unpack() {
GenericArgKind::Lifetime(lt) => Some((lt, idx)),
_ => None,
GenericArgKind::Lifetime(lt) => {
self.regions.insert((lt, idx));
}
GenericArgKind::Type(t) => match t.kind() {
ty::Param(_) => {
self.types.insert((t, idx));
}
_ => {}
},
_ => {}
}
}));
}
}
_ => {}
}

View File

@ -45,7 +45,6 @@ trait Deserializer3<T, U> {
trait Deserializer4 {
type Out<'x>;
//~^ Missing bound
fn deserialize<'a, T>(&self, input: &'a T) -> Self::Out<'a>;
}
@ -53,7 +52,6 @@ struct Wrap<T>(T);
trait Des {
type Out<'x, D>;
//~^ Missing bound
fn des<'z, T>(&self, data: &'z Wrap<T>) -> Self::Out<'z, Wrap<T>>;
}
/*
@ -92,5 +90,5 @@ impl Des3 for () {
}
}
*/
fn main() {}

View File

@ -16,12 +16,6 @@ error: Missing bound: T: 'x
LL | type Out<'x>;
| ^^^^
error: Missing bound: T: 'x
--> $DIR/self-outlives-lint.rs:40:13
|
LL | type Out<'x, 'y>;
| ^^^^^^^^
error: Missing bound: U: 'y
--> $DIR/self-outlives-lint.rs:40:13
|
@ -29,28 +23,22 @@ LL | type Out<'x, 'y>;
| ^^^^^^^^
error: Missing bound: T: 'x
--> $DIR/self-outlives-lint.rs:47:13
--> $DIR/self-outlives-lint.rs:40:13
|
LL | type Out<'x>;
| ^^^^
LL | type Out<'x, 'y>;
| ^^^^^^^^
error: Missing bound: T: 'x
--> $DIR/self-outlives-lint.rs:55:13
error: Missing bound: D: 'x
--> $DIR/self-outlives-lint.rs:67:13
|
LL | type Out<'x, D>;
| ^^^^^^^
error: Missing bound: T: 'x
--> $DIR/self-outlives-lint.rs:69:13
error: Missing bound: D: 'x
--> $DIR/self-outlives-lint.rs:81:13
|
LL | type Out<'x, D>;
| ^^^^^^^
error: Missing bound: T: 'x
--> $DIR/self-outlives-lint.rs:83:13
|
LL | type Out<'x, D>;
| ^^^^^^^
error: aborting due to 9 previous errors
error: aborting due to 7 previous errors