Skip to content

Commit a8c44d3

Browse files
committed
Some cleanup
1 parent 0bc340f commit a8c44d3

File tree

3 files changed

+216
-59
lines changed

3 files changed

+216
-59
lines changed

compiler/rustc_typeck/src/check/wfcheck.rs

Lines changed: 105 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use crate::check::{FnCtxt, Inherited};
22
use crate::constrained_generic_params::{identify_constrained_generic_params, Parameter};
33

4+
use crate::traits::query::type_op::{self, TypeOp, TypeOpOutput};
45
use rustc_ast as ast;
56
use rustc_data_structures::fx::FxHashSet;
67
use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder};
@@ -15,13 +16,14 @@ use rustc_infer::infer::TyCtxtInferExt;
1516
use rustc_middle::hir::map as hir_map;
1617
use rustc_middle::ty::subst::{GenericArgKind, InternalSubsts, Subst};
1718
use rustc_middle::ty::trait_def::TraitSpecializationKind;
18-
use rustc_middle::ty::{self, AdtKind, GenericParamDefKind, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeVisitor, WithConstness};
19-
use rustc_session::lint;
19+
use rustc_middle::ty::{
20+
self, AdtKind, GenericParamDefKind, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeVisitor,
21+
WithConstness,
22+
};
2023
use rustc_session::parse::feature_err;
2124
use rustc_span::symbol::{sym, Ident, Symbol};
22-
use rustc_span::{DUMMY_SP, Span};
23-
use rustc_trait_selection::traits::query::evaluate_obligation::{InferCtxtExt as _};
24-
use rustc_trait_selection::traits::query::outlives_bounds::{InferCtxtExt as _};
25+
use rustc_span::Span;
26+
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
2527
use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode, WellFormedLoc};
2628

2729
use std::convert::TryInto;
@@ -255,75 +257,119 @@ pub fn check_trait_item(tcx: TyCtxt<'_>, def_id: LocalDefId) {
255257
}
256258
}
257259

