]>
Commit | Line | Data |
---|---|---|
1a4d82fc JJ |
1 | //! Orphan checker: every impl either implements a trait defined in this |
2 | //! crate or pertains to a type defined in this crate. | |
3 | ||
5099ac24 | 4 | use rustc_data_structures::fx::FxHashSet; |
dfeec247 | 5 | use rustc_errors::struct_span_err; |
064997fb | 6 | use rustc_errors::{Diagnostic, ErrorGuaranteed}; |
dfeec247 | 7 | use rustc_hir as hir; |
74b04a01 | 8 | use rustc_infer::infer::TyCtxtInferExt; |
04454e1e | 9 | use rustc_middle::ty::subst::GenericArgKind; |
923072b8 FG |
10 | use rustc_middle::ty::subst::InternalSubsts; |
11 | use rustc_middle::ty::util::IgnoreRegions; | |
12 | use rustc_middle::ty::{ | |
064997fb | 13 | self, ImplPolarity, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, |
923072b8 | 14 | }; |
5099ac24 FG |
15 | use rustc_session::lint; |
16 | use rustc_span::def_id::{DefId, LocalDefId}; | |
3c0e092e | 17 | use rustc_span::Span; |
ba9703b0 | 18 | use rustc_trait_selection::traits; |
5099ac24 | 19 | use std::ops::ControlFlow; |
60c5eb7d | 20 | |
064997fb FG |
21 | #[instrument(skip(tcx), level = "debug")] |
22 | pub(crate) fn orphan_check_impl( | |
23 | tcx: TyCtxt<'_>, | |
24 | impl_def_id: LocalDefId, | |
25 | ) -> Result<(), ErrorGuaranteed> { | |
26 | let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); | |
27 | if let Some(err) = trait_ref.error_reported() { | |
28 | return Err(err); | |
29 | } | |
5099ac24 | 30 | |
064997fb FG |
31 | let ret = do_orphan_check_impl(tcx, trait_ref, impl_def_id); |
32 | if tcx.trait_is_auto(trait_ref.def_id) { | |
33 | lint_auto_trait_impl(tcx, trait_ref, impl_def_id); | |
3c0e092e | 34 | } |
064997fb FG |
35 | |
36 | ret | |
1a4d82fc JJ |
37 | } |
38 | ||
064997fb FG |
39 | fn do_orphan_check_impl<'tcx>( |
40 | tcx: TyCtxt<'tcx>, | |
41 | trait_ref: ty::TraitRef<'tcx>, | |
42 | def_id: LocalDefId, | |
43 | ) -> Result<(), ErrorGuaranteed> { | |
3c0e092e | 44 | let trait_def_id = trait_ref.def_id; |
1a4d82fc | 45 | |
3c0e092e | 46 | let item = tcx.hir().item(hir::ItemId { def_id }); |
5e7ed085 FG |
47 | let hir::ItemKind::Impl(ref impl_) = item.kind else { |
48 | bug!("{:?} is not an impl: {:?}", def_id, item); | |
3c0e092e | 49 | }; |
064997fb | 50 | let sp = tcx.def_span(def_id); |
3c0e092e | 51 | let tr = impl_.of_trait.as_ref().unwrap(); |
04454e1e FG |
52 | |
53 | // Ensure no opaque types are present in this impl header. See issues #76202 and #86411 for examples, | |
54 | // and #84660 where it would otherwise allow unsoundness. | |
55 | if trait_ref.has_opaque_types() { | |
56 | trace!("{:#?}", item); | |
57 | // First we find the opaque type in question. | |
58 | for ty in trait_ref.substs { | |
59 | for ty in ty.walk() { | |
60 | let ty::subst::GenericArgKind::Type(ty) = ty.unpack() else { continue }; | |
61 | let ty::Opaque(def_id, _) = *ty.kind() else { continue }; | |
62 | trace!(?def_id); | |
63 | ||
64 | // Then we search for mentions of the opaque type's type alias in the HIR | |
65 | struct SpanFinder<'tcx> { | |
66 | sp: Span, | |
67 | def_id: DefId, | |
68 | tcx: TyCtxt<'tcx>, | |
69 | } | |
70 | impl<'v, 'tcx> hir::intravisit::Visitor<'v> for SpanFinder<'tcx> { | |
71 | #[instrument(level = "trace", skip(self, _id))] | |
72 | fn visit_path(&mut self, path: &'v hir::Path<'v>, _id: hir::HirId) { | |
73 | // You can't mention an opaque type directly, so we look for type aliases | |
74 | if let hir::def::Res::Def(hir::def::DefKind::TyAlias, def_id) = path.res { | |
75 | // And check if that type alias's type contains the opaque type we're looking for | |
76 | for arg in self.tcx.type_of(def_id).walk() { | |
77 | if let GenericArgKind::Type(ty) = arg.unpack() { | |
78 | if let ty::Opaque(def_id, _) = *ty.kind() { | |
79 | if def_id == self.def_id { | |
80 | // Finally we update the span to the mention of the type alias | |
81 | self.sp = path.span; | |
82 | return; | |
83 | } | |
84 | } | |
85 | } | |
86 | } | |
87 | } | |
88 | hir::intravisit::walk_path(self, path) | |
89 | } | |
90 | } | |
91 | ||
92 | let mut visitor = SpanFinder { sp, def_id, tcx }; | |
93 | hir::intravisit::walk_item(&mut visitor, item); | |
94 | let reported = tcx | |
95 | .sess | |
96 | .struct_span_err(visitor.sp, "cannot implement trait on type alias impl trait") | |
97 | .span_note(tcx.def_span(def_id), "type alias impl trait defined here") | |
98 | .emit(); | |
99 | return Err(reported); | |
100 | } | |
101 | } | |
102 | span_bug!(sp, "opaque type not found, but `has_opaque_types` is set") | |
103 | } | |
104 | ||
3c0e092e XL |
105 | match traits::orphan_check(tcx, item.def_id.to_def_id()) { |
106 | Ok(()) => {} | |
107 | Err(err) => emit_orphan_check_error( | |
108 | tcx, | |
109 | sp, | |
064997fb | 110 | item.span, |
3c0e092e | 111 | tr.path.span, |
04454e1e | 112 | trait_ref.self_ty(), |
3c0e092e XL |
113 | impl_.self_ty.span, |
114 | &impl_.generics, | |
115 | err, | |
116 | )?, | |
117 | } | |
118 | ||
119 | // In addition to the above rules, we restrict impls of auto traits | |
120 | // so that they can only be implemented on nominal types, such as structs, | |
121 | // enums or foreign types. To see why this restriction exists, consider the | |
122 | // following example (#22978). Imagine that crate A defines an auto trait | |
123 | // `Foo` and a fn that operates on pairs of types: | |
124 | // | |
125 | // ``` | |
126 | // // Crate A | |
127 | // auto trait Foo { } | |
128 | // fn two_foos<A:Foo,B:Foo>(..) { | |
129 | // one_foo::<(A,B)>(..) | |
130 | // } | |
131 | // fn one_foo<T:Foo>(..) { .. } | |
132 | // ``` | |
133 | // | |
134 | // This type-checks fine; in particular the fn | |
135 | // `two_foos` is able to conclude that `(A,B):Foo` | |
136 | // because `A:Foo` and `B:Foo`. | |
137 | // | |
138 | // Now imagine that crate B comes along and does the following: | |
139 | // | |
140 | // ``` | |
141 | // struct A { } | |
142 | // struct B { } | |
143 | // impl Foo for A { } | |
144 | // impl Foo for B { } | |
145 | // impl !Send for (A, B) { } | |
146 | // ``` | |
147 | // | |
148 | // This final impl is legal according to the orphan | |
149 | // rules, but it invalidates the reasoning from | |
150 | // `two_foos` above. | |
151 | debug!( | |
152 | "trait_ref={:?} trait_def_id={:?} trait_is_auto={}", | |
153 | trait_ref, | |
154 | trait_def_id, | |
155 | tcx.trait_is_auto(trait_def_id) | |
156 | ); | |
157 | ||
158 | if tcx.trait_is_auto(trait_def_id) && !trait_def_id.is_local() { | |
159 | let self_ty = trait_ref.self_ty(); | |
160 | let opt_self_def_id = match *self_ty.kind() { | |
5e7ed085 | 161 | ty::Adt(self_def, _) => Some(self_def.did()), |
3c0e092e XL |
162 | ty::Foreign(did) => Some(did), |
163 | _ => None, | |
164 | }; | |
60c5eb7d | 165 | |
3c0e092e XL |
166 | let msg = match opt_self_def_id { |
167 | // We only want to permit nominal types, but not *all* nominal types. | |
168 | // They must be local to the current crate, so that people | |
169 | // can't do `unsafe impl Send for Rc<SomethingLocal>` or | |
170 | // `impl !Send for Box<SomethingLocalAndSend>`. | |
171 | Some(self_def_id) => { | |
172 | if self_def_id.is_local() { | |
173 | None | |
174 | } else { | |
175 | Some(( | |
176 | format!( | |
177 | "cross-crate traits with a default impl, like `{}`, \ | |
178 | can only be implemented for a struct/enum type \ | |
179 | defined in the current crate", | |
180 | tcx.def_path_str(trait_def_id) | |
181 | ), | |
182 | "can't implement cross-crate trait for type in another crate", | |
183 | )) | |
c34b1796 | 184 | } |
0bf4aa26 | 185 | } |
3c0e092e XL |
186 | _ => Some(( |
187 | format!( | |
188 | "cross-crate traits with a default impl, like `{}`, can \ | |
189 | only be implemented for a struct/enum type, not `{}`", | |
190 | tcx.def_path_str(trait_def_id), | |
191 | self_ty | |
192 | ), | |
193 | "can't implement cross-crate trait with a default impl for \ | |
194 | non-struct/enum type", | |
195 | )), | |
196 | }; | |
c34b1796 | 197 | |
3c0e092e | 198 | if let Some((msg, label)) = msg { |
5e7ed085 FG |
199 | let reported = |
200 | struct_span_err!(tcx.sess, sp, E0321, "{}", msg).span_label(sp, label).emit(); | |
201 | return Err(reported); | |
3c0e092e XL |
202 | } |
203 | } | |
204 | ||
3c0e092e XL |
205 | Ok(()) |
206 | } | |
207 | ||
a2a8927a | 208 | fn emit_orphan_check_error<'tcx>( |
3c0e092e XL |
209 | tcx: TyCtxt<'tcx>, |
210 | sp: Span, | |
064997fb | 211 | full_impl_span: Span, |
3c0e092e | 212 | trait_span: Span, |
04454e1e | 213 | self_ty: Ty<'tcx>, |
3c0e092e XL |
214 | self_ty_span: Span, |
215 | generics: &hir::Generics<'tcx>, | |
216 | err: traits::OrphanCheckErr<'tcx>, | |
5e7ed085 FG |
217 | ) -> Result<!, ErrorGuaranteed> { |
218 | Err(match err { | |
3c0e092e | 219 | traits::OrphanCheckErr::NonLocalInputType(tys) => { |
04454e1e FG |
220 | let msg = match self_ty.kind() { |
221 | ty::Adt(..) => "can be implemented for types defined outside of the crate", | |
222 | _ if self_ty.is_primitive() => "can be implemented for primitive types", | |
223 | _ => "can be implemented for arbitrary types", | |
224 | }; | |
3c0e092e XL |
225 | let mut err = struct_span_err!( |
226 | tcx.sess, | |
227 | sp, | |
228 | E0117, | |
04454e1e | 229 | "only traits defined in the current crate {msg}" |
dfeec247 | 230 | ); |
3c0e092e XL |
231 | err.span_label(sp, "impl doesn't use only types from inside the current crate"); |
232 | for (ty, is_target_ty) in &tys { | |
233 | let mut ty = *ty; | |
234 | tcx.infer_ctxt().enter(|infcx| { | |
235 | // Remove the lifetimes unnecessary for this error. | |
236 | ty = infcx.freshen(ty); | |
237 | }); | |
238 | ty = match ty.kind() { | |
239 | // Remove the type arguments from the output, as they are not relevant. | |
240 | // You can think of this as the reverse of `resolve_vars_if_possible`. | |
241 | // That way if we had `Vec<MyType>`, we will properly attribute the | |
242 | // problem to `Vec<T>` and avoid confusing the user if they were to see | |
243 | // `MyType` in the error. | |
5e7ed085 | 244 | ty::Adt(def, _) => tcx.mk_adt(*def, ty::List::empty()), |
3c0e092e | 245 | _ => ty, |
0bf4aa26 | 246 | }; |
3c0e092e XL |
247 | let this = "this".to_string(); |
248 | let (ty, postfix) = match &ty.kind() { | |
249 | ty::Slice(_) => (this, " because slices are always foreign"), | |
250 | ty::Array(..) => (this, " because arrays are always foreign"), | |
251 | ty::Tuple(..) => (this, " because tuples are always foreign"), | |
064997fb FG |
252 | ty::RawPtr(ptr_ty) => { |
253 | emit_newtype_suggestion_for_raw_ptr( | |
254 | full_impl_span, | |
255 | self_ty, | |
256 | self_ty_span, | |
257 | ptr_ty, | |
258 | &mut err, | |
259 | ); | |
260 | ||
261 | (format!("`{}`", ty), " because raw pointers are always foreign") | |
262 | } | |
3c0e092e | 263 | _ => (format!("`{}`", ty), ""), |
0bf4aa26 | 264 | }; |
064997fb | 265 | |
3c0e092e XL |
266 | let msg = format!("{} is not defined in the current crate{}", ty, postfix); |
267 | if *is_target_ty { | |
268 | // Point at `D<A>` in `impl<A, B> for C<B> in D<A>` | |
269 | err.span_label(self_ty_span, &msg); | |
270 | } else { | |
271 | // Point at `C<B>` in `impl<A, B> for C<B> in D<A>` | |
272 | err.span_label(trait_span, &msg); | |
273 | } | |
274 | } | |
275 | err.note("define and implement a trait or new type instead"); | |
276 | err.emit() | |
277 | } | |
278 | traits::OrphanCheckErr::UncoveredTy(param_ty, local_type) => { | |
279 | let mut sp = sp; | |
280 | for param in generics.params { | |
281 | if param.name.ident().to_string() == param_ty.to_string() { | |
282 | sp = param.span; | |
1a4d82fc | 283 | } |
c34b1796 | 284 | } |
1b1a35ee | 285 | |
3c0e092e XL |
286 | match local_type { |
287 | Some(local_type) => struct_span_err!( | |
288 | tcx.sess, | |
289 | sp, | |
290 | E0210, | |
291 | "type parameter `{}` must be covered by another type \ | |
292 | when it appears before the first local type (`{}`)", | |
293 | param_ty, | |
294 | local_type | |
295 | ) | |
296 | .span_label( | |
297 | sp, | |
298 | format!( | |
299 | "type parameter `{}` must be covered by another type \ | |
300 | when it appears before the first local type (`{}`)", | |
301 | param_ty, local_type | |
302 | ), | |
303 | ) | |
304 | .note( | |
305 | "implementing a foreign trait is only possible if at \ | |
306 | least one of the types for which it is implemented is local, \ | |
307 | and no uncovered type parameters appear before that first \ | |
308 | local type", | |
309 | ) | |
310 | .note( | |
311 | "in this case, 'before' refers to the following order: \ | |
312 | `impl<..> ForeignTrait<T1, ..., Tn> for T0`, \ | |
313 | where `T0` is the first and `Tn` is the last", | |
314 | ) | |
315 | .emit(), | |
316 | None => struct_span_err!( | |
317 | tcx.sess, | |
318 | sp, | |
319 | E0210, | |
320 | "type parameter `{}` must be used as the type parameter for some \ | |
321 | local type (e.g., `MyStruct<{}>`)", | |
322 | param_ty, | |
323 | param_ty | |
324 | ) | |
325 | .span_label( | |
326 | sp, | |
327 | format!( | |
328 | "type parameter `{}` must be used as the type parameter for some \ | |
329 | local type", | |
330 | param_ty, | |
331 | ), | |
332 | ) | |
333 | .note( | |
334 | "implementing a foreign trait is only possible if at \ | |
335 | least one of the types for which it is implemented is local", | |
336 | ) | |
337 | .note( | |
338 | "only traits defined in the current crate can be \ | |
339 | implemented for a type parameter", | |
340 | ) | |
341 | .emit(), | |
1b1a35ee | 342 | } |
1a4d82fc | 343 | } |
5e7ed085 | 344 | }) |
1a4d82fc | 345 | } |
5099ac24 | 346 | |
064997fb FG |
347 | fn emit_newtype_suggestion_for_raw_ptr( |
348 | full_impl_span: Span, | |
349 | self_ty: Ty<'_>, | |
350 | self_ty_span: Span, | |
351 | ptr_ty: &ty::TypeAndMut<'_>, | |
352 | diag: &mut Diagnostic, | |
353 | ) { | |
354 | if !self_ty.needs_subst() { | |
355 | let mut_key = if ptr_ty.mutbl == rustc_middle::mir::Mutability::Mut { "mut " } else { "" }; | |
356 | let msg_sugg = "consider introducing a new wrapper type".to_owned(); | |
357 | let sugg = vec![ | |
358 | ( | |
359 | full_impl_span.shrink_to_lo(), | |
360 | format!("struct WrapperType(*{}{});\n\n", mut_key, ptr_ty.ty), | |
361 | ), | |
362 | (self_ty_span, "WrapperType".to_owned()), | |
363 | ]; | |
364 | diag.multipart_suggestion(msg_sugg, sugg, rustc_errors::Applicability::MaybeIncorrect); | |
365 | } | |
366 | } | |
367 | ||
5099ac24 FG |
368 | /// Lint impls of auto traits if they are likely to have |
369 | /// unsound or surprising effects on auto impls. | |
064997fb FG |
370 | fn lint_auto_trait_impl<'tcx>( |
371 | tcx: TyCtxt<'tcx>, | |
372 | trait_ref: ty::TraitRef<'tcx>, | |
373 | impl_def_id: LocalDefId, | |
374 | ) { | |
375 | if tcx.impl_polarity(impl_def_id) != ImplPolarity::Positive { | |
376 | return; | |
377 | } | |
5099ac24 | 378 | |
064997fb FG |
379 | assert_eq!(trait_ref.substs.len(), 1); |
380 | let self_ty = trait_ref.self_ty(); | |
381 | let (self_type_did, substs) = match self_ty.kind() { | |
382 | ty::Adt(def, substs) => (def.did(), substs), | |
383 | _ => { | |
384 | // FIXME: should also lint for stuff like `&i32` but | |
385 | // considering that auto traits are unstable, that | |
386 | // isn't too important for now as this only affects | |
387 | // crates using `nightly`, and std. | |
5099ac24 FG |
388 | return; |
389 | } | |
064997fb | 390 | }; |
5099ac24 | 391 | |
064997fb FG |
392 | // Impls which completely cover a given root type are fine as they |
393 | // disable auto impls entirely. So only lint if the substs | |
394 | // are not a permutation of the identity substs. | |
395 | let Err(arg) = tcx.uses_unique_generic_params(substs, IgnoreRegions::Yes) else { | |
396 | // ok | |
397 | return; | |
398 | }; | |
5099ac24 | 399 | |
064997fb FG |
400 | // Ideally: |
401 | // | |
402 | // - compute the requirements for the auto impl candidate | |
403 | // - check whether these are implied by the non covering impls | |
404 | // - if not, emit the lint | |
405 | // | |
406 | // What we do here is a bit simpler: | |
407 | // | |
408 | // - badly check if an auto impl candidate definitely does not apply | |
409 | // for the given simplified type | |
410 | // - if so, do not lint | |
411 | if fast_reject_auto_impl(tcx, trait_ref.def_id, self_ty) { | |
412 | // ok | |
413 | return; | |
5099ac24 FG |
414 | } |
415 | ||
064997fb FG |
416 | tcx.struct_span_lint_hir( |
417 | lint::builtin::SUSPICIOUS_AUTO_TRAIT_IMPLS, | |
418 | tcx.hir().local_def_id_to_hir_id(impl_def_id), | |
419 | tcx.def_span(impl_def_id), | |
420 | |err| { | |
421 | let item_span = tcx.def_span(self_type_did); | |
422 | let self_descr = tcx.def_kind(self_type_did).descr(self_type_did); | |
423 | let mut err = err.build(&format!( | |
424 | "cross-crate traits with a default impl, like `{}`, \ | |
5099ac24 | 425 | should not be specialized", |
064997fb FG |
426 | tcx.def_path_str(trait_ref.def_id), |
427 | )); | |
428 | match arg { | |
429 | ty::util::NotUniqueParam::DuplicateParam(arg) => { | |
430 | err.note(&format!("`{}` is mentioned multiple times", arg)); | |
923072b8 | 431 | } |
064997fb FG |
432 | ty::util::NotUniqueParam::NotParam(arg) => { |
433 | err.note(&format!("`{}` is not a generic parameter", arg)); | |
434 | } | |
435 | } | |
436 | err.span_note( | |
437 | item_span, | |
438 | &format!( | |
439 | "try using the same sequence of generic parameters as the {} definition", | |
440 | self_descr, | |
441 | ), | |
442 | ); | |
443 | err.emit(); | |
444 | }, | |
445 | ); | |
5099ac24 FG |
446 | } |
447 | ||
448 | fn fast_reject_auto_impl<'tcx>(tcx: TyCtxt<'tcx>, trait_def_id: DefId, self_ty: Ty<'tcx>) -> bool { | |
449 | struct DisableAutoTraitVisitor<'tcx> { | |
450 | tcx: TyCtxt<'tcx>, | |
451 | trait_def_id: DefId, | |
452 | self_ty_root: Ty<'tcx>, | |
453 | seen: FxHashSet<DefId>, | |
454 | } | |
455 | ||
456 | impl<'tcx> TypeVisitor<'tcx> for DisableAutoTraitVisitor<'tcx> { | |
457 | type BreakTy = (); | |
458 | fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { | |
459 | let tcx = self.tcx; | |
460 | if t != self.self_ty_root { | |
461 | for impl_def_id in tcx.non_blanket_impls_for_ty(self.trait_def_id, t) { | |
462 | match tcx.impl_polarity(impl_def_id) { | |
463 | ImplPolarity::Negative => return ControlFlow::BREAK, | |
464 | ImplPolarity::Reservation => {} | |
465 | // FIXME(@lcnr): That's probably not good enough, idk | |
466 | // | |
467 | // We might just want to take the rustdoc code and somehow avoid | |
468 | // explicit impls for `Self`. | |
469 | ImplPolarity::Positive => return ControlFlow::CONTINUE, | |
470 | } | |
471 | } | |
472 | } | |
473 | ||
474 | match t.kind() { | |
923072b8 | 475 | ty::Adt(def, substs) if def.is_phantom_data() => substs.visit_with(self), |
5099ac24 FG |
476 | ty::Adt(def, substs) => { |
477 | // @lcnr: This is the only place where cycles can happen. We avoid this | |
478 | // by only visiting each `DefId` once. | |
479 | // | |
480 | // This will be is incorrect in subtle cases, but I don't care :) | |
5e7ed085 | 481 | if self.seen.insert(def.did()) { |
5099ac24 FG |
482 | for ty in def.all_fields().map(|field| field.ty(tcx, substs)) { |
483 | ty.visit_with(self)?; | |
484 | } | |
485 | } | |
486 | ||
487 | ControlFlow::CONTINUE | |
488 | } | |
489 | _ => t.super_visit_with(self), | |
490 | } | |
491 | } | |
492 | } | |
493 | ||
494 | let self_ty_root = match self_ty.kind() { | |
5e7ed085 | 495 | ty::Adt(def, _) => tcx.mk_adt(*def, InternalSubsts::identity_for_item(tcx, def.did())), |
5099ac24 FG |
496 | _ => unimplemented!("unexpected self ty {:?}", self_ty), |
497 | }; | |
498 | ||
499 | self_ty_root | |
500 | .visit_with(&mut DisableAutoTraitVisitor { | |
501 | tcx, | |
502 | self_ty_root, | |
503 | trait_def_id, | |
504 | seen: FxHashSet::default(), | |
505 | }) | |
506 | .is_break() | |
507 | } |