Skip to content

Commit fedce67

Browse files
committed
Auto merge of #48326 - RalfJung:generic-bounds, r=petrochenkov
Warn about ignored generic bounds in `for` This adds a new lint to fix #42181. For consistency and to avoid code duplication, I also moved the existing "bounds in type aliases are ignored" here. Questions to the reviewer: * Is it okay to just remove a diagnostic error code like this? Should I instead keep the warning about type aliases where it is? The old code provided a detailed explanation of what's going on when asked, that information is now lost. On the other hand, `span_warn!` seems deprecated (after this patch, it has exactly one user left!). * Did I miss any syntactic construct that can appear as `for` in the surface syntax? I covered function types (`for<'a> fn(...)`), generic traits (`for <'a> Fn(...)`, can appear both as bounds as as trait objects) and bounds (`for<'a> F: ...`). * For the sake of backwards compatibility, this adds a warning, not an error. @nikomatsakis suggested an error in #42181 (comment), but I feel that can only happen in a new epoch -- right? Cc @eddyb
2 parents 2079a08 + 780b544 commit fedce67

20 files changed

+324
-108
lines changed

src/librustc/lint/context.rs

+21
Original file line numberDiff line numberDiff line change
@@ -824,6 +824,17 @@ impl<'a, 'tcx> hir_visit::Visitor<'tcx> for LateContext<'a, 'tcx> {
824824
hir_visit::walk_generics(self, g);
825825
}
826826

827+
fn visit_where_predicate(&mut self, p: &'tcx hir::WherePredicate) {
828+
run_lints!(self, check_where_predicate, late_passes, p);
829+
hir_visit::walk_where_predicate(self, p);
830+
}
831+
832+
fn visit_poly_trait_ref(&mut self, t: &'tcx hir::PolyTraitRef,
833+
m: hir::TraitBoundModifier) {
834+
run_lints!(self, check_poly_trait_ref, late_passes, t, m);
835+
hir_visit::walk_poly_trait_ref(self, t, m);
836+
}
837+
827838
fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem) {
828839
let generics = self.generics.take();
829840
self.generics = Some(&trait_item.generics);
@@ -986,6 +997,16 @@ impl<'a> ast_visit::Visitor<'a> for EarlyContext<'a> {
986997
ast_visit::walk_generics(self, g);
987998
}
988999