258-
// Require that the user writes as where clauses on GATs the implicit
259-
// outlives bounds involving trait parameters in trait functions and
260-
// lifetimes passed as GAT substs. See `self-outlives-lint` test.
260+
check_gat_where_clauses(tcx, trait_item, encl_trait_def_id);
261+
}
262+
263+
/// Require that the user writes as where clauses on GATs the implicit
264+
/// outlives bounds involving trait parameters in trait functions and
265+
/// lifetimes passed as GAT substs. See `self-outlives-lint` test.
266+
fn check_gat_where_clauses(
267+
tcx: TyCtxt<'_>,
268+
trait_item: &hir::TraitItem<'_>,
269+
encl_trait_def_id: DefId,
270+
) {
261271
let item = tcx.associated_item(trait_item.def_id);
272+
// If the current trait item isn't a type, it isn't a GAT
273+
if !matches!(item.kind, ty::AssocKind::Type) {
274+
return;
275+
}
262276
let generics: &ty::Generics = tcx.generics_of(trait_item.def_id);
263-
if matches!(item.kind, ty::AssocKind::Type) && generics.params.len() > 0 {
264-
let associated_items: &ty::AssocItems<'_> = tcx.associated_items(encl_trait_def_id);
265-
associated_items
266-
.in_definition_order()
267-
.filter(|item| matches!(item.kind, ty::AssocKind::Fn))
268-
.for_each(|item| {
269-
tcx.infer_ctxt().enter(|infcx| {
270-
let sig: ty::Binder<'_, ty::FnSig<'_>> = tcx.fn_sig(item.def_id);
271-
let sig = infcx.replace_bound_vars_with_placeholders(sig);
272-
let output = sig.output();
273-
let mut visitor = RegionsInGATs {
274-
tcx,
275-
gat: trait_item.def_id.to_def_id(),
276-
regions: FxHashSet::default(),
277-
};
278-
output.visit_with(&mut visitor);
279-
for input in sig.inputs() {
280-
let bounds = infcx.implied_outlives_bounds(ty::ParamEnv::empty(), hir_id, input, DUMMY_SP);
281-
debug!(?bounds);
282-
let mut clauses = FxHashSet::default();
283-
for bound in bounds {
284-
match bound {
285-
traits::query::OutlivesBound::RegionSubParam(r, p) => {
286-
for idx in visitor.regions.iter().filter(|(proj_r, _)| proj_r == &r).map(|r| r.1) {
287-
let param_r = tcx.mk_region(ty::RegionKind::ReEarlyBound(ty::EarlyBoundRegion {
288-
def_id: generics.params[idx].def_id,
289-
index: idx as u32,
290-
name: generics.params[idx].name,
291-
}));
292-
let clause = ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(tcx.mk_ty(ty::Param(p)), param_r));
293-
let clause = tcx.mk_predicate(ty::Binder::dummy(clause));
294-
clauses.insert(clause);
295-
}
296-
}
297-
_ => {}
298-
}
299-
}
300-
debug!(?clauses);
301-
if !clauses.is_empty() {
302-
let written_predicates: ty::GenericPredicates<'_> = tcx.predicates_of(trait_item.def_id);
303-
for clause in clauses {
304-
let found = written_predicates.predicates.iter().find(|p| p.0 == clause).is_some();
305-
debug!(?clause, ?found);
306-
let mut error = tcx.sess.struct_span_err(
307-
trait_item.generics.span,
308-
&format!("Missing bound: {}", clause),
277+
// If the current associated type doesn't have any (own) params, it's not a GAT
278+
if generics.params.len() == 0 {
279+
return;
280+
}
281+
let associated_items: &ty::AssocItems<'_> = tcx.associated_items(encl_trait_def_id);
282+
// For every function in this trait...
283+
for item in
284+
associated_items.in_definition_order().filter(|item| matches!(item.kind, ty::AssocKind::Fn))
285+
{
286+
tcx.infer_ctxt().enter(|infcx| {
287+
let sig: ty::Binder<'_, ty::FnSig<'_>> = tcx.fn_sig(item.def_id);
288+
let sig = infcx.replace_bound_vars_with_placeholders(sig);
289+
// Find out what regions are passed as GAT substs
290+
let mut visitor = GATSubstCollector {
291+
tcx,
292+
gat: trait_item.def_id.to_def_id(),
293+
regions: FxHashSet::default(),
294+
_types: FxHashSet::default(),
295+
};
296+
sig.output().visit_with(&mut visitor);
297+
// If there are none, then it nothing to do
298+
if visitor.regions.is_empty() {
299+
return;
300+
}
301+
let mut clauses = FxHashSet::default();
302+
// Otherwise, find the clauses required from implied bounds
303+
for input in sig.inputs() {
304+
// For a given input type, find the implied bounds
305+
let TypeOpOutput { output: bounds, .. } = match ty::ParamEnv::empty()
306+
.and(type_op::implied_outlives_bounds::ImpliedOutlivesBounds { ty: input })
307+
.fully_perform(&infcx)
308+
{
309+
Ok(o) => o,
310+
Err(_) => continue,
311+
};
312+
debug!(?bounds);
313+
for bound in bounds {
314+
match bound {
315+
traits::query::OutlivesBound::RegionSubParam(r, p) => {
316+
// If the implied bound is a `RegionSubParam` and
317+
// the region is used a GAT subst...
318+
for idx in visitor
319+
.regions
320+
.iter()
321+
.filter(|(proj_r, _)| proj_r == &r)
322+
.map(|r| r.1)
323+
{
324+
// Then create a clause that is required on the GAT
325+
let param_r = tcx.mk_region(ty::RegionKind::ReEarlyBound(
326+
ty::EarlyBoundRegion {
327+
def_id: generics.params[idx].def_id,
328+
index: idx as u32,
329+
name: generics.params[idx].name,
330+
},
331+
));
332+
let clause = ty::PredicateKind::TypeOutlives(
333+
ty::OutlivesPredicate(tcx.mk_ty(ty::Param(p)), param_r),
309334
);
310-
error.emit();
335+
let clause = tcx.mk_predicate(ty::Binder::dummy(clause));
336+
clauses.insert(clause);
311337
}
312338
}
339+
_ => {}
313340
}
314-
})
315-
});
341+
}
342+
}
343+
// If there are any missing clauses, emit an error
344+
debug!(?clauses);
345+
if !clauses.is_empty() {
346+
let written_predicates: ty::GenericPredicates<'_> =
347+
tcx.predicates_of(trait_item.def_id);
348+
for clause in clauses {
349+
let found =
350+
written_predicates.predicates.iter().find(|p| p.0 == clause).is_some();
351+
debug!(?clause, ?found);
352+
let mut error = tcx.sess.struct_span_err(
353+
trait_item.generics.span,
354+
&format!("Missing bound: {}", clause),
355+
);
356+
error.emit();
357+
}
358+
}
359+
})
316360
}
317361
}
318362

319-
struct RegionsInGATs<'tcx> {
363+
struct GATSubstCollector<'tcx> {
320364
tcx: TyCtxt<'tcx>,
321365
gat: DefId,
322366
// Which region appears and which parameter index its subsituted for
323367
regions: FxHashSet<(ty::Region<'tcx>, usize)>,
368+
// Which params appears and which parameter index its subsituted for
369+
_types: FxHashSet<(Ty<'tcx>, usize)>,
324370
}
325371

326-
impl<'tcx> TypeVisitor<'tcx> for RegionsInGATs<'tcx> {
372+
impl<'tcx> TypeVisitor<'tcx> for GATSubstCollector<'tcx> {
327373
type BreakTy = !;
328374

329375
fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {

src/test/ui/generic-associated-types/self-outlives-lint.rs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
trait Iterable {
66
type Item<'x>;
7+
//~^ Missing bound
78
fn iter<'a>(&'a self) -> Self::Item<'a>;
89
}
910

@@ -18,6 +19,7 @@ impl<T> Iterable for T {
1819

1920
trait Deserializer<T> {
2021
type Out<'x>;
22+
//~^ Missing bound
2123
fn deserialize<'a>(&self, input: &'a T) -> Self::Out<'a>;
2224
}
2325

@@ -30,12 +32,65 @@ impl<T> Deserializer<T> for () {
3032

3133
trait Deserializer2<T> {
3234
type Out<'x>;
35+
//~^ Missing bound
3336
fn deserialize2<'a, 'b: 'a>(&self, input: &'a T, input2: &'b T) -> Self::Out<'a>;
3437
}
3538

3639
trait Deserializer3<T, U> {
3740
type Out<'x, 'y>;
41+
//~^ Missing bound
42+
//~^^ Missing bound
3843
fn deserialize2<'a, 'b>(&self, input: &'a T, input2: &'b U) -> Self::Out<'a, 'b>;
3944
}
4045

46+
trait Deserializer4 {
47+
type Out<'x>;
48+
//~^ Missing bound
49+
fn deserialize<'a, T>(&self, input: &'a T) -> Self::Out<'a>;
50+
}
51+
52+
struct Wrap<T>(T);
53+
54+
trait Des {
55+
type Out<'x, D>;
56+
//~^ Missing bound
57+
fn des<'z, T>(&self, data: &'z Wrap<T>) -> Self::Out<'z, Wrap<T>>;
58+
}
59+
/*
60+
impl Des for () {
61+
type Out<'x, D> = &'x D;
62+
fn des<'a, T>(&self, data: &'a Wrap<T>) -> Self::Out<'a, Wrap<T>> {
63+
data
64+
}
65+
}
66+
*/
67+
68+
trait Des2 {
69+
type Out<'x, D>;
70+
//~^ Missing bound
71+
fn des<'z, T>(&self, data: &'z Wrap<T>) -> Self::Out<'z, T>;
72+
}
73+
/*
74+
impl Des2 for () {
75+
type Out<'x, D> = &'x D;
76+
fn des<'a, T>(&self, data: &'a Wrap<T>) -> Self::Out<'a, T> {
77+
data
78+
}
79+
}
80+
*/
81+
82+
trait Des3 {
83+
type Out<'x, D>;
84+
//~^ Missing bound
85+
fn des<'z, T>(&self, data: &'z T) -> Self::Out<'z, T>;
86+
}
87+
/*
88+
impl Des3 for () {
89+
type Out<'x, D> = &'x D;
90+
fn des<'a, T>(&self, data: &'a T) -> Self::Out<'a, T> {
91+
data
92+
}
93+
}
94+
*/
95+
4196
fn main() {}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
error: Missing bound: Self: 'x
2+
--> $DIR/self-outlives-lint.rs:6:14
3+
|
4+
LL | type Item<'x>;
5+
| ^^^^
6+
7+
error: Missing bound: T: 'x
8+
--> $DIR/self-outlives-lint.rs:21:13
9+
|
10+
LL | type Out<'x>;
11+
| ^^^^
12+
13+
error: Missing bound: T: 'x
14+
--> $DIR/self-outlives-lint.rs:34:13
15+
|
16+
LL | type Out<'x>;
17+
| ^^^^
18+
19+
error: Missing bound: T: 'x
20+
--> $DIR/self-outlives-lint.rs:40:13
21+
|
22+
LL | type Out<'x, 'y>;
23+
| ^^^^^^^^
24+
25+
error: Missing bound: U: 'y
26+
--> $DIR/self-outlives-lint.rs:40:13
27+
|
28+
LL | type Out<'x, 'y>;
29+
| ^^^^^^^^
30+
31+
error: Missing bound: T: 'x
32+
--> $DIR/self-outlives-lint.rs:47:13
33+
|
34+
LL | type Out<'x>;
35+
| ^^^^
36+
37+
error: Missing bound: T: 'x
38+
--> $DIR/self-outlives-lint.rs:55:13
39+
|
40+
LL | type Out<'x, D>;
41+
| ^^^^^^^
42+
43+
error: Missing bound: T: 'x
44+
--> $DIR/self-outlives-lint.rs:69:13
45+
|
46+
LL | type Out<'x, D>;
47+
| ^^^^^^^
48+
49+
error: Missing bound: T: 'x
50+
--> $DIR/self-outlives-lint.rs:83:13
51+
|
52+
LL | type Out<'x, D>;
53+
| ^^^^^^^
54+
55+
error: aborting due to 9 previous errors
56+

0 commit comments

Comments
 (0)