1
Fork 0

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) 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 /// result. After this, no more unification operations should be
/// done -- or the compiler will panic -- but it is legal to use /// done -- or the compiler will panic -- but it is legal to use
/// `resolve_vars_if_possible` as well as `fully_resolve`. /// `resolve_vars_if_possible` as well as `fully_resolve`.
pub fn resolve_regions_and_report_errors( pub fn resolve_regions(
&self, &self,
region_context: DefId, region_context: DefId,
outlives_env: &OutlivesEnvironment<'tcx>, outlives_env: &OutlivesEnvironment<'tcx>,
mode: RegionckMode, mode: RegionckMode,
) { ) -> Vec<RegionResolutionError<'tcx>> {
let (var_infos, data) = { let (var_infos, data) = {
let mut inner = self.inner.borrow_mut(); let mut inner = self.inner.borrow_mut();
let inner = &mut *inner; 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)); let old_value = self.lexical_region_resolutions.replace(Some(lexical_region_resolutions));
assert!(old_value.is_none()); 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() { if !self.is_tainted_by_errors() {
// As a heuristic, just skip reporting region errors // As a heuristic, just skip reporting region errors
// altogether if other errors have been reported while // 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( fn add_implied_bounds(
&mut self, &mut self,
infcx: &InferCtxt<'a, 'tcx>, infcx: &InferCtxt<'a, 'tcx>,

View file

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

View file

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