Skip to content

Commit f8a2f98

Browse files
committed
coherence: move the builtin trait checks to their own module
no functional changes
1 parent 8f62c29 commit f8a2f98

File tree

2 files changed

+361
-352
lines changed

2 files changed

+361
-352
lines changed
Lines changed: 357 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,357 @@
1+
// Copyright 2016 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+
//! Check properties that are required by built-in traits and set
12+
//! up data structures required by type-checking/translation.
13+
14+
use rustc::middle::free_region::FreeRegionMap;
15+
use rustc::middle::lang_items::UnsizeTraitLangItem;
16+
17+
use rustc::traits::{self, ObligationCause, Reveal};
18+
use rustc::ty::{self, Ty, TyCtxt};
19+
use rustc::ty::ParameterEnvironment;
20+
use rustc::ty::TypeFoldable;
21+
use rustc::ty::subst::Subst;
22+
use rustc::ty::util::CopyImplementationError;
23+
use rustc::infer;
24+
25+
use rustc::hir::map as hir_map;
26+
use rustc::hir::{self, ItemImpl};
27+
28+
pub fn check<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
29+
populate_destructors(tcx);
30+
check_implementations_of_copy(tcx);
31+
check_implementations_of_coerce_unsized(tcx);
32+
}
33+
34+
fn populate_destructors<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
35+
let drop_trait = match tcx.lang_items.drop_trait() {
36+
Some(id) => id,
37+
None => return,
38+
};
39+
tcx.populate_implementations_for_trait_if_necessary(drop_trait);
40+
let drop_trait = tcx.lookup_trait_def(drop_trait);
41+
42+
drop_trait.for_each_impl(tcx, |impl_did| {
43+
let items = tcx.associated_item_def_ids(impl_did);
44+
if items.is_empty() {
45+
// We'll error out later. For now, just don't ICE.
46+
return;
47+
}
48+
let method_def_id = items[0];
49+
50+
let self_type = tcx.item_type(impl_did);
51+
match self_type.sty {
52+
ty::TyAdt(type_def, _) => {
53+
type_def.set_destructor(method_def_id);
54+
}
55+
_ => {
56+
// Destructors only work on nominal types.
57+
if let Some(impl_node_id) = tcx.map.as_local_node_id(impl_did) {
58+
match tcx.map.find(impl_node_id) {
59+
Some(hir_map::NodeItem(item)) => {
60+
let span = match item.node {
61+
ItemImpl(.., ref ty, _) => ty.span,
62+
_ => item.span,
63+
};
64+
struct_span_err!(tcx.sess,
65+
span,
66+
E0120,
67+
"the Drop trait may only be implemented on \
68+
structures")
69+
.span_label(span,
70+
&format!("implementing Drop requires a struct"))
71+
.emit();
72+
}
73+
_ => {
74+
bug!("didn't find impl in ast map");
75+
}
76+
}
77+
} else {
78+
bug!("found external impl of Drop trait on \
79+
something other than a struct");
80+
}
81+
}
82+
}
83+
});
84+
}
85+
86+
fn check_implementations_of_copy<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
87+
let copy_trait = match tcx.lang_items.copy_trait() {
88+
Some(id) => id,
89+
None => return,
90+
};
91+
tcx.populate_implementations_for_trait_if_necessary(copy_trait);
92+
let copy_trait = tcx.lookup_trait_def(copy_trait);
93+
94+
copy_trait.for_each_impl(tcx, |impl_did| {
95+
debug!("check_implementations_of_copy: impl_did={:?}", impl_did);
96+
97+
let impl_node_id = if let Some(n) = tcx.map.as_local_node_id(impl_did) {
98+
n
99+
} else {
100+
debug!("check_implementations_of_copy(): impl not in this \
101+
crate");
102+
return;
103+
};
104+
105+
let self_type = tcx.item_type(impl_did);
106+
debug!("check_implementations_of_copy: self_type={:?} (bound)",
107+
self_type);
108+
109+
let span = tcx.map.span(impl_node_id);
110+
let param_env = ParameterEnvironment::for_item(tcx, impl_node_id);
111+
let self_type = self_type.subst(tcx, &param_env.free_substs);
112+
assert!(!self_type.has_escaping_regions());
113+
114+
debug!("check_implementations_of_copy: self_type={:?} (free)",
115+
self_type);
116+
117+
match param_env.can_type_implement_copy(tcx, self_type, span) {
118+
Ok(()) => {}
119+
Err(CopyImplementationError::InfrigingField(name)) => {
120+
struct_span_err!(tcx.sess,
121+
span,
122+
E0204,
123+
"the trait `Copy` may not be implemented for this type")
124+
.span_label(span, &format!("field `{}` does not implement `Copy`", name))
125+
.emit()
126+
}
127+
Err(CopyImplementationError::InfrigingVariant(name)) => {
128+
let item = tcx.map.expect_item(impl_node_id);
129+
let span = if let ItemImpl(.., Some(ref tr), _, _) = item.node {
130+
tr.path.span
131+
} else {
132+
span
133+
};
134+
135+
struct_span_err!(tcx.sess,
136+
span,
137+
E0205,
138+
"the trait `Copy` may not be implemented for this type")
139+
.span_label(span,
140+
&format!("variant `{}` does not implement `Copy`", name))
141+
.emit()
142+
}
143+
Err(CopyImplementationError::NotAnAdt) => {
144+
let item = tcx.map.expect_item(impl_node_id);
145+
let span = if let ItemImpl(.., ref ty, _) = item.node {
146+
ty.span
147+
} else {
148+
span
149+
};
150+
151+
struct_span_err!(tcx.sess,
152+
span,
153+
E0206,
154+
"the trait `Copy` may not be implemented for this type")
155+
.span_label(span, &format!("type is not a structure or enumeration"))
156+
.emit();
157+
}
158+
Err(CopyImplementationError::HasDestructor) => {
159+
struct_span_err!(tcx.sess,
160+
span,
161+
E0184,
162+
"the trait `Copy` may not be implemented for this type; the \
163+
type has a destructor")
164+
.span_label(span, &format!("Copy not allowed on types with destructors"))
165+
.emit();
166+
}
167+
}
168+
});
169+
}
170+
171+
fn check_implementations_of_coerce_unsized<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
172+
let coerce_unsized_trait = match tcx.lang_items.coerce_unsized_trait() {
173+
Some(id) => id,
174+
None => return,
175+
};
176+
let unsize_trait = match tcx.lang_items.require(UnsizeTraitLangItem) {
177+
Ok(id) => id,
178+
Err(err) => {
179+
tcx.sess.fatal(&format!("`CoerceUnsized` implementation {}", err));
180+
}
181+
};
182+
183+
let trait_def = tcx.lookup_trait_def(coerce_unsized_trait);
184+
185+
trait_def.for_each_impl(tcx, |impl_did| {
186+
debug!("check_implementations_of_coerce_unsized: impl_did={:?}",
187+
impl_did);
188+
189+
let impl_node_id = if let Some(n) = tcx.map.as_local_node_id(impl_did) {
190+
n
191+
} else {
192+
debug!("check_implementations_of_coerce_unsized(): impl not \
193+
in this crate");
194+
return;
195+
};
196+
197+
let source = tcx.item_type(impl_did);
198+
let trait_ref = tcx.impl_trait_ref(impl_did).unwrap();
199+
let target = trait_ref.substs.type_at(1);
200+
debug!("check_implementations_of_coerce_unsized: {:?} -> {:?} (bound)",
201+
source,
202+
target);
203+
204+
let span = tcx.map.span(impl_node_id);
205+
let param_env = ParameterEnvironment::for_item(tcx, impl_node_id);
206+
let source = source.subst(tcx, &param_env.free_substs);
207+
let target = target.subst(tcx, &param_env.free_substs);
208+
assert!(!source.has_escaping_regions());
209+
210+
debug!("check_implementations_of_coerce_unsized: {:?} -> {:?} (free)",
211+
source,
212+
target);
213+
214+
tcx.infer_ctxt(None, Some(param_env), Reveal::ExactMatch).enter(|infcx| {
215+
let cause = ObligationCause::misc(span, impl_node_id);
216+
let check_mutbl = |mt_a: ty::TypeAndMut<'tcx>,
217+
mt_b: ty::TypeAndMut<'tcx>,
218+
mk_ptr: &Fn(Ty<'tcx>) -> Ty<'tcx>| {
219+
if (mt_a.mutbl, mt_b.mutbl) == (hir::MutImmutable, hir::MutMutable) {
220+
infcx.report_mismatched_types(&cause,
221+
mk_ptr(mt_b.ty),
222+
target,
223+
ty::error::TypeError::Mutability)
224+
.emit();
225+
}
226+
(mt_a.ty, mt_b.ty, unsize_trait, None)
227+
};
228+
let (source, target, trait_def_id, kind) = match (&source.sty, &target.sty) {
229+
(&ty::TyBox(a), &ty::TyBox(b)) => (a, b, unsize_trait, None),
230+
231+
(&ty::TyRef(r_a, mt_a), &ty::TyRef(r_b, mt_b)) => {
232+
infcx.sub_regions(infer::RelateObjectBound(span), r_b, r_a);
233+
check_mutbl(mt_a, mt_b, &|ty| tcx.mk_imm_ref(r_b, ty))
234+
}
235+
236+
(&ty::TyRef(_, mt_a), &ty::TyRawPtr(mt_b)) |
237+
(&ty::TyRawPtr(mt_a), &ty::TyRawPtr(mt_b)) => {
238+
check_mutbl(mt_a, mt_b, &|ty| tcx.mk_imm_ptr(ty))
239+
}
240+
241+
(&ty::TyAdt(def_a, substs_a), &ty::TyAdt(def_b, substs_b))
242+
if def_a.is_struct() && def_b.is_struct() => {
243+
if def_a != def_b {
244+
let source_path = tcx.item_path_str(def_a.did);
245+
let target_path = tcx.item_path_str(def_b.did);
246+
span_err!(tcx.sess,
247+
span,
248+
E0377,
249+
"the trait `CoerceUnsized` may only be implemented \
250+
for a coercion between structures with the same \
251+
definition; expected {}, found {}",
252+
source_path,
253+
target_path);
254+
return;
255+
}
256+
257+
let fields = &def_a.struct_variant().fields;
258+
let diff_fields = fields.iter()
259+
.enumerate()
260+
.filter_map(|(i, f)| {
261+
let (a, b) = (f.ty(tcx, substs_a), f.ty(tcx, substs_b));
262+
263+
if tcx.item_type(f.did).is_phantom_data() {
264+
// Ignore PhantomData fields
265+
return None;
266+
}
267+
268+
// Ignore fields that aren't significantly changed
269+
if let Ok(ok) = infcx.sub_types(false, &cause, b, a) {
270+
if ok.obligations.is_empty() {
271+
return None;
272+
}
273+
}
274+
275+
// Collect up all fields that were significantly changed
276+
// i.e. those that contain T in coerce_unsized T -> U
277+
Some((i, a, b))
278+
})
279+
.collect::<Vec<_>>();
280+
281+
if diff_fields.is_empty() {
282+
span_err!(tcx.sess,
283+
span,
284+
E0374,
285+
"the trait `CoerceUnsized` may only be implemented \
286+
for a coercion between structures with one field \
287+
being coerced, none found");
288+
return;
289+
} else if diff_fields.len() > 1 {
290+
let item = tcx.map.expect_item(impl_node_id);
291+
let span = if let ItemImpl(.., Some(ref t), _, _) = item.node {
292+
t.path.span
293+
} else {
294+
tcx.map.span(impl_node_id)
295+
};
296+
297+
let mut err = struct_span_err!(tcx.sess,
298+
span,
299+
E0375,
300+
"implementing the trait \
301+
`CoerceUnsized` requires multiple \
302+
coercions");
303+
err.note("`CoerceUnsized` may only be implemented for \
304+
a coercion between structures with one field being coerced");
305+
err.note(&format!("currently, {} fields need coercions: {}",
306+
diff_fields.len(),
307+
diff_fields.iter()
308+
.map(|&(i, a, b)| {
309+
format!("{} ({} to {})", fields[i].name, a, b)
310+
})
311+
.collect::<Vec<_>>()
312+
.join(", ")));
313+
err.span_label(span, &format!("requires multiple coercions"));
314+
err.emit();
315+
return;
316+
}
317+
318+
let (i, a, b) = diff_fields[0];
319+
let kind = ty::adjustment::CustomCoerceUnsized::Struct(i);
320+
(a, b, coerce_unsized_trait, Some(kind))
321+
}
322+
323+
_ => {
324+
span_err!(tcx.sess,
325+
span,
326+
E0376,
327+
"the trait `CoerceUnsized` may only be implemented \
328+
for a coercion between structures");
329+
return;
330+
}
331+
};
332+
333+
let mut fulfill_cx = traits::FulfillmentContext::new();
334+
335+
// Register an obligation for `A: Trait<B>`.
336+
let cause = traits::ObligationCause::misc(span, impl_node_id);
337+
let predicate =
338+
tcx.predicate_for_trait_def(cause, trait_def_id, 0, source, &[target]);
339+
fulfill_cx.register_predicate_obligation(&infcx, predicate);
340+
341+
// Check that all transitive obligations are satisfied.
342+
if let Err(errors) = fulfill_cx.select_all_or_error(&infcx) {
343+
infcx.report_fulfillment_errors(&errors);
344+
}
345+
346+
// Finally, resolve all regions.
347+
let mut free_regions = FreeRegionMap::new();
348+
free_regions.relate_free_regions_from_predicates(&infcx.parameter_environment
349+
.caller_bounds);
350+
infcx.resolve_regions_and_report_errors(&free_regions, impl_node_id);
351+
352+
if let Some(kind) = kind {
353+
tcx.custom_coerce_unsized_kinds.borrow_mut().insert(impl_did, kind);
354+
}
355+
});
356+
});
357+
}

0 commit comments

Comments
 (0)