From 82148cdc66d9c704c686d35840f70b213f0a1514 Mon Sep 17 00:00:00 2001 From: jackh726 Date: Sat, 16 Oct 2021 02:29:59 -0400 Subject: [PATCH] Change outlives clause checking algorithm --- compiler/rustc_infer/src/infer/mod.rs | 21 +- compiler/rustc_typeck/src/check/regionck.rs | 2 +- compiler/rustc_typeck/src/check/wfcheck.rs | 190 ++++++++++-------- .../self-outlives-lint.rs | 4 +- .../self-outlives-lint.stderr | 28 +-- 5 files changed, 138 insertions(+), 107 deletions(-) diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 18836d5a68e2..ad716c76c80a 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -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> { 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 diff --git a/compiler/rustc_typeck/src/check/regionck.rs b/compiler/rustc_typeck/src/check/regionck.rs index 7c8b75271871..4ed149e1fe79 100644 --- a/compiler/rustc_typeck/src/check/regionck.rs +++ b/compiler/rustc_typeck/src/check/regionck.rs @@ -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>, diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_typeck/src/check/wfcheck.rs index f9a309c775da..dc9784594c21 100644 --- a/compiler/rustc_typeck/src/check/wfcheck.rs +++ b/compiler/rustc_typeck/src/check/wfcheck.rs @@ -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 { 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)); + } + _ => {} + }, + _ => {} } - })); + } } _ => {} } diff --git a/src/test/ui/generic-associated-types/self-outlives-lint.rs b/src/test/ui/generic-associated-types/self-outlives-lint.rs index 92ac1a4249e4..7a1fb51eafa0 100644 --- a/src/test/ui/generic-associated-types/self-outlives-lint.rs +++ b/src/test/ui/generic-associated-types/self-outlives-lint.rs @@ -45,7 +45,6 @@ trait Deserializer3 { 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); trait Des { type Out<'x, D>; - //~^ Missing bound fn des<'z, T>(&self, data: &'z Wrap) -> Self::Out<'z, Wrap>; } /* @@ -92,5 +90,5 @@ impl Des3 for () { } } */ - + fn main() {} diff --git a/src/test/ui/generic-associated-types/self-outlives-lint.stderr b/src/test/ui/generic-associated-types/self-outlives-lint.stderr index 3fb413354952..42af6d25a238 100644 --- a/src/test/ui/generic-associated-types/self-outlives-lint.stderr +++ b/src/test/ui/generic-associated-types/self-outlives-lint.stderr @@ -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