|
| 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, ¶m_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, ¶m_env.free_substs); |
| 207 | + let target = target.subst(tcx, ¶m_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