rustc_trait_selection/traits/
vtable.rs1use std::fmt::Debug;
2use std::ops::ControlFlow;
3
4use rustc_hir::def_id::DefId;
5use rustc_infer::traits::util::PredicateSet;
6use rustc_middle::bug;
7use rustc_middle::query::Providers;
8use rustc_middle::ty::{
9 self, GenericArgs, GenericParamDefKind, Ty, TyCtxt, TypeVisitableExt, Upcast, VtblEntry,
10};
11use rustc_span::DUMMY_SP;
12use smallvec::{SmallVec, smallvec};
13use tracing::debug;
14
15use crate::traits::{impossible_predicates, is_vtable_safe_method};
16
17#[derive(Clone, Debug)]
18pub enum VtblSegment<'tcx> {
19 MetadataDSA,
20 TraitOwnEntries { trait_ref: ty::TraitRef<'tcx>, emit_vptr: bool },
21}
22
23pub fn prepare_vtable_segments<'tcx, T>(
27 tcx: TyCtxt<'tcx>,
28 trait_ref: ty::TraitRef<'tcx>,
29 segment_visitor: impl FnMut(VtblSegment<'tcx>) -> ControlFlow<T>,
30) -> Option<T> {
31 prepare_vtable_segments_inner(tcx, trait_ref, segment_visitor).break_value()
32}
33
34fn prepare_vtable_segments_inner<'tcx, T>(
37 tcx: TyCtxt<'tcx>,
38 trait_ref: ty::TraitRef<'tcx>,
39 mut segment_visitor: impl FnMut(VtblSegment<'tcx>) -> ControlFlow<T>,
40) -> ControlFlow<T> {
41 segment_visitor(VtblSegment::MetadataDSA)?;
87
88 let mut emit_vptr_on_new_entry = false;
89 let mut visited = PredicateSet::new(tcx);
90 let predicate = trait_ref.upcast(tcx);
91 let mut stack: SmallVec<[(ty::TraitRef<'tcx>, _, _); 5]> =
92 smallvec![(trait_ref, emit_vptr_on_new_entry, maybe_iter(None))];
93 visited.insert(predicate);
94
95 'outer: loop {
119 'diving_in: loop {
121 let &(inner_most_trait_ref, _, _) = stack.last().unwrap();
122
123 let mut direct_super_traits_iter = tcx
124 .explicit_super_predicates_of(inner_most_trait_ref.def_id)
125 .iter_identity_copied()
126 .filter_map(move |(pred, _)| {
127 pred.instantiate_supertrait(tcx, ty::Binder::dummy(inner_most_trait_ref))
128 .as_trait_clause()
129 })
130 .map(move |pred| {
131 tcx.normalize_erasing_late_bound_regions(
132 ty::TypingEnv::fully_monomorphized(),
133 pred,
134 )
135 .trait_ref
136 });
137
138 match direct_super_traits_iter
140 .find(|&super_trait| visited.insert(super_trait.upcast(tcx)))
141 {
142 Some(next_super_trait) => stack.push((
144 next_super_trait,
145 emit_vptr_on_new_entry,
146 maybe_iter(Some(direct_super_traits_iter)),
147 )),
148
149 None => break 'diving_in,
151 }
152 }
153
154 while let Some((inner_most_trait_ref, emit_vptr, mut siblings)) = stack.pop() {
156 let has_entries = ty::elaborate::supertrait_def_ids(tcx, inner_most_trait_ref.def_id)
161 .any(|def_id| has_own_existential_vtable_entries(tcx, def_id));
162
163 segment_visitor(VtblSegment::TraitOwnEntries {
164 trait_ref: inner_most_trait_ref,
165 emit_vptr: emit_vptr && has_entries && !tcx.sess.opts.unstable_opts.no_trait_vptr,
166 })?;
167
168 emit_vptr_on_new_entry |= has_entries;
171
172 if let Some(next_inner_most_trait_ref) =
173 siblings.find(|&sibling| visited.insert(sibling.upcast(tcx)))
174 {
175 stack.push((next_inner_most_trait_ref, emit_vptr_on_new_entry, siblings));
176
177 continue 'outer;
179 }
180 }
181
182 return ControlFlow::Continue(());
184 }
185}
186
187fn maybe_iter<I: Iterator>(i: Option<I>) -> impl Iterator<Item = I::Item> {
189 i.into_iter().flatten()
191}
192
193fn has_own_existential_vtable_entries(tcx: TyCtxt<'_>, trait_def_id: DefId) -> bool {
194 own_existential_vtable_entries_iter(tcx, trait_def_id).next().is_some()
195}
196
197fn own_existential_vtable_entries(tcx: TyCtxt<'_>, trait_def_id: DefId) -> &[DefId] {
198 tcx.arena.alloc_from_iter(own_existential_vtable_entries_iter(tcx, trait_def_id))
199}
200
201fn own_existential_vtable_entries_iter(
202 tcx: TyCtxt<'_>,
203 trait_def_id: DefId,
204) -> impl Iterator<Item = DefId> {
205 let trait_methods =
206 tcx.associated_items(trait_def_id).in_definition_order().filter(|item| item.is_fn());
207
208 let own_entries = trait_methods.filter_map(move |&trait_method| {
210 debug!("own_existential_vtable_entry: trait_method={:?}", trait_method);
211 let def_id = trait_method.def_id;
212
213 if !is_vtable_safe_method(tcx, trait_def_id, trait_method) {
215 debug!("own_existential_vtable_entry: not vtable safe");
216 return None;
217 }
218
219 Some(def_id)
220 });
221
222 own_entries
223}
224
225fn vtable_entries<'tcx>(
228 tcx: TyCtxt<'tcx>,
229 trait_ref: ty::TraitRef<'tcx>,
230) -> &'tcx [VtblEntry<'tcx>] {
231 debug_assert!(!trait_ref.has_non_region_infer() && !trait_ref.has_non_region_param());
232 debug_assert_eq!(
233 tcx.normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), trait_ref),
234 trait_ref,
235 "vtable trait ref should be normalized"
236 );
237
238 debug!("vtable_entries({:?})", trait_ref);
239
240 let mut entries = vec![];
241
242 let vtable_segment_callback = |segment| -> ControlFlow<()> {
243 match segment {
244 VtblSegment::MetadataDSA => {
245 entries.extend(TyCtxt::COMMON_VTABLE_ENTRIES);
246 }
247 VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => {
248 let existential_trait_ref = ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref);
249
250 let own_existential_entries =
252 tcx.own_existential_vtable_entries(existential_trait_ref.def_id);
253
254 let own_entries = own_existential_entries.iter().copied().map(|def_id| {
255 debug!("vtable_entries: trait_method={:?}", def_id);
256
257 let args = tcx.normalize_erasing_regions(
260 ty::TypingEnv::fully_monomorphized(),
261 GenericArgs::for_item(tcx, def_id, |param, _| match param.kind {
262 GenericParamDefKind::Lifetime => tcx.lifetimes.re_erased.into(),
263 GenericParamDefKind::Type { .. }
264 | GenericParamDefKind::Const { .. } => {
265 trait_ref.args[param.index as usize]
266 }
267 }),
268 );
269
270 let predicates = tcx.predicates_of(def_id).instantiate_own(tcx, args);
275 if impossible_predicates(
276 tcx,
277 predicates.map(|(predicate, _)| predicate).collect(),
278 ) {
279 debug!("vtable_entries: predicates do not hold");
280 return VtblEntry::Vacant;
281 }
282
283 let instance = ty::Instance::expect_resolve_for_vtable(
284 tcx,
285 ty::TypingEnv::fully_monomorphized(),
286 def_id,
287 args,
288 DUMMY_SP,
289 );
290
291 VtblEntry::Method(instance)
292 });
293
294 entries.extend(own_entries);
295
296 if emit_vptr {
297 entries.push(VtblEntry::TraitVPtr(trait_ref));
298 }
299 }
300 }
301
302 ControlFlow::Continue(())
303 };
304
305 let _ = prepare_vtable_segments(tcx, trait_ref, vtable_segment_callback);
306
307 tcx.arena.alloc_from_iter(entries)
308}
309
310pub(crate) fn first_method_vtable_slot<'tcx>(tcx: TyCtxt<'tcx>, key: ty::TraitRef<'tcx>) -> usize {
313 debug_assert!(!key.has_non_region_infer() && !key.has_non_region_param());
314 debug_assert_eq!(
315 tcx.normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), key),
316 key,
317 "vtable trait ref should be normalized"
318 );
319
320 let ty::Dynamic(source, _, _) = *key.self_ty().kind() else {
321 bug!();
322 };
323 let source_principal = tcx.instantiate_bound_regions_with_erased(
324 source.principal().unwrap().with_self_ty(tcx, key.self_ty()),
325 );
326
327 if tcx.instantiate_and_check_impossible_predicates((
329 source_principal.def_id,
330 source_principal.args,
331 )) {
332 return 0;
333 }
334
335 let target_principal = ty::ExistentialTraitRef::erase_self_ty(tcx, key);
336
337 let vtable_segment_callback = {
338 let mut vptr_offset = 0;
339 move |segment| {
340 match segment {
341 VtblSegment::MetadataDSA => {
342 vptr_offset += TyCtxt::COMMON_VTABLE_ENTRIES.len();
343 }
344 VtblSegment::TraitOwnEntries { trait_ref: vtable_principal, emit_vptr } => {
345 if ty::ExistentialTraitRef::erase_self_ty(tcx, vtable_principal)
346 == target_principal
347 {
348 return ControlFlow::Break(vptr_offset);
349 }
350
351 vptr_offset +=
352 tcx.own_existential_vtable_entries(vtable_principal.def_id).len();
353
354 if emit_vptr {
355 vptr_offset += 1;
356 }
357 }
358 }
359 ControlFlow::Continue(())
360 }
361 };
362
363 prepare_vtable_segments(tcx, source_principal, vtable_segment_callback).unwrap()
364}
365
366pub(crate) fn supertrait_vtable_slot<'tcx>(
371 tcx: TyCtxt<'tcx>,
372 key: (
373 Ty<'tcx>, Ty<'tcx>, ),
376) -> Option<usize> {
377 debug_assert!(!key.has_non_region_infer() && !key.has_non_region_param());
378 debug_assert_eq!(
379 tcx.normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), key),
380 key,
381 "upcasting trait refs should be normalized"
382 );
383
384 let (source, target) = key;
385
386 let ty::Dynamic(target_data, _, _) = *target.kind() else {
388 bug!();
389 };
390 let target_principal = tcx.instantiate_bound_regions_with_erased(target_data.principal()?);
391
392 let ty::Dynamic(source_data, _, _) = *source.kind() else {
394 bug!();
395 };
396 let source_principal = tcx.instantiate_bound_regions_with_erased(
397 source_data.principal().unwrap().with_self_ty(tcx, source),
398 );
399
400 if tcx.instantiate_and_check_impossible_predicates((
402 source_principal.def_id,
403 source_principal.args,
404 )) {
405 return None;
406 }
407
408 let vtable_segment_callback = {
409 let mut vptr_offset = 0;
410 move |segment| {
411 match segment {
412 VtblSegment::MetadataDSA => {
413 vptr_offset += TyCtxt::COMMON_VTABLE_ENTRIES.len();
414 }
415 VtblSegment::TraitOwnEntries { trait_ref: vtable_principal, emit_vptr } => {
416 vptr_offset +=
417 tcx.own_existential_vtable_entries(vtable_principal.def_id).len();
418 if ty::ExistentialTraitRef::erase_self_ty(tcx, vtable_principal)
419 == target_principal
420 {
421 if emit_vptr {
422 return ControlFlow::Break(Some(vptr_offset));
423 } else {
424 return ControlFlow::Break(None);
425 }
426 }
427
428 if emit_vptr {
429 vptr_offset += 1;
430 }
431 }
432 }
433 ControlFlow::Continue(())
434 }
435 };
436
437 prepare_vtable_segments(tcx, source_principal, vtable_segment_callback).unwrap()
438}
439
440pub(super) fn provide(providers: &mut Providers) {
441 *providers = Providers {
442 own_existential_vtable_entries,
443 vtable_entries,
444 first_method_vtable_slot,
445 supertrait_vtable_slot,
446 ..*providers
447 };
448}