]> git.lizzy.rs Git - rust.git/commitdiff
Change outlives clause checking algorithm
authorjackh726 <jack.huey@umassmed.edu>
Sat, 16 Oct 2021 06:29:59 +0000 (02:29 -0400)
committerjackh726 <jack.huey@umassmed.edu>
Sat, 16 Oct 2021 23:04:39 +0000 (19:04 -0400)
compiler/rustc_infer/src/infer/mod.rs
compiler/rustc_typeck/src/check/regionck.rs
compiler/rustc_typeck/src/check/wfcheck.rs
src/test/ui/generic-associated-types/self-outlives-lint.rs
src/test/ui/generic-associated-types/self-outlives-lint.stderr

index 18836d5a68e26e8cdf67f5f1b18c9182ef6c54a9..ad716c76c80aa3ef21f87b0ec5300b0cca3fb491 100644 (file)
@@ -1255,16 +1255,16 @@ pub fn set_tainted_by_errors(&self) {
         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 @@ pub fn resolve_regions_and_report_errors(
         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
index 7c8b75271871a9a1275ba4cece3796abdfcbf81a..4ed149e1fe792782f8e2bc23b589dc08dcfcdcdb 100644 (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>,
index f9a309c775dae73a789a63240496384d648e1d01..dc9784594c21ba7544ba3538fb93028cfa96a0ce 100644 (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};
 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_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));
+                            }
+                            _ => {}
+                        },
+                        _ => {}
                     }
-                }));
+                }
             }
             _ => {}
         }
index 92ac1a4249e4bd5da8184deea400e4781178d32b..7a1fb51eafa0a3bfbc079d7e343d8ce055a75aba 100644 (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 @@ trait Deserializer4 {
 
 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 @@ fn des<'a, T>(&self, data: &'a T) -> Self::Out<'a, T> {
     }
 }
 */
-  
+
 fn main() {}
index 3fb41335495299f99839578c97879697a8854968..42af6d25a2380787b0e12245943c03cca9526bf7 100644 (file)
@@ -16,41 +16,29 @@ error: Missing bound: T: 'x
 LL |     type Out<'x>;
    |             ^^^^
 
-error: Missing bound: T: 'x
+error: Missing bound: U: 'y
   --> $DIR/self-outlives-lint.rs:40:13
    |
 LL |     type Out<'x, 'y>;
    |             ^^^^^^^^
 
-error: Missing bound: U: 'y
+error: Missing bound: T: 'x
   --> $DIR/self-outlives-lint.rs:40:13
    |
 LL |     type Out<'x, 'y>;
    |             ^^^^^^^^
 
-error: Missing bound: T: 'x
-  --> $DIR/self-outlives-lint.rs:47:13
-   |
-LL |     type Out<'x>;
-   |             ^^^^
-
-error: Missing bound: T: 'x
-  --> $DIR/self-outlives-lint.rs:55: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:67:13
    |
 LL |     type Out<'x, D>;
    |             ^^^^^^^
 
-error: Missing bound: T: 'x
-  --> $DIR/self-outlives-lint.rs:83:13
+error: Missing bound: D: 'x
+  --> $DIR/self-outlives-lint.rs:81:13
    |
 LL |     type Out<'x, D>;
    |             ^^^^^^^
 
-error: aborting due to 9 previous errors
+error: aborting due to 7 previous errors