diff options
Diffstat (limited to 'compiler')
| -rw-r--r-- | compiler/rustc_typeck/src/check/wfcheck.rs | 104 |
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 { |
