From 0bc340f7564a7a046b3acaa69d6aa1653ed27d66 Mon Sep 17 00:00:00 2001 From: jackh726 Date: Thu, 7 Oct 2021 17:00:39 -0400 Subject: [PATCH 01/10] Error initial pass --- compiler/rustc_typeck/src/check/wfcheck.rs | 104 +++++++++++++++++- .../self-outlives-lint.rs | 41 +++++++ 2 files changed, 139 insertions(+), 6 deletions(-) create mode 100644 src/test/ui/generic-associated-types/self-outlives-lint.rs diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_typeck/src/check/wfcheck.rs index 30aab38b1eb85..cb4d640f98bd9 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 { + 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> { + Some(self.tcx) + } } fn could_be_self(trait_def_id: LocalDefId, ty: &hir::Ty<'_>) -> bool { diff --git a/src/test/ui/generic-associated-types/self-outlives-lint.rs b/src/test/ui/generic-associated-types/self-outlives-lint.rs new file mode 100644 index 0000000000000..43870a88479d5 --- /dev/null +++ b/src/test/ui/generic-associated-types/self-outlives-lint.rs @@ -0,0 +1,41 @@ +#![feature(generic_associated_types)] + +// check-fail + +trait Iterable { + type Item<'x>; + fn iter<'a>(&'a self) -> Self::Item<'a>; +} + +/* +impl Iterable for T { + type Item<'a> = &'a T; + fn iter<'a>(&'a self) -> Self::Item<'a> { + self + } +} +*/ + +trait Deserializer { + type Out<'x>; + fn deserialize<'a>(&self, input: &'a T) -> Self::Out<'a>; +} + +/* +impl Deserializer for () { + type Out<'a> = &'a T; + fn deserialize<'a>(&self, input: &'a T) -> Self::Out<'a> { input } +} +*/ + +trait Deserializer2 { + type Out<'x>; + fn deserialize2<'a, 'b: 'a>(&self, input: &'a T, input2: &'b T) -> Self::Out<'a>; +} + +trait Deserializer3 { + type Out<'x, 'y>; + fn deserialize2<'a, 'b>(&self, input: &'a T, input2: &'b U) -> Self::Out<'a, 'b>; +} + +fn main() {} From a8c44d344b4b416f119c0b24bb3cb3899b2f5ab1 Mon Sep 17 00:00:00 2001 From: jackh726 Date: Fri, 8 Oct 2021 12:09:34 -0400 Subject: [PATCH 02/10] Some cleanup --- compiler/rustc_typeck/src/check/wfcheck.rs | 164 +++++++++++------- .../self-outlives-lint.rs | 55 ++++++ .../self-outlives-lint.stderr | 56 ++++++ 3 files changed, 216 insertions(+), 59 deletions(-) create mode 100644 src/test/ui/generic-associated-types/self-outlives-lint.stderr diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_typeck/src/check/wfcheck.rs index cb4d640f98bd9..f9a309c775dae 100644 --- a/compiler/rustc_typeck/src/check/wfcheck.rs +++ b/compiler/rustc_typeck/src/check/wfcheck.rs @@ -1,6 +1,7 @@ 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}; @@ -15,13 +16,14 @@ use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::hir::map as hir_map; 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, TypeVisitor, WithConstness}; -use rustc_session::lint; +use rustc_middle::ty::{ + self, AdtKind, GenericParamDefKind, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeVisitor, + WithConstness, +}; use rustc_session::parse::feature_err; use rustc_span::symbol::{sym, Ident, Symbol}; -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_span::Span; +use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode, WellFormedLoc}; use std::convert::TryInto; @@ -255,75 +257,119 @@ pub fn check_trait_item(tcx: TyCtxt<'_>, def_id: LocalDefId) { } } - // 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. + check_gat_where_clauses(tcx, trait_item, encl_trait_def_id); +} + +/// 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. +fn check_gat_where_clauses( + tcx: TyCtxt<'_>, + trait_item: &hir::TraitItem<'_>, + encl_trait_def_id: DefId, +) { let item = tcx.associated_item(trait_item.def_id); + // If the current trait item isn't a type, it isn't a GAT + if !matches!(item.kind, ty::AssocKind::Type) { + return; + } 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), + // If the current associated type doesn't have any (own) params, it's not a GAT + if generics.params.len() == 0 { + return; + } + let associated_items: &ty::AssocItems<'_> = tcx.associated_items(encl_trait_def_id); + // 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), ); - error.emit(); + 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(); + } + } + }) } } -struct RegionsInGATs<'tcx> { +struct GATSubstCollector<'tcx> { tcx: TyCtxt<'tcx>, gat: DefId, // 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)>, } -impl<'tcx> TypeVisitor<'tcx> for RegionsInGATs<'tcx> { +impl<'tcx> TypeVisitor<'tcx> for GATSubstCollector<'tcx> { type BreakTy = !; fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow { 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 43870a88479d5..92ac1a4249e4b 100644 --- a/src/test/ui/generic-associated-types/self-outlives-lint.rs +++ b/src/test/ui/generic-associated-types/self-outlives-lint.rs @@ -4,6 +4,7 @@ trait Iterable { type Item<'x>; + //~^ Missing bound fn iter<'a>(&'a self) -> Self::Item<'a>; } @@ -18,6 +19,7 @@ impl Iterable for T { trait Deserializer { type Out<'x>; + //~^ Missing bound fn deserialize<'a>(&self, input: &'a T) -> Self::Out<'a>; } @@ -30,12 +32,65 @@ impl Deserializer for () { trait Deserializer2 { type Out<'x>; + //~^ Missing bound fn deserialize2<'a, 'b: 'a>(&self, input: &'a T, input2: &'b T) -> Self::Out<'a>; } trait Deserializer3 { type Out<'x, 'y>; + //~^ Missing bound + //~^^ Missing bound fn deserialize2<'a, 'b>(&self, input: &'a T, input2: &'b U) -> Self::Out<'a, 'b>; } +trait Deserializer4 { + type Out<'x>; + //~^ Missing bound + fn deserialize<'a, T>(&self, input: &'a T) -> Self::Out<'a>; +} + +struct Wrap(T); + +trait Des { + type Out<'x, D>; + //~^ Missing bound + fn des<'z, T>(&self, data: &'z Wrap) -> Self::Out<'z, Wrap>; +} +/* +impl Des for () { + type Out<'x, D> = &'x D; + fn des<'a, T>(&self, data: &'a Wrap) -> Self::Out<'a, Wrap> { + data + } +} +*/ + +trait Des2 { + type Out<'x, D>; + //~^ Missing bound + fn des<'z, T>(&self, data: &'z Wrap) -> Self::Out<'z, T>; +} +/* +impl Des2 for () { + type Out<'x, D> = &'x D; + fn des<'a, T>(&self, data: &'a Wrap) -> Self::Out<'a, T> { + data + } +} +*/ + +trait Des3 { + type Out<'x, D>; + //~^ Missing bound + fn des<'z, T>(&self, data: &'z T) -> Self::Out<'z, T>; +} +/* +impl Des3 for () { + type Out<'x, D> = &'x D; + fn des<'a, T>(&self, data: &'a T) -> Self::Out<'a, T> { + data + } +} +*/ + 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 new file mode 100644 index 0000000000000..3fb4133549529 --- /dev/null +++ b/src/test/ui/generic-associated-types/self-outlives-lint.stderr @@ -0,0 +1,56 @@ +error: Missing bound: Self: 'x + --> $DIR/self-outlives-lint.rs:6:14 + | +LL | type Item<'x>; + | ^^^^ + +error: Missing bound: T: 'x + --> $DIR/self-outlives-lint.rs:21:13 + | +LL | type Out<'x>; + | ^^^^ + +error: Missing bound: T: 'x + --> $DIR/self-outlives-lint.rs:34:13 + | +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 + | +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 + | +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 + From 82148cdc66d9c704c686d35840f70b213f0a1514 Mon Sep 17 00:00:00 2001 From: jackh726 Date: Sat, 16 Oct 2021 02:29:59 -0400 Subject: [PATCH 03/10] 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 | 26 +-- 5 files changed, 137 insertions(+), 106 deletions(-) diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 18836d5a68e26..ad716c76c80aa 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 7c8b75271871a..4ed149e1fe792 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 f9a309c775dae..dc9784594c21b 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 92ac1a4249e4b..7a1fb51eafa0a 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 3fb4133549529..42af6d25a2380 100644 --- a/src/test/ui/generic-associated-types/self-outlives-lint.stderr +++ b/src/test/ui/generic-associated-types/self-outlives-lint.stderr @@ -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 From e391796d4729c5103a993461c72b5eebc1dac943 Mon Sep 17 00:00:00 2001 From: jackh726 Date: Sat, 16 Oct 2021 11:50:56 -0400 Subject: [PATCH 04/10] Better error --- compiler/rustc_typeck/src/check/wfcheck.rs | 40 +++++++++---- compiler/rustc_typeck/src/lib.rs | 1 + .../self-outlives-lint.rs | 29 ++++++---- .../self-outlives-lint.stderr | 56 ++++++++++--------- 4 files changed, 81 insertions(+), 45 deletions(-) diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_typeck/src/check/wfcheck.rs index dc9784594c21b..0ab9855d3250a 100644 --- a/compiler/rustc_typeck/src/check/wfcheck.rs +++ b/compiler/rustc_typeck/src/check/wfcheck.rs @@ -288,7 +288,6 @@ fn check_gat_where_clauses( associated_items.in_definition_order().filter(|item| matches!(item.kind, ty::AssocKind::Fn)) { 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); @@ -308,7 +307,7 @@ fn check_gat_where_clauses( 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.add_implied_bounds(&infcx, wf_tys.clone(), id, DUMMY_SP); outlives_environment.save_implied_bounds(id); let region_bound_pairs = outlives_environment.region_bound_pairs_map().get(&id).unwrap(); @@ -349,7 +348,6 @@ fn check_gat_where_clauses( 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, @@ -372,13 +370,35 @@ fn check_gat_where_clauses( 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(); + let clauses: Vec<_> = clauses + .drain_filter(|clause| { + written_predicates.predicates.iter().find(|p| &p.0 == clause).is_none() + }) + .map(|clause| format!("{}", clause)) + .collect(); + if !clauses.is_empty() { + let mut err = tcx.sess.struct_span_err( + trait_item.span, + &format!("Missing required bounds on {}", trait_item.ident), + ); + + let suggestion = format!( + "{} {}", + if !trait_item.generics.where_clause.predicates.is_empty() { + "," + } else { + " where" + }, + clauses.join(", "), + ); + err.span_suggestion( + trait_item.generics.where_clause.tail_span_for_suggestion(), + "add the required where clauses", + suggestion, + Applicability::MachineApplicable, + ); + + err.emit() } } } diff --git a/compiler/rustc_typeck/src/lib.rs b/compiler/rustc_typeck/src/lib.rs index 971776c882a15..50afdc23631b9 100644 --- a/compiler/rustc_typeck/src/lib.rs +++ b/compiler/rustc_typeck/src/lib.rs @@ -69,6 +69,7 @@ This API is completely unstable and subject to change. #![feature(never_type)] #![feature(slice_partition_dedup)] #![feature(control_flow_enum)] +#![feature(hash_drain_filter)] #![recursion_limit = "256"] #[macro_use] 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 7a1fb51eafa0a..76273c5249ec1 100644 --- a/src/test/ui/generic-associated-types/self-outlives-lint.rs +++ b/src/test/ui/generic-associated-types/self-outlives-lint.rs @@ -2,9 +2,10 @@ // check-fail +// We have a `&'a self`, so we need a `Self: 'a` trait Iterable { type Item<'x>; - //~^ Missing bound + //~^ Missing required bounds fn iter<'a>(&'a self) -> Self::Item<'a>; } @@ -17,9 +18,10 @@ impl Iterable for T { } */ +// We have a `&'a T`, so we need a `T: 'x` trait Deserializer { type Out<'x>; - //~^ Missing bound + //~^ Missing required bounds fn deserialize<'a>(&self, input: &'a T) -> Self::Out<'a>; } @@ -30,19 +32,21 @@ impl Deserializer for () { } */ +// We have a `&'b T` and a `'b: 'a`, so it is implied that `T: 'a`. Therefore, we need a `T: 'x` trait Deserializer2 { type Out<'x>; - //~^ Missing bound - fn deserialize2<'a, 'b: 'a>(&self, input: &'a T, input2: &'b T) -> Self::Out<'a>; + //~^ Missing required bounds + fn deserialize2<'a, 'b: 'a>(&self, input1: &'b T) -> Self::Out<'a>; } +// We have a `&'a T` and a `&'b U`, so we need a `T: 'x` and a `U: 'y` trait Deserializer3 { type Out<'x, 'y>; - //~^ Missing bound - //~^^ Missing bound + //~^ Missing required bounds fn deserialize2<'a, 'b>(&self, input: &'a T, input2: &'b U) -> Self::Out<'a, 'b>; } +// `T` is a param on the function, so it can't be named by the associated type trait Deserializer4 { type Out<'x>; fn deserialize<'a, T>(&self, input: &'a T) -> Self::Out<'a>; @@ -50,36 +54,41 @@ trait Deserializer4 { struct Wrap(T); +// Even though we might theoretically want `D: 'x`, because we pass `Wrap` and +// we see `&'z Wrap`, we are conservative and only add bounds for direct params trait Des { type Out<'x, D>; fn des<'z, T>(&self, data: &'z Wrap) -> Self::Out<'z, Wrap>; } /* impl Des for () { - type Out<'x, D> = &'x D; + type Out<'x, D> = &'x D; // Not okay fn des<'a, T>(&self, data: &'a Wrap) -> Self::Out<'a, Wrap> { data } } */ +// We have `T` and `'z` as GAT substs. Because of `&'z Wrap`, there is an +// implied bound that `T: 'z`, so we require `D: 'x` trait Des2 { type Out<'x, D>; - //~^ Missing bound + //~^ Missing required bounds fn des<'z, T>(&self, data: &'z Wrap) -> Self::Out<'z, T>; } /* impl Des2 for () { type Out<'x, D> = &'x D; fn des<'a, T>(&self, data: &'a Wrap) -> Self::Out<'a, T> { - data + &data.0 } } */ +// We see `&'z T`, so we require `D: 'x` trait Des3 { type Out<'x, D>; - //~^ Missing bound + //~^ Missing required bounds fn des<'z, T>(&self, data: &'z T) -> Self::Out<'z, T>; } /* 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 42af6d25a2380..077c64421a8ce 100644 --- a/src/test/ui/generic-associated-types/self-outlives-lint.stderr +++ b/src/test/ui/generic-associated-types/self-outlives-lint.stderr @@ -1,44 +1,50 @@ -error: Missing bound: Self: 'x - --> $DIR/self-outlives-lint.rs:6:14 +error: Missing required bounds on Item + --> $DIR/self-outlives-lint.rs:7:5 | LL | type Item<'x>; - | ^^^^ + | ^^^^^^^^^^^^^- + | | + | help: add the required where clauses: `where Self: 'x` -error: Missing bound: T: 'x - --> $DIR/self-outlives-lint.rs:21:13 +error: Missing required bounds on Out + --> $DIR/self-outlives-lint.rs:23:5 | LL | type Out<'x>; - | ^^^^ + | ^^^^^^^^^^^^- + | | + | help: add the required where clauses: `where T: 'x` -error: Missing bound: T: 'x - --> $DIR/self-outlives-lint.rs:34:13 +error: Missing required bounds on Out + --> $DIR/self-outlives-lint.rs:37:5 | LL | type Out<'x>; - | ^^^^ + | ^^^^^^^^^^^^- + | | + | help: add the required where clauses: `where T: 'x` -error: Missing bound: U: 'y - --> $DIR/self-outlives-lint.rs:40:13 +error: Missing required bounds on Out + --> $DIR/self-outlives-lint.rs:44:5 | LL | type Out<'x, 'y>; - | ^^^^^^^^ + | ^^^^^^^^^^^^^^^^- + | | + | help: add the required where clauses: `where U: 'y, T: 'x` -error: Missing bound: T: 'x - --> $DIR/self-outlives-lint.rs:40:13 - | -LL | type Out<'x, 'y>; - | ^^^^^^^^ - -error: Missing bound: D: 'x - --> $DIR/self-outlives-lint.rs:67:13 +error: Missing required bounds on Out + --> $DIR/self-outlives-lint.rs:75:5 | LL | type Out<'x, D>; - | ^^^^^^^ + | ^^^^^^^^^^^^^^^- + | | + | help: add the required where clauses: `where D: 'x` -error: Missing bound: D: 'x - --> $DIR/self-outlives-lint.rs:81:13 +error: Missing required bounds on Out + --> $DIR/self-outlives-lint.rs:90:5 | LL | type Out<'x, D>; - | ^^^^^^^ + | ^^^^^^^^^^^^^^^- + | | + | help: add the required where clauses: `where D: 'x` -error: aborting due to 7 previous errors +error: aborting due to 6 previous errors From c2da21063f4a3ae87c51de4182a6bc2f784e327c Mon Sep 17 00:00:00 2001 From: jackh726 Date: Sat, 16 Oct 2021 18:53:41 -0400 Subject: [PATCH 05/10] Edit GATs tests to apply lint --- .../collections-project-default.rs | 2 +- .../generic-associated-types/collections.rs | 2 +- .../generic-associated-type-bounds.rs | 2 +- .../generic-associated-types/issue-70303.rs | 2 +- .../generic-associated-types/issue-76535.rs | 2 +- .../issue-76535.stderr | 6 ++-- .../generic-associated-types/issue-79422.rs | 4 +-- .../issue-79422.stderr | 6 ++-- .../generic-associated-types/issue-86787.rs | 5 +-- .../issue-86787.stderr | 36 ++++--------------- .../generic-associated-types/issue-88287.rs | 4 ++- .../generic-associated-types/issue-88360.rs | 3 +- .../issue-88360.stderr | 2 +- .../projection-type-lifetime-mismatch.rs | 2 +- .../streaming_iterator.rs | 8 ++--- .../variance_constraints.rs | 2 +- 16 files changed, 35 insertions(+), 53 deletions(-) diff --git a/src/test/ui/generic-associated-types/collections-project-default.rs b/src/test/ui/generic-associated-types/collections-project-default.rs index 0944bf110c1b5..5b94cdee7c9c7 100644 --- a/src/test/ui/generic-associated-types/collections-project-default.rs +++ b/src/test/ui/generic-associated-types/collections-project-default.rs @@ -8,7 +8,7 @@ // check that we don't normalize with trait defaults. trait Collection { - type Iter<'iter>: Iterator where T: 'iter; + type Iter<'iter>: Iterator where T: 'iter, Self: 'iter; type Family: CollectionFamily; // Test associated type defaults with parameters type Sibling: Collection = diff --git a/src/test/ui/generic-associated-types/collections.rs b/src/test/ui/generic-associated-types/collections.rs index f14c6dac1b1ed..b0f2fb3f56785 100644 --- a/src/test/ui/generic-associated-types/collections.rs +++ b/src/test/ui/generic-associated-types/collections.rs @@ -8,7 +8,7 @@ // run-pass trait Collection { - type Iter<'iter>: Iterator where T: 'iter; + type Iter<'iter>: Iterator where T: 'iter, Self: 'iter; type Family: CollectionFamily; // Test associated type defaults with parameters type Sibling: Collection = diff --git a/src/test/ui/generic-associated-types/generic-associated-type-bounds.rs b/src/test/ui/generic-associated-types/generic-associated-type-bounds.rs index 5d3a3a893527e..d7c4dbda2644e 100644 --- a/src/test/ui/generic-associated-types/generic-associated-type-bounds.rs +++ b/src/test/ui/generic-associated-types/generic-associated-type-bounds.rs @@ -3,7 +3,7 @@ #![feature(generic_associated_types)] pub trait X { - type Y<'a>; + type Y<'a> where Self: 'a; fn m(&self) -> Self::Y<'_>; } diff --git a/src/test/ui/generic-associated-types/issue-70303.rs b/src/test/ui/generic-associated-types/issue-70303.rs index d238f53bde7be..568996e1a17aa 100644 --- a/src/test/ui/generic-associated-types/issue-70303.rs +++ b/src/test/ui/generic-associated-types/issue-70303.rs @@ -3,7 +3,7 @@ #![feature(generic_associated_types)] trait Document { - type Cursor<'a>: DocCursor<'a>; + type Cursor<'a>: DocCursor<'a> where Self: 'a; fn cursor(&self) -> Self::Cursor<'_>; } diff --git a/src/test/ui/generic-associated-types/issue-76535.rs b/src/test/ui/generic-associated-types/issue-76535.rs index 1dad856d5a370..20c6924afa614 100644 --- a/src/test/ui/generic-associated-types/issue-76535.rs +++ b/src/test/ui/generic-associated-types/issue-76535.rs @@ -3,7 +3,7 @@ pub trait SubTrait {} pub trait SuperTrait { - type SubType<'a>: SubTrait; + type SubType<'a>: SubTrait where Self: 'a; fn get_sub<'a>(&'a mut self) -> Self::SubType<'a>; } diff --git a/src/test/ui/generic-associated-types/issue-76535.stderr b/src/test/ui/generic-associated-types/issue-76535.stderr index 0a7eb5dde6009..64eeec1b2fcbe 100644 --- a/src/test/ui/generic-associated-types/issue-76535.stderr +++ b/src/test/ui/generic-associated-types/issue-76535.stderr @@ -7,7 +7,7 @@ LL | let sub: Box> = Box::new(SuperStruc note: associated type defined here, with 1 lifetime parameter: `'a` --> $DIR/issue-76535.rs:6:10 | -LL | type SubType<'a>: SubTrait; +LL | type SubType<'a>: SubTrait where Self: 'a; | ^^^^^^^ -- help: add missing lifetime argument | @@ -25,7 +25,7 @@ note: for a trait to be "object safe" it needs to allow building a vtable to all | LL | pub trait SuperTrait { | ---------- this trait cannot be made into an object... -LL | type SubType<'a>: SubTrait; +LL | type SubType<'a>: SubTrait where Self: 'a; | ^^^^^^^ ...because it contains the generic associated type `SubType` = help: consider moving `SubType` to another trait @@ -40,7 +40,7 @@ note: for a trait to be "object safe" it needs to allow building a vtable to all | LL | pub trait SuperTrait { | ---------- this trait cannot be made into an object... -LL | type SubType<'a>: SubTrait; +LL | type SubType<'a>: SubTrait where Self: 'a; | ^^^^^^^ ...because it contains the generic associated type `SubType` = help: consider moving `SubType` to another trait = note: required because of the requirements on the impl of `CoerceUnsized>>>` for `Box` diff --git a/src/test/ui/generic-associated-types/issue-79422.rs b/src/test/ui/generic-associated-types/issue-79422.rs index 7f0ac34835884..47ef38ff45d65 100644 --- a/src/test/ui/generic-associated-types/issue-79422.rs +++ b/src/test/ui/generic-associated-types/issue-79422.rs @@ -17,12 +17,12 @@ impl<'a, T> RefCont<'a, T> for Box { } trait MapLike { - type VRefCont<'a>: RefCont<'a, V>; + type VRefCont<'a>: RefCont<'a, V> where Self: 'a; fn get<'a>(&'a self, key: &K) -> Option>; } impl MapLike for std::collections::BTreeMap { - type VRefCont<'a> = &'a V; + type VRefCont<'a> where Self: 'a = &'a V; fn get<'a>(&'a self, key: &K) -> Option<&'a V> { std::collections::BTreeMap::get(self, key) } diff --git a/src/test/ui/generic-associated-types/issue-79422.stderr b/src/test/ui/generic-associated-types/issue-79422.stderr index b6f856a97e725..8b6f9b866e5ef 100644 --- a/src/test/ui/generic-associated-types/issue-79422.stderr +++ b/src/test/ui/generic-associated-types/issue-79422.stderr @@ -7,7 +7,7 @@ LL | as Box>>; note: associated type defined here, with 1 lifetime parameter: `'a` --> $DIR/issue-79422.rs:20:10 | -LL | type VRefCont<'a>: RefCont<'a, V>; +LL | type VRefCont<'a>: RefCont<'a, V> where Self: 'a; | ^^^^^^^^ -- help: add missing lifetime argument | @@ -25,7 +25,7 @@ note: for a trait to be "object safe" it needs to allow building a vtable to all | LL | trait MapLike { | ------- this trait cannot be made into an object... -LL | type VRefCont<'a>: RefCont<'a, V>; +LL | type VRefCont<'a>: RefCont<'a, V> where Self: 'a; | ^^^^^^^^ ...because it contains the generic associated type `VRefCont` = help: consider moving `VRefCont` to another trait @@ -40,7 +40,7 @@ note: for a trait to be "object safe" it needs to allow building a vtable to all | LL | trait MapLike { | ------- this trait cannot be made into an object... -LL | type VRefCont<'a>: RefCont<'a, V>; +LL | type VRefCont<'a>: RefCont<'a, V> where Self: 'a; | ^^^^^^^^ ...because it contains the generic associated type `VRefCont` = help: consider moving `VRefCont` to another trait = note: required because of the requirements on the impl of `CoerceUnsized + 'static)>>>` for `Box>` diff --git a/src/test/ui/generic-associated-types/issue-86787.rs b/src/test/ui/generic-associated-types/issue-86787.rs index f1f05ea6627e8..0f62f83e2563b 100644 --- a/src/test/ui/generic-associated-types/issue-86787.rs +++ b/src/test/ui/generic-associated-types/issue-86787.rs @@ -9,6 +9,7 @@ enum Either { pub trait HasChildrenOf { type T; type TRef<'a>; + //~^ Missing required bounds fn ref_children<'a>(&'a self) -> Vec>; fn take_children(self) -> Vec; @@ -20,9 +21,9 @@ where Right: HasChildrenOf, { type T = Either; + // We used to error below because the where clause doesn't match the trait. + // Now, we error early on the trait itself. type TRef<'a> - //~^ `impl` associated type signature - //~^^ `impl` associated type signature where ::T: 'a, ::T: 'a diff --git a/src/test/ui/generic-associated-types/issue-86787.stderr b/src/test/ui/generic-associated-types/issue-86787.stderr index 648eff77d73bb..87dcd875de703 100644 --- a/src/test/ui/generic-associated-types/issue-86787.stderr +++ b/src/test/ui/generic-associated-types/issue-86787.stderr @@ -1,32 +1,10 @@ -error: `impl` associated type signature for `TRef` doesn't match `trait` associated type signature - --> $DIR/issue-86787.rs:23:5 +error: Missing required bounds on TRef + --> $DIR/issue-86787.rs:11:5 | -LL | type TRef<'a>; - | -------------- expected -... -LL | / type TRef<'a> -LL | | -LL | | -LL | | where -LL | | ::T: 'a, -LL | | ::T: 'a -LL | | = Either<&'a Left::T, &'a Right::T>; - | |________________________________________^ found +LL | type TRef<'a>; + | ^^^^^^^^^^^^^- + | | + | help: add the required where clauses: `where Self: 'a` -error: `impl` associated type signature for `TRef` doesn't match `trait` associated type signature - --> $DIR/issue-86787.rs:23:5 - | -LL | type TRef<'a>; - | -------------- expected -... -LL | / type TRef<'a> -LL | | -LL | | -LL | | where -LL | | ::T: 'a, -LL | | ::T: 'a -LL | | = Either<&'a Left::T, &'a Right::T>; - | |________________________________________^ found - -error: aborting due to 2 previous errors +error: aborting due to previous error diff --git a/src/test/ui/generic-associated-types/issue-88287.rs b/src/test/ui/generic-associated-types/issue-88287.rs index 2e65af594a6bd..df5586ed422f8 100644 --- a/src/test/ui/generic-associated-types/issue-88287.rs +++ b/src/test/ui/generic-associated-types/issue-88287.rs @@ -13,7 +13,8 @@ trait SearchableResource { trait SearchableResourceExt: SearchableResource { type Future<'f, A: 'f + ?Sized, B: 'f>: Future, ()>> + 'f where - A: SearchableResource; + A: SearchableResource, + Self: 'f; fn search<'c>(&'c self, client: &'c ()) -> Self::Future<'c, Self, Criteria>; } @@ -29,6 +30,7 @@ where type Future<'f, A, B: 'f> where A: SearchableResource + ?Sized + 'f, + Self: 'f, = SearchFutureTy<'f, A, B>; fn search<'c>(&'c self, _client: &'c ()) -> Self::Future<'c, Self, Criteria> { diff --git a/src/test/ui/generic-associated-types/issue-88360.rs b/src/test/ui/generic-associated-types/issue-88360.rs index 06af3f5ec96d6..8ee98201aba7a 100644 --- a/src/test/ui/generic-associated-types/issue-88360.rs +++ b/src/test/ui/generic-associated-types/issue-88360.rs @@ -1,13 +1,14 @@ #![feature(generic_associated_types)] trait GatTrait { - type Gat<'a>; + type Gat<'a> where Self: 'a; fn test(&self) -> Self::Gat<'_>; } trait SuperTrait where + Self: 'static, for<'a> Self: GatTrait = &'a T>, { fn copy(&self) -> Self::Gat<'_> where T: Copy { diff --git a/src/test/ui/generic-associated-types/issue-88360.stderr b/src/test/ui/generic-associated-types/issue-88360.stderr index cfbf3aaa4e65d..5f769d799faa1 100644 --- a/src/test/ui/generic-associated-types/issue-88360.stderr +++ b/src/test/ui/generic-associated-types/issue-88360.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/issue-88360.rs:14:9 + --> $DIR/issue-88360.rs:15:9 | LL | trait SuperTrait | - this type parameter diff --git a/src/test/ui/generic-associated-types/projection-type-lifetime-mismatch.rs b/src/test/ui/generic-associated-types/projection-type-lifetime-mismatch.rs index b976ee3261fcc..bcbcfc1899637 100644 --- a/src/test/ui/generic-associated-types/projection-type-lifetime-mismatch.rs +++ b/src/test/ui/generic-associated-types/projection-type-lifetime-mismatch.rs @@ -1,7 +1,7 @@ #![feature(generic_associated_types)] pub trait X { - type Y<'a>; + type Y<'a> where Self: 'a; fn m(&self) -> Self::Y<'_>; } diff --git a/src/test/ui/generic-associated-types/streaming_iterator.rs b/src/test/ui/generic-associated-types/streaming_iterator.rs index 2feff9f4c6f2f..f83d4d7b68e48 100644 --- a/src/test/ui/generic-associated-types/streaming_iterator.rs +++ b/src/test/ui/generic-associated-types/streaming_iterator.rs @@ -5,12 +5,12 @@ use std::fmt::Display; trait StreamingIterator { - type Item<'a>; + type Item<'a> where Self: 'a; // Applying the lifetime parameter `'a` to `Self::Item` inside the trait. fn next<'a>(&'a mut self) -> Option>; } -struct Foo { +struct Foo { // Applying a concrete lifetime to the constructor outside the trait. bar: ::Item<'static>, } @@ -30,7 +30,7 @@ struct StreamEnumerate { } impl StreamingIterator for StreamEnumerate { - type Item<'a> = (usize, I::Item<'a>); + type Item<'a> where Self: 'a = (usize, I::Item<'a>); fn next<'a>(&'a mut self) -> Option> { match self.iter.next() { None => None, @@ -44,7 +44,7 @@ impl StreamingIterator for StreamEnumerate { } impl StreamingIterator for I { - type Item<'a> = ::Item; + type Item<'a> where Self: 'a = ::Item; fn next(&mut self) -> Option<::Item<'_>> { Iterator::next(self) } diff --git a/src/test/ui/generic-associated-types/variance_constraints.rs b/src/test/ui/generic-associated-types/variance_constraints.rs index 7bc250ee87b94..7d0f7638ac89d 100644 --- a/src/test/ui/generic-associated-types/variance_constraints.rs +++ b/src/test/ui/generic-associated-types/variance_constraints.rs @@ -3,7 +3,7 @@ #![feature(generic_associated_types)] trait A { - type B<'a>; + type B<'a> where Self: 'a; fn make_b<'a>(&'a self) -> Self::B<'a>; } From f9e14af7f0e63e9788c7d78c0dfc7d99d8f67f9a Mon Sep 17 00:00:00 2001 From: jackh726 Date: Mon, 18 Oct 2021 09:40:27 -0400 Subject: [PATCH 06/10] Don't just check params --- compiler/rustc_typeck/src/check/wfcheck.rs | 10 +++------- .../ui/generic-associated-types/self-outlives-lint.rs | 4 ++-- .../generic-associated-types/self-outlives-lint.stderr | 10 +++++++++- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_typeck/src/check/wfcheck.rs index 0ab9855d3250a..7680f8f9ca097 100644 --- a/compiler/rustc_typeck/src/check/wfcheck.rs +++ b/compiler/rustc_typeck/src/check/wfcheck.rs @@ -301,7 +301,6 @@ fn check_gat_where_clauses( 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 { @@ -423,12 +422,9 @@ impl<'tcx> TypeVisitor<'tcx> for GATSubstCollector<'tcx> { GenericArgKind::Lifetime(lt) => { self.regions.insert((lt, idx)); } - GenericArgKind::Type(t) => match t.kind() { - ty::Param(_) => { - self.types.insert((t, idx)); - } - _ => {} - }, + GenericArgKind::Type(t) => { + 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 76273c5249ec1..eb2970522e652 100644 --- a/src/test/ui/generic-associated-types/self-outlives-lint.rs +++ b/src/test/ui/generic-associated-types/self-outlives-lint.rs @@ -54,10 +54,10 @@ trait Deserializer4 { struct Wrap(T); -// Even though we might theoretically want `D: 'x`, because we pass `Wrap` and -// we see `&'z Wrap`, we are conservative and only add bounds for direct params +// We pass `Wrap` and we see `&'z Wrap`, so we require `D: 'x` trait Des { type Out<'x, D>; + //~^ Missing required bounds fn des<'z, T>(&self, data: &'z Wrap) -> Self::Out<'z, Wrap>; } /* 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 077c64421a8ce..3dacae65267cf 100644 --- a/src/test/ui/generic-associated-types/self-outlives-lint.stderr +++ b/src/test/ui/generic-associated-types/self-outlives-lint.stderr @@ -30,6 +30,14 @@ LL | type Out<'x, 'y>; | | | help: add the required where clauses: `where U: 'y, T: 'x` +error: Missing required bounds on Out + --> $DIR/self-outlives-lint.rs:59:5 + | +LL | type Out<'x, D>; + | ^^^^^^^^^^^^^^^- + | | + | help: add the required where clauses: `where D: 'x` + error: Missing required bounds on Out --> $DIR/self-outlives-lint.rs:75:5 | @@ -46,5 +54,5 @@ LL | type Out<'x, D>; | | | help: add the required where clauses: `where D: 'x` -error: aborting due to 6 previous errors +error: aborting due to 7 previous errors From b84044acfff3635dd1f01a83f6cee08610e11889 Mon Sep 17 00:00:00 2001 From: jackh726 Date: Sun, 31 Oct 2021 21:36:04 -0400 Subject: [PATCH 07/10] Review comments and more tests --- compiler/rustc_typeck/src/check/wfcheck.rs | 69 ++++++++++++++++++- .../self-outlives-lint.rs | 68 ++++++++++++++++++ .../self-outlives-lint.stderr | 48 ++++++++++--- 3 files changed, 174 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_typeck/src/check/wfcheck.rs index 7680f8f9ca097..36dbc04d0a6f5 100644 --- a/compiler/rustc_typeck/src/check/wfcheck.rs +++ b/compiler/rustc_typeck/src/check/wfcheck.rs @@ -266,6 +266,16 @@ pub fn check_trait_item(tcx: TyCtxt<'_>, def_id: LocalDefId) { /// 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. +/// +/// This trait will be our running example. We are currently WF checking the `Item` item... +/// +/// ```rust +/// trait LendingIterator { +/// type Item<'me>; // <-- WF checking this trait item +/// +/// fn next<'a>(&'a mut self) -> Option>; +/// } +/// ``` fn check_gat_where_clauses( tcx: TyCtxt<'_>, trait_item: &hir::TraitItem<'_>, @@ -282,16 +292,25 @@ fn check_gat_where_clauses( return; } let associated_items: &ty::AssocItems<'_> = tcx.associated_items(encl_trait_def_id); - let mut clauses = FxHashSet::default(); + let mut clauses: Option>> = None; // For every function in this trait... + // In our example, this would be the `next` method for item in associated_items.in_definition_order().filter(|item| matches!(item.kind, ty::AssocKind::Fn)) { + // The clauses we that we would require from this function + let mut function_clauses = FxHashSet::default(); + let id = hir::HirId::make_owner(item.def_id.expect_local()); let param_env = tcx.param_env(item.def_id.expect_local()); let sig = tcx.fn_sig(item.def_id); + // Get the signature using placeholders. In our example, this would + // convert the late-bound 'a into a free region. let sig = tcx.liberate_late_bound_regions(item.def_id, sig); + // Collect the arguments that are given to this GAT in the return type + // of the function signature. In our example, the GAT in the return + // type is `::Item<'a>`, so 'a and Self are arguments. let mut visitor = GATSubstCollector { tcx, gat: trait_item.def_id.to_def_id(), @@ -299,11 +318,30 @@ fn check_gat_where_clauses( types: FxHashSet::default(), }; sig.output().visit_with(&mut visitor); + + // If both regions and types are empty, then this GAT isn't in the + // return type, and we shouldn't try to do clause analysis + // (particularly, doing so would end up with an empty set of clauses, + // since the current method would require none, and we take the + // intersection of requirements of all methods) + if visitor.types.is_empty() && visitor.regions.is_empty() { + continue; + } + + // The types we can assume to be well-formed. In our example, this + // would be &'a mut Self, from the first argument. let mut wf_tys = FxHashSet::default(); wf_tys.extend(sig.inputs()); + // For each region argument (e.g., 'a in our example), check for a + // relationship to the type arguments (e.g., Self). If there is an + // outlives relationship (`Self: 'a`), then we want to ensure that is + // reflected in a where clause on the GAT itself. for (region, region_idx) in &visitor.regions { for (ty, ty_idx) in &visitor.types { + // Unfortunately, we have to use a new `InferCtxt` for each + // pair, because region constraints get added and solved there, + // and we need to test each pair individually. tcx.infer_ctxt().enter(|infcx| { let mut outlives_environment = OutlivesEnvironment::new(param_env); outlives_environment.add_implied_bounds(&infcx, wf_tys.clone(), id, DUMMY_SP); @@ -328,6 +366,7 @@ fn check_gat_where_clauses( Some(tcx.lifetimes.re_root_empty), param_env, ); + // In our example, requires that Self: 'a outlives.type_must_outlive(origin, sup_type, sub_region); let errors = infcx.resolve_regions( @@ -338,14 +377,21 @@ fn check_gat_where_clauses( debug!(?errors, "errors"); + // If we were able to prove that Self: 'a without an error, + // it must be because of the implied or explicit bounds... if errors.is_empty() { debug!(?ty_idx, ?region_idx); debug!("required clause: {} must outlive {}", ty, region); + // Translate into the generic parameters of the GAT. In + // our example, the type was Self, which will also be + // Self in the GAT. 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, })); + // Same for the region. In our example, 'a corresponds + // to the 'me parameter. let region_param = generics.param_at(*region_idx, tcx); let region_param = tcx.mk_region(ty::RegionKind::ReEarlyBound(ty::EarlyBoundRegion { @@ -353,22 +399,35 @@ fn check_gat_where_clauses( index: region_param.index, name: region_param.name, })); + // The predicate we expect to see. (In our example, + // `Self: 'me`.) let clause = ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate( ty_param, region_param, )); let clause = tcx.mk_predicate(ty::Binder::dummy(clause)); - clauses.insert(clause); + function_clauses.insert(clause); } }); } } + + match clauses.as_mut() { + Some(clauses) => { + clauses.drain_filter(|p| !function_clauses.contains(p)); + } + None => { + clauses = Some(function_clauses); + } + } } // If there are any missing clauses, emit an error + let mut clauses = clauses.unwrap_or_default(); debug!(?clauses); if !clauses.is_empty() { - let written_predicates: ty::GenericPredicates<'_> = tcx.predicates_of(trait_item.def_id); + let written_predicates: ty::GenericPredicates<'_> = + tcx.explicit_predicates_of(trait_item.def_id); let clauses: Vec<_> = clauses .drain_filter(|clause| { written_predicates.predicates.iter().find(|p| &p.0 == clause).is_none() @@ -402,6 +461,10 @@ fn check_gat_where_clauses( } } +/// TypeVisitor that looks for uses of GATs like +/// `>::GAT` and adds the arguments `P0..Pm` into +/// the two vectors, `regions` and `types` (depending on their kind). For each +/// parameter `Pi` also track the index `i`. struct GATSubstCollector<'tcx> { tcx: TyCtxt<'tcx>, gat: DefId, 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 eb2970522e652..4de850384e7bb 100644 --- a/src/test/ui/generic-associated-types/self-outlives-lint.rs +++ b/src/test/ui/generic-associated-types/self-outlives-lint.rs @@ -2,6 +2,8 @@ // check-fail +use std::fmt::Debug; + // We have a `&'a self`, so we need a `Self: 'a` trait Iterable { type Item<'x>; @@ -100,4 +102,70 @@ impl Des3 for () { } */ +// Similar case to before, except with GAT. +trait NoGat<'a> { + type Bar; + fn method(&'a self) -> Self::Bar; +} + +// Lifetime is not on function; except `Self: 'a` +trait TraitLifetime<'a> { + type Bar<'b>; + //~^ Missing required bounds + fn method(&'a self) -> Self::Bar<'a>; +} + +// Like above, but we have a where clause that can prove what we want +trait TraitLifetimeWhere<'a> where Self: 'a { + type Bar<'b>; + //~^ Missing required bounds + fn method(&'a self) -> Self::Bar<'a>; +} + +// Explicit bound instead of implicit; we want to still error +trait ExplicitBound { + type Bar<'b>; + //~^ Missing required bounds + fn method<'b>(&self, token: &'b ()) -> Self::Bar<'b> where Self: 'b; +} + +// The use of the GAT here is not in the return, we don't want to error +trait NotInReturn { + type Bar<'b>; + fn method<'b>(&'b self) where Self::Bar<'b>: Debug; +} + +// We obviously error for `Iterator`, but we should also error for `Item` +trait IterableTwo { + type Item<'a>; + type Iterator<'a>: Iterator>; + //~^ Missing required bounds + fn iter<'a>(&'a self) -> Self::Iterator<'a>; +} + +// We also should report region outlives clauses +trait RegionOutlives { + type Bar<'a, 'b>; + //~^ Missing required bounds + fn foo<'x, 'y>(&self, input: &'x &'y ()) -> Self::Bar<'x, 'y>; +} + +/* +impl Foo for () { + type Bar<'a, 'b> = &'a &'b (); + fn foo<'x, 'y>(&self, input: &'x &'y ()) -> Self::Bar<'x, 'y> { + input + } +} +*/ + +// If there are multiple methods that return the GAT, require a set of clauses +// that can be satisfied by *all* methods +trait MultipleMethods { + type Bar<'me>; + + fn gimme<'a>(&'a self) -> Self::Bar<'a>; + fn gimme_default(&self) -> Self::Bar<'static>; +} + 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 3dacae65267cf..acccbf24319c0 100644 --- a/src/test/ui/generic-associated-types/self-outlives-lint.stderr +++ b/src/test/ui/generic-associated-types/self-outlives-lint.stderr @@ -1,5 +1,5 @@ error: Missing required bounds on Item - --> $DIR/self-outlives-lint.rs:7:5 + --> $DIR/self-outlives-lint.rs:9:5 | LL | type Item<'x>; | ^^^^^^^^^^^^^- @@ -7,7 +7,7 @@ LL | type Item<'x>; | help: add the required where clauses: `where Self: 'x` error: Missing required bounds on Out - --> $DIR/self-outlives-lint.rs:23:5 + --> $DIR/self-outlives-lint.rs:25:5 | LL | type Out<'x>; | ^^^^^^^^^^^^- @@ -15,7 +15,7 @@ LL | type Out<'x>; | help: add the required where clauses: `where T: 'x` error: Missing required bounds on Out - --> $DIR/self-outlives-lint.rs:37:5 + --> $DIR/self-outlives-lint.rs:39:5 | LL | type Out<'x>; | ^^^^^^^^^^^^- @@ -23,7 +23,7 @@ LL | type Out<'x>; | help: add the required where clauses: `where T: 'x` error: Missing required bounds on Out - --> $DIR/self-outlives-lint.rs:44:5 + --> $DIR/self-outlives-lint.rs:46:5 | LL | type Out<'x, 'y>; | ^^^^^^^^^^^^^^^^- @@ -31,7 +31,7 @@ LL | type Out<'x, 'y>; | help: add the required where clauses: `where U: 'y, T: 'x` error: Missing required bounds on Out - --> $DIR/self-outlives-lint.rs:59:5 + --> $DIR/self-outlives-lint.rs:61:5 | LL | type Out<'x, D>; | ^^^^^^^^^^^^^^^- @@ -39,7 +39,7 @@ LL | type Out<'x, D>; | help: add the required where clauses: `where D: 'x` error: Missing required bounds on Out - --> $DIR/self-outlives-lint.rs:75:5 + --> $DIR/self-outlives-lint.rs:77:5 | LL | type Out<'x, D>; | ^^^^^^^^^^^^^^^- @@ -47,12 +47,44 @@ LL | type Out<'x, D>; | help: add the required where clauses: `where D: 'x` error: Missing required bounds on Out - --> $DIR/self-outlives-lint.rs:90:5 + --> $DIR/self-outlives-lint.rs:92:5 | LL | type Out<'x, D>; | ^^^^^^^^^^^^^^^- | | | help: add the required where clauses: `where D: 'x` -error: aborting due to 7 previous errors +error: Missing required bounds on Bar + --> $DIR/self-outlives-lint.rs:113:5 + | +LL | type Bar<'b>; + | ^^^^^^^^^^^^- + | | + | help: add the required where clauses: `where Self: 'b, Self: 'a` + +error: Missing required bounds on Bar + --> $DIR/self-outlives-lint.rs:120:5 + | +LL | type Bar<'b>; + | ^^^^^^^^^^^^- + | | + | help: add the required where clauses: `where Self: 'b, Self: 'a` + +error: Missing required bounds on Bar + --> $DIR/self-outlives-lint.rs:127:5 + | +LL | type Bar<'b>; + | ^^^^^^^^^^^^- + | | + | help: add the required where clauses: `where Self: 'b` + +error: Missing required bounds on Iterator + --> $DIR/self-outlives-lint.rs:141:5 + | +LL | type Iterator<'a>: Iterator>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- + | | + | help: add the required where clauses: `where Self: 'a` + +error: aborting due to 11 previous errors From 18421b1f08420869181d8bf08114bef6547215f8 Mon Sep 17 00:00:00 2001 From: jackh726 Date: Sun, 31 Oct 2021 21:53:26 -0400 Subject: [PATCH 08/10] Add lint for region pairs too --- compiler/rustc_typeck/src/check/wfcheck.rs | 69 +++++++++++++++++++ .../self-outlives-lint.stderr | 10 ++- 2 files changed, 78 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_typeck/src/check/wfcheck.rs index 36dbc04d0a6f5..5d884edfe1806 100644 --- a/compiler/rustc_typeck/src/check/wfcheck.rs +++ b/compiler/rustc_typeck/src/check/wfcheck.rs @@ -412,6 +412,75 @@ fn check_gat_where_clauses( } } + // For each region argument (e.g., 'a in our example), also check for a + // relationship to the other region arguments. If there is an + // outlives relationship, then we want to ensure that is + // reflected in a where clause on the GAT itself. + for (region_a, region_a_idx) in &visitor.regions { + for (region_b, region_b_idx) in &visitor.regions { + if region_a == region_b { + continue; + } + + // Unfortunately, we have to use a new `InferCtxt` for each + // pair, because region constraints get added and solved there, + // and we need to test each pair individually. + tcx.infer_ctxt().enter(|infcx| { + let mut outlives_environment = OutlivesEnvironment::new(param_env); + outlives_environment.add_implied_bounds(&infcx, wf_tys.clone(), id, DUMMY_SP); + outlives_environment.save_implied_bounds(id); + + let cause = + ObligationCause::new(DUMMY_SP, id, ObligationCauseCode::MiscObligation); + + let origin = SubregionOrigin::from_obligation_cause(&cause, || { + infer::RelateRegionParamBound(cause.span) + }); + + use rustc_infer::infer::outlives::obligations::TypeOutlivesDelegate; + (&infcx).push_sub_region_constraint(origin, region_a, region_b); + + let errors = infcx.resolve_regions( + trait_item.def_id.to_def_id(), + &outlives_environment, + RegionckMode::default(), + ); + + debug!(?errors, "errors"); + + // If we were able to prove that Self: 'a without an error, + // it must be because of the implied or explicit bounds... + if errors.is_empty() { + debug!(?region_a_idx, ?region_b_idx); + debug!("required clause: {} must outlive {}", region_a, region_b); + // Translate into the generic parameters of the GAT. + let region_a_param = generics.param_at(*region_a_idx, tcx); + let region_a_param = + tcx.mk_region(ty::RegionKind::ReEarlyBound(ty::EarlyBoundRegion { + def_id: region_a_param.def_id, + index: region_a_param.index, + name: region_a_param.name, + })); + // Same for the region. + let region_b_param = generics.param_at(*region_b_idx, tcx); + let region_b_param = + tcx.mk_region(ty::RegionKind::ReEarlyBound(ty::EarlyBoundRegion { + def_id: region_b_param.def_id, + index: region_b_param.index, + name: region_b_param.name, + })); + // The predicate we expect to see. + let clause = ty::PredicateKind::RegionOutlives(ty::OutlivesPredicate( + region_a_param, + region_b_param, + )); + let clause = tcx.mk_predicate(ty::Binder::dummy(clause)); + function_clauses.insert(clause); + } + }); + } + } + match clauses.as_mut() { Some(clauses) => { clauses.drain_filter(|p| !function_clauses.contains(p)); 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 acccbf24319c0..df858c27cd3c5 100644 --- a/src/test/ui/generic-associated-types/self-outlives-lint.stderr +++ b/src/test/ui/generic-associated-types/self-outlives-lint.stderr @@ -86,5 +86,13 @@ LL | type Iterator<'a>: Iterator>; | | | help: add the required where clauses: `where Self: 'a` -error: aborting due to 11 previous errors +error: Missing required bounds on Bar + --> $DIR/self-outlives-lint.rs:148:5 + | +LL | type Bar<'a, 'b>; + | ^^^^^^^^^^^^^^^^- + | | + | help: add the required where clauses: `where 'a: 'b` + +error: aborting due to 12 previous errors From ef5e31ac06716e2fd5c5f0dc0991b3760dc0c3ae Mon Sep 17 00:00:00 2001 From: jackh726 Date: Sun, 31 Oct 2021 23:04:42 -0400 Subject: [PATCH 09/10] Move outlives checking to separate functions --- compiler/rustc_typeck/src/check/wfcheck.rs | 271 +++++++++++---------- 1 file changed, 146 insertions(+), 125 deletions(-) diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_typeck/src/check/wfcheck.rs index 5d884edfe1806..3f5d4383838a8 100644 --- a/compiler/rustc_typeck/src/check/wfcheck.rs +++ b/compiler/rustc_typeck/src/check/wfcheck.rs @@ -339,76 +339,36 @@ fn check_gat_where_clauses( // reflected in a where clause on the GAT itself. for (region, region_idx) in &visitor.regions { for (ty, ty_idx) in &visitor.types { - // Unfortunately, we have to use a new `InferCtxt` for each - // pair, because region constraints get added and solved there, - // and we need to test each pair individually. - tcx.infer_ctxt().enter(|infcx| { - let mut outlives_environment = OutlivesEnvironment::new(param_env); - outlives_environment.add_implied_bounds(&infcx, wf_tys.clone(), id, DUMMY_SP); - 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, - ); - // In our example, requires that Self: 'a - 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 we were able to prove that Self: 'a without an error, - // it must be because of the implied or explicit bounds... - if errors.is_empty() { - debug!(?ty_idx, ?region_idx); - debug!("required clause: {} must outlive {}", ty, region); - // Translate into the generic parameters of the GAT. In - // our example, the type was Self, which will also be - // Self in the GAT. - 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, + // In our example, requires that Self: 'a + if ty_known_to_outlive(tcx, id, param_env, &wf_tys, *ty, *region) { + debug!(?ty_idx, ?region_idx); + debug!("required clause: {} must outlive {}", ty, region); + // Translate into the generic parameters of the GAT. In + // our example, the type was Self, which will also be + // Self in the GAT. + 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, + })); + // Same for the region. In our example, 'a corresponds + // to the 'me parameter. + let region_param = generics.param_at(*region_idx, tcx); + 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, })); - // Same for the region. In our example, 'a corresponds - // to the 'me parameter. - let region_param = generics.param_at(*region_idx, tcx); - 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, - })); - // The predicate we expect to see. (In our example, - // `Self: 'me`.) - let clause = ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate( - ty_param, - region_param, - )); - let clause = tcx.mk_predicate(ty::Binder::dummy(clause)); - function_clauses.insert(clause); - } - }); + // The predicate we expect to see. (In our example, + // `Self: 'me`.) + let clause = ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate( + ty_param, + region_param, + )); + let clause = tcx.mk_predicate(ty::Binder::dummy(clause)); + function_clauses.insert(clause); + } } } @@ -422,62 +382,33 @@ fn check_gat_where_clauses( continue; } - // Unfortunately, we have to use a new `InferCtxt` for each - // pair, because region constraints get added and solved there, - // and we need to test each pair individually. - tcx.infer_ctxt().enter(|infcx| { - let mut outlives_environment = OutlivesEnvironment::new(param_env); - outlives_environment.add_implied_bounds(&infcx, wf_tys.clone(), id, DUMMY_SP); - outlives_environment.save_implied_bounds(id); - - let cause = - ObligationCause::new(DUMMY_SP, id, ObligationCauseCode::MiscObligation); - - let origin = SubregionOrigin::from_obligation_cause(&cause, || { - infer::RelateRegionParamBound(cause.span) - }); - - use rustc_infer::infer::outlives::obligations::TypeOutlivesDelegate; - (&infcx).push_sub_region_constraint(origin, region_a, region_b); - - let errors = infcx.resolve_regions( - trait_item.def_id.to_def_id(), - &outlives_environment, - RegionckMode::default(), - ); - - debug!(?errors, "errors"); - - // If we were able to prove that Self: 'a without an error, - // it must be because of the implied or explicit bounds... - if errors.is_empty() { - debug!(?region_a_idx, ?region_b_idx); - debug!("required clause: {} must outlive {}", region_a, region_b); - // Translate into the generic parameters of the GAT. - let region_a_param = generics.param_at(*region_a_idx, tcx); - let region_a_param = - tcx.mk_region(ty::RegionKind::ReEarlyBound(ty::EarlyBoundRegion { - def_id: region_a_param.def_id, - index: region_a_param.index, - name: region_a_param.name, - })); - // Same for the region. - let region_b_param = generics.param_at(*region_b_idx, tcx); - let region_b_param = - tcx.mk_region(ty::RegionKind::ReEarlyBound(ty::EarlyBoundRegion { - def_id: region_b_param.def_id, - index: region_b_param.index, - name: region_b_param.name, - })); - // The predicate we expect to see. - let clause = ty::PredicateKind::RegionOutlives(ty::OutlivesPredicate( - region_a_param, - region_b_param, - )); - let clause = tcx.mk_predicate(ty::Binder::dummy(clause)); - function_clauses.insert(clause); - } - }); + if region_known_to_outlive(tcx, id, param_env, &wf_tys, *region_a, *region_b) { + debug!(?region_a_idx, ?region_b_idx); + debug!("required clause: {} must outlive {}", region_a, region_b); + // Translate into the generic parameters of the GAT. + let region_a_param = generics.param_at(*region_a_idx, tcx); + let region_a_param = + tcx.mk_region(ty::RegionKind::ReEarlyBound(ty::EarlyBoundRegion { + def_id: region_a_param.def_id, + index: region_a_param.index, + name: region_a_param.name, + })); + // Same for the region. + let region_b_param = generics.param_at(*region_b_idx, tcx); + let region_b_param = + tcx.mk_region(ty::RegionKind::ReEarlyBound(ty::EarlyBoundRegion { + def_id: region_b_param.def_id, + index: region_b_param.index, + name: region_b_param.name, + })); + // The predicate we expect to see. + let clause = ty::PredicateKind::RegionOutlives(ty::OutlivesPredicate( + region_a_param, + region_b_param, + )); + let clause = tcx.mk_predicate(ty::Binder::dummy(clause)); + function_clauses.insert(clause); + } } } @@ -530,6 +461,96 @@ fn check_gat_where_clauses( } } +/// Given a known `param_env` and a set of well formed types, can we prove that +/// `ty` outlives `region`. +fn ty_known_to_outlive<'tcx>( + tcx: TyCtxt<'tcx>, + id: hir::HirId, + param_env: ty::ParamEnv<'tcx>, + wf_tys: &FxHashSet>, + ty: Ty<'tcx>, + region: ty::Region<'tcx>, +) -> bool { + // Unfortunately, we have to use a new `InferCtxt` each call, because + // region constraints get added and solved there and we need to test each + // call individually. + tcx.infer_ctxt().enter(|infcx| { + let mut outlives_environment = OutlivesEnvironment::new(param_env); + outlives_environment.add_implied_bounds(&infcx, wf_tys.clone(), id, DUMMY_SP); + 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(infcx.tcx.lifetimes.re_root_empty), + param_env, + ); + outlives.type_must_outlive(origin, sup_type, sub_region); + + let errors = infcx.resolve_regions( + id.expect_owner().to_def_id(), + &outlives_environment, + RegionckMode::default(), + ); + + debug!(?errors, "errors"); + + // If we were able to prove that the type outlives the region without + // an error, it must be because of the implied or explicit bounds... + errors.is_empty() + }) +} + +fn region_known_to_outlive<'tcx>( + tcx: TyCtxt<'tcx>, + id: hir::HirId, + param_env: ty::ParamEnv<'tcx>, + wf_tys: &FxHashSet>, + region_a: ty::Region<'tcx>, + region_b: ty::Region<'tcx>, +) -> bool { + // Unfortunately, we have to use a new `InferCtxt` each call, because + // region constraints get added and solved there and we need to test each + // call individually. + tcx.infer_ctxt().enter(|infcx| { + let mut outlives_environment = OutlivesEnvironment::new(param_env); + outlives_environment.add_implied_bounds(&infcx, wf_tys.clone(), id, DUMMY_SP); + outlives_environment.save_implied_bounds(id); + + let cause = ObligationCause::new(DUMMY_SP, id, ObligationCauseCode::MiscObligation); + + let origin = SubregionOrigin::from_obligation_cause(&cause, || { + infer::RelateRegionParamBound(cause.span) + }); + + use rustc_infer::infer::outlives::obligations::TypeOutlivesDelegate; + (&infcx).push_sub_region_constraint(origin, region_a, region_b); + + let errors = infcx.resolve_regions( + id.expect_owner().to_def_id(), + &outlives_environment, + RegionckMode::default(), + ); + + debug!(?errors, "errors"); + + // If we were able to prove that the type outlives the region without + // an error, it must be because of the implied or explicit bounds... + errors.is_empty() + }) +} + /// TypeVisitor that looks for uses of GATs like /// `>::GAT` and adds the arguments `P0..Pm` into /// the two vectors, `regions` and `types` (depending on their kind). For each From b6edcbd7b5db718ff5e8ba229812b35f803ddd7a Mon Sep 17 00:00:00 2001 From: jackh726 Date: Fri, 5 Nov 2021 21:33:14 -0400 Subject: [PATCH 10/10] Review comments --- compiler/rustc_typeck/src/check/wfcheck.rs | 55 ++++++++++++++----- .../self-outlives-lint.rs | 2 + .../self-outlives-lint.stderr | 16 +++--- 3 files changed, 51 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_typeck/src/check/wfcheck.rs index 3f5d4383838a8..06bfc427bbabc 100644 --- a/compiler/rustc_typeck/src/check/wfcheck.rs +++ b/compiler/rustc_typeck/src/check/wfcheck.rs @@ -263,7 +263,7 @@ pub fn check_trait_item(tcx: TyCtxt<'_>, def_id: LocalDefId) { check_gat_where_clauses(tcx, trait_item, encl_trait_def_id); } -/// Require that the user writes as where clauses on GATs the implicit +/// Require that the user writes where clauses on GATs for the implicit /// outlives bounds involving trait parameters in trait functions and /// lifetimes passed as GAT substs. See `self-outlives-lint` test. /// @@ -288,6 +288,7 @@ fn check_gat_where_clauses( } let generics: &ty::Generics = tcx.generics_of(trait_item.def_id); // If the current associated type doesn't have any (own) params, it's not a GAT + // FIXME(jackh726): we can also warn in the more general case if generics.params.len() == 0 { return; } @@ -311,20 +312,15 @@ fn check_gat_where_clauses( // Collect the arguments that are given to this GAT in the return type // of the function signature. In our example, the GAT in the return // type is `::Item<'a>`, so 'a and Self are arguments. - 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 (regions, types) = + GATSubstCollector::visit(tcx, trait_item.def_id.to_def_id(), sig.output()); // If both regions and types are empty, then this GAT isn't in the // return type, and we shouldn't try to do clause analysis // (particularly, doing so would end up with an empty set of clauses, // since the current method would require none, and we take the // intersection of requirements of all methods) - if visitor.types.is_empty() && visitor.regions.is_empty() { + if types.is_empty() && regions.is_empty() { continue; } @@ -337,8 +333,8 @@ fn check_gat_where_clauses( // relationship to the type arguments (e.g., Self). If there is an // outlives relationship (`Self: 'a`), then we want to ensure that is // reflected in a where clause on the GAT itself. - for (region, region_idx) in &visitor.regions { - for (ty, ty_idx) in &visitor.types { + for (region, region_idx) in ®ions { + for (ty, ty_idx) in &types { // In our example, requires that Self: 'a if ty_known_to_outlive(tcx, id, param_env, &wf_tys, *ty, *region) { debug!(?ty_idx, ?region_idx); @@ -376,8 +372,8 @@ fn check_gat_where_clauses( // relationship to the other region arguments. If there is an // outlives relationship, then we want to ensure that is // reflected in a where clause on the GAT itself. - for (region_a, region_a_idx) in &visitor.regions { - for (region_b, region_b_idx) in &visitor.regions { + for (region_a, region_a_idx) in ®ions { + for (region_b, region_b_idx) in ®ions { if region_a == region_b { continue; } @@ -412,6 +408,16 @@ fn check_gat_where_clauses( } } + // Imagine we have: + // ``` + // trait Foo { + // type Bar<'me>; + // fn gimme(&self) -> Self::Bar<'_>; + // fn gimme_default(&self) -> Self::Bar<'static>; + // } + // ``` + // We only want to require clauses on `Bar` that we can prove from *all* functions (in this + // case, `'me` can be `static` from `gimme_default`) match clauses.as_mut() { Some(clauses) => { clauses.drain_filter(|p| !function_clauses.contains(p)); @@ -428,12 +434,14 @@ fn check_gat_where_clauses( if !clauses.is_empty() { let written_predicates: ty::GenericPredicates<'_> = tcx.explicit_predicates_of(trait_item.def_id); - let clauses: Vec<_> = clauses + let mut clauses: Vec<_> = clauses .drain_filter(|clause| { written_predicates.predicates.iter().find(|p| &p.0 == clause).is_none() }) .map(|clause| format!("{}", clause)) .collect(); + // We sort so that order is predictable + clauses.sort(); if !clauses.is_empty() { let mut err = tcx.sess.struct_span_err( trait_item.span, @@ -461,6 +469,8 @@ fn check_gat_where_clauses( } } +// FIXME(jackh726): refactor some of the shared logic between the two functions below + /// Given a known `param_env` and a set of well formed types, can we prove that /// `ty` outlives `region`. fn ty_known_to_outlive<'tcx>( @@ -564,6 +574,23 @@ struct GATSubstCollector<'tcx> { types: FxHashSet<(Ty<'tcx>, usize)>, } +impl<'tcx> GATSubstCollector<'tcx> { + fn visit>( + tcx: TyCtxt<'tcx>, + gat: DefId, + t: T, + ) -> (FxHashSet<(ty::Region<'tcx>, usize)>, FxHashSet<(Ty<'tcx>, usize)>) { + let mut visitor = GATSubstCollector { + tcx, + gat, + regions: FxHashSet::default(), + types: FxHashSet::default(), + }; + t.visit_with(&mut visitor); + (visitor.regions, visitor.types) + } +} + impl<'tcx> TypeVisitor<'tcx> for GATSubstCollector<'tcx> { type BreakTy = !; 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 4de850384e7bb..af90d158855d8 100644 --- a/src/test/ui/generic-associated-types/self-outlives-lint.rs +++ b/src/test/ui/generic-associated-types/self-outlives-lint.rs @@ -109,6 +109,7 @@ trait NoGat<'a> { } // Lifetime is not on function; except `Self: 'a` +// FIXME: we require two bounds (`where Self: 'a, Self: 'b`) when we should only require one trait TraitLifetime<'a> { type Bar<'b>; //~^ Missing required bounds @@ -116,6 +117,7 @@ trait TraitLifetime<'a> { } // Like above, but we have a where clause that can prove what we want +// FIXME: we require two bounds (`where Self: 'a, Self: 'b`) when we should only require one trait TraitLifetimeWhere<'a> where Self: 'a { type Bar<'b>; //~^ Missing required bounds 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 df858c27cd3c5..bf85780f69f7a 100644 --- a/src/test/ui/generic-associated-types/self-outlives-lint.stderr +++ b/src/test/ui/generic-associated-types/self-outlives-lint.stderr @@ -28,7 +28,7 @@ error: Missing required bounds on Out LL | type Out<'x, 'y>; | ^^^^^^^^^^^^^^^^- | | - | help: add the required where clauses: `where U: 'y, T: 'x` + | help: add the required where clauses: `where T: 'x, U: 'y` error: Missing required bounds on Out --> $DIR/self-outlives-lint.rs:61:5 @@ -55,23 +55,23 @@ LL | type Out<'x, D>; | help: add the required where clauses: `where D: 'x` error: Missing required bounds on Bar - --> $DIR/self-outlives-lint.rs:113:5 + --> $DIR/self-outlives-lint.rs:114:5 | LL | type Bar<'b>; | ^^^^^^^^^^^^- | | - | help: add the required where clauses: `where Self: 'b, Self: 'a` + | help: add the required where clauses: `where Self: 'a, Self: 'b` error: Missing required bounds on Bar - --> $DIR/self-outlives-lint.rs:120:5 + --> $DIR/self-outlives-lint.rs:122:5 | LL | type Bar<'b>; | ^^^^^^^^^^^^- | | - | help: add the required where clauses: `where Self: 'b, Self: 'a` + | help: add the required where clauses: `where Self: 'a, Self: 'b` error: Missing required bounds on Bar - --> $DIR/self-outlives-lint.rs:127:5 + --> $DIR/self-outlives-lint.rs:129:5 | LL | type Bar<'b>; | ^^^^^^^^^^^^- @@ -79,7 +79,7 @@ LL | type Bar<'b>; | help: add the required where clauses: `where Self: 'b` error: Missing required bounds on Iterator - --> $DIR/self-outlives-lint.rs:141:5 + --> $DIR/self-outlives-lint.rs:143:5 | LL | type Iterator<'a>: Iterator>; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- @@ -87,7 +87,7 @@ LL | type Iterator<'a>: Iterator>; | help: add the required where clauses: `where Self: 'a` error: Missing required bounds on Bar - --> $DIR/self-outlives-lint.rs:148:5 + --> $DIR/self-outlives-lint.rs:150:5 | LL | type Bar<'a, 'b>; | ^^^^^^^^^^^^^^^^-