Skip to content

Commit 4a4c61b

Browse files
committed
move the impl-params-constrained check out of collect
This helps with incr. comp. because otherwise the Collect(Impl) check winds up touching all of the impl items; since Collect(Impl) also produces the types for the impl header, this creates a polluted graph where the impl types depend on the impl items.
1 parent ae8cb22 commit 4a4c61b

File tree

6 files changed

+160
-111
lines changed

6 files changed

+160
-111
lines changed

src/librustc_typeck/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ rustc = { path = "../librustc" }
1818
rustc_back = { path = "../librustc_back" }
1919
rustc_const_eval = { path = "../librustc_const_eval" }
2020
rustc_const_math = { path = "../librustc_const_math" }
21+
rustc_data_structures = { path = "../librustc_data_structures" }
2122
rustc_platform_intrinsics = { path = "../librustc_platform_intrinsics" }
2223
syntax_pos = { path = "../libsyntax_pos" }
2324
rustc_errors = { path = "../librustc_errors" }
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
// Copyright 2012-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+
use constrained_type_params as ctp;
12+
use rustc::hir;
13+
use rustc::hir::def_id::DefId;
14+
use rustc::ty;
15+
use rustc::util::nodemap::FxHashSet;
16+
17+
use syntax_pos::Span;
18+
19+
use CrateCtxt;
20+
21+
/// Checks that all the type/lifetime parameters on an impl also
22+
/// appear in the trait ref or self-type (or are constrained by a
23+
/// where-clause). These rules are needed to ensure that, given a
24+
/// trait ref like `<T as Trait<U>>`, we can derive the values of all
25+
/// parameters on the impl (which is needed to make specialization
26+
/// possible).
27+
///
28+
/// However, in the case of lifetimes, we only enforce these rules if
29+
/// the lifetime parameter is used in an associated type. This is a
30+
/// concession to backwards compatibility; see comment at the end of
31+
/// the fn for details.
32+
///
33+
/// Example:
34+
///
35+
/// ```
36+
/// impl<T> Trait<Foo> for Bar { ... }
37+
/// ^ T does not appear in `Foo` or `Bar`, error!
38+
///
39+
/// impl<T> Trait<Foo<T>> for Bar { ... }
40+
/// ^ T appears in `Foo<T>`, ok.
41+
///
42+
/// impl<T> Trait<Foo> for Bar where Bar: Iterator<Item=T> { ... }
43+
/// ^ T is bound to `<Bar as Iterator>::Item`, ok.
44+
///
45+
/// impl<'a> Trait<Foo> for Bar { }
46+
/// ^ 'a is unused, but for back-compat we allow it
47+
///
48+
/// impl<'a> Trait<Foo> for Bar { type X = &'a i32; }
49+
/// ^ 'a is unused and appears in assoc type, error
50+
/// ```
51+
pub fn enforce_impl_params_are_constrained<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
52+
impl_hir_generics: &hir::Generics,
53+
impl_def_id: DefId,
54+
impl_item_ids: &[hir::ImplItemId])
55+
{
56+
// Every lifetime used in an associated type must be constrained.
57+
let impl_scheme = ccx.tcx.lookup_item_type(impl_def_id);
58+
let impl_predicates = ccx.tcx.lookup_predicates(impl_def_id);
59+
let impl_trait_ref = ccx.tcx.impl_trait_ref(impl_def_id);
60+
61+
let mut input_parameters = ctp::parameters_for_impl(impl_scheme.ty, impl_trait_ref);
62+
ctp::identify_constrained_type_params(
63+
&impl_predicates.predicates.as_slice(), impl_trait_ref, &mut input_parameters);
64+
65+
// Disallow ANY unconstrained type parameters.
66+
for (ty_param, param) in impl_scheme.generics.types.iter().zip(&impl_hir_generics.ty_params) {
67+
let param_ty = ty::ParamTy::for_def(ty_param);
68+
if !input_parameters.contains(&ctp::Parameter::from(param_ty)) {
69+
report_unused_parameter(ccx, param.span, "type", &param_ty.to_string());
70+
}
71+
}
72+
73+
// Disallow unconstrained lifetimes, but only if they appear in assoc types.
74+
let lifetimes_in_associated_types: FxHashSet<_> = impl_item_ids.iter()
75+
.map(|item_id| ccx.tcx.map.local_def_id(item_id.id))
76+
.filter(|&def_id| {
77+
let item = ccx.tcx.associated_item(def_id);
78+
item.kind == ty::AssociatedKind::Type && item.has_value
79+
})
80+
.flat_map(|def_id| {
81+
ctp::parameters_for(&ccx.tcx.lookup_item_type(def_id).ty, true)
82+
}).collect();
83+
for (ty_lifetime, lifetime) in impl_scheme.generics.regions.iter()
84+
.zip(&impl_hir_generics.lifetimes)
85+
{
86+
let param = ctp::Parameter::from(ty_lifetime.to_early_bound_region_data());
87+
88+
if
89+
lifetimes_in_associated_types.contains(&param) && // (*)
90+
!input_parameters.contains(&param)
91+
{
92+
report_unused_parameter(ccx, lifetime.lifetime.span,
93+
"lifetime", &lifetime.lifetime.name.to_string());
94+
}
95+
}
96+
97+
// (*) This is a horrible concession to reality. I think it'd be
98+
// better to just ban unconstrianed lifetimes outright, but in
99+
// practice people do non-hygenic macros like:
100+
//
101+
// ```
102+
// macro_rules! __impl_slice_eq1 {
103+
// ($Lhs: ty, $Rhs: ty, $Bound: ident) => {
104+
// impl<'a, 'b, A: $Bound, B> PartialEq<$Rhs> for $Lhs where A: PartialEq<B> {
105+
// ....
106+
// }
107+
// }
108+
// }
109+
// ```
110+
//
111+
// In a concession to backwards compatbility, we continue to
112+
// permit those, so long as the lifetimes aren't used in
113+
// associated types. I believe this is sound, because lifetimes
114+
// used elsewhere are not projected back out.
115+
}
116+
117+
fn report_unused_parameter(ccx: &CrateCtxt,
118+
span: Span,
119+
kind: &str,
120+
name: &str)
121+
{
122+
struct_span_err!(
123+
ccx.tcx.sess, span, E0207,
124+
"the {} parameter `{}` is not constrained by the \
125+
impl trait, self type, or predicates",
126+
kind, name)
127+
.span_label(span, &format!("unconstrained {} parameter", kind))
128+
.emit();
129+
}

