mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-30 10:45:18 +00:00
Change outlives clause checking algorithm
This commit is contained in:
parent
a8c44d344b
commit
82148cdc66
@ -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
|
||||
|
@ -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>,
|
||||
|
@ -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,
|
||||
®ion_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));
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
@ -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() {}
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user