]>
Commit | Line | Data |
---|---|---|
2b03887a FG |
1 | //! Check properties that are required by built-in traits and set |
2 | //! up data structures required by type-checking/codegen. | |
3 | ||
4 | use crate::errors::{CopyImplOnNonAdt, CopyImplOnTypeWithDtor, DropImplOnWrongItem}; | |
5 | use rustc_errors::{struct_span_err, MultiSpan}; | |
6 | use rustc_hir as hir; | |
7 | use rustc_hir::def_id::{DefId, LocalDefId}; | |
8 | use rustc_hir::lang_items::LangItem; | |
9 | use rustc_hir::ItemKind; | |
2b03887a FG |
10 | use rustc_infer::infer::outlives::env::OutlivesEnvironment; |
11 | use rustc_infer::infer::TyCtxtInferExt; | |
9c376795 | 12 | use rustc_infer::infer::{self, RegionResolutionError}; |
2b03887a | 13 | use rustc_middle::ty::adjustment::CoerceUnsizedInfo; |
9ffffee4 | 14 | use rustc_middle::ty::{self, suggest_constraining_type_params, Ty, TyCtxt, TypeVisitableExt}; |
2b03887a | 15 | use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt; |
9c376795 FG |
16 | use rustc_trait_selection::traits::misc::{ |
17 | type_allowed_to_implement_copy, CopyImplementationError, InfringingFieldsReason, | |
18 | }; | |
2b03887a FG |
19 | use rustc_trait_selection::traits::predicate_for_trait_def; |
20 | use rustc_trait_selection::traits::{self, ObligationCause}; | |
21 | use std::collections::BTreeMap; | |
22 | ||
23 | pub fn check_trait(tcx: TyCtxt<'_>, trait_def_id: DefId) { | |
24 | let lang_items = tcx.lang_items(); | |
25 | Checker { tcx, trait_def_id } | |
26 | .check(lang_items.drop_trait(), visit_implementation_of_drop) | |
27 | .check(lang_items.copy_trait(), visit_implementation_of_copy) | |
28 | .check(lang_items.coerce_unsized_trait(), visit_implementation_of_coerce_unsized) | |
29 | .check(lang_items.dispatch_from_dyn_trait(), visit_implementation_of_dispatch_from_dyn); | |
30 | } | |
31 | ||
32 | struct Checker<'tcx> { | |
33 | tcx: TyCtxt<'tcx>, | |
34 | trait_def_id: DefId, | |
35 | } | |
36 | ||
37 | impl<'tcx> Checker<'tcx> { | |
38 | fn check<F>(&self, trait_def_id: Option<DefId>, mut f: F) -> &Self | |
39 | where | |
40 | F: FnMut(TyCtxt<'tcx>, LocalDefId), | |
41 | { | |
42 | if Some(self.trait_def_id) == trait_def_id { | |
43 | for &impl_def_id in self.tcx.hir().trait_impls(self.trait_def_id) { | |
44 | f(self.tcx, impl_def_id); | |
45 | } | |
46 | } | |
47 | self | |
48 | } | |
49 | } | |
50 | ||
51 | fn visit_implementation_of_drop(tcx: TyCtxt<'_>, impl_did: LocalDefId) { | |
52 | // Destructors only work on local ADT types. | |
9ffffee4 | 53 | match tcx.type_of(impl_did).subst_identity().kind() { |
2b03887a FG |
54 | ty::Adt(def, _) if def.did().is_local() => return, |
55 | ty::Error(_) => return, | |
56 | _ => {} | |
57 | } | |
58 | ||
9ffffee4 | 59 | let impl_ = tcx.hir().expect_item(impl_did).expect_impl(); |
2b03887a | 60 | |
9c376795 | 61 | tcx.sess.emit_err(DropImplOnWrongItem { span: impl_.self_ty.span }); |
2b03887a FG |
62 | } |
63 | ||
64 | fn visit_implementation_of_copy(tcx: TyCtxt<'_>, impl_did: LocalDefId) { | |
65 | debug!("visit_implementation_of_copy: impl_did={:?}", impl_did); | |
66 | ||
9ffffee4 | 67 | let self_type = tcx.type_of(impl_did).subst_identity(); |
2b03887a FG |
68 | debug!("visit_implementation_of_copy: self_type={:?} (bound)", self_type); |
69 | ||
70 | let param_env = tcx.param_env(impl_did); | |
71 | assert!(!self_type.has_escaping_bound_vars()); | |
72 | ||
73 | debug!("visit_implementation_of_copy: self_type={:?} (free)", self_type); | |
74 | ||
75 | let span = match tcx.hir().expect_item(impl_did).kind { | |
76 | ItemKind::Impl(hir::Impl { polarity: hir::ImplPolarity::Negative(_), .. }) => return, | |
77 | ItemKind::Impl(impl_) => impl_.self_ty.span, | |
78 | _ => bug!("expected Copy impl item"), | |
79 | }; | |
80 | ||
9ffffee4 | 81 | let cause = traits::ObligationCause::misc(span, impl_did); |
9c376795 | 82 | match type_allowed_to_implement_copy(tcx, param_env, self_type, cause) { |
2b03887a FG |
83 | Ok(()) => {} |
84 | Err(CopyImplementationError::InfrigingFields(fields)) => { | |
85 | let mut err = struct_span_err!( | |
86 | tcx.sess, | |
87 | span, | |
88 | E0204, | |
89 | "the trait `Copy` may not be implemented for this type" | |
90 | ); | |
91 | ||
92 | // We'll try to suggest constraining type parameters to fulfill the requirements of | |
93 | // their `Copy` implementation. | |
94 | let mut errors: BTreeMap<_, Vec<_>> = Default::default(); | |
95 | let mut bounds = vec![]; | |
96 | ||
9c376795 | 97 | for (field, ty, reason) in fields { |
2b03887a | 98 | let field_span = tcx.def_span(field.did); |
2b03887a | 99 | err.span_label(field_span, "this field does not implement `Copy`"); |
9c376795 FG |
100 | |
101 | match reason { | |
102 | InfringingFieldsReason::Fulfill(fulfillment_errors) => { | |
103 | for error in fulfillment_errors { | |
104 | let error_predicate = error.obligation.predicate; | |
105 | // Only note if it's not the root obligation, otherwise it's trivial and | |
106 | // should be self-explanatory (i.e. a field literally doesn't implement Copy). | |
107 | ||
108 | // FIXME: This error could be more descriptive, especially if the error_predicate | |
109 | // contains a foreign type or if it's a deeply nested type... | |
110 | if error_predicate != error.root_obligation.predicate { | |
111 | errors | |
112 | .entry((ty.to_string(), error_predicate.to_string())) | |
113 | .or_default() | |
114 | .push(error.obligation.cause.span); | |
115 | } | |
116 | if let ty::PredicateKind::Clause(ty::Clause::Trait( | |
117 | ty::TraitPredicate { | |
118 | trait_ref, | |
119 | polarity: ty::ImplPolarity::Positive, | |
120 | .. | |
121 | }, | |
122 | )) = error_predicate.kind().skip_binder() | |
123 | { | |
124 | let ty = trait_ref.self_ty(); | |
125 | if let ty::Param(_) = ty.kind() { | |
126 | bounds.push(( | |
127 | format!("{ty}"), | |
128 | trait_ref.print_only_trait_path().to_string(), | |
129 | Some(trait_ref.def_id), | |
130 | )); | |
131 | } | |
132 | } | |
133 | } | |
2b03887a | 134 | } |
9c376795 FG |
135 | InfringingFieldsReason::Regions(region_errors) => { |
136 | for error in region_errors { | |
137 | let ty = ty.to_string(); | |
138 | match error { | |
139 | RegionResolutionError::ConcreteFailure(origin, a, b) => { | |
140 | let predicate = format!("{b}: {a}"); | |
141 | errors | |
142 | .entry((ty.clone(), predicate.clone())) | |
143 | .or_default() | |
144 | .push(origin.span()); | |
145 | if let ty::RegionKind::ReEarlyBound(ebr) = *b && ebr.has_name() { | |
146 | bounds.push((b.to_string(), a.to_string(), None)); | |
147 | } | |
148 | } | |
149 | RegionResolutionError::GenericBoundFailure(origin, a, b) => { | |
150 | let predicate = format!("{a}: {b}"); | |
151 | errors | |
152 | .entry((ty.clone(), predicate.clone())) | |
153 | .or_default() | |
154 | .push(origin.span()); | |
155 | if let infer::region_constraints::GenericKind::Param(_) = a { | |
156 | bounds.push((a.to_string(), b.to_string(), None)); | |
157 | } | |
158 | } | |
159 | _ => continue, | |
160 | } | |
2b03887a FG |
161 | } |
162 | } | |
163 | } | |
164 | } | |
165 | for ((ty, error_predicate), spans) in errors { | |
166 | let span: MultiSpan = spans.into(); | |
167 | err.span_note( | |
168 | span, | |
169 | &format!("the `Copy` impl for `{}` requires that `{}`", ty, error_predicate), | |
170 | ); | |
171 | } | |
172 | suggest_constraining_type_params( | |
173 | tcx, | |
174 | tcx.hir().get_generics(impl_did).expect("impls always have generics"), | |
175 | &mut err, | |
176 | bounds.iter().map(|(param, constraint, def_id)| { | |
177 | (param.as_str(), constraint.as_str(), *def_id) | |
178 | }), | |
9ffffee4 | 179 | None, |
2b03887a FG |
180 | ); |
181 | err.emit(); | |
182 | } | |
183 | Err(CopyImplementationError::NotAnAdt) => { | |
184 | tcx.sess.emit_err(CopyImplOnNonAdt { span }); | |
185 | } | |
186 | Err(CopyImplementationError::HasDestructor) => { | |
187 | tcx.sess.emit_err(CopyImplOnTypeWithDtor { span }); | |
188 | } | |
189 | } | |
190 | } | |
191 | ||
9c376795 | 192 | fn visit_implementation_of_coerce_unsized(tcx: TyCtxt<'_>, impl_did: LocalDefId) { |
2b03887a FG |
193 | debug!("visit_implementation_of_coerce_unsized: impl_did={:?}", impl_did); |
194 | ||
195 | // Just compute this for the side-effects, in particular reporting | |
196 | // errors; other parts of the code may demand it for the info of | |
197 | // course. | |
198 | let span = tcx.def_span(impl_did); | |
199 | tcx.at(span).coerce_unsized_info(impl_did); | |
200 | } | |
201 | ||
9c376795 | 202 | fn visit_implementation_of_dispatch_from_dyn(tcx: TyCtxt<'_>, impl_did: LocalDefId) { |
2b03887a FG |
203 | debug!("visit_implementation_of_dispatch_from_dyn: impl_did={:?}", impl_did); |
204 | ||
9ffffee4 | 205 | let span = tcx.def_span(impl_did); |
2b03887a FG |
206 | |
207 | let dispatch_from_dyn_trait = tcx.require_lang_item(LangItem::DispatchFromDyn, Some(span)); | |
208 | ||
9ffffee4 | 209 | let source = tcx.type_of(impl_did).subst_identity(); |
2b03887a FG |
210 | assert!(!source.has_escaping_bound_vars()); |
211 | let target = { | |
9c376795 | 212 | let trait_ref = tcx.impl_trait_ref(impl_did).unwrap().subst_identity(); |
2b03887a FG |
213 | assert_eq!(trait_ref.def_id, dispatch_from_dyn_trait); |
214 | ||
215 | trait_ref.substs.type_at(1) | |
216 | }; | |
217 | ||
218 | debug!("visit_implementation_of_dispatch_from_dyn: {:?} -> {:?}", source, target); | |
219 | ||
220 | let param_env = tcx.param_env(impl_did); | |
221 | ||
222 | let create_err = |msg: &str| struct_span_err!(tcx.sess, span, E0378, "{}", msg); | |
223 | ||
224 | let infcx = tcx.infer_ctxt().build(); | |
9ffffee4 | 225 | let cause = ObligationCause::misc(span, impl_did); |
2b03887a FG |
226 | |
227 | use rustc_type_ir::sty::TyKind::*; | |
228 | match (source.kind(), target.kind()) { | |
229 | (&Ref(r_a, _, mutbl_a), Ref(r_b, _, mutbl_b)) | |
230 | if infcx.at(&cause, param_env).eq(r_a, *r_b).is_ok() && mutbl_a == *mutbl_b => {} | |
231 | (&RawPtr(tm_a), &RawPtr(tm_b)) if tm_a.mutbl == tm_b.mutbl => (), | |
232 | (&Adt(def_a, substs_a), &Adt(def_b, substs_b)) | |
233 | if def_a.is_struct() && def_b.is_struct() => | |
234 | { | |
235 | if def_a != def_b { | |
236 | let source_path = tcx.def_path_str(def_a.did()); | |
237 | let target_path = tcx.def_path_str(def_b.did()); | |
238 | ||
239 | create_err(&format!( | |
240 | "the trait `DispatchFromDyn` may only be implemented \ | |
241 | for a coercion between structures with the same \ | |
242 | definition; expected `{}`, found `{}`", | |
243 | source_path, target_path, | |
244 | )) | |
245 | .emit(); | |
246 | ||
247 | return; | |
248 | } | |
249 | ||
250 | if def_a.repr().c() || def_a.repr().packed() { | |
251 | create_err( | |
252 | "structs implementing `DispatchFromDyn` may not have \ | |
253 | `#[repr(packed)]` or `#[repr(C)]`", | |
254 | ) | |
255 | .emit(); | |
256 | } | |
257 | ||
258 | let fields = &def_a.non_enum_variant().fields; | |
259 | ||
260 | let coerced_fields = fields | |
261 | .iter() | |
262 | .filter(|field| { | |
263 | let ty_a = field.ty(tcx, substs_a); | |
264 | let ty_b = field.ty(tcx, substs_b); | |
265 | ||
266 | if let Ok(layout) = tcx.layout_of(param_env.and(ty_a)) { | |
267 | if layout.is_zst() && layout.align.abi.bytes() == 1 { | |
268 | // ignore ZST fields with alignment of 1 byte | |
269 | return false; | |
270 | } | |
271 | } | |
272 | ||
273 | if let Ok(ok) = infcx.at(&cause, param_env).eq(ty_a, ty_b) { | |
274 | if ok.obligations.is_empty() { | |
275 | create_err( | |
276 | "the trait `DispatchFromDyn` may only be implemented \ | |
277 | for structs containing the field being coerced, \ | |
278 | ZST fields with 1 byte alignment, and nothing else", | |
279 | ) | |
280 | .note(&format!( | |
281 | "extra field `{}` of type `{}` is not allowed", | |
282 | field.name, ty_a, | |
283 | )) | |
284 | .emit(); | |
285 | ||
286 | return false; | |
287 | } | |
288 | } | |
289 | ||
290 | return true; | |
291 | }) | |
292 | .collect::<Vec<_>>(); | |
293 | ||
294 | if coerced_fields.is_empty() { | |
295 | create_err( | |
296 | "the trait `DispatchFromDyn` may only be implemented \ | |
297 | for a coercion between structures with a single field \ | |
298 | being coerced, none found", | |
299 | ) | |
300 | .emit(); | |
301 | } else if coerced_fields.len() > 1 { | |
302 | create_err("implementing the `DispatchFromDyn` trait requires multiple coercions") | |
303 | .note( | |
304 | "the trait `DispatchFromDyn` may only be implemented \ | |
305 | for a coercion between structures with a single field \ | |
306 | being coerced", | |
307 | ) | |
308 | .note(&format!( | |
309 | "currently, {} fields need coercions: {}", | |
310 | coerced_fields.len(), | |
311 | coerced_fields | |
312 | .iter() | |
313 | .map(|field| { | |
314 | format!( | |
315 | "`{}` (`{}` to `{}`)", | |
316 | field.name, | |
317 | field.ty(tcx, substs_a), | |
318 | field.ty(tcx, substs_b), | |
319 | ) | |
320 | }) | |
321 | .collect::<Vec<_>>() | |
322 | .join(", ") | |
323 | )) | |
324 | .emit(); | |
325 | } else { | |
326 | let errors = traits::fully_solve_obligations( | |
327 | &infcx, | |
328 | coerced_fields.into_iter().map(|field| { | |
329 | predicate_for_trait_def( | |
330 | tcx, | |
331 | param_env, | |
332 | cause.clone(), | |
333 | dispatch_from_dyn_trait, | |
334 | 0, | |
487cf647 | 335 | [field.ty(tcx, substs_a), field.ty(tcx, substs_b)], |
2b03887a FG |
336 | ) |
337 | }), | |
338 | ); | |
339 | if !errors.is_empty() { | |
487cf647 | 340 | infcx.err_ctxt().report_fulfillment_errors(&errors, None); |
2b03887a FG |
341 | } |
342 | ||
343 | // Finally, resolve all regions. | |
344 | let outlives_env = OutlivesEnvironment::new(param_env); | |
9c376795 FG |
345 | let _ = infcx |
346 | .err_ctxt() | |
347 | .check_region_obligations_and_report_errors(impl_did, &outlives_env); | |
2b03887a FG |
348 | } |
349 | } | |
350 | _ => { | |
351 | create_err( | |
352 | "the trait `DispatchFromDyn` may only be implemented \ | |
353 | for a coercion between structures", | |
354 | ) | |
355 | .emit(); | |
356 | } | |
357 | } | |
358 | } | |
359 | ||
360 | pub fn coerce_unsized_info<'tcx>(tcx: TyCtxt<'tcx>, impl_did: DefId) -> CoerceUnsizedInfo { | |
361 | debug!("compute_coerce_unsized_info(impl_did={:?})", impl_did); | |
362 | ||
363 | // this provider should only get invoked for local def-ids | |
364 | let impl_did = impl_did.expect_local(); | |
365 | let span = tcx.def_span(impl_did); | |
366 | ||
367 | let coerce_unsized_trait = tcx.require_lang_item(LangItem::CoerceUnsized, Some(span)); | |
368 | ||
369 | let unsize_trait = tcx.lang_items().require(LangItem::Unsize).unwrap_or_else(|err| { | |
370 | tcx.sess.fatal(&format!("`CoerceUnsized` implementation {}", err.to_string())); | |
371 | }); | |
372 | ||
9ffffee4 | 373 | let source = tcx.type_of(impl_did).subst_identity(); |
9c376795 | 374 | let trait_ref = tcx.impl_trait_ref(impl_did).unwrap().subst_identity(); |
2b03887a FG |
375 | assert_eq!(trait_ref.def_id, coerce_unsized_trait); |
376 | let target = trait_ref.substs.type_at(1); | |
377 | debug!("visit_implementation_of_coerce_unsized: {:?} -> {:?} (bound)", source, target); | |
378 | ||
379 | let param_env = tcx.param_env(impl_did); | |
380 | assert!(!source.has_escaping_bound_vars()); | |
381 | ||
382 | let err_info = CoerceUnsizedInfo { custom_kind: None }; | |
383 | ||
384 | debug!("visit_implementation_of_coerce_unsized: {:?} -> {:?} (free)", source, target); | |
385 | ||
386 | let infcx = tcx.infer_ctxt().build(); | |
9ffffee4 | 387 | let cause = ObligationCause::misc(span, impl_did); |
2b03887a FG |
388 | let check_mutbl = |mt_a: ty::TypeAndMut<'tcx>, |
389 | mt_b: ty::TypeAndMut<'tcx>, | |
390 | mk_ptr: &dyn Fn(Ty<'tcx>) -> Ty<'tcx>| { | |
487cf647 | 391 | if mt_a.mutbl < mt_b.mutbl { |
2b03887a FG |
392 | infcx |
393 | .err_ctxt() | |
394 | .report_mismatched_types( | |
395 | &cause, | |
396 | mk_ptr(mt_b.ty), | |
397 | target, | |
398 | ty::error::TypeError::Mutability, | |
399 | ) | |
400 | .emit(); | |
401 | } | |
402 | (mt_a.ty, mt_b.ty, unsize_trait, None) | |
403 | }; | |
404 | let (source, target, trait_def_id, kind) = match (source.kind(), target.kind()) { | |
405 | (&ty::Ref(r_a, ty_a, mutbl_a), &ty::Ref(r_b, ty_b, mutbl_b)) => { | |
406 | infcx.sub_regions(infer::RelateObjectBound(span), r_b, r_a); | |
407 | let mt_a = ty::TypeAndMut { ty: ty_a, mutbl: mutbl_a }; | |
408 | let mt_b = ty::TypeAndMut { ty: ty_b, mutbl: mutbl_b }; | |
409 | check_mutbl(mt_a, mt_b, &|ty| tcx.mk_imm_ref(r_b, ty)) | |
410 | } | |
411 | ||
412 | (&ty::Ref(_, ty_a, mutbl_a), &ty::RawPtr(mt_b)) => { | |
413 | let mt_a = ty::TypeAndMut { ty: ty_a, mutbl: mutbl_a }; | |
414 | check_mutbl(mt_a, mt_b, &|ty| tcx.mk_imm_ptr(ty)) | |
415 | } | |
416 | ||
417 | (&ty::RawPtr(mt_a), &ty::RawPtr(mt_b)) => check_mutbl(mt_a, mt_b, &|ty| tcx.mk_imm_ptr(ty)), | |
418 | ||
419 | (&ty::Adt(def_a, substs_a), &ty::Adt(def_b, substs_b)) | |
420 | if def_a.is_struct() && def_b.is_struct() => | |
421 | { | |
422 | if def_a != def_b { | |
423 | let source_path = tcx.def_path_str(def_a.did()); | |
424 | let target_path = tcx.def_path_str(def_b.did()); | |
425 | struct_span_err!( | |
426 | tcx.sess, | |
427 | span, | |
428 | E0377, | |
429 | "the trait `CoerceUnsized` may only be implemented \ | |
430 | for a coercion between structures with the same \ | |
431 | definition; expected `{}`, found `{}`", | |
432 | source_path, | |
433 | target_path | |
434 | ) | |
435 | .emit(); | |
436 | return err_info; | |
437 | } | |
438 | ||
439 | // Here we are considering a case of converting | |
9ffffee4 | 440 | // `S<P0...Pn>` to `S<Q0...Qn>`. As an example, let's imagine a struct `Foo<T, U>`, |
2b03887a FG |
441 | // which acts like a pointer to `U`, but carries along some extra data of type `T`: |
442 | // | |
443 | // struct Foo<T, U> { | |
444 | // extra: T, | |
445 | // ptr: *mut U, | |
446 | // } | |
447 | // | |
448 | // We might have an impl that allows (e.g.) `Foo<T, [i32; 3]>` to be unsized | |
449 | // to `Foo<T, [i32]>`. That impl would look like: | |
450 | // | |
451 | // impl<T, U: Unsize<V>, V> CoerceUnsized<Foo<T, V>> for Foo<T, U> {} | |
452 | // | |
453 | // Here `U = [i32; 3]` and `V = [i32]`. At runtime, | |
454 | // when this coercion occurs, we would be changing the | |
455 | // field `ptr` from a thin pointer of type `*mut [i32; | |
456 | // 3]` to a fat pointer of type `*mut [i32]` (with | |
9c376795 | 457 | // extra data `3`). **The purpose of this check is to |
2b03887a FG |
458 | // make sure that we know how to do this conversion.** |
459 | // | |
460 | // To check if this impl is legal, we would walk down | |
461 | // the fields of `Foo` and consider their types with | |
462 | // both substitutes. We are looking to find that | |
463 | // exactly one (non-phantom) field has changed its | |
464 | // type, which we will expect to be the pointer that | |
465 | // is becoming fat (we could probably generalize this | |
466 | // to multiple thin pointers of the same type becoming | |
467 | // fat, but we don't). In this case: | |
468 | // | |
469 | // - `extra` has type `T` before and type `T` after | |
470 | // - `ptr` has type `*mut U` before and type `*mut V` after | |
471 | // | |
472 | // Since just one field changed, we would then check | |
473 | // that `*mut U: CoerceUnsized<*mut V>` is implemented | |
474 | // (in other words, that we know how to do this | |
475 | // conversion). This will work out because `U: | |
476 | // Unsize<V>`, and we have a builtin rule that `*mut | |
477 | // U` can be coerced to `*mut V` if `U: Unsize<V>`. | |
478 | let fields = &def_a.non_enum_variant().fields; | |
479 | let diff_fields = fields | |
480 | .iter() | |
481 | .enumerate() | |
482 | .filter_map(|(i, f)| { | |
483 | let (a, b) = (f.ty(tcx, substs_a), f.ty(tcx, substs_b)); | |
484 | ||
9ffffee4 | 485 | if tcx.type_of(f.did).subst_identity().is_phantom_data() { |
2b03887a FG |
486 | // Ignore PhantomData fields |
487 | return None; | |
488 | } | |
489 | ||
490 | // Ignore fields that aren't changed; it may | |
491 | // be that we could get away with subtyping or | |
492 | // something more accepting, but we use | |
493 | // equality because we want to be able to | |
494 | // perform this check without computing | |
495 | // variance where possible. (This is because | |
496 | // we may have to evaluate constraint | |
497 | // expressions in the course of execution.) | |
498 | // See e.g., #41936. | |
499 | if let Ok(ok) = infcx.at(&cause, param_env).eq(a, b) { | |
500 | if ok.obligations.is_empty() { | |
501 | return None; | |
502 | } | |
503 | } | |
504 | ||
505 | // Collect up all fields that were significantly changed | |
506 | // i.e., those that contain T in coerce_unsized T -> U | |
507 | Some((i, a, b)) | |
508 | }) | |
509 | .collect::<Vec<_>>(); | |
510 | ||
511 | if diff_fields.is_empty() { | |
512 | struct_span_err!( | |
513 | tcx.sess, | |
514 | span, | |
515 | E0374, | |
516 | "the trait `CoerceUnsized` may only be implemented \ | |
517 | for a coercion between structures with one field \ | |
518 | being coerced, none found" | |
519 | ) | |
520 | .emit(); | |
521 | return err_info; | |
522 | } else if diff_fields.len() > 1 { | |
523 | let item = tcx.hir().expect_item(impl_did); | |
9c376795 FG |
524 | let span = if let ItemKind::Impl(hir::Impl { of_trait: Some(t), .. }) = &item.kind { |
525 | t.path.span | |
526 | } else { | |
527 | tcx.def_span(impl_did) | |
528 | }; | |
2b03887a FG |
529 | |
530 | struct_span_err!( | |
531 | tcx.sess, | |
532 | span, | |
533 | E0375, | |
534 | "implementing the trait \ | |
535 | `CoerceUnsized` requires multiple \ | |
536 | coercions" | |
537 | ) | |
538 | .note( | |
539 | "`CoerceUnsized` may only be implemented for \ | |
540 | a coercion between structures with one field being coerced", | |
541 | ) | |
542 | .note(&format!( | |
543 | "currently, {} fields need coercions: {}", | |
544 | diff_fields.len(), | |
545 | diff_fields | |
546 | .iter() | |
547 | .map(|&(i, a, b)| { format!("`{}` (`{}` to `{}`)", fields[i].name, a, b) }) | |
548 | .collect::<Vec<_>>() | |
549 | .join(", ") | |
550 | )) | |
551 | .span_label(span, "requires multiple coercions") | |
552 | .emit(); | |
553 | return err_info; | |
554 | } | |
555 | ||
556 | let (i, a, b) = diff_fields[0]; | |
557 | let kind = ty::adjustment::CustomCoerceUnsized::Struct(i); | |
558 | (a, b, coerce_unsized_trait, Some(kind)) | |
559 | } | |
560 | ||
561 | _ => { | |
562 | struct_span_err!( | |
563 | tcx.sess, | |
564 | span, | |
565 | E0376, | |
566 | "the trait `CoerceUnsized` may only be implemented \ | |
567 | for a coercion between structures" | |
568 | ) | |
569 | .emit(); | |
570 | return err_info; | |
571 | } | |
572 | }; | |
573 | ||
574 | // Register an obligation for `A: Trait<B>`. | |
9ffffee4 | 575 | let cause = traits::ObligationCause::misc(span, impl_did); |
2b03887a | 576 | let predicate = |
487cf647 | 577 | predicate_for_trait_def(tcx, param_env, cause, trait_def_id, 0, [source, target]); |
2b03887a FG |
578 | let errors = traits::fully_solve_obligation(&infcx, predicate); |
579 | if !errors.is_empty() { | |
487cf647 | 580 | infcx.err_ctxt().report_fulfillment_errors(&errors, None); |
2b03887a FG |
581 | } |
582 | ||
583 | // Finally, resolve all regions. | |
584 | let outlives_env = OutlivesEnvironment::new(param_env); | |
9c376795 | 585 | let _ = infcx.err_ctxt().check_region_obligations_and_report_errors(impl_did, &outlives_env); |
2b03887a FG |
586 | |
587 | CoerceUnsizedInfo { custom_kind: kind } | |
588 | } |