src/librustc_typeck/check/mod.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ mod closure;
143143
mod callee;
144144
mod compare_method;
145145
mod intrinsic;
146+
mod impl_parameters_used;
146147
mod op;
147148

148149
/// closures defined within the function. For example:
@@ -815,7 +816,7 @@ pub fn check_item_type<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, it: &'tcx hir::Item) {
815816
it.id);
816817
}
817818
hir::ItemFn(..) => {} // entirely within check_item_body
818-
hir::ItemImpl(.., ref impl_item_ids) => {
819+
hir::ItemImpl(_, _, ref hir_generics, _, _, ref impl_item_ids) => {
819820
debug!("ItemImpl {} with id {}", it.name, it.id);
820821
let impl_def_id = ccx.tcx.map.local_def_id(it.id);
821822
if let Some(impl_trait_ref) = ccx.tcx.impl_trait_ref(impl_def_id) {
@@ -827,6 +828,12 @@ pub fn check_item_type<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, it: &'tcx hir::Item) {
827828
let trait_def_id = impl_trait_ref.def_id;
828829
check_on_unimplemented(ccx, trait_def_id, it);
829830
}
831+
832+
impl_parameters_used::enforce_impl_params_are_constrained(ccx,
833+
hir_generics,
834+
impl_def_id,
835+
impl_item_ids);
836+
830837
}
831838
hir::ItemTrait(..) => {
832839
let def_id = ccx.tcx.map.local_def_id(it.id);

src/librustc_typeck/collect.rs

Lines changed: 9 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -753,7 +753,15 @@ fn convert_item(ccx: &CrateCtxt, it: &hir::Item) {
753753
});
754754
tcx.impl_trait_refs.borrow_mut().insert(def_id, trait_ref);
755755

756-
enforce_impl_params_are_constrained(ccx, generics, &mut ty_predicates, def_id);
756+
// Subtle: before we store the predicates into the tcx, we
757+
// sort them so that predicates like `T: Foo<Item=U>` come
758+
// before uses of `U`. This avoids false ambiguity errors
759+
// in trait checking. See `setup_constraining_predicates`
760+
// for details.
761+
ctp::setup_constraining_predicates(&mut ty_predicates.predicates,
762+
trait_ref,
763+
&mut ctp::parameters_for_impl(selfty, trait_ref));
764+
757765
tcx.predicates.borrow_mut().insert(def_id, ty_predicates.clone());
758766

759767

@@ -788,8 +796,6 @@ fn convert_item(ccx: &CrateCtxt, it: &hir::Item) {
788796
for &impl_item_id in impl_item_ids {
789797
convert_impl_item(ccx, impl_item_id);
790798
}
791-
792-
enforce_impl_lifetimes_are_constrained(ccx, generics, def_id, impl_item_ids);
793799
},
794800
hir::ItemTrait(.., ref trait_items) => {
795801
let trait_def = trait_def_of_item(ccx, it);
@@ -2084,110 +2090,3 @@ pub fn mk_item_substs<'gcx: 'tcx, 'tcx>(astconv: &AstConv<'gcx, 'tcx>,
20842090
|def, _| tcx.mk_region(def.to_early_bound_region()),
20852091
|def, _| tcx.mk_param_from_def(def))
20862092
}
2087-
2088-
/// Checks that all the type parameters on an impl
2089-
fn enforce_impl_params_are_constrained<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
2090-
generics: &hir::Generics,
2091-
impl_predicates: &mut ty::GenericPredicates<'tcx>,
2092-
impl_def_id: DefId)
2093-
{
2094-
let impl_ty = ccx.tcx.item_type(impl_def_id);
2095-
let impl_trait_ref = ccx.tcx.impl_trait_ref(impl_def_id);
2096-
2097-
// The trait reference is an input, so find all type parameters
2098-
// reachable from there, to start (if this is an inherent impl,
2099-
// then just examine the self type).
2100-
let mut input_parameters: FxHashSet<_> =
2101-
ctp::parameters_for(&impl_ty, false).into_iter().collect();
2102-
if let Some(ref trait_ref) = impl_trait_ref {
2103-
input_parameters.extend(ctp::parameters_for(trait_ref, false));
2104-
}
2105-
2106-
ctp::setup_constraining_predicates(&mut impl_predicates.predicates,
2107-
impl_trait_ref,
2108-
&mut input_parameters);
2109-
2110-
let ty_generics = generics_of_def_id(ccx, impl_def_id);
2111-
for (ty_param, param) in ty_generics.types.iter().zip(&generics.ty_params) {
2112-
let param_ty = ty::ParamTy::for_def(ty_param);
2113-
if !input_parameters.contains(&ctp::Parameter::from(param_ty)) {
2114-
report_unused_parameter(ccx, param.span, "type", &param_ty.to_string());
2115-
}
2116-
}
2117-
}
2118-
2119-
fn enforce_impl_lifetimes_are_constrained<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
2120-
ast_generics: &hir::Generics,
2121-
impl_def_id: DefId,
2122-
impl_item_ids: &[hir::ImplItemId])
2123-
{
2124-
// Every lifetime used in an associated type must be constrained.
2125-
let impl_ty = ccx.tcx.item_type(impl_def_id);
2126-
let impl_predicates = ccx.tcx.item_predicates(impl_def_id);
2127-
let impl_trait_ref = ccx.tcx.impl_trait_ref(impl_def_id);
2128-
2129-
let mut input_parameters: FxHashSet<_> =
2130-
ctp::parameters_for(&impl_ty, false).into_iter().collect();
2131-
if let Some(ref trait_ref) = impl_trait_ref {
2132-
input_parameters.extend(ctp::parameters_for(trait_ref, false));
2133-
}
2134-
ctp::identify_constrained_type_params(
2135-
&impl_predicates.predicates.as_slice(), impl_trait_ref, &mut input_parameters);
2136-
2137-
let lifetimes_in_associated_types: FxHashSet<_> = impl_item_ids.iter()
2138-
.map(|item_id| ccx.tcx.map.local_def_id(item_id.id))
2139-
.filter(|&def_id| {
2140-
let item = ccx.tcx.associated_item(def_id);
2141-
item.kind == ty::AssociatedKind::Type && item.has_value
2142-
})
2143-
.flat_map(|def_id| {
2144-
ctp::parameters_for(&ccx.tcx.item_type(def_id), true)
2145-
}).collect();
2146-
2147-
for (ty_lifetime, lifetime) in ccx.tcx.item_generics(impl_def_id).regions.iter()
2148-
.zip(&ast_generics.lifetimes)
2149-
{
2150-
let param = ctp::Parameter::from(ty_lifetime.to_early_bound_region_data());
2151-
2152-
if
2153-
lifetimes_in_associated_types.contains(&param) && // (*)
2154-
!input_parameters.contains(&param)
2155-
{
2156-
report_unused_parameter(ccx, lifetime.lifetime.span,
2157-
"lifetime", &lifetime.lifetime.name.to_string());
2158-
}
2159-
}
2160-
2161-
// (*) This is a horrible concession to reality. I think it'd be
2162-
// better to just ban unconstrianed lifetimes outright, but in
2163-
// practice people do non-hygenic macros like:
2164-
//
2165-
// ```
2166-
// macro_rules! __impl_slice_eq1 {
2167-
// ($Lhs: ty, $Rhs: ty, $Bound: ident) => {
2168-
// impl<'a, 'b, A: $Bound, B> PartialEq<$Rhs> for $Lhs where A: PartialEq<B> {
2169-
// ....
2170-
// }
2171-
// }
2172-
// }
2173-
// ```
2174-
//
2175-
// In a concession to backwards compatbility, we continue to
2176-
// permit those, so long as the lifetimes aren't used in
2177-
// associated types. I believe this is sound, because lifetimes
2178-
// used elsewhere are not projected back out.
2179-
}
2180-
2181-
fn report_unused_parameter(ccx: &CrateCtxt,
2182-
span: Span,
2183-
kind: &str,
2184-
name: &str)
2185-
{
2186-
struct_span_err!(
2187-
ccx.tcx.sess, span, E0207,
2188-
"the {} parameter `{}` is not constrained by the \
2189-
impl trait, self type, or predicates",
2190-
kind, name)
2191-
.span_label(span, &format!("unconstrained {} parameter", kind))
2192-
.emit();
2193-
}

