coherence: move the builtin trait checks to their own module
no functional changes
This commit is contained in:
parent
8f62c29200
commit
f8a2f9838d
357
src/librustc_typeck/coherence/builtin.rs
Normal file
357
src/librustc_typeck/coherence/builtin.rs
Normal file
@ -0,0 +1,357 @@
|
|||||||
|
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
|
||||||
|
// file at the top-level directory of this distribution and at
|
||||||
|
// http://rust-lang.org/COPYRIGHT.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
//! Check properties that are required by built-in traits and set
|
||||||
|
//! up data structures required by type-checking/translation.
|
||||||
|
|
||||||
|
use rustc::middle::free_region::FreeRegionMap;
|
||||||
|
use rustc::middle::lang_items::UnsizeTraitLangItem;
|
||||||
|
|
||||||
|
use rustc::traits::{self, ObligationCause, Reveal};
|
||||||
|
use rustc::ty::{self, Ty, TyCtxt};
|
||||||
|
use rustc::ty::ParameterEnvironment;
|
||||||
|
use rustc::ty::TypeFoldable;
|
||||||
|
use rustc::ty::subst::Subst;
|
||||||
|
use rustc::ty::util::CopyImplementationError;
|
||||||
|
use rustc::infer;
|
||||||
|
|
||||||
|
use rustc::hir::map as hir_map;
|
||||||
|
use rustc::hir::{self, ItemImpl};
|
||||||
|
|
||||||
|
pub fn check<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
|
||||||
|
populate_destructors(tcx);
|
||||||
|
check_implementations_of_copy(tcx);
|
||||||
|
check_implementations_of_coerce_unsized(tcx);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn populate_destructors<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
|
||||||
|
let drop_trait = match tcx.lang_items.drop_trait() {
|
||||||
|
Some(id) => id,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
tcx.populate_implementations_for_trait_if_necessary(drop_trait);
|
||||||
|
let drop_trait = tcx.lookup_trait_def(drop_trait);
|
||||||
|
|
||||||
|
drop_trait.for_each_impl(tcx, |impl_did| {
|
||||||
|
let items = tcx.associated_item_def_ids(impl_did);
|
||||||
|
if items.is_empty() {
|
||||||
|
// We'll error out later. For now, just don't ICE.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let method_def_id = items[0];
|
||||||
|
|
||||||
|
let self_type = tcx.item_type(impl_did);
|
||||||
|
match self_type.sty {
|
||||||
|
ty::TyAdt(type_def, _) => {
|
||||||
|
type_def.set_destructor(method_def_id);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// Destructors only work on nominal types.
|
||||||
|
if let Some(impl_node_id) = tcx.map.as_local_node_id(impl_did) {
|
||||||
|
match tcx.map.find(impl_node_id) {
|
||||||
|
Some(hir_map::NodeItem(item)) => {
|
||||||
|
let span = match item.node {
|
||||||
|
ItemImpl(.., ref ty, _) => ty.span,
|
||||||
|
_ => item.span,
|
||||||
|
};
|
||||||
|
struct_span_err!(tcx.sess,
|
||||||
|
span,
|
||||||
|
E0120,
|
||||||
|
"the Drop trait may only be implemented on \
|
||||||
|
structures")
|
||||||
|
.span_label(span,
|
||||||
|
&format!("implementing Drop requires a struct"))
|
||||||
|
.emit();
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
bug!("didn't find impl in ast map");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
bug!("found external impl of Drop trait on \
|
||||||
|
something other than a struct");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_implementations_of_copy<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
|
||||||
|
let copy_trait = match tcx.lang_items.copy_trait() {
|
||||||
|
Some(id) => id,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
tcx.populate_implementations_for_trait_if_necessary(copy_trait);
|
||||||
|
let copy_trait = tcx.lookup_trait_def(copy_trait);
|
||||||
|
|
||||||
|
copy_trait.for_each_impl(tcx, |impl_did| {
|
||||||
|
debug!("check_implementations_of_copy: impl_did={:?}", impl_did);
|
||||||
|
|
||||||
|
let impl_node_id = if let Some(n) = tcx.map.as_local_node_id(impl_did) {
|
||||||
|
n
|
||||||
|
} else {
|
||||||
|
debug!("check_implementations_of_copy(): impl not in this \
|
||||||
|
crate");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let self_type = tcx.item_type(impl_did);
|
||||||
|
debug!("check_implementations_of_copy: self_type={:?} (bound)",
|
||||||
|
self_type);
|
||||||
|
|
||||||
|
let span = tcx.map.span(impl_node_id);
|
||||||
|
let param_env = ParameterEnvironment::for_item(tcx, impl_node_id);
|
||||||
|
let self_type = self_type.subst(tcx, ¶m_env.free_substs);
|
||||||
|
assert!(!self_type.has_escaping_regions());
|
||||||
|
|
||||||
|
debug!("check_implementations_of_copy: self_type={:?} (free)",
|
||||||
|
self_type);
|
||||||
|
|
||||||
|
match param_env.can_type_implement_copy(tcx, self_type, span) {
|
||||||
|
Ok(()) => {}
|
||||||
|
Err(CopyImplementationError::InfrigingField(name)) => {
|
||||||
|
struct_span_err!(tcx.sess,
|
||||||
|
span,
|
||||||
|
E0204,
|
||||||
|
"the trait `Copy` may not be implemented for this type")
|
||||||
|
.span_label(span, &format!("field `{}` does not implement `Copy`", name))
|
||||||
|
.emit()
|
||||||
|
}
|
||||||
|
Err(CopyImplementationError::InfrigingVariant(name)) => {
|
||||||
|
let item = tcx.map.expect_item(impl_node_id);
|
||||||
|
let span = if let ItemImpl(.., Some(ref tr), _, _) = item.node {
|
||||||
|
tr.path.span
|
||||||
|
} else {
|
||||||
|
span
|
||||||
|
};
|
||||||
|
|
||||||
|
struct_span_err!(tcx.sess,
|
||||||
|
span,
|
||||||
|
E0205,
|
||||||
|
"the trait `Copy` may not be implemented for this type")
|
||||||
|
.span_label(span,
|
||||||
|
&format!("variant `{}` does not implement `Copy`", name))
|
||||||
|
.emit()
|
||||||
|
}
|
||||||
|
Err(CopyImplementationError::NotAnAdt) => {
|
||||||
|
let item = tcx.map.expect_item(impl_node_id);
|
||||||
|
let span = if let ItemImpl(.., ref ty, _) = item.node {
|
||||||
|
ty.span
|
||||||
|
} else {
|
||||||
|
span
|
||||||
|
};
|
||||||
|
|
||||||
|
struct_span_err!(tcx.sess,
|
||||||
|
span,
|
||||||
|
E0206,
|
||||||
|
"the trait `Copy` may not be implemented for this type")
|
||||||
|
.span_label(span, &format!("type is not a structure or enumeration"))
|
||||||
|
.emit();
|
||||||
|
}
|
||||||
|
Err(CopyImplementationError::HasDestructor) => {
|
||||||
|
struct_span_err!(tcx.sess,
|
||||||
|
span,
|
||||||
|
E0184,
|
||||||
|
"the trait `Copy` may not be implemented for this type; the \
|
||||||
|
type has a destructor")
|
||||||
|
.span_label(span, &format!("Copy not allowed on types with destructors"))
|
||||||
|
.emit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_implementations_of_coerce_unsized<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
|
||||||
|
let coerce_unsized_trait = match tcx.lang_items.coerce_unsized_trait() {
|
||||||
|
Some(id) => id,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
let unsize_trait = match tcx.lang_items.require(UnsizeTraitLangItem) {
|
||||||
|
Ok(id) => id,
|
||||||
|
Err(err) => {
|
||||||
|
tcx.sess.fatal(&format!("`CoerceUnsized` implementation {}", err));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let trait_def = tcx.lookup_trait_def(coerce_unsized_trait);
|
||||||
|
|
||||||
|
trait_def.for_each_impl(tcx, |impl_did| {
|
||||||
|
debug!("check_implementations_of_coerce_unsized: impl_did={:?}",
|
||||||
|
impl_did);
|
||||||
|
|
||||||
|
let impl_node_id = if let Some(n) = tcx.map.as_local_node_id(impl_did) {
|
||||||
|
n
|
||||||
|
} else {
|
||||||
|
debug!("check_implementations_of_coerce_unsized(): impl not \
|
||||||
|
in this crate");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let source = tcx.item_type(impl_did);
|
||||||
|
let trait_ref = tcx.impl_trait_ref(impl_did).unwrap();
|
||||||
|
let target = trait_ref.substs.type_at(1);
|
||||||
|
debug!("check_implementations_of_coerce_unsized: {:?} -> {:?} (bound)",
|
||||||
|
source,
|
||||||
|
target);
|
||||||
|
|
||||||
|
let span = tcx.map.span(impl_node_id);
|
||||||
|
let param_env = ParameterEnvironment::for_item(tcx, impl_node_id);
|
||||||
|
let source = source.subst(tcx, ¶m_env.free_substs);
|
||||||
|
let target = target.subst(tcx, ¶m_env.free_substs);
|
||||||
|
assert!(!source.has_escaping_regions());
|
||||||
|
|
||||||
|
debug!("check_implementations_of_coerce_unsized: {:?} -> {:?} (free)",
|
||||||
|
source,
|
||||||
|
target);
|
||||||
|
|
||||||
|
tcx.infer_ctxt(None, Some(param_env), Reveal::ExactMatch).enter(|infcx| {
|
||||||
|
let cause = ObligationCause::misc(span, impl_node_id);
|
||||||
|
let check_mutbl = |mt_a: ty::TypeAndMut<'tcx>,
|
||||||
|
mt_b: ty::TypeAndMut<'tcx>,
|
||||||
|
mk_ptr: &Fn(Ty<'tcx>) -> Ty<'tcx>| {
|
||||||
|
if (mt_a.mutbl, mt_b.mutbl) == (hir::MutImmutable, hir::MutMutable) {
|
||||||
|
infcx.report_mismatched_types(&cause,
|
||||||
|
mk_ptr(mt_b.ty),
|
||||||
|
target,
|
||||||
|
ty::error::TypeError::Mutability)
|
||||||
|
.emit();
|
||||||
|
}
|
||||||
|
(mt_a.ty, mt_b.ty, unsize_trait, None)
|
||||||
|
};
|
||||||
|
let (source, target, trait_def_id, kind) = match (&source.sty, &target.sty) {
|
||||||
|
(&ty::TyBox(a), &ty::TyBox(b)) => (a, b, unsize_trait, None),
|
||||||
|
|
||||||
|
(&ty::TyRef(r_a, mt_a), &ty::TyRef(r_b, mt_b)) => {
|
||||||
|
infcx.sub_regions(infer::RelateObjectBound(span), r_b, r_a);
|
||||||
|
check_mutbl(mt_a, mt_b, &|ty| tcx.mk_imm_ref(r_b, ty))
|
||||||
|
}
|
||||||
|
|
||||||
|
(&ty::TyRef(_, mt_a), &ty::TyRawPtr(mt_b)) |
|
||||||
|
(&ty::TyRawPtr(mt_a), &ty::TyRawPtr(mt_b)) => {
|
||||||
|
check_mutbl(mt_a, mt_b, &|ty| tcx.mk_imm_ptr(ty))
|
||||||
|
}
|
||||||
|
|
||||||
|
(&ty::TyAdt(def_a, substs_a), &ty::TyAdt(def_b, substs_b))
|
||||||
|
if def_a.is_struct() && def_b.is_struct() => {
|
||||||
|
if def_a != def_b {
|
||||||
|
let source_path = tcx.item_path_str(def_a.did);
|
||||||
|
let target_path = tcx.item_path_str(def_b.did);
|
||||||
|
span_err!(tcx.sess,
|
||||||
|
span,
|
||||||
|
E0377,
|
||||||
|
"the trait `CoerceUnsized` may only be implemented \
|
||||||
|
for a coercion between structures with the same \
|
||||||
|
definition; expected {}, found {}",
|
||||||
|
source_path,
|
||||||
|
target_path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let fields = &def_a.struct_variant().fields;
|
||||||
|
let diff_fields = fields.iter()
|
||||||
|
.enumerate()
|
||||||
|
.filter_map(|(i, f)| {
|
||||||
|
let (a, b) = (f.ty(tcx, substs_a), f.ty(tcx, substs_b));
|
||||||
|
|
||||||
|
if tcx.item_type(f.did).is_phantom_data() {
|
||||||
|
// Ignore PhantomData fields
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore fields that aren't significantly changed
|
||||||
|
if let Ok(ok) = infcx.sub_types(false, &cause, b, a) {
|
||||||
|
if ok.obligations.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect up all fields that were significantly changed
|
||||||
|
// i.e. those that contain T in coerce_unsized T -> U
|
||||||
|
Some((i, a, b))
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
if diff_fields.is_empty() {
|
||||||
|
span_err!(tcx.sess,
|
||||||
|
span,
|
||||||
|
E0374,
|
||||||
|
"the trait `CoerceUnsized` may only be implemented \
|
||||||
|
for a coercion between structures with one field \
|
||||||
|
being coerced, none found");
|
||||||
|
return;
|
||||||
|
} else if diff_fields.len() > 1 {
|
||||||
|
let item = tcx.map.expect_item(impl_node_id);
|
||||||
|
let span = if let ItemImpl(.., Some(ref t), _, _) = item.node {
|
||||||
|
t.path.span
|
||||||
|
} else {
|
||||||
|
tcx.map.span(impl_node_id)
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut err = struct_span_err!(tcx.sess,
|
||||||
|
span,
|
||||||
|
E0375,
|
||||||
|
"implementing the trait \
|
||||||
|
`CoerceUnsized` requires multiple \
|
||||||
|
coercions");
|
||||||
|
err.note("`CoerceUnsized` may only be implemented for \
|
||||||
|
a coercion between structures with one field being coerced");
|
||||||
|
err.note(&format!("currently, {} fields need coercions: {}",
|
||||||
|
diff_fields.len(),
|
||||||
|
diff_fields.iter()
|
||||||
|
.map(|&(i, a, b)| {
|
||||||
|
format!("{} ({} to {})", fields[i].name, a, b)
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(", ")));
|
||||||
|
err.span_label(span, &format!("requires multiple coercions"));
|
||||||
|
err.emit();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let (i, a, b) = diff_fields[0];
|
||||||
|
let kind = ty::adjustment::CustomCoerceUnsized::Struct(i);
|
||||||
|
(a, b, coerce_unsized_trait, Some(kind))
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => {
|
||||||
|
span_err!(tcx.sess,
|
||||||
|
span,
|
||||||
|
E0376,
|
||||||
|
"the trait `CoerceUnsized` may only be implemented \
|
||||||
|
for a coercion between structures");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut fulfill_cx = traits::FulfillmentContext::new();
|
||||||
|
|
||||||
|
// Register an obligation for `A: Trait<B>`.
|
||||||
|
let cause = traits::ObligationCause::misc(span, impl_node_id);
|
||||||
|
let predicate =
|
||||||
|
tcx.predicate_for_trait_def(cause, trait_def_id, 0, source, &[target]);
|
||||||
|
fulfill_cx.register_predicate_obligation(&infcx, predicate);
|
||||||
|
|
||||||
|
// Check that all transitive obligations are satisfied.
|
||||||
|
if let Err(errors) = fulfill_cx.select_all_or_error(&infcx) {
|
||||||
|
infcx.report_fulfillment_errors(&errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, resolve all regions.
|
||||||
|
let mut free_regions = FreeRegionMap::new();
|
||||||
|
free_regions.relate_free_regions_from_predicates(&infcx.parameter_environment
|
||||||
|
.caller_bounds);
|
||||||
|
infcx.resolve_regions_and_report_errors(&free_regions, impl_node_id);
|
||||||
|
|
||||||
|
if let Some(kind) = kind {
|
||||||
|
tcx.custom_coerce_unsized_kinds.borrow_mut().insert(impl_did, kind);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
@ -16,28 +16,23 @@
|
|||||||
// mappings. That mapping code resides here.
|
// mappings. That mapping code resides here.
|
||||||
|
|
||||||
use hir::def_id::DefId;
|
use hir::def_id::DefId;
|
||||||
use middle::lang_items::UnsizeTraitLangItem;
|
use rustc::traits::Reveal;
|
||||||
use rustc::ty::subst::Subst;
|
|
||||||
use rustc::ty::{self, TyCtxt, TypeFoldable};
|
use rustc::ty::{self, TyCtxt, TypeFoldable};
|
||||||
use rustc::traits::{self, ObligationCause, Reveal};
|
|
||||||
use rustc::ty::ParameterEnvironment;
|
|
||||||
use rustc::ty::{Ty, TyBool, TyChar, TyError};
|
use rustc::ty::{Ty, TyBool, TyChar, TyError};
|
||||||
use rustc::ty::{TyParam, TyRawPtr};
|
use rustc::ty::{TyParam, TyRawPtr};
|
||||||
use rustc::ty::{TyRef, TyAdt, TyDynamic, TyNever, TyTuple};
|
use rustc::ty::{TyRef, TyAdt, TyDynamic, TyNever, TyTuple};
|
||||||
use rustc::ty::{TyStr, TyArray, TySlice, TyFloat, TyInfer, TyInt};
|
use rustc::ty::{TyStr, TyArray, TySlice, TyFloat, TyInfer, TyInt};
|
||||||
use rustc::ty::{TyUint, TyClosure, TyBox, TyFnDef, TyFnPtr};
|
use rustc::ty::{TyUint, TyClosure, TyBox, TyFnDef, TyFnPtr};
|
||||||
use rustc::ty::{TyProjection, TyAnon};
|
use rustc::ty::{TyProjection, TyAnon};
|
||||||
use rustc::ty::util::CopyImplementationError;
|
|
||||||
use middle::free_region::FreeRegionMap;
|
|
||||||
use CrateCtxt;
|
use CrateCtxt;
|
||||||
use rustc::infer::{self, InferCtxt};
|
use rustc::infer::{InferCtxt};
|
||||||
use syntax_pos::Span;
|
use syntax_pos::Span;
|
||||||
use rustc::dep_graph::DepNode;
|
use rustc::dep_graph::DepNode;
|
||||||
use rustc::hir::map as hir_map;
|
|
||||||
use rustc::hir::itemlikevisit::ItemLikeVisitor;
|
use rustc::hir::itemlikevisit::ItemLikeVisitor;
|
||||||
use rustc::hir::{Item, ItemImpl};
|
use rustc::hir::{Item, ItemImpl};
|
||||||
use rustc::hir;
|
use rustc::hir;
|
||||||
|
|
||||||
|
mod builtin;
|
||||||
mod orphan;
|
mod orphan;
|
||||||
mod overlap;
|
mod overlap;
|
||||||
mod unsafety;
|
mod unsafety;
|
||||||
@ -96,18 +91,7 @@ fn check(&self) {
|
|||||||
self.crate_context.tcx.visit_all_item_likes_in_krate(
|
self.crate_context.tcx.visit_all_item_likes_in_krate(
|
||||||
DepNode::CoherenceCheckImpl,
|
DepNode::CoherenceCheckImpl,
|
||||||
&mut CoherenceCheckVisitor { cc: self });
|
&mut CoherenceCheckVisitor { cc: self });
|
||||||
|
builtin::check(self.crate_context.tcx);
|
||||||
// Populate the table of destructors. It might seem a bit strange to
|
|
||||||
// do this here, but it's actually the most convenient place, since
|
|
||||||
// the coherence tables contain the trait -> type mappings.
|
|
||||||
self.populate_destructors();
|
|
||||||
|
|
||||||
// Check to make sure implementations of `Copy` are legal.
|
|
||||||
self.check_implementations_of_copy();
|
|
||||||
|
|
||||||
// Check to make sure implementations of `CoerceUnsized` are legal
|
|
||||||
// and collect the necessary information from them.
|
|
||||||
self.check_implementations_of_coerce_unsized();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_implementation(&self, item: &Item) {
|
fn check_implementation(&self, item: &Item) {
|
||||||
@ -161,338 +145,6 @@ fn add_trait_impl(&self, impl_trait_ref: ty::TraitRef<'gcx>, impl_def_id: DefId)
|
|||||||
let trait_def = self.crate_context.tcx.lookup_trait_def(impl_trait_ref.def_id);
|
let trait_def = self.crate_context.tcx.lookup_trait_def(impl_trait_ref.def_id);
|
||||||
trait_def.record_local_impl(self.crate_context.tcx, impl_def_id, impl_trait_ref);
|
trait_def.record_local_impl(self.crate_context.tcx, impl_def_id, impl_trait_ref);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Destructors
|
|
||||||
//
|
|
||||||
|
|
||||||
fn populate_destructors(&self) {
|
|
||||||
let tcx = self.crate_context.tcx;
|
|
||||||
let drop_trait = match tcx.lang_items.drop_trait() {
|
|
||||||
Some(id) => id,
|
|
||||||
None => return,
|
|
||||||
};
|
|
||||||
tcx.populate_implementations_for_trait_if_necessary(drop_trait);
|
|
||||||
let drop_trait = tcx.lookup_trait_def(drop_trait);
|
|
||||||
|
|
||||||
drop_trait.for_each_impl(tcx, |impl_did| {
|
|
||||||
let items = tcx.associated_item_def_ids(impl_did);
|
|
||||||
if items.is_empty() {
|
|
||||||
// We'll error out later. For now, just don't ICE.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let method_def_id = items[0];
|
|
||||||
|
|
||||||
let self_type = tcx.item_type(impl_did);
|
|
||||||
match self_type.sty {
|
|
||||||
ty::TyAdt(type_def, _) => {
|
|
||||||
type_def.set_destructor(method_def_id);
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
// Destructors only work on nominal types.
|
|
||||||
if let Some(impl_node_id) = tcx.map.as_local_node_id(impl_did) {
|
|
||||||
match tcx.map.find(impl_node_id) {
|
|
||||||
Some(hir_map::NodeItem(item)) => {
|
|
||||||
let span = match item.node {
|
|
||||||
ItemImpl(.., ref ty, _) => ty.span,
|
|
||||||
_ => item.span,
|
|
||||||
};
|
|
||||||
struct_span_err!(tcx.sess,
|
|
||||||
span,
|
|
||||||
E0120,
|
|
||||||
"the Drop trait may only be implemented on \
|
|
||||||
structures")
|
|
||||||
.span_label(span,
|
|
||||||
&format!("implementing Drop requires a struct"))
|
|
||||||
.emit();
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
bug!("didn't find impl in ast map");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
bug!("found external impl of Drop trait on \
|
|
||||||
something other than a struct");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Ensures that implementations of the built-in trait `Copy` are legal.
|
|
||||||
fn check_implementations_of_copy(&self) {
|
|
||||||
let tcx = self.crate_context.tcx;
|
|
||||||
let copy_trait = match tcx.lang_items.copy_trait() {
|
|
||||||
Some(id) => id,
|
|
||||||
None => return,
|
|
||||||
};
|
|
||||||
tcx.populate_implementations_for_trait_if_necessary(copy_trait);
|
|
||||||
let copy_trait = tcx.lookup_trait_def(copy_trait);
|
|
||||||
|
|
||||||
copy_trait.for_each_impl(tcx, |impl_did| {
|
|
||||||
debug!("check_implementations_of_copy: impl_did={:?}", impl_did);
|
|
||||||
|
|
||||||
let impl_node_id = if let Some(n) = tcx.map.as_local_node_id(impl_did) {
|
|
||||||
n
|
|
||||||
} else {
|
|
||||||
debug!("check_implementations_of_copy(): impl not in this \
|
|
||||||
crate");
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
let self_type = tcx.item_type(impl_did);
|
|
||||||
debug!("check_implementations_of_copy: self_type={:?} (bound)",
|
|
||||||
self_type);
|
|
||||||
|
|
||||||
let span = tcx.map.span(impl_node_id);
|
|
||||||
let param_env = ParameterEnvironment::for_item(tcx, impl_node_id);
|
|
||||||
let self_type = self_type.subst(tcx, ¶m_env.free_substs);
|
|
||||||
assert!(!self_type.has_escaping_regions());
|
|
||||||
|
|
||||||
debug!("check_implementations_of_copy: self_type={:?} (free)",
|
|
||||||
self_type);
|
|
||||||
|
|
||||||
match param_env.can_type_implement_copy(tcx, self_type, span) {
|
|
||||||
Ok(()) => {}
|
|
||||||
Err(CopyImplementationError::InfrigingField(name)) => {
|
|
||||||
struct_span_err!(tcx.sess,
|
|
||||||
span,
|
|
||||||
E0204,
|
|
||||||
"the trait `Copy` may not be implemented for this type")
|
|
||||||
.span_label(span, &format!("field `{}` does not implement `Copy`", name))
|
|
||||||
.emit()
|
|
||||||
}
|
|
||||||
Err(CopyImplementationError::InfrigingVariant(name)) => {
|
|
||||||
let item = tcx.map.expect_item(impl_node_id);
|
|
||||||
let span = if let ItemImpl(.., Some(ref tr), _, _) = item.node {
|
|
||||||
tr.path.span
|
|
||||||
} else {
|
|
||||||
span
|
|
||||||
};
|
|
||||||
|
|
||||||
struct_span_err!(tcx.sess,
|
|
||||||
span,
|
|
||||||
E0205,
|
|
||||||
"the trait `Copy` may not be implemented for this type")
|
|
||||||
.span_label(span,
|
|
||||||
&format!("variant `{}` does not implement `Copy`", name))
|
|
||||||
.emit()
|
|
||||||
}
|
|
||||||
Err(CopyImplementationError::NotAnAdt) => {
|
|
||||||
let item = tcx.map.expect_item(impl_node_id);
|
|
||||||
let span = if let ItemImpl(.., ref ty, _) = item.node {
|
|
||||||
ty.span
|
|
||||||
} else {
|
|
||||||
span
|
|
||||||
};
|
|
||||||
|
|
||||||
struct_span_err!(tcx.sess,
|
|
||||||
span,
|
|
||||||
E0206,
|
|
||||||
"the trait `Copy` may not be implemented for this type")
|
|
||||||
.span_label(span, &format!("type is not a structure or enumeration"))
|
|
||||||
.emit();
|
|
||||||
}
|
|
||||||
Err(CopyImplementationError::HasDestructor) => {
|
|
||||||
struct_span_err!(tcx.sess,
|
|
||||||
span,
|
|
||||||
E0184,
|
|
||||||
"the trait `Copy` may not be implemented for this type; the \
|
|
||||||
type has a destructor")
|
|
||||||
.span_label(span, &format!("Copy not allowed on types with destructors"))
|
|
||||||
.emit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Process implementations of the built-in trait `CoerceUnsized`.
|
|
||||||
fn check_implementations_of_coerce_unsized(&self) {
|
|
||||||
let tcx = self.crate_context.tcx;
|
|
||||||
let coerce_unsized_trait = match tcx.lang_items.coerce_unsized_trait() {
|
|
||||||
Some(id) => id,
|
|
||||||
None => return,
|
|
||||||
};
|
|
||||||
let unsize_trait = match tcx.lang_items.require(UnsizeTraitLangItem) {
|
|
||||||
Ok(id) => id,
|
|
||||||
Err(err) => {
|
|
||||||
tcx.sess.fatal(&format!("`CoerceUnsized` implementation {}", err));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let trait_def = tcx.lookup_trait_def(coerce_unsized_trait);
|
|
||||||
|
|
||||||
trait_def.for_each_impl(tcx, |impl_did| {
|
|
||||||
debug!("check_implementations_of_coerce_unsized: impl_did={:?}",
|
|
||||||
impl_did);
|
|
||||||
|
|
||||||
let impl_node_id = if let Some(n) = tcx.map.as_local_node_id(impl_did) {
|
|
||||||
n
|
|
||||||
} else {
|
|
||||||
debug!("check_implementations_of_coerce_unsized(): impl not \
|
|
||||||
in this crate");
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
let source = tcx.item_type(impl_did);
|
|
||||||
let trait_ref = self.crate_context.tcx.impl_trait_ref(impl_did).unwrap();
|
|
||||||
let target = trait_ref.substs.type_at(1);
|
|
||||||
debug!("check_implementations_of_coerce_unsized: {:?} -> {:?} (bound)",
|
|
||||||
source,
|
|
||||||
target);
|
|
||||||
|
|
||||||
let span = tcx.map.span(impl_node_id);
|
|
||||||
let param_env = ParameterEnvironment::for_item(tcx, impl_node_id);
|
|
||||||
let source = source.subst(tcx, ¶m_env.free_substs);
|
|
||||||
let target = target.subst(tcx, ¶m_env.free_substs);
|
|
||||||
assert!(!source.has_escaping_regions());
|
|
||||||
|
|
||||||
debug!("check_implementations_of_coerce_unsized: {:?} -> {:?} (free)",
|
|
||||||
source,
|
|
||||||
target);
|
|
||||||
|
|
||||||
tcx.infer_ctxt(None, Some(param_env), Reveal::ExactMatch).enter(|infcx| {
|
|
||||||
let cause = ObligationCause::misc(span, impl_node_id);
|
|
||||||
let check_mutbl = |mt_a: ty::TypeAndMut<'gcx>,
|
|
||||||
mt_b: ty::TypeAndMut<'gcx>,
|
|
||||||
mk_ptr: &Fn(Ty<'gcx>) -> Ty<'gcx>| {
|
|
||||||
if (mt_a.mutbl, mt_b.mutbl) == (hir::MutImmutable, hir::MutMutable) {
|
|
||||||
infcx.report_mismatched_types(&cause,
|
|
||||||
mk_ptr(mt_b.ty),
|
|
||||||
target,
|
|
||||||
ty::error::TypeError::Mutability).emit();
|
|
||||||
}
|
|
||||||
(mt_a.ty, mt_b.ty, unsize_trait, None)
|
|
||||||
};
|
|
||||||
let (source, target, trait_def_id, kind) = match (&source.sty, &target.sty) {
|
|
||||||
(&ty::TyBox(a), &ty::TyBox(b)) => (a, b, unsize_trait, None),
|
|
||||||
|
|
||||||
(&ty::TyRef(r_a, mt_a), &ty::TyRef(r_b, mt_b)) => {
|
|
||||||
infcx.sub_regions(infer::RelateObjectBound(span), r_b, r_a);
|
|
||||||
check_mutbl(mt_a, mt_b, &|ty| tcx.mk_imm_ref(r_b, ty))
|
|
||||||
}
|
|
||||||
|
|
||||||
(&ty::TyRef(_, mt_a), &ty::TyRawPtr(mt_b)) |
|
|
||||||
(&ty::TyRawPtr(mt_a), &ty::TyRawPtr(mt_b)) => {
|
|
||||||
check_mutbl(mt_a, mt_b, &|ty| tcx.mk_imm_ptr(ty))
|
|
||||||
}
|
|
||||||
|
|
||||||
(&ty::TyAdt(def_a, substs_a), &ty::TyAdt(def_b, substs_b))
|
|
||||||
if def_a.is_struct() && def_b.is_struct() => {
|
|
||||||
if def_a != def_b {
|
|
||||||
let source_path = tcx.item_path_str(def_a.did);
|
|
||||||
let target_path = tcx.item_path_str(def_b.did);
|
|
||||||
span_err!(tcx.sess,
|
|
||||||
span,
|
|
||||||
E0377,
|
|
||||||
"the trait `CoerceUnsized` may only be implemented \
|
|
||||||
for a coercion between structures with the same \
|
|
||||||
definition; expected {}, found {}",
|
|
||||||
source_path,
|
|
||||||
target_path);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let fields = &def_a.struct_variant().fields;
|
|
||||||
let diff_fields = fields.iter()
|
|
||||||
.enumerate()
|
|
||||||
.filter_map(|(i, f)| {
|
|
||||||
let (a, b) = (f.ty(tcx, substs_a), f.ty(tcx, substs_b));
|
|
||||||
|
|
||||||
if tcx.item_type(f.did).is_phantom_data() {
|
|
||||||
// Ignore PhantomData fields
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ignore fields that aren't significantly changed
|
|
||||||
if let Ok(ok) = infcx.sub_types(false, &cause, b, a) {
|
|
||||||
if ok.obligations.is_empty() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Collect up all fields that were significantly changed
|
|
||||||
// i.e. those that contain T in coerce_unsized T -> U
|
|
||||||
Some((i, a, b))
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
if diff_fields.is_empty() {
|
|
||||||
span_err!(tcx.sess,
|
|
||||||
span,
|
|
||||||
E0374,
|
|
||||||
"the trait `CoerceUnsized` may only be implemented \
|
|
||||||
for a coercion between structures with one field \
|
|
||||||
being coerced, none found");
|
|
||||||
return;
|
|
||||||
} else if diff_fields.len() > 1 {
|
|
||||||
let item = tcx.map.expect_item(impl_node_id);
|
|
||||||
let span = if let ItemImpl(.., Some(ref t), _, _) = item.node {
|
|
||||||
t.path.span
|
|
||||||
} else {
|
|
||||||
tcx.map.span(impl_node_id)
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut err = struct_span_err!(tcx.sess,
|
|
||||||
span,
|
|
||||||
E0375,
|
|
||||||
"implementing the trait \
|
|
||||||
`CoerceUnsized` requires multiple \
|
|
||||||
coercions");
|
|
||||||
err.note("`CoerceUnsized` may only be implemented for \
|
|
||||||
a coercion between structures with one field being coerced");
|
|
||||||
err.note(&format!("currently, {} fields need coercions: {}",
|
|
||||||
diff_fields.len(),
|
|
||||||
diff_fields.iter()
|
|
||||||
.map(|&(i, a, b)| {
|
|
||||||
format!("{} ({} to {})", fields[i].name, a, b)
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
.join(", ")));
|
|
||||||
err.span_label(span, &format!("requires multiple coercions"));
|
|
||||||
err.emit();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let (i, a, b) = diff_fields[0];
|
|
||||||
let kind = ty::adjustment::CustomCoerceUnsized::Struct(i);
|
|
||||||
(a, b, coerce_unsized_trait, Some(kind))
|
|
||||||
}
|
|
||||||
|
|
||||||
_ => {
|
|
||||||
span_err!(tcx.sess,
|
|
||||||
span,
|
|
||||||
E0376,
|
|
||||||
"the trait `CoerceUnsized` may only be implemented \
|
|
||||||
for a coercion between structures");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut fulfill_cx = traits::FulfillmentContext::new();
|
|
||||||
|
|
||||||
// Register an obligation for `A: Trait<B>`.
|
|
||||||
let cause = traits::ObligationCause::misc(span, impl_node_id);
|
|
||||||
let predicate =
|
|
||||||
tcx.predicate_for_trait_def(cause, trait_def_id, 0, source, &[target]);
|
|
||||||
fulfill_cx.register_predicate_obligation(&infcx, predicate);
|
|
||||||
|
|
||||||
// Check that all transitive obligations are satisfied.
|
|
||||||
if let Err(errors) = fulfill_cx.select_all_or_error(&infcx) {
|
|
||||||
infcx.report_fulfillment_errors(&errors);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finally, resolve all regions.
|
|
||||||
let mut free_regions = FreeRegionMap::new();
|
|
||||||
free_regions.relate_free_regions_from_predicates(&infcx.parameter_environment
|
|
||||||
.caller_bounds);
|
|
||||||
infcx.resolve_regions_and_report_errors(&free_regions, impl_node_id);
|
|
||||||
|
|
||||||
if let Some(kind) = kind {
|
|
||||||
tcx.custom_coerce_unsized_kinds.borrow_mut().insert(impl_did, kind);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn enforce_trait_manually_implementable(tcx: TyCtxt, sp: Span, trait_def_id: DefId) {
|
fn enforce_trait_manually_implementable(tcx: TyCtxt, sp: Span, trait_def_id: DefId) {
|
||||||
|
Loading…
Reference in New Issue
Block a user