about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_typeck/src/check/wfcheck.rs104
1 files changed, 98 insertions, 6 deletions
diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_typeck/src/check/wfcheck.rs
index 30aab38b1eb..cb4d640f98b 100644
--- a/compiler/rustc_typeck/src/check/wfcheck.rs
+++ b/compiler/rustc_typeck/src/check/wfcheck.rs
@@ -11,16 +11,17 @@ use rustc_hir::intravisit::Visitor;
 use rustc_hir::itemlikevisit::ParItemLikeVisitor;
 use rustc_hir::lang_items::LangItem;
 use rustc_hir::ItemKind;
+use rustc_infer::infer::TyCtxtInferExt;
 use rustc_middle::hir::map as hir_map;
-use rustc_middle::ty::subst::{InternalSubsts, Subst};
+use rustc_middle::ty::subst::{GenericArgKind, InternalSubsts, Subst};
 use rustc_middle::ty::trait_def::TraitSpecializationKind;
-use rustc_middle::ty::{
-    self, AdtKind, GenericParamDefKind, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness,
-};
+use rustc_middle::ty::{self, AdtKind, GenericParamDefKind, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeVisitor, WithConstness};
+use rustc_session::lint;
 use rustc_session::parse::feature_err;
 use rustc_span::symbol::{sym, Ident, Symbol};
-use rustc_span::Span;
-use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
+use rustc_span::{DUMMY_SP, Span};
+use rustc_trait_selection::traits::query::evaluate_obligation::{InferCtxtExt as _};
+use rustc_trait_selection::traits::query::outlives_bounds::{InferCtxtExt as _};
 use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode, WellFormedLoc};
 
 use std::convert::TryInto;
@@ -253,6 +254,97 @@ pub fn check_trait_item(tcx: TyCtxt<'_>, def_id: LocalDefId) {
                 .emit();
         }
     }
+
+    // Require that the user writes as where clauses on GATs the implicit
+    // outlives bounds involving trait parameters in trait functions and
+    // lifetimes passed as GAT substs. See `self-outlives-lint` test.
+    let item = tcx.associated_item(trait_item.def_id);
+    let generics: &ty::Generics = tcx.generics_of(trait_item.def_id);
+    if matches!(item.kind, ty::AssocKind::Type) && generics.params.len() > 0 {
+        let associated_items: &ty::AssocItems<'_> = tcx.associated_items(encl_trait_def_id);
+        associated_items
+            .in_definition_order()
+            .filter(|item| matches!(item.kind, ty::AssocKind::Fn))
+            .for_each(|item| {
+                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);
+                    let output = sig.output();
+                    let mut visitor = RegionsInGATs {
+                        tcx,
+                        gat: trait_item.def_id.to_def_id(),
+                        regions: FxHashSet::default(),
+                    };
+                    output.visit_with(&mut visitor);
+                    for input in sig.inputs() {
+                        let bounds = infcx.implied_outlives_bounds(ty::ParamEnv::empty(), hir_id, input, DUMMY_SP);
+                        debug!(?bounds);
+                        let mut clauses = FxHashSet::default();
+                        for bound in bounds {
+                            match bound {
+                                traits::query::OutlivesBound::RegionSubParam(r, p) => {
+                                    for idx in visitor.regions.iter().filter(|(proj_r, _)| proj_r == &r).map(|r| r.1) {
+                                        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);
+                                    }
+                                }
+                                _ => {}
+                            }
+                        }
+                        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();
+                            }
+                        }
+                    }
+                })
+            });
+    }
+}
+
+struct RegionsInGATs<'tcx> {
+    tcx: TyCtxt<'tcx>,
+    gat: DefId,
+    // Which region appears and which parameter index its subsituted for
+    regions: FxHashSet<(ty::Region<'tcx>, usize)>,
+}
+
+impl<'tcx> TypeVisitor<'tcx> for RegionsInGATs<'tcx> {
+    type BreakTy = !;
+
+    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)| {
+                    match subst.unpack() {
+                        GenericArgKind::Lifetime(lt) => Some((lt, idx)),
+                        _ => None,
+                    }
+                }));
+            }
+            _ => {}
+        }
+        t.super_visit_with(self)
+    }
+
+    fn tcx_for_anon_const_substs(&self) -> Option<TyCtxt<'tcx>> {
+        Some(self.tcx)
+    }
 }
 
 fn could_be_self(trait_def_id: LocalDefId, ty: &hir::Ty<'_>) -> bool {