]>
Commit | Line | Data |
---|---|---|
1a4d82fc JJ |
1 | //! "Object safety" refers to the ability for a trait to be converted |
2 | //! to an object. In general, traits may only be converted to an | |
3 | //! object if all of their methods meet certain criteria. In particular, | |
4 | //! they must: | |
5 | //! | |
a1dfa0c6 XL |
6 | //! - have a suitable receiver from which we can extract a vtable and coerce to a "thin" version |
7 | //! that doesn't contain the vtable; | |
1a4d82fc | 8 | //! - not reference the erased type `Self` except for in this receiver; |
9fa01778 | 9 | //! - not have generic type parameters. |
1a4d82fc | 10 | |
1a4d82fc JJ |
11 | use super::elaborate_predicates; |
12 | ||
74b04a01 | 13 | use crate::infer::TyCtxtInferExt; |
ba9703b0 | 14 | use crate::traits::query::evaluate_obligation::InferCtxtExt; |
9fa01778 | 15 | use crate::traits::{self, Obligation, ObligationCause}; |
ba9703b0 | 16 | use rustc_errors::{Applicability, FatalError}; |
dfeec247 XL |
17 | use rustc_hir as hir; |
18 | use rustc_hir::def_id::DefId; | |
f9f354fc XL |
19 | use rustc_middle::ty::subst::{GenericArg, InternalSubsts, Subst}; |
20 | use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeVisitor, WithConstness}; | |
21 | use rustc_middle::ty::{Predicate, ToPredicate}; | |
dfeec247 XL |
22 | use rustc_session::lint::builtin::WHERE_CLAUSES_OBJECT_SAFETY; |
23 | use rustc_span::symbol::Symbol; | |
74b04a01 XL |
24 | use rustc_span::Span; |
25 | use smallvec::SmallVec; | |
dfeec247 | 26 | |
74b04a01 | 27 | use std::iter; |
1a4d82fc | 28 | |
74b04a01 | 29 | pub use crate::traits::{MethodViolationCode, ObjectSafetyViolation}; |
1a4d82fc | 30 | |
dfeec247 XL |
31 | /// Returns the object safety violations that affect |
32 | /// astconv -- currently, `Self` in supertraits. This is needed | |
33 | /// because `object_safety_violations` can't be used during | |
34 | /// type collection. | |
35 | pub fn astconv_object_safety_violations( | |
36 | tcx: TyCtxt<'_>, | |
37 | trait_def_id: DefId, | |
38 | ) -> Vec<ObjectSafetyViolation> { | |
39 | debug_assert!(tcx.generics_of(trait_def_id).has_self); | |
40 | let violations = traits::supertrait_def_ids(tcx, trait_def_id) | |
74b04a01 XL |
41 | .map(|def_id| predicates_reference_self(tcx, def_id, true)) |
42 | .filter(|spans| !spans.is_empty()) | |
ba9703b0 | 43 | .map(ObjectSafetyViolation::SupertraitSelf) |
dfeec247 XL |
44 | .collect(); |
45 | ||
46 | debug!("astconv_object_safety_violations(trait_def_id={:?}) = {:?}", trait_def_id, violations); | |
47 | ||
48 | violations | |
49 | } | |
1a4d82fc | 50 | |
ba9703b0 XL |
51 | fn object_safety_violations( |
52 | tcx: TyCtxt<'tcx>, | |
53 | trait_def_id: DefId, | |
54 | ) -> &'tcx [ObjectSafetyViolation] { | |
dfeec247 XL |
55 | debug_assert!(tcx.generics_of(trait_def_id).has_self); |
56 | debug!("object_safety_violations: {:?}", trait_def_id); | |
1a4d82fc | 57 | |
ba9703b0 XL |
58 | tcx.arena.alloc_from_iter( |
59 | traits::supertrait_def_ids(tcx, trait_def_id) | |
60 | .flat_map(|def_id| object_safety_violations_for_trait(tcx, def_id)), | |
61 | ) | |
dfeec247 | 62 | } |
a7813a04 | 63 | |
dfeec247 XL |
64 | /// We say a method is *vtable safe* if it can be invoked on a trait |
65 | /// object. Note that object-safe traits can have some | |
66 | /// non-vtable-safe methods, so long as they require `Self: Sized` or | |
67 | /// otherwise ensure that they cannot be used when `Self = Trait`. | |
68 | pub fn is_vtable_safe_method(tcx: TyCtxt<'_>, trait_def_id: DefId, method: &ty::AssocItem) -> bool { | |
69 | debug_assert!(tcx.generics_of(trait_def_id).has_self); | |
70 | debug!("is_vtable_safe_method({:?}, {:?})", trait_def_id, method); | |
71 | // Any method that has a `Self: Sized` bound cannot be called. | |
72 | if generics_require_sized_self(tcx, method.def_id) { | |
73 | return false; | |
74 | } | |
a1dfa0c6 | 75 | |
dfeec247 XL |
76 | match virtual_call_violation_for_method(tcx, trait_def_id, method) { |
77 | None | Some(MethodViolationCode::WhereClauseReferencesSelf) => true, | |
78 | Some(_) => false, | |
85aaf69f | 79 | } |
dfeec247 | 80 | } |
1a4d82fc | 81 | |
dfeec247 XL |
82 | fn object_safety_violations_for_trait( |
83 | tcx: TyCtxt<'_>, | |
84 | trait_def_id: DefId, | |
85 | ) -> Vec<ObjectSafetyViolation> { | |
86 | // Check methods for violations. | |
87 | let mut violations: Vec<_> = tcx | |
88 | .associated_items(trait_def_id) | |
74b04a01 | 89 | .in_definition_order() |
ba9703b0 | 90 | .filter(|item| item.kind == ty::AssocKind::Fn) |
dfeec247 XL |
91 | .filter_map(|item| { |
92 | object_safety_violation_for_method(tcx, trait_def_id, &item) | |
74b04a01 | 93 | .map(|(code, span)| ObjectSafetyViolation::Method(item.ident.name, code, span)) |
dfeec247 XL |
94 | }) |
95 | .filter(|violation| { | |
96 | if let ObjectSafetyViolation::Method( | |
97 | _, | |
98 | MethodViolationCode::WhereClauseReferencesSelf, | |
99 | span, | |
100 | ) = violation | |
101 | { | |
102 | // Using `CRATE_NODE_ID` is wrong, but it's hard to get a more precise id. | |
103 | // It's also hard to get a use site span, so we use the method definition span. | |
104 | tcx.struct_span_lint_hir( | |
105 | WHERE_CLAUSES_OBJECT_SAFETY, | |
106 | hir::CRATE_HIR_ID, | |
107 | *span, | |
74b04a01 XL |
108 | |lint| { |
109 | let mut err = lint.build(&format!( | |
110 | "the trait `{}` cannot be made into an object", | |
111 | tcx.def_path_str(trait_def_id) | |
112 | )); | |
113 | let node = tcx.hir().get_if_local(trait_def_id); | |
114 | let msg = if let Some(hir::Node::Item(item)) = node { | |
115 | err.span_label( | |
116 | item.ident.span, | |
117 | "this trait cannot be made into an object...", | |
118 | ); | |
119 | format!("...because {}", violation.error_msg()) | |
120 | } else { | |
121 | format!( | |
122 | "the trait cannot be made into an object because {}", | |
123 | violation.error_msg() | |
124 | ) | |
125 | }; | |
126 | err.span_label(*span, &msg); | |
127 | match (node, violation.solution()) { | |
128 | (Some(_), Some((note, None))) => { | |
129 | err.help(¬e); | |
130 | } | |
131 | (Some(_), Some((note, Some((sugg, span))))) => { | |
132 | err.span_suggestion( | |
133 | span, | |
134 | ¬e, | |
135 | sugg, | |
136 | Applicability::MachineApplicable, | |
137 | ); | |
138 | } | |
139 | // Only provide the help if its a local trait, otherwise it's not actionable. | |
140 | _ => {} | |
141 | } | |
142 | err.emit(); | |
143 | }, | |
144 | ); | |
dfeec247 XL |
145 | false |
146 | } else { | |
147 | true | |
148 | } | |
149 | }) | |
150 | .collect(); | |
e1599b0c | 151 | |
dfeec247 XL |
152 | // Check the trait itself. |
153 | if trait_has_sized_self(tcx, trait_def_id) { | |
74b04a01 XL |
154 | // We don't want to include the requirement from `Sized` itself to be `Sized` in the list. |
155 | let spans = get_sized_bounds(tcx, trait_def_id); | |
156 | violations.push(ObjectSafetyViolation::SizedSelf(spans)); | |
dfeec247 | 157 | } |
74b04a01 XL |
158 | let spans = predicates_reference_self(tcx, trait_def_id, false); |
159 | if !spans.is_empty() { | |
160 | violations.push(ObjectSafetyViolation::SupertraitSelf(spans)); | |
e1599b0c XL |
161 | } |
162 | ||
dfeec247 XL |
163 | violations.extend( |
164 | tcx.associated_items(trait_def_id) | |
74b04a01 | 165 | .in_definition_order() |
dfeec247 XL |
166 | .filter(|item| item.kind == ty::AssocKind::Const) |
167 | .map(|item| ObjectSafetyViolation::AssocConst(item.ident.name, item.ident.span)), | |
168 | ); | |
169 | ||
170 | debug!( | |
171 | "object_safety_violations_for_trait(trait_def_id={:?}) = {:?}", | |
172 | trait_def_id, violations | |
173 | ); | |
174 | ||
175 | violations | |
176 | } | |
177 | ||
ba9703b0 XL |
178 | fn sized_trait_bound_spans<'tcx>( |
179 | tcx: TyCtxt<'tcx>, | |
180 | bounds: hir::GenericBounds<'tcx>, | |
181 | ) -> impl 'tcx + Iterator<Item = Span> { | |
182 | bounds.iter().filter_map(move |b| match b { | |
183 | hir::GenericBound::Trait(trait_ref, hir::TraitBoundModifier::None) | |
184 | if trait_has_sized_self( | |
185 | tcx, | |
186 | trait_ref.trait_ref.trait_def_id().unwrap_or_else(|| FatalError.raise()), | |
187 | ) => | |
188 | { | |
189 | // Fetch spans for supertraits that are `Sized`: `trait T: Super` | |
190 | Some(trait_ref.span) | |
191 | } | |
192 | _ => None, | |
193 | }) | |
194 | } | |
195 | ||
74b04a01 XL |
196 | fn get_sized_bounds(tcx: TyCtxt<'_>, trait_def_id: DefId) -> SmallVec<[Span; 1]> { |
197 | tcx.hir() | |
198 | .get_if_local(trait_def_id) | |
199 | .and_then(|node| match node { | |
200 | hir::Node::Item(hir::Item { | |
201 | kind: hir::ItemKind::Trait(.., generics, bounds, _), | |
202 | .. | |
203 | }) => Some( | |
204 | generics | |
205 | .where_clause | |
206 | .predicates | |
207 | .iter() | |
208 | .filter_map(|pred| { | |
209 | match pred { | |
210 | hir::WherePredicate::BoundPredicate(pred) | |
ba9703b0 | 211 | if pred.bounded_ty.hir_id.owner.to_def_id() == trait_def_id => |
74b04a01 XL |
212 | { |
213 | // Fetch spans for trait bounds that are Sized: | |
214 | // `trait T where Self: Pred` | |
ba9703b0 | 215 | Some(sized_trait_bound_spans(tcx, pred.bounds)) |
74b04a01 XL |
216 | } |
217 | _ => None, | |
218 | } | |
219 | }) | |
220 | .flatten() | |
ba9703b0 XL |
221 | // Fetch spans for supertraits that are `Sized`: `trait T: Super`. |
222 | .chain(sized_trait_bound_spans(tcx, bounds)) | |
74b04a01 XL |
223 | .collect::<SmallVec<[Span; 1]>>(), |
224 | ), | |
225 | _ => None, | |
226 | }) | |
227 | .unwrap_or_else(SmallVec::new) | |
228 | } | |
229 | ||
230 | fn predicates_reference_self( | |
231 | tcx: TyCtxt<'_>, | |
232 | trait_def_id: DefId, | |
233 | supertraits_only: bool, | |
234 | ) -> SmallVec<[Span; 1]> { | |
dfeec247 XL |
235 | let trait_ref = ty::Binder::dummy(ty::TraitRef::identity(tcx, trait_def_id)); |
236 | let predicates = if supertraits_only { | |
237 | tcx.super_predicates_of(trait_def_id) | |
238 | } else { | |
239 | tcx.predicates_of(trait_def_id) | |
240 | }; | |
241 | let self_ty = tcx.types.self_param; | |
ba9703b0 | 242 | let has_self_ty = |arg: &GenericArg<'_>| arg.walk().any(|arg| arg == self_ty.into()); |
dfeec247 XL |
243 | predicates |
244 | .predicates | |
245 | .iter() | |
74b04a01 XL |
246 | .map(|(predicate, sp)| (predicate.subst_supertrait(tcx, &trait_ref), sp)) |
247 | .filter_map(|(predicate, &sp)| { | |
3dfed10e XL |
248 | match predicate.skip_binders() { |
249 | ty::PredicateAtom::Trait(ref data, _) => { | |
dfeec247 | 250 | // In the case of a trait predicate, we can skip the "self" type. |
3dfed10e | 251 | if data.trait_ref.substs[1..].iter().any(has_self_ty) { Some(sp) } else { None } |
8faf50e0 | 252 | } |
3dfed10e | 253 | ty::PredicateAtom::Projection(ref data) => { |
dfeec247 XL |
254 | // And similarly for projections. This should be redundant with |
255 | // the previous check because any projection should have a | |
256 | // matching `Trait` predicate with the same inputs, but we do | |
257 | // the check to be safe. | |
258 | // | |
259 | // Note that we *do* allow projection *outputs* to contain | |
260 | // `self` (i.e., `trait Foo: Bar<Output=Self::Result> { type Result; }`), | |
261 | // we just require the user to specify *both* outputs | |
262 | // in the object type (i.e., `dyn Foo<Output=(), Result=()>`). | |
263 | // | |
264 | // This is ALT2 in issue #56288, see that for discussion of the | |
265 | // possible alternatives. | |
3dfed10e | 266 | if data.projection_ty.trait_ref(tcx).substs[1..].iter().any(has_self_ty) { |
74b04a01 XL |
267 | Some(sp) |
268 | } else { | |
269 | None | |
270 | } | |
dfeec247 | 271 | } |
3dfed10e XL |
272 | ty::PredicateAtom::WellFormed(..) |
273 | | ty::PredicateAtom::ObjectSafe(..) | |
274 | | ty::PredicateAtom::TypeOutlives(..) | |
275 | | ty::PredicateAtom::RegionOutlives(..) | |
276 | | ty::PredicateAtom::ClosureKind(..) | |
277 | | ty::PredicateAtom::Subtype(..) | |
278 | | ty::PredicateAtom::ConstEvaluatable(..) | |
1b1a35ee XL |
279 | | ty::PredicateAtom::ConstEquate(..) |
280 | | ty::PredicateAtom::TypeWellFormedFromEnv(..) => None, | |
dfeec247 XL |
281 | } |
282 | }) | |
74b04a01 | 283 | .collect() |
dfeec247 | 284 | } |
1a4d82fc | 285 | |
dfeec247 XL |
286 | fn trait_has_sized_self(tcx: TyCtxt<'_>, trait_def_id: DefId) -> bool { |
287 | generics_require_sized_self(tcx, trait_def_id) | |
288 | } | |
289 | ||
290 | fn generics_require_sized_self(tcx: TyCtxt<'_>, def_id: DefId) -> bool { | |
291 | let sized_def_id = match tcx.lang_items().sized_trait() { | |
292 | Some(def_id) => def_id, | |
293 | None => { | |
294 | return false; /* No Sized trait, can't require it! */ | |
a7813a04 | 295 | } |
dfeec247 XL |
296 | }; |
297 | ||
298 | // Search for a predicate like `Self : Sized` amongst the trait bounds. | |
299 | let predicates = tcx.predicates_of(def_id); | |
300 | let predicates = predicates.instantiate_identity(tcx).predicates; | |
f9f354fc | 301 | elaborate_predicates(tcx, predicates.into_iter()).any(|obligation| { |
3dfed10e XL |
302 | match obligation.predicate.skip_binders() { |
303 | ty::PredicateAtom::Trait(ref trait_pred, _) => { | |
304 | trait_pred.def_id() == sized_def_id && trait_pred.self_ty().is_param(0) | |
f9f354fc | 305 | } |
3dfed10e XL |
306 | ty::PredicateAtom::Projection(..) |
307 | | ty::PredicateAtom::Subtype(..) | |
308 | | ty::PredicateAtom::RegionOutlives(..) | |
309 | | ty::PredicateAtom::WellFormed(..) | |
310 | | ty::PredicateAtom::ObjectSafe(..) | |
311 | | ty::PredicateAtom::ClosureKind(..) | |
312 | | ty::PredicateAtom::TypeOutlives(..) | |
313 | | ty::PredicateAtom::ConstEvaluatable(..) | |
1b1a35ee XL |
314 | | ty::PredicateAtom::ConstEquate(..) |
315 | | ty::PredicateAtom::TypeWellFormedFromEnv(..) => false, | |
a7813a04 | 316 | } |
dfeec247 XL |
317 | }) |
318 | } | |
1a4d82fc | 319 | |
dfeec247 XL |
320 | /// Returns `Some(_)` if this method makes the containing trait not object safe. |
321 | fn object_safety_violation_for_method( | |
322 | tcx: TyCtxt<'_>, | |
323 | trait_def_id: DefId, | |
324 | method: &ty::AssocItem, | |
74b04a01 | 325 | ) -> Option<(MethodViolationCode, Span)> { |
dfeec247 XL |
326 | debug!("object_safety_violation_for_method({:?}, {:?})", trait_def_id, method); |
327 | // Any method that has a `Self : Sized` requisite is otherwise | |
328 | // exempt from the regulations. | |
329 | if generics_require_sized_self(tcx, method.def_id) { | |
330 | return None; | |
331 | } | |
cc61c64b | 332 | |
74b04a01 XL |
333 | let violation = virtual_call_violation_for_method(tcx, trait_def_id, method); |
334 | // Get an accurate span depending on the violation. | |
335 | violation.map(|v| { | |
336 | let node = tcx.hir().get_if_local(method.def_id); | |
337 | let span = match (v, node) { | |
338 | (MethodViolationCode::ReferencesSelfInput(arg), Some(node)) => node | |
339 | .fn_decl() | |
340 | .and_then(|decl| decl.inputs.get(arg + 1)) | |
341 | .map_or(method.ident.span, |arg| arg.span), | |
342 | (MethodViolationCode::UndispatchableReceiver, Some(node)) => node | |
343 | .fn_decl() | |
344 | .and_then(|decl| decl.inputs.get(0)) | |
345 | .map_or(method.ident.span, |arg| arg.span), | |
346 | (MethodViolationCode::ReferencesSelfOutput, Some(node)) => { | |
347 | node.fn_decl().map_or(method.ident.span, |decl| decl.output.span()) | |
348 | } | |
349 | _ => method.ident.span, | |
350 | }; | |
351 | (v, span) | |
352 | }) | |
dfeec247 | 353 | } |
85aaf69f | 354 | |
dfeec247 XL |
355 | /// Returns `Some(_)` if this method cannot be called on a trait |
356 | /// object; this does not necessarily imply that the enclosing trait | |
357 | /// is not object safe, because the method might have a where clause | |
358 | /// `Self:Sized`. | |
359 | fn virtual_call_violation_for_method<'tcx>( | |
360 | tcx: TyCtxt<'tcx>, | |
361 | trait_def_id: DefId, | |
362 | method: &ty::AssocItem, | |
363 | ) -> Option<MethodViolationCode> { | |
364 | // The method's first parameter must be named `self` | |
ba9703b0 | 365 | if !method.fn_has_self_parameter { |
74b04a01 XL |
366 | // We'll attempt to provide a structured suggestion for `Self: Sized`. |
367 | let sugg = | |
368 | tcx.hir().get_if_local(method.def_id).as_ref().and_then(|node| node.generics()).map( | |
369 | |generics| match generics.where_clause.predicates { | |
370 | [] => (" where Self: Sized", generics.where_clause.span), | |
371 | [.., pred] => (", Self: Sized", pred.span().shrink_to_hi()), | |
372 | }, | |
373 | ); | |
374 | return Some(MethodViolationCode::StaticMethod(sugg)); | |
a7813a04 | 375 | } |
c34b1796 | 376 | |
dfeec247 XL |
377 | let sig = tcx.fn_sig(method.def_id); |
378 | ||
74b04a01 | 379 | for (i, input_ty) in sig.skip_binder().inputs()[1..].iter().enumerate() { |
dfeec247 | 380 | if contains_illegal_self_type_reference(tcx, trait_def_id, input_ty) { |
74b04a01 | 381 | return Some(MethodViolationCode::ReferencesSelfInput(i)); |
dfeec247 XL |
382 | } |
383 | } | |
384 | if contains_illegal_self_type_reference(tcx, trait_def_id, sig.output().skip_binder()) { | |
74b04a01 | 385 | return Some(MethodViolationCode::ReferencesSelfOutput); |
c34b1796 AL |
386 | } |
387 | ||
dfeec247 XL |
388 | // We can't monomorphize things like `fn foo<A>(...)`. |
389 | let own_counts = tcx.generics_of(method.def_id).own_counts(); | |
390 | if own_counts.types + own_counts.consts != 0 { | |
391 | return Some(MethodViolationCode::Generic); | |
a7813a04 | 392 | } |
1a4d82fc | 393 | |
dfeec247 XL |
394 | if tcx |
395 | .predicates_of(method.def_id) | |
396 | .predicates | |
397 | .iter() | |
398 | // A trait object can't claim to live more than the concrete type, | |
399 | // so outlives predicates will always hold. | |
400 | .cloned() | |
401 | .filter(|(p, _)| p.to_opt_type_outlives().is_none()) | |
402 | .collect::<Vec<_>>() | |
403 | // Do a shallow visit so that `contains_illegal_self_type_reference` | |
404 | // may apply it's custom visiting. | |
405 | .visit_tys_shallow(|t| contains_illegal_self_type_reference(tcx, trait_def_id, t)) | |
406 | { | |
407 | return Some(MethodViolationCode::WhereClauseReferencesSelf); | |
408 | } | |
409 | ||
410 | let receiver_ty = | |
411 | tcx.liberate_late_bound_regions(method.def_id, &sig.map_bound(|sig| sig.inputs()[0])); | |
412 | ||
413 | // Until `unsized_locals` is fully implemented, `self: Self` can't be dispatched on. | |
414 | // However, this is already considered object-safe. We allow it as a special case here. | |
415 | // FIXME(mikeyhew) get rid of this `if` statement once `receiver_is_dispatchable` allows | |
416 | // `Receiver: Unsize<Receiver[Self => dyn Trait]>`. | |
417 | if receiver_ty != tcx.types.self_param { | |
418 | if !receiver_is_dispatchable(tcx, method, receiver_ty) { | |
419 | return Some(MethodViolationCode::UndispatchableReceiver); | |
420 | } else { | |
421 | // Do sanity check to make sure the receiver actually has the layout of a pointer. | |
a7813a04 | 422 | |
ba9703b0 | 423 | use rustc_target::abi::Abi; |
dfeec247 XL |
424 | |
425 | let param_env = tcx.param_env(method.def_id); | |
426 | ||
1b1a35ee | 427 | let abi_of_ty = |ty: Ty<'tcx>| -> Option<&Abi> { |
dfeec247 | 428 | match tcx.layout_of(param_env.and(ty)) { |
1b1a35ee XL |
429 | Ok(layout) => Some(&layout.abi), |
430 | Err(err) => { | |
431 | // #78372 | |
432 | tcx.sess.delay_span_bug( | |
433 | tcx.def_span(method.def_id), | |
434 | &format!("error: {}\n while computing layout for type {:?}", err, ty), | |
435 | ); | |
436 | None | |
437 | } | |
a7813a04 | 438 | } |
dfeec247 XL |
439 | }; |
440 | ||
441 | // e.g., `Rc<()>` | |
442 | let unit_receiver_ty = | |
443 | receiver_for_self_ty(tcx, receiver_ty, tcx.mk_unit(), method.def_id); | |
444 | ||
445 | match abi_of_ty(unit_receiver_ty) { | |
1b1a35ee | 446 | Some(Abi::Scalar(..)) => (), |
dfeec247 XL |
447 | abi => { |
448 | tcx.sess.delay_span_bug( | |
449 | tcx.def_span(method.def_id), | |
450 | &format!( | |
451 | "receiver when `Self = ()` should have a Scalar ABI; found {:?}", | |
452 | abi | |
453 | ), | |
454 | ); | |
0bf4aa26 XL |
455 | } |
456 | } | |
c34b1796 | 457 | |
dfeec247 XL |
458 | let trait_object_ty = |
459 | object_ty_for_trait(tcx, trait_def_id, tcx.mk_region(ty::ReStatic)); | |
1a4d82fc | 460 | |
dfeec247 XL |
461 | // e.g., `Rc<dyn Trait>` |
462 | let trait_object_receiver = | |
463 | receiver_for_self_ty(tcx, receiver_ty, trait_object_ty, method.def_id); | |
1a4d82fc | 464 | |
dfeec247 | 465 | match abi_of_ty(trait_object_receiver) { |
1b1a35ee | 466 | Some(Abi::ScalarPair(..)) => (), |
dfeec247 XL |
467 | abi => { |
468 | tcx.sess.delay_span_bug( | |
469 | tcx.def_span(method.def_id), | |
470 | &format!( | |
1b1a35ee | 471 | "receiver when `Self = {}` should have a ScalarPair ABI; found {:?}", |
dfeec247 XL |
472 | trait_object_ty, abi |
473 | ), | |
474 | ); | |
475 | } | |
476 | } | |
a7813a04 | 477 | } |
dfeec247 | 478 | } |
1a4d82fc | 479 | |
dfeec247 XL |
480 | None |
481 | } | |
abe05a73 | 482 | |
dfeec247 XL |
483 | /// Performs a type substitution to produce the version of `receiver_ty` when `Self = self_ty`. |
484 | /// For example, for `receiver_ty = Rc<Self>` and `self_ty = Foo`, returns `Rc<Foo>`. | |
485 | fn receiver_for_self_ty<'tcx>( | |
486 | tcx: TyCtxt<'tcx>, | |
487 | receiver_ty: Ty<'tcx>, | |
488 | self_ty: Ty<'tcx>, | |
489 | method_def_id: DefId, | |
490 | ) -> Ty<'tcx> { | |
491 | debug!("receiver_for_self_ty({:?}, {:?}, {:?})", receiver_ty, self_ty, method_def_id); | |
492 | let substs = InternalSubsts::for_item(tcx, method_def_id, |param, _| { | |
493 | if param.index == 0 { self_ty.into() } else { tcx.mk_param_from_def(param) } | |
494 | }); | |
495 | ||
496 | let result = receiver_ty.subst(tcx, substs); | |
497 | debug!( | |
498 | "receiver_for_self_ty({:?}, {:?}, {:?}) = {:?}", | |
499 | receiver_ty, self_ty, method_def_id, result | |
500 | ); | |
501 | result | |
502 | } | |
1a4d82fc | 503 | |
dfeec247 XL |
504 | /// Creates the object type for the current trait. For example, |
505 | /// if the current trait is `Deref`, then this will be | |
506 | /// `dyn Deref<Target = Self::Target> + 'static`. | |
507 | fn object_ty_for_trait<'tcx>( | |
508 | tcx: TyCtxt<'tcx>, | |
509 | trait_def_id: DefId, | |
510 | lifetime: ty::Region<'tcx>, | |
511 | ) -> Ty<'tcx> { | |
512 | debug!("object_ty_for_trait: trait_def_id={:?}", trait_def_id); | |
1a4d82fc | 513 | |
dfeec247 | 514 | let trait_ref = ty::TraitRef::identity(tcx, trait_def_id); |
8faf50e0 | 515 | |
dfeec247 XL |
516 | let trait_predicate = |
517 | ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref)); | |
a1dfa0c6 | 518 | |
dfeec247 XL |
519 | let mut associated_types = traits::supertraits(tcx, ty::Binder::dummy(trait_ref)) |
520 | .flat_map(|super_trait_ref| { | |
74b04a01 XL |
521 | tcx.associated_items(super_trait_ref.def_id()) |
522 | .in_definition_order() | |
523 | .map(move |item| (super_trait_ref, item)) | |
dfeec247 XL |
524 | }) |
525 | .filter(|(_, item)| item.kind == ty::AssocKind::Type) | |
526 | .collect::<Vec<_>>(); | |
a1dfa0c6 | 527 | |
dfeec247 XL |
528 | // existential predicates need to be in a specific order |
529 | associated_types.sort_by_cached_key(|(_, item)| tcx.def_path_hash(item.def_id)); | |
a1dfa0c6 | 530 | |
dfeec247 XL |
531 | let projection_predicates = associated_types.into_iter().map(|(super_trait_ref, item)| { |
532 | // We *can* get bound lifetimes here in cases like | |
533 | // `trait MyTrait: for<'s> OtherTrait<&'s T, Output=bool>`. | |
534 | // | |
535 | // binder moved to (*)... | |
536 | let super_trait_ref = super_trait_ref.skip_binder(); | |
537 | ty::ExistentialPredicate::Projection(ty::ExistentialProjection { | |
538 | ty: tcx.mk_projection(item.def_id, super_trait_ref.substs), | |
539 | item_def_id: item.def_id, | |
540 | substs: super_trait_ref.substs, | |
541 | }) | |
542 | }); | |
a1dfa0c6 | 543 | |
dfeec247 XL |
544 | let existential_predicates = |
545 | tcx.mk_existential_predicates(iter::once(trait_predicate).chain(projection_predicates)); | |
a1dfa0c6 | 546 | |
dfeec247 XL |
547 | let object_ty = tcx.mk_dynamic( |
548 | // (*) ... binder re-introduced here | |
549 | ty::Binder::bind(existential_predicates), | |
550 | lifetime, | |
551 | ); | |
a7813a04 | 552 | |
dfeec247 | 553 | debug!("object_ty_for_trait: object_ty=`{}`", object_ty); |
a1dfa0c6 | 554 | |
dfeec247 XL |
555 | object_ty |
556 | } | |
a1dfa0c6 | 557 | |
dfeec247 XL |
558 | /// Checks the method's receiver (the `self` argument) can be dispatched on when `Self` is a |
559 | /// trait object. We require that `DispatchableFromDyn` be implemented for the receiver type | |
560 | /// in the following way: | |
561 | /// - let `Receiver` be the type of the `self` argument, i.e `Self`, `&Self`, `Rc<Self>`, | |
562 | /// - require the following bound: | |
563 | /// | |
564 | /// ``` | |
565 | /// Receiver[Self => T]: DispatchFromDyn<Receiver[Self => dyn Trait]> | |
566 | /// ``` | |
567 | /// | |
568 | /// where `Foo[X => Y]` means "the same type as `Foo`, but with `X` replaced with `Y`" | |
569 | /// (substitution notation). | |
570 | /// | |
571 | /// Some examples of receiver types and their required obligation: | |
572 | /// - `&'a mut self` requires `&'a mut Self: DispatchFromDyn<&'a mut dyn Trait>`, | |
573 | /// - `self: Rc<Self>` requires `Rc<Self>: DispatchFromDyn<Rc<dyn Trait>>`, | |
574 | /// - `self: Pin<Box<Self>>` requires `Pin<Box<Self>>: DispatchFromDyn<Pin<Box<dyn Trait>>>`. | |
575 | /// | |
576 | /// The only case where the receiver is not dispatchable, but is still a valid receiver | |
577 | /// type (just not object-safe), is when there is more than one level of pointer indirection. | |
578 | /// E.g., `self: &&Self`, `self: &Rc<Self>`, `self: Box<Box<Self>>`. In these cases, there | |
579 | /// is no way, or at least no inexpensive way, to coerce the receiver from the version where | |
580 | /// `Self = dyn Trait` to the version where `Self = T`, where `T` is the unknown erased type | |
581 | /// contained by the trait object, because the object that needs to be coerced is behind | |
582 | /// a pointer. | |
583 | /// | |
584 | /// In practice, we cannot use `dyn Trait` explicitly in the obligation because it would result | |
585 | /// in a new check that `Trait` is object safe, creating a cycle (until object_safe_for_dispatch | |
586 | /// is stabilized, see tracking issue https://github.com/rust-lang/rust/issues/43561). | |
587 | /// Instead, we fudge a little by introducing a new type parameter `U` such that | |
588 | /// `Self: Unsize<U>` and `U: Trait + ?Sized`, and use `U` in place of `dyn Trait`. | |
589 | /// Written as a chalk-style query: | |
590 | /// | |
591 | /// forall (U: Trait + ?Sized) { | |
592 | /// if (Self: Unsize<U>) { | |
593 | /// Receiver: DispatchFromDyn<Receiver[Self => U]> | |
594 | /// } | |
595 | /// } | |
596 | /// | |
597 | /// for `self: &'a mut Self`, this means `&'a mut Self: DispatchFromDyn<&'a mut U>` | |
598 | /// for `self: Rc<Self>`, this means `Rc<Self>: DispatchFromDyn<Rc<U>>` | |
599 | /// for `self: Pin<Box<Self>>`, this means `Pin<Box<Self>>: DispatchFromDyn<Pin<Box<U>>>` | |
600 | // | |
601 | // FIXME(mikeyhew) when unsized receivers are implemented as part of unsized rvalues, add this | |
602 | // fallback query: `Receiver: Unsize<Receiver[Self => U]>` to support receivers like | |
603 | // `self: Wrapper<Self>`. | |
604 | #[allow(dead_code)] | |
605 | fn receiver_is_dispatchable<'tcx>( | |
606 | tcx: TyCtxt<'tcx>, | |
607 | method: &ty::AssocItem, | |
608 | receiver_ty: Ty<'tcx>, | |
609 | ) -> bool { | |
610 | debug!("receiver_is_dispatchable: method = {:?}, receiver_ty = {:?}", method, receiver_ty); | |
611 | ||
612 | let traits = (tcx.lang_items().unsize_trait(), tcx.lang_items().dispatch_from_dyn_trait()); | |
613 | let (unsize_did, dispatch_from_dyn_did) = if let (Some(u), Some(cu)) = traits { | |
614 | (u, cu) | |
615 | } else { | |
616 | debug!("receiver_is_dispatchable: Missing Unsize or DispatchFromDyn traits"); | |
617 | return false; | |
618 | }; | |
619 | ||
620 | // the type `U` in the query | |
74b04a01 | 621 | // use a bogus type parameter to mimic a forall(U) query using u32::MAX for now. |
dfeec247 XL |
622 | // FIXME(mikeyhew) this is a total hack. Once object_safe_for_dispatch is stabilized, we can |
623 | // replace this with `dyn Trait` | |
624 | let unsized_self_ty: Ty<'tcx> = | |
ba9703b0 | 625 | tcx.mk_ty_param(u32::MAX, Symbol::intern("RustaceansAreAwesome")); |
dfeec247 XL |
626 | |
627 | // `Receiver[Self => U]` | |
628 | let unsized_receiver_ty = | |
629 | receiver_for_self_ty(tcx, receiver_ty, unsized_self_ty, method.def_id); | |
630 | ||
631 | // create a modified param env, with `Self: Unsize<U>` and `U: Trait` added to caller bounds | |
632 | // `U: ?Sized` is already implied here | |
633 | let param_env = { | |
f035d41b | 634 | let param_env = tcx.param_env(method.def_id); |
dfeec247 XL |
635 | |
636 | // Self: Unsize<U> | |
637 | let unsize_predicate = ty::TraitRef { | |
638 | def_id: unsize_did, | |
639 | substs: tcx.mk_substs_trait(tcx.types.self_param, &[unsized_self_ty.into()]), | |
640 | } | |
641 | .without_const() | |
f9f354fc | 642 | .to_predicate(tcx); |
dfeec247 XL |
643 | |
644 | // U: Trait<Arg1, ..., ArgN> | |
645 | let trait_predicate = { | |
646 | let substs = | |
647 | InternalSubsts::for_item(tcx, method.container.assert_trait(), |param, _| { | |
648 | if param.index == 0 { | |
649 | unsized_self_ty.into() | |
650 | } else { | |
651 | tcx.mk_param_from_def(param) | |
652 | } | |
653 | }); | |
a1dfa0c6 | 654 | |
f9f354fc | 655 | ty::TraitRef { def_id: unsize_did, substs }.without_const().to_predicate(tcx) |
a1dfa0c6 XL |
656 | }; |
657 | ||
dfeec247 | 658 | let caller_bounds: Vec<Predicate<'tcx>> = param_env |
f035d41b | 659 | .caller_bounds() |
dfeec247 | 660 | .iter() |
dfeec247 XL |
661 | .chain(iter::once(unsize_predicate)) |
662 | .chain(iter::once(trait_predicate)) | |
663 | .collect(); | |
a1dfa0c6 | 664 | |
1b1a35ee | 665 | ty::ParamEnv::new(tcx.intern_predicates(&caller_bounds), param_env.reveal()) |
dfeec247 | 666 | }; |
a1dfa0c6 | 667 | |
dfeec247 XL |
668 | // Receiver: DispatchFromDyn<Receiver[Self => U]> |
669 | let obligation = { | |
670 | let predicate = ty::TraitRef { | |
671 | def_id: dispatch_from_dyn_did, | |
672 | substs: tcx.mk_substs_trait(receiver_ty, &[unsized_receiver_ty.into()]), | |
673 | } | |
674 | .without_const() | |
f9f354fc | 675 | .to_predicate(tcx); |
a1dfa0c6 | 676 | |
dfeec247 XL |
677 | Obligation::new(ObligationCause::dummy(), param_env, predicate) |
678 | }; | |
a1dfa0c6 | 679 | |
dfeec247 XL |
680 | tcx.infer_ctxt().enter(|ref infcx| { |
681 | // the receiver is dispatchable iff the obligation holds | |
682 | infcx.predicate_must_hold_modulo_regions(&obligation) | |
683 | }) | |
684 | } | |
a7813a04 | 685 | |
dfeec247 XL |
686 | fn contains_illegal_self_type_reference<'tcx>( |
687 | tcx: TyCtxt<'tcx>, | |
688 | trait_def_id: DefId, | |
689 | ty: Ty<'tcx>, | |
690 | ) -> bool { | |
691 | // This is somewhat subtle. In general, we want to forbid | |
692 | // references to `Self` in the argument and return types, | |
693 | // since the value of `Self` is erased. However, there is one | |
694 | // exception: it is ok to reference `Self` in order to access | |
695 | // an associated type of the current trait, since we retain | |
696 | // the value of those associated types in the object type | |
697 | // itself. | |
698 | // | |
699 | // ```rust | |
700 | // trait SuperTrait { | |
701 | // type X; | |
702 | // } | |
703 | // | |
704 | // trait Trait : SuperTrait { | |
705 | // type Y; | |
706 | // fn foo(&self, x: Self) // bad | |
707 | // fn foo(&self) -> Self // bad | |
708 | // fn foo(&self) -> Option<Self> // bad | |
709 | // fn foo(&self) -> Self::Y // OK, desugars to next example | |
710 | // fn foo(&self) -> <Self as Trait>::Y // OK | |
711 | // fn foo(&self) -> Self::X // OK, desugars to next example | |
712 | // fn foo(&self) -> <Self as SuperTrait>::X // OK | |
713 | // } | |
714 | // ``` | |
715 | // | |
716 | // However, it is not as simple as allowing `Self` in a projected | |
717 | // type, because there are illegal ways to use `Self` as well: | |
718 | // | |
719 | // ```rust | |
720 | // trait Trait : SuperTrait { | |
721 | // ... | |
722 | // fn foo(&self) -> <Self as SomeOtherTrait>::X; | |
723 | // } | |
724 | // ``` | |
725 | // | |
726 | // Here we will not have the type of `X` recorded in the | |
727 | // object type, and we cannot resolve `Self as SomeOtherTrait` | |
728 | // without knowing what `Self` is. | |
729 | ||
f9f354fc XL |
730 | struct IllegalSelfTypeVisitor<'tcx> { |
731 | tcx: TyCtxt<'tcx>, | |
732 | self_ty: Ty<'tcx>, | |
733 | trait_def_id: DefId, | |
734 | supertraits: Option<Vec<ty::PolyTraitRef<'tcx>>>, | |
735 | } | |
a7813a04 | 736 | |
f9f354fc XL |
737 | impl<'tcx> TypeVisitor<'tcx> for IllegalSelfTypeVisitor<'tcx> { |
738 | fn visit_ty(&mut self, t: Ty<'tcx>) -> bool { | |
1b1a35ee | 739 | match t.kind() { |
f9f354fc XL |
740 | ty::Param(_) => t == self.self_ty, |
741 | ty::Projection(ref data) => { | |
742 | // This is a projected type `<Foo as SomeTrait>::X`. | |
743 | ||
744 | // Compute supertraits of current trait lazily. | |
745 | if self.supertraits.is_none() { | |
746 | let trait_ref = | |
747 | ty::Binder::bind(ty::TraitRef::identity(self.tcx, self.trait_def_id)); | |
748 | self.supertraits = Some(traits::supertraits(self.tcx, trait_ref).collect()); | |
749 | } | |
1a4d82fc | 750 | |
f9f354fc XL |
751 | // Determine whether the trait reference `Foo as |
752 | // SomeTrait` is in fact a supertrait of the | |
753 | // current trait. In that case, this type is | |
754 | // legal, because the type `X` will be specified | |
755 | // in the object type. Note that we can just use | |
756 | // direct equality here because all of these types | |
757 | // are part of the formal parameter listing, and | |
758 | // hence there should be no inference variables. | |
759 | let projection_trait_ref = ty::Binder::bind(data.trait_ref(self.tcx)); | |
760 | let is_supertrait_of_current_trait = | |
761 | self.supertraits.as_ref().unwrap().contains(&projection_trait_ref); | |
762 | ||
763 | if is_supertrait_of_current_trait { | |
764 | false // do not walk contained types, do not report error, do collect $200 | |
765 | } else { | |
766 | t.super_visit_with(self) // DO walk contained types, POSSIBLY reporting an error | |
767 | } | |
dfeec247 | 768 | } |
f9f354fc | 769 | _ => t.super_visit_with(self), // walk contained types, if any |
ba9703b0 | 770 | } |
dfeec247 | 771 | } |
dfeec247 | 772 | |
f9f354fc XL |
773 | fn visit_const(&mut self, _c: &ty::Const<'tcx>) -> bool { |
774 | // FIXME(#72219) Look into the unevaluated constants for object safety violations. | |
775 | // Do not walk substitutions of unevaluated consts, as they contain `Self`, even | |
776 | // though the const expression doesn't necessary use it. Currently type variables | |
777 | // inside array length expressions are forbidden, so they can't break the above | |
778 | // rules. | |
779 | false | |
780 | } | |
ba9703b0 XL |
781 | } |
782 | ||
f9f354fc XL |
783 | ty.visit_with(&mut IllegalSelfTypeVisitor { |
784 | tcx, | |
785 | self_ty: tcx.types.self_param, | |
786 | trait_def_id, | |
787 | supertraits: None, | |
788 | }) | |
1a4d82fc | 789 | } |
7cac9316 | 790 | |
f035d41b | 791 | pub fn provide(providers: &mut ty::query::Providers) { |
74b04a01 | 792 | *providers = ty::query::Providers { object_safety_violations, ..*providers }; |
7cac9316 | 793 | } |