Skip to content

Commit dd667da

Browse files
committed
Auto merge of #48326 - RalfJung:generic-bounds, r=<try>
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 27a046e + 0073ce3 commit dd667da

15 files changed

+321
-86
lines changed

src/librustc/lint/context.rs

+21
Original file line numberDiff line numberDiff line change
@@ -793,6 +793,17 @@ impl<'a, 'tcx> hir_visit::Visitor<'tcx> for LateContext<'a, 'tcx> {
793793
hir_visit::walk_generics(self, g);
794794
}
795795

796+
fn visit_where_predicate(&mut self, p: &'tcx hir::WherePredicate) {
797+
run_lints!(self, check_where_predicate, late_passes, p);
798+
hir_visit::walk_where_predicate(self, p);
799+
}
800+
801+
fn visit_poly_trait_ref(&mut self, t: &'tcx hir::PolyTraitRef,
802+
m: hir::TraitBoundModifier) {
803+
run_lints!(self, check_poly_trait_ref, late_passes, t, m);
804+
hir_visit::walk_poly_trait_ref(self, t, m);
805+
}
806+
796807
fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem) {
797808
let generics = self.generics.take();
798809
self.generics = Some(&trait_item.generics);
@@ -955,6 +966,16 @@ impl<'a> ast_visit::Visitor<'a> for EarlyContext<'a> {
955966
ast_visit::walk_generics(self, g);
956967
}
957968

969+
fn visit_where_predicate(&mut self, p: &'a ast::WherePredicate) {
970+
run_lints!(self, check_where_predicate, early_passes, p);
971+
ast_visit::walk_where_predicate(self, p);
972+
}
973+
974+
fn visit_poly_trait_ref(&mut self, t: &'a ast::PolyTraitRef, m: &'a ast::TraitBoundModifier) {
975+
run_lints!(self, check_poly_trait_ref, early_passes, t, m);
976+
ast_visit::walk_poly_trait_ref(self, t, m);
977+
}
978+
958979
fn visit_trait_item(&mut self, trait_item: &'a ast::TraitItem) {
959980
self.with_lint_attrs(trait_item.id, &trait_item.attrs, |cx| {
960981
run_lints!(cx, check_trait_item, early_passes, trait_item);

src/librustc/lint/mod.rs

+6
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,9 @@ pub trait LateLintPass<'a, 'tcx>: LintPass {
158158
fn check_ty(&mut self, _: &LateContext<'a, 'tcx>, _: &'tcx hir::Ty) { }
159159
fn check_generic_param(&mut self, _: &LateContext<'a, 'tcx>, _: &'tcx hir::GenericParam) { }
160160
fn check_generics(&mut self, _: &LateContext<'a, 'tcx>, _: &'tcx hir::Generics) { }
161+
fn check_where_predicate(&mut self, _: &LateContext<'a, 'tcx>, _: &'tcx hir::WherePredicate) { }
162+
fn check_poly_trait_ref(&mut self, _: &LateContext<'a, 'tcx>, _: &'tcx hir::PolyTraitRef,
163+
_: hir::TraitBoundModifier) { }
161164
fn check_fn(&mut self,
162165
_: &LateContext<'a, 'tcx>,
163166
_: FnKind<'tcx>,
@@ -230,6 +233,9 @@ pub trait EarlyLintPass: LintPass {
230233
fn check_ty(&mut self, _: &EarlyContext, _: &ast::Ty) { }
231234
fn check_generic_param(&mut self, _: &EarlyContext, _: &ast::GenericParam) { }
232235
fn check_generics(&mut self, _: &EarlyContext, _: &ast::Generics) { }
236+
fn check_where_predicate(&mut self, _: &EarlyContext, _: &ast::WherePredicate) { }
237+
fn check_poly_trait_ref(&mut self, _: &EarlyContext, _: &ast::PolyTraitRef,
238+
_: &ast::TraitBoundModifier) { }
233239
fn check_fn(&mut self, _: &EarlyContext,
234240
_: ast_visit::FnKind, _: &ast::FnDecl, _: Span, _: ast::NodeId) { }
235241
fn check_fn_post(&mut self, _: &EarlyContext,

src/librustc_lint/builtin.rs

+94
Original file line numberDiff line numberDiff line change
@@ -1386,3 +1386,97 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnreachablePub {
13861386
self.perform_lint(cx, "item", impl_item.id, &impl_item.vis, impl_item.span, false);
13871387
}
13881388
}
1389+
1390+
/// Lint for trait and lifetime bounds that are (accidentally) accepted by the parser, but
1391+
/// ignored later.
1392+
1393+
pub struct IgnoredGenericBounds;
1394+
1395+
declare_lint! {
1396+
IGNORED_GENERIC_BOUNDS,
1397+
Deny,
1398+
"these generic bounds are ignored"
1399+
}
1400+
1401+
impl LintPass for IgnoredGenericBounds {
1402+
fn get_lints(&self) -> LintArray {
1403+
lint_array!(IGNORED_GENERIC_BOUNDS)
1404+
}
1405+
}
1406+
1407+
impl IgnoredGenericBounds {
1408+
fn ensure_no_param_bounds(
1409+
cx: &EarlyContext,
1410+
generics: &Vec<ast::GenericParam>,
1411+
thing: &'static str,
1412+
) {
1413+
for param in generics.iter() {
1414+
match param {
1415+
&ast::GenericParam::Lifetime(ref lifetime) => {
1416+
if !lifetime.bounds.is_empty() {
1417+
let spans : Vec<_> = lifetime.bounds.iter().map(|b| b.span).collect();
1418+
cx.span_lint(
1419+
IGNORED_GENERIC_BOUNDS,
1420+
spans,
1421+
format!("bounds on generic lifetime parameters are ignored in {}",
1422+
thing).as_ref()
1423+
);
1424+
}
1425+
}
1426+
&ast::GenericParam::Type(ref ty) => {
1427+
if !ty.bounds.is_empty() {
1428+
let spans : Vec<_> = ty.bounds.iter().map(|b| b.span()).collect();
1429+
cx.span_lint(
1430+
IGNORED_GENERIC_BOUNDS,
1431+
spans,
1432+
format!("bounds on generic type parameters are ignored in {}", thing)
1433+
.as_ref()
1434+
);
1435+
}
1436+
}
1437+
}
1438+
}
1439+
}
1440+
}
1441+
1442+
impl EarlyLintPass for IgnoredGenericBounds {
1443+
fn check_item(&mut self, cx: &EarlyContext, item: &ast::Item) {
1444+
match item.node {
1445+
ast::ItemKind::Ty(_, ref generics) => {
1446+
if !generics.where_clause.predicates.is_empty() {
1447+
let spans : Vec<_> = generics.where_clause.predicates.iter()
1448+
.map(|pred| pred.span()).collect();
1449+
cx.span_lint(IGNORED_GENERIC_BOUNDS, spans,
1450+
"where clauses are ignored in type aliases");
1451+
}
1452+
IgnoredGenericBounds::ensure_no_param_bounds(cx, &generics.params,
1453+
"type aliases");
1454+
}
1455+
_ => {}
1456+
}
1457+
}
1458+
1459+
fn check_where_predicate(&mut self, cx: &EarlyContext, p: &ast::WherePredicate) {
1460+
if let &ast::WherePredicate::BoundPredicate(ref bound_predicate) = p {
1461+
// A type binding, eg `for<'c> Foo: Send+Clone+'c`
1462+
IgnoredGenericBounds::ensure_no_param_bounds(cx,
1463+
&bound_predicate.bound_generic_params, "higher-ranked trait bounds (i.e., `for`)");
1464+
}
1465+
}
1466+
1467+
fn check_poly_trait_ref(&mut self, cx: &EarlyContext, t: &ast::PolyTraitRef,
1468+
_: &ast::TraitBoundModifier) {
1469+
IgnoredGenericBounds::ensure_no_param_bounds(cx, &t.bound_generic_params,
1470+
"higher-ranked trait bounds (i.e., `for`)");
1471+
}
1472+
1473+
fn check_ty(&mut self, cx: &EarlyContext, ty: &ast::Ty) {
1474+
match ty.node {
1475+
ast::TyKind::BareFn(ref fn_ty) => {
1476+
IgnoredGenericBounds::ensure_no_param_bounds(cx, &fn_ty.generic_params,
1477+
"higher-ranked function types (i.e., `for`)");
1478+
}
1479+
_ => {}
1480+
}
1481+
}
1482+
}

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
AnonymousParameters,
110110
IllegalFloatLiteralPattern,
111111
UnusedDocComment,
112+
IgnoredGenericBounds,
112113
);
113114

114115
add_early_builtin_with_new!(sess,

src/librustc_typeck/collect.rs

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

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

src/librustc_typeck/diagnostics.rs

+1-20
Original file line numberDiff line numberDiff line change
@@ -1524,26 +1524,6 @@ static BAR: _ = "test"; // error, explicitly write out the type instead
15241524
```
15251525
"##,
15261526

1527-
E0122: r##"
1528-
An attempt was made to add a generic constraint to a type alias. This constraint
1529-
is entirely ignored. For backwards compatibility, Rust still allows this with a
1530-
warning. Consider the example below:
1531-
1532-
```
1533-
trait Foo{}
1534-
1535-
type MyType<R: Foo> = (R, ());
1536-
1537-
fn main() {
1538-
let t: MyType<u32>;
1539-
}
1540-
```
1541-
1542-
We're able to declare a variable of type `MyType<u32>`, despite the fact that
1543-
`u32` does not implement `Foo`. As a result, one should avoid using generic
1544-
constraints in concert with type aliases.
1545-
"##,
1546-
15471527
E0124: r##"
15481528
You declared two fields of a struct with the same name. Erroneous code
15491529
example:
@@ -4757,6 +4737,7 @@ register_diagnostics! {
47574737
// E0086,
47584738
// E0103,
47594739
// E0104,
4740+
// E0122, // bounds in type aliases are ignored, turned into proper lint
47604741
// E0123,
47614742
// E0127,
47624743
// 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/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

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

11+
#![warn(ignored_generic_bounds)]
12+
1113
trait Tr {}
1214
type Huh<T> where T: Tr = isize; //~ ERROR type parameter `T` is unused
13-
//~| WARNING E0122
15+
//~| WARNING where clauses are ignored in type aliases
1416
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/issue-39122.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@
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 Foo<T: std::ops::Add> = T; //~ ERROR bounds on generic type parameters are ignored
1212

13-
type Bar<T> where T: std::ops::Add = T; //~ WARNING E0122
13+
type Bar<T> where T: std::ops::Add = T; //~ ERROR where clauses are ignored

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

+3-2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#![feature(associated_type_defaults)]
1515
#![deny(private_in_public)]
1616
#![allow(improper_ctypes)]
17+
#![warn(ignored_generic_bounds)]
1718

1819
mod types {
1920
struct Priv;
@@ -58,7 +59,7 @@ mod traits {
5859
pub trait PubTr {}
5960

6061
pub type Alias<T: PrivTr> = T; //~ ERROR private trait `traits::PrivTr` in public interface
61-
//~^ WARN bounds are ignored in type aliases
62+
//~^ WARNING bounds on generic type parameters are ignored
6263
//~| WARNING hard error
6364
pub trait Tr1: PrivTr {} //~ ERROR private trait `traits::PrivTr` in public interface
6465
//~^ WARNING hard error
@@ -85,7 +86,7 @@ mod traits_where {
8586
pub type Alias<T> where T: PrivTr = T;
8687
//~^ ERROR private trait `traits_where::PrivTr` in public interface
8788
//~| WARNING hard error
88-
//~| WARNING E0122
89+
//~| WARNING where clauses are ignored in type aliases
8990
pub trait Tr2<T> where T: PrivTr {}
9091
//~^ ERROR private trait `traits_where::PrivTr` in public interface
9192
//~| WARNING hard error

src/test/compile-fail/rfc1623.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ static NON_ELIDABLE_FN: &fn(&u8, &u8) -> &u8 =
2222
struct SomeStruct<'x, 'y, 'z: 'x> {
2323
foo: &'x Foo<'z>,
2424
bar: &'x Bar<'z>,
25-
f: &'y for<'a, 'b: 'a> Fn(&'a Foo<'b>) -> &'a Bar<'b>,
25+
f: &'y for<'a, 'b> Fn(&'a Foo<'b>) -> &'a Bar<'b>,
2626
}
2727

2828
fn id<T>(t: T) -> T {

0 commit comments

Comments
 (0)