Change outlives clause checking algorithm
This commit is contained in:
parent
a8c44d344b
commit
82148cdc66
5 changed files with 138 additions and 107 deletions
|
@ -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
|
||||||
|
|
|
@ -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>,
|
||||||
|
|
|
@ -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,
|
||||||
{
|
®ion_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));
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>>;
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -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
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue