Skip to content

Commit 91eedfe

Browse files
committed
Report errors for type parameters that are not constrained, either by
variance or an associated type.
1 parent 2594d56 commit 91eedfe

File tree

6 files changed

+180
-70
lines changed

6 files changed

+180
-70
lines changed

src/librustc/middle/ty.rs

+7
Original file line numberDiff line numberDiff line change
@@ -2955,6 +2955,13 @@ impl<'tcx> TyS<'tcx> {
29552955
assert_eq!(r, Some(self));
29562956
walker
29572957
}
2958+
2959+
pub fn as_opt_param_ty(&self) -> Option<ty::ParamTy> {
2960+
match self.sty {
2961+
ty::ty_param(ref d) => Some(d.clone()),
2962+
_ => None,
2963+
}
2964+
}
29582965
}
29592966

29602967
pub fn walk_ty<'tcx, F>(ty_root: Ty<'tcx>, mut f: F)

src/librustc_typeck/check/wf.rs

+139-11
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,22 @@
1010

1111
use astconv::AstConv;
1212
use check::{FnCtxt, Inherited, blank_fn_ctxt, vtable, regionck};
13+
use constrained_type_params::identify_constrained_type_params;
1314
use CrateCtxt;
1415
use middle::region;
15-
use middle::subst;
16+
use middle::subst::{self, TypeSpace, FnSpace, ParamSpace, SelfSpace};
1617
use middle::traits;
1718
use middle::ty::{self, Ty};
1819
use middle::ty::liberate_late_bound_regions;
1920
use middle::ty_fold::{TypeFolder, TypeFoldable, super_fold_ty};
20-
use util::ppaux::Repr;
21+
use util::ppaux::{Repr, UserString};
2122

2223
use std::collections::HashSet;
2324
use syntax::ast;
2425
use syntax::ast_util::{local_def};
2526
use syntax::attr;
2627
use syntax::codemap::Span;
27-
use syntax::parse::token;
28+
use syntax::parse::token::{self, special_idents};
2829
use syntax::visit;
2930
use syntax::visit::Visitor;
3031

@@ -38,6 +39,10 @@ impl<'ccx, 'tcx> CheckTypeWellFormedVisitor<'ccx, 'tcx> {
3839
CheckTypeWellFormedVisitor { ccx: ccx, cache: HashSet::new() }
3940
}
4041

42+
fn tcx(&self) -> &ty::ctxt<'tcx> {
43+
self.ccx.tcx
44+
}
45+
4146
/// Checks that the field types (in a struct def'n) or argument types (in an enum def'n) are
4247
/// well-formed, meaning that they do not require any constraints not declared in the struct
4348
/// definition itself. For example, this definition would be illegal:
@@ -96,23 +101,29 @@ impl<'ccx, 'tcx> CheckTypeWellFormedVisitor<'ccx, 'tcx> {
96101
ast::ItemConst(..) => {
97102
self.check_item_type(item);
98103
}
99-
ast::ItemStruct(ref struct_def, _) => {
104+
ast::ItemStruct(ref struct_def, ref ast_generics) => {
100105
self.check_type_defn(item, |fcx| {
101106
vec![struct_variant(fcx, &**struct_def)]
102107
});
108+
109+
self.check_variances_for_type_defn(item, ast_generics);
103110
}
104-
ast::ItemEnum(ref enum_def, _) => {
111+
ast::ItemEnum(ref enum_def, ref ast_generics) => {
105112
self.check_type_defn(item, |fcx| {
106113
enum_variants(fcx, enum_def)
107114
});
115+
116+
self.check_variances_for_type_defn(item, ast_generics);
108117
}
109-
ast::ItemTrait(..) => {
118+
ast::ItemTrait(_, ref ast_generics, _, _) => {
110119
let trait_predicates =
111120
ty::lookup_predicates(ccx.tcx, local_def(item.id));
112121
reject_non_type_param_bounds(
113122
ccx.tcx,
114123
item.span,
115124
&trait_predicates);
125+
self.check_variances(item, ast_generics, &trait_predicates,
126+
self.tcx().lang_items.phantom_fn());
116127
}
117128
_ => {}
118129
}
@@ -280,6 +291,123 @@ impl<'ccx, 'tcx> CheckTypeWellFormedVisitor<'ccx, 'tcx> {
280291
}
281292
});
282293
}
294+
295+
fn check_variances_for_type_defn(&self,
296+
item: &ast::Item,
297+
ast_generics: &ast::Generics)
298+
{
299+
let item_def_id = local_def(item.id);
300+
let predicates = ty::lookup_predicates(self.tcx(), item_def_id);
301+
self.check_variances(item,
302+
ast_generics,
303+
&predicates,
304+
self.tcx().lang_items.phantom_data());
305+
}
306+
307+
fn check_variances(&self,
308+
item: &ast::Item,
309+
ast_generics: &ast::Generics,
310+
ty_predicates: &ty::GenericPredicates<'tcx>,
311+
suggested_marker_id: Option<ast::DefId>)
312+
{
313+
let variance_lang_items = &[
314+
self.tcx().lang_items.phantom_fn(),
315+
self.tcx().lang_items.phantom_data(),
316+
];
317+
318+
let item_def_id = local_def(item.id);
319+
let is_lang_item = variance_lang_items.iter().any(|n| *n == Some(item_def_id));
320+
if is_lang_item {
321+
return;
322+
}
323+
324+
let variances = ty::item_variances(self.tcx(), item_def_id);
325+
326+
let mut constrained_parameters: HashSet<_> =
327+
variances.types
328+
.iter_enumerated()
329+
.filter(|&(_, _, &variance)| variance != ty::Bivariant)
330+
.map(|(space, index, _)| self.param_ty(ast_generics, space, index))
331+
.collect();
332+
333+
identify_constrained_type_params(self.tcx(),
334+
ty_predicates.predicates.as_slice(),
335+
None,
336+
&mut constrained_parameters);
337+
338+
for (space, index, _) in variances.types.iter_enumerated() {
339+
let param_ty = self.param_ty(ast_generics, space, index);
340+
if constrained_parameters.contains(&param_ty) {
341+
continue;
342+
}
343+
let span = self.ty_param_span(ast_generics, item, space, index);
344+
self.report_bivariance(span, param_ty.name, suggested_marker_id);
345+
}
346+
347+
for (space, index, &variance) in variances.regions.iter_enumerated() {
348+
if variance != ty::Bivariant {
349+
continue;
350+
}
351+
352+
assert_eq!(space, TypeSpace);
353+
let span = ast_generics.lifetimes[index].lifetime.span;
354+
let name = ast_generics.lifetimes[index].lifetime.name;
355+
self.report_bivariance(span, name, suggested_marker_id);
356+
}
357+
}
358+
359+
fn param_ty(&self,
360+
ast_generics: &ast::Generics,
361+
space: ParamSpace,
362+
index: usize)
363+
-> ty::ParamTy
364+
{
365+
let name = match space {
366+
TypeSpace => ast_generics.ty_params[index].ident.name,
367+
SelfSpace => special_idents::type_self.name,
368+
FnSpace => self.tcx().sess.bug("Fn space occupied?"),
369+
};
370+
371+
ty::ParamTy { space: space, idx: index as u32, name: name }
372+
}
373+
374+
fn ty_param_span(&self,
375+
ast_generics: &ast::Generics,
376+
item: &ast::Item,
377+
space: ParamSpace,
378+
index: usize)
379+
-> Span
380+
{
381+
match space {
382+
TypeSpace => ast_generics.ty_params[index].span,
383+
SelfSpace => item.span,
384+
FnSpace => self.tcx().sess.span_bug(item.span, "Fn space occupied?"),
385+
}
386+
}
387+
388+
fn report_bivariance(&self,
389+
span: Span,
390+
param_name: ast::Name,
391+
suggested_marker_id: Option<ast::DefId>)
392+
{
393+
self.tcx().sess.span_err(
394+
span,
395+
&format!("parameter `{}` is never used",
396+
param_name.user_string(self.tcx()))[]);
397+
398+
match suggested_marker_id {
399+
Some(def_id) => {
400+
self.tcx().sess.span_help(
401+
span,
402+
format!("consider removing `{}` or using a marker such as `{}`",
403+
param_name.user_string(self.tcx()),
404+
ty::item_path_str(self.tcx(), def_id)).as_slice());
405+
}
406+
None => {
407+
// no lang items, no help!
408+
}
409+
}
410+
}
283411
}
284412

285413
// Reject any predicates that do not involve a type parameter.
@@ -347,9 +475,9 @@ impl<'ccx, 'tcx, 'v> Visitor<'v> for CheckTypeWellFormedVisitor<'ccx, 'tcx> {
347475
match fk {
348476
visit::FkFnBlock | visit::FkItemFn(..) => {}
349477
visit::FkMethod(..) => {
350-
match ty::impl_or_trait_item(self.ccx.tcx, local_def(id)) {
478+
match ty::impl_or_trait_item(self.tcx(), local_def(id)) {
351479
ty::ImplOrTraitItem::MethodTraitItem(ty_method) => {
352-
reject_shadowing_type_parameters(self.ccx.tcx, span, &ty_method.generics)
480+
reject_shadowing_type_parameters(self.tcx(), span, &ty_method.generics)
353481
}
354482
_ => {}
355483
}
@@ -363,14 +491,14 @@ impl<'ccx, 'tcx, 'v> Visitor<'v> for CheckTypeWellFormedVisitor<'ccx, 'tcx> {
363491
&ast::TraitItem::ProvidedMethod(_) |
364492
&ast::TraitItem::TypeTraitItem(_) => {},
365493
&ast::TraitItem::RequiredMethod(ref method) => {
366-
match ty::impl_or_trait_item(self.ccx.tcx, local_def(method.id)) {
494+
match ty::impl_or_trait_item(self.tcx(), local_def(method.id)) {
367495
ty::ImplOrTraitItem::MethodTraitItem(ty_method) => {
368496
reject_non_type_param_bounds(
369-
self.ccx.tcx,
497+
self.tcx(),
370498
method.span,
371499
&ty_method.predicates);
372500
reject_shadowing_type_parameters(
373-
self.ccx.tcx,
501+
self.tcx(),
374502
method.span,
375503
&ty_method.generics);
376504
}

src/librustc_typeck/collect.rs

+7-49
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ There are some shortcomings in this design:
8787

8888
use astconv::{self, AstConv, ty_of_arg, ast_ty_to_ty, ast_region_to_region};
8989
use middle::def;
90+
use constrained_type_params::identify_constrained_type_params;
9091
use middle::lang_items::SizedTraitLangItem;
9192
use middle::region;
9293
use middle::resolve_lifetime;
@@ -1960,51 +1961,15 @@ fn enforce_impl_ty_params_are_constrained<'tcx>(tcx: &ty::ctxt<'tcx>,
19601961
let mut input_parameters: HashSet<_> =
19611962
impl_trait_ref.iter()
19621963
.flat_map(|t| t.input_types().iter()) // Types in trait ref, if any
1963-
.chain(Some(impl_scheme.ty).iter()) // Self type, always
1964+
.chain(Some(impl_scheme.ty).iter()) // Self type, always
19641965
.flat_map(|t| t.walk())
1965-
.filter_map(to_opt_param_ty)
1966+
.filter_map(|t| t.as_opt_param_ty())
19661967
.collect();
19671968

1968-
loop {
1969-
let num_inputs = input_parameters.len();
1970-
1971-
let projection_predicates =
1972-
impl_predicates.predicates
1973-
.iter()
1974-
.filter_map(|predicate| {
1975-
match *predicate {
1976-
// Ignore higher-ranked binders. For the purposes
1977-
// of this check, they don't matter because they
1978-
// only affect named regions, and we're just
1979-
// concerned about type parameters here.
1980-
ty::Predicate::Projection(ref data) => Some(data.0.clone()),
1981-
_ => None,
1982-
}
1983-
});
1984-
1985-
for projection in projection_predicates {
1986-
// Special case: watch out for some kind of sneaky attempt
1987-
// to project out an associated type defined by this very trait.
1988-
if Some(projection.projection_ty.trait_ref.clone()) == impl_trait_ref {
1989-
continue;
1990-
}
1991-
1992-
let relies_only_on_inputs =
1993-
projection.projection_ty.trait_ref.input_types().iter()
1994-
.flat_map(|t| t.walk())
1995-
.filter_map(to_opt_param_ty)
1996-
.all(|t| input_parameters.contains(&t));
1997-
1998-
if relies_only_on_inputs {
1999-
input_parameters.extend(
2000-
projection.ty.walk().filter_map(to_opt_param_ty));
2001-
}
2002-
}
2003-
2004-
if input_parameters.len() == num_inputs {
2005-
break;
2006-
}
2007-
}
1969+
identify_constrained_type_params(tcx,
1970+
impl_predicates.predicates.as_slice(),
1971+
impl_trait_ref,
1972+
&mut input_parameters);
20081973

20091974
for (index, ty_param) in ast_generics.ty_params.iter().enumerate() {
20101975
let param_ty = ty::ParamTy { space: TypeSpace,
@@ -2025,11 +1990,4 @@ fn enforce_impl_ty_params_are_constrained<'tcx>(tcx: &ty::ctxt<'tcx>,
20251990
}
20261991
}
20271992
}
2028-
2029-
fn to_opt_param_ty<'tcx>(ty: Ty<'tcx>) -> Option<ty::ParamTy> {
2030-
match ty.sty {
2031-
ty::ty_param(ref d) => Some(d.clone()),
2032-
_ => None,
2033-
}
2034-
}
20351993
}

src/librustc_typeck/constrained_type_params.rs

+10-10
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,16 @@ pub fn identify_constrained_type_params<'tcx>(_tcx: &ty::ctxt<'tcx>,
2323

2424
let projection_predicates =
2525
predicates.iter()
26-
.filter_map(|predicate| {
27-
match *predicate {
28-
// Ignore higher-ranked binders. For the purposes
29-
// of this check, they don't matter because they
30-
// only affect named regions, and we're just
31-
// concerned about type parameters here.
32-
ty::Predicate::Projection(ref data) => Some(data.0.clone()),
33-
_ => None,
34-
}
35-
});
26+
.filter_map(|predicate| {
27+
match *predicate {
28+
// Ignore higher-ranked binders. For the purposes
29+
// of this check, they don't matter because they
30+
// only affect named regions, and we're just
31+
// concerned about type parameters here.
32+
ty::Predicate::Projection(ref data) => Some(data.0.clone()),
33+
_ => None,
34+
}
35+
});
3636

3737
for projection in projection_predicates {
3838
// Special case: watch out for some kind of sneaky attempt

src/librustc_typeck/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ mod check;
123123
mod rscope;
124124
mod astconv;
125125
mod collect;
126+
mod constrained_type_params;
126127
mod coherence;
127128
mod variance;
128129

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Test that we can parse a unit struct with a where clause, even if
12+
// it leads to a error later on since `T` is unused.
13+
14+
struct Foo<T> where T: Copy; //~ ERROR parameter `T` is never used
15+
16+
fn main() {}

0 commit comments

Comments
 (0)