1000+
fn visit_where_predicate(&mut self, p: &'a ast::WherePredicate) {
1001+
run_lints!(self, check_where_predicate, early_passes, p);
1002+
ast_visit::walk_where_predicate(self, p);
1003+
}
1004+
1005+
fn visit_poly_trait_ref(&mut self, t: &'a ast::PolyTraitRef, m: &'a ast::TraitBoundModifier) {
1006+
run_lints!(self, check_poly_trait_ref, early_passes, t, m);
1007+
ast_visit::walk_poly_trait_ref(self, t, m);
1008+
}
1009+
9891010
fn visit_trait_item(&mut self, trait_item: &'a ast::TraitItem) {
9901011
self.with_lint_attrs(trait_item.id, &trait_item.attrs, |cx| {
9911012
run_lints!(cx, check_trait_item, early_passes, trait_item);

src/librustc/lint/mod.rs

+6
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,9 @@ pub trait LateLintPass<'a, 'tcx>: LintPass {
181181
fn check_ty(&mut self, _: &LateContext<'a, 'tcx>, _: &'tcx hir::Ty) { }
182182
fn check_generic_param(&mut self, _: &LateContext<'a, 'tcx>, _: &'tcx hir::GenericParam) { }
183183
fn check_generics(&mut self, _: &LateContext<'a, 'tcx>, _: &'tcx hir::Generics) { }
184+
fn check_where_predicate(&mut self, _: &LateContext<'a, 'tcx>, _: &'tcx hir::WherePredicate) { }
185+
fn check_poly_trait_ref(&mut self, _: &LateContext<'a, 'tcx>, _: &'tcx hir::PolyTraitRef,
186+
_: hir::TraitBoundModifier) { }
184187
fn check_fn(&mut self,
185188
_: &LateContext<'a, 'tcx>,
186189
_: FnKind<'tcx>,
@@ -253,6 +256,9 @@ pub trait EarlyLintPass: LintPass {
253256
fn check_ty(&mut self, _: &EarlyContext, _: &ast::Ty) { }
254257
fn check_generic_param(&mut self, _: &EarlyContext, _: &ast::GenericParam) { }
255258
fn check_generics(&mut self, _: &EarlyContext, _: &ast::Generics) { }
259+
fn check_where_predicate(&mut self, _: &EarlyContext, _: &ast::WherePredicate) { }
260+
fn check_poly_trait_ref(&mut self, _: &EarlyContext, _: &ast::PolyTraitRef,
261+
_: &ast::TraitBoundModifier) { }
256262
fn check_fn(&mut self, _: &EarlyContext,
257263
_: ast_visit::FnKind, _: &ast::FnDecl, _: Span, _: ast::NodeId) { }
258264
fn check_fn_post(&mut self, _: &EarlyContext,

src/librustc_lint/builtin.rs

+47
Original file line numberDiff line numberDiff line change
@@ -1315,3 +1315,50 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnreachablePub {
13151315
self.perform_lint(cx, "item", impl_item.id, &impl_item.vis, impl_item.span, false);
13161316
}
13171317
}
1318+
1319+
/// Lint for trait and lifetime bounds that are (accidentally) accepted by the parser, but
1320+
/// ignored later.
1321+
1322+
pub struct IgnoredGenericBounds;
1323+
1324+
declare_lint! {
1325+
IGNORED_GENERIC_BOUNDS,
1326+
Warn,
1327+
"these generic bounds are ignored"
1328+
}
1329+
1330+
impl LintPass for IgnoredGenericBounds {
1331+
fn get_lints(&self) -> LintArray {
1332+
lint_array!(IGNORED_GENERIC_BOUNDS)
1333+
}
1334+
}
1335+
1336+
impl EarlyLintPass for IgnoredGenericBounds {
1337+
fn check_item(&mut self, cx: &EarlyContext, item: &ast::Item) {
1338+
let type_alias_generics = match item.node {
1339+
ast::ItemKind::Ty(_, ref generics) => generics,
1340+
_ => return,
1341+
};
1342+
// There must not be a where clause
1343+
if !type_alias_generics.where_clause.predicates.is_empty() {
1344+
let spans : Vec<_> = type_alias_generics.where_clause.predicates.iter()
1345+
.map(|pred| pred.span()).collect();
1346+
cx.span_lint(IGNORED_GENERIC_BOUNDS, spans,
1347+
"where clauses are ignored in type aliases");
1348+
}
1349+
// The parameters must not have bounds
1350+
for param in type_alias_generics.params.iter() {
1351+
let spans : Vec<_> = match param {
1352+
&ast::GenericParam::Lifetime(ref l) => l.bounds.iter().map(|b| b.span).collect(),
1353+
&ast::GenericParam::Type(ref ty) => ty.bounds.iter().map(|b| b.span()).collect(),
1354+
};
1355+
if !spans.is_empty() {
1356+
cx.span_lint(
1357+
IGNORED_GENERIC_BOUNDS,
1358+
spans,
1359+
"bounds on generic parameters are ignored in type aliases",
1360+
);
1361+
}
1362+
}
1363+
}
1364+
}

src/librustc_lint/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) {
109109
UnusedImportBraces,
110110
AnonymousParameters,
111111
UnusedDocComment,
112+
IgnoredGenericBounds,
112113
);
113114

114115
add_early_builtin_with_new!(sess,

src/librustc_passes/ast_validation.rs

+41
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,33 @@ impl<'a> AstValidator<'a> {
136136
in patterns")
137137
}
138138
}
139+
140+
fn check_late_bound_lifetime_defs(&self, params: &Vec<GenericParam>) {
141+
// Check: Only lifetime parameters
142+
let non_lifetime_param_spans : Vec<_> = params.iter()
143+
.filter_map(|param| match *param {
144+
GenericParam::Lifetime(_) => None,
145+
GenericParam::Type(ref t) => Some(t.span),
146+
}).collect();
147+
if !non_lifetime_param_spans.is_empty() {
148+
self.err_handler().span_err(non_lifetime_param_spans,
149+
"only lifetime parameters can be used in this context");
150+
}
151+
152+
// Check: No bounds on lifetime parameters
153+
for param in params.iter() {
154+
match *param {
155+
GenericParam::Lifetime(ref l) => {
156+
if !l.bounds.is_empty() {
157+
let spans : Vec<_> = l.bounds.iter().map(|b| b.span).collect();
158+
self.err_handler().span_err(spans,
159+
"lifetime bounds cannot be used in this context");
160+
}
161+
}
162+
GenericParam::Type(_) => {}
163+
}
164+
}
165+
}
139166
}
140167

141168
impl<'a> Visitor<'a> for AstValidator<'a> {
@@ -157,6 +184,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
157184
struct_span_err!(self.session, span, E0561,
158185
"patterns aren't allowed in function pointer types").emit();
159186
});
187+
self.check_late_bound_lifetime_defs(&bfty.generic_params);
160188
}
161189
TyKind::TraitObject(ref bounds, ..) => {
162190
let mut any_lifetime_bounds = false;
@@ -417,6 +445,19 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
417445

418446
visit::walk_pat(self, pat)
419447
}
448+
449+
fn visit_where_predicate(&mut self, p: &'a WherePredicate) {
450+
if let &WherePredicate::BoundPredicate(ref bound_predicate) = p {
451+
// A type binding, eg `for<'c> Foo: Send+Clone+'c`
452+
self.check_late_bound_lifetime_defs(&bound_predicate.bound_generic_params);
453+
}
454+
visit::walk_where_predicate(self, p);
455+
}
456+
457+
fn visit_poly_trait_ref(&mut self, t: &'a PolyTraitRef, m: &'a TraitBoundModifier) {
458+
self.check_late_bound_lifetime_defs(&t.bound_generic_params);
459+
visit::walk_poly_trait_ref(self, t, m);
460+
}
420461
}
421462

422463
// Bans nested `impl Trait`, e.g. `impl Into<impl Debug>`.

src/librustc_typeck/collect.rs

+1-40
Original file line numberDiff line numberDiff line change
@@ -356,39 +356,6 @@ fn is_param<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
356356
}
357357
}
358358

359-
fn ensure_no_param_bounds(tcx: TyCtxt,
360-
span: Span,
361-
generics: &hir::Generics,
362-
thing: &'static str) {
363-
let mut warn = false;
364-
365-
for ty_param in generics.ty_params() {
366-
if !ty_param.bounds.is_empty() {
367-
warn = true;
368-
}
369-
}
370-
371-
for lft_param in generics.lifetimes() {
372-
if !lft_param.bounds.is_empty() {
373-
warn = true;
374-
}
375-
}
376-
377-
if !generics.where_clause.predicates.is_empty() {
378-
warn = true;
379-
}
380-
381-
if warn {
382-
// According to accepted RFC #XXX, we should
383-
// eventually accept these, but it will not be
384-
// part of this PR. Still, convert to warning to
385-
// make bootstrapping easier.
386-
span_warn!(tcx.sess, span, E0122,
387-
"generic bounds are ignored in {}",
388-
thing);
389-
}
390-
}
391-
392359
fn convert_item<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, item_id: ast::NodeId) {
393360
let it = tcx.hir.expect_item(item_id);
394361
debug!("convert: item {} with id {}", it.name, it.id);
@@ -449,13 +416,7 @@ fn convert_item<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, item_id: ast::NodeId) {
449416
convert_variant_ctor(tcx, struct_def.id());
450417
}
451418
},
452-
hir::ItemTy(_, ref generics) => {
453-
ensure_no_param_bounds(tcx, it.span, generics, "type aliases");
454-
tcx.generics_of(def_id);
455-
tcx.type_of(def_id);
456-
tcx.predicates_of(def_id);
457-
}
458-
hir::ItemStatic(..) | hir::ItemConst(..) | hir::ItemFn(..) => {
419+
hir::ItemTy(..) | hir::ItemStatic(..) | hir::ItemConst(..) | hir::ItemFn(..) => {
459420
tcx.generics_of(def_id);
460421
tcx.type_of(def_id);
461422
tcx.predicates_of(def_id);

src/librustc_typeck/diagnostics.rs

+1-20
Original file line numberDiff line numberDiff line change
@@ -1489,26 +1489,6 @@ static BAR: _ = "test"; // error, explicitly write out the type instead
14891489
```
14901490
"##,
14911491

1492-
E0122: r##"
1493-
An attempt was made to add a generic constraint to a type alias. This constraint
1494-
is entirely ignored. For backwards compatibility, Rust still allows this with a
1495-
warning. Consider the example below:
1496-
1497-
```
1498-
trait Foo{}
1499-
1500-
type MyType<R: Foo> = (R, ());
1501-
1502-
fn main() {
1503-
let t: MyType<u32>;
1504-
}
1505-
```
1506-
1507-
We're able to declare a variable of type `MyType<u32>`, despite the fact that
1508-
`u32` does not implement `Foo`. As a result, one should avoid using generic
1509-
constraints in concert with type aliases.
1510-
"##,
1511-
15121492
E0124: r##"
15131493
You declared two fields of a struct with the same name. Erroneous code
15141494
example:
@@ -4815,6 +4795,7 @@ register_diagnostics! {
48154795
// E0086,
48164796
// E0103,
48174797
// E0104,
4798+
// E0122, // bounds in type aliases are ignored, turned into proper lint
48184799
// E0123,
48194800
// E0127,
48204801
// E0129,

src/libsyntax/ast.rs

+19
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,15 @@ pub enum TyParamBound {
294294
RegionTyParamBound(Lifetime)
295295
}
296296

297+
impl TyParamBound {
298+
pub fn span(&self) -> Span {
299+
match self {
300+
&TraitTyParamBound(ref t, ..) => t.span,
301+
&RegionTyParamBound(ref l) => l.span,
302+
}
303+
}
304+
}
305+
297306
/// A modifier on a bound, currently this is only used for `?Sized`, where the
298307
/// modifier is `Maybe`. Negative bounds should also be handled here.
299308
#[derive(Copy, Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
@@ -404,6 +413,16 @@ pub enum WherePredicate {
404413
EqPredicate(WhereEqPredicate),
405414
}
406415

416+
impl WherePredicate {
417+
pub fn span(&self) -> Span {
418+
match self {
419+
&WherePredicate::BoundPredicate(ref p) => p.span,
420+
&WherePredicate::RegionPredicate(ref p) => p.span,
421+
&WherePredicate::EqPredicate(ref p) => p.span,
422+
}
423+
}
424+
}
425+
407426
/// A type bound.
408427
///
409428
/// E.g. `for<'c> Foo: Send+Clone+'c`

src/libsyntax/parse/parser.rs

+3-12
Original file line numberDiff line numberDiff line change
@@ -4951,6 +4951,7 @@ impl<'a> Parser<'a> {
49514951
}
49524952
));
49534953
// FIXME: Decide what should be used here, `=` or `==`.
4954+
// FIXME: We are just dropping the binders in lifetime_defs on the floor here.
49544955
} else if self.eat(&token::Eq) || self.eat(&token::EqEq) {
49554956
let rhs_ty = self.parse_ty()?;
49564957
where_clause.predicates.push(ast::WherePredicate::EqPredicate(
@@ -5608,18 +5609,8 @@ impl<'a> Parser<'a> {
56085609
self.expect_lt()?;
56095610
let params = self.parse_generic_params()?;
56105611
self.expect_gt()?;
5611-
5612-
let first_non_lifetime_param_span = params.iter()
5613-
.filter_map(|param| match *param {
5614-
ast::GenericParam::Lifetime(_) => None,
5615-
ast::GenericParam::Type(ref t) => Some(t.span),
5616-
})
5617-
.next();
5618-
5619-
if let Some(span) = first_non_lifetime_param_span {
5620-
self.span_err(span, "only lifetime parameters can be used in this context");
5621-
}
5622-
5612+
// We rely on AST validation to rule out invalid cases: There must not be type
5613+
// parameters, and the lifetime parameters must not have bounds.
56235614
Ok(params)
56245615
} else {
56255616
Ok(Vec::new())

src/test/compile-fail/issue-39122.rs src/test/compile-fail/bounds-lifetime.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
type Foo<T: std::ops::Add> = T; //~ WARNING E0122
11+
type A = for<'b, 'a: 'b> fn(); //~ ERROR lifetime bounds cannot be used in this context
12+
type B = for<'b, 'a: 'b,> fn(); //~ ERROR lifetime bounds cannot be used in this context
13+
type C = for<'b, 'a: 'b +> fn(); //~ ERROR lifetime bounds cannot be used in this context
14+
type D = for<'a, T> fn(); //~ ERROR only lifetime parameters can be used in this context
15+
type E = for<T> Fn(); //~ ERROR only lifetime parameters can be used in this context
1216

13-
type Bar<T> where T: std::ops::Add = T; //~ WARNING E0122
17+
fn main() {}

src/test/compile-fail/dst-bad-assign-3.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,7 @@
1212

1313
#![feature(unsized_tuple_coercion)]
1414

15-
type Fat<T: ?Sized> = (isize, &'static str, T);
16-
//~^ WARNING bounds are ignored
15+
type Fat<T> = (isize, &'static str, T);
1716

1817
#[derive(PartialEq,Eq)]
1918
struct Bar;

src/test/compile-fail/issue-17994.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,5 @@
1010

1111
trait Tr {}
1212
type Huh<T> where T: Tr = isize; //~ ERROR type parameter `T` is unused
13-
//~| WARNING E0122
13+
//~| WARNING where clauses are ignored in type aliases
1414
fn main() {}

src/test/compile-fail/issue-23046.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,15 @@
1010

1111
pub enum Expr<'var, VAR> {
1212
Let(Box<Expr<'var, VAR>>,
13-
Box<for<'v: 'var> Fn(Expr<'v, VAR>) -> Expr<'v, VAR> + 'var>)
13+
Box<for<'v> Fn(Expr<'v, VAR>) -> Expr<'v, VAR> + 'var>)
1414
}
1515

1616
pub fn add<'var, VAR>
1717
(a: Expr<'var, VAR>, b: Expr<'var, VAR>) -> Expr<'var, VAR> {
1818
loop {}
1919
}
2020

21-
pub fn let_<'var, VAR, F: for<'v: 'var> Fn(Expr<'v, VAR>) -> Expr<'v, VAR>>
21+
pub fn let_<'var, VAR, F: for<'v> Fn(Expr<'v, VAR>) -> Expr<'v, VAR>>
2222
(a: Expr<'var, VAR>, b: F) -> Expr<'var, VAR> {
2323
loop {}
2424
}

src/test/compile-fail/private-in-public-warn.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ mod traits {
5858
pub trait PubTr {}
5959

6060
pub type Alias<T: PrivTr> = T; //~ ERROR private trait `traits::PrivTr` in public interface
61-
//~^ WARN bounds are ignored in type aliases
61+
//~^ WARNING bounds on generic parameters are ignored
6262
//~| WARNING hard error
6363
pub trait Tr1: PrivTr {} //~ ERROR private trait `traits::PrivTr` in public interface
6464
//~^ WARNING hard error
@@ -85,7 +85,7 @@ mod traits_where {
8585
pub type Alias<T> where T: PrivTr = T;
8686
//~^ ERROR private trait `traits_where::PrivTr` in public interface
8787
//~| WARNING hard error
88-
//~| WARNING E0122
88+
//~| WARNING where clauses are ignored in type aliases
8989
pub trait Tr2<T> where T: PrivTr {}
9090
//~^ ERROR private trait `traits_where::PrivTr` in public interface
9191
//~| WARNING hard error

0 commit comments

Comments
 (0)