src/librustc_typeck/constrained_type_params.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,18 @@ impl From<ty::EarlyBoundRegion> for Parameter {
2323
fn from(param: ty::EarlyBoundRegion) -> Self { Parameter(param.index) }
2424
}
2525

26+
/// Return the set of parameters constrained by the impl header.
27+
pub fn parameters_for_impl<'tcx>(impl_self_ty: Ty<'tcx>,
28+
impl_trait_ref: Option<ty::TraitRef<'tcx>>)
29+
-> FxHashSet<Parameter>
30+
{
31+
let vec = match impl_trait_ref {
32+
Some(tr) => parameters_for(&tr, false),
33+
None => parameters_for(&impl_self_ty, false),
34+
};
35+
vec.into_iter().collect()
36+
}
37+
2638
/// If `include_projections` is false, returns the list of parameters that are
2739
/// constrained by `t` - i.e. the value of each parameter in the list is
2840
/// uniquely determined by `t` (see RFC 447). If it is true, return the list

src/librustc_typeck/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ extern crate rustc_platform_intrinsics as intrinsics;
9595
extern crate rustc_back;
9696
extern crate rustc_const_math;
9797
extern crate rustc_const_eval;
98+
extern crate rustc_data_structures;
9899
extern crate rustc_errors as errors;
99100

100101
pub use rustc::dep_graph;

0 commit comments

Comments
 (0)