rustc_trait_selection/traits/
vtable.rs
1use 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 = has_own_existential_vtable_entries(tcx, inner_most_trait_ref.def_id);
157
158 segment_visitor(VtblSegment::TraitOwnEntries {
159 trait_ref: inner_most_trait_ref,
160 emit_vptr: emit_vptr && has_entries && !tcx.sess.opts.unstable_opts.no_trait_vptr,
161 })?;
162
163 emit_vptr_on_new_entry |= has_entries;
166
167 if let Some(next_inner_most_trait_ref) =
168 siblings.find(|&sibling| visited.insert(sibling.upcast(tcx)))
169 {
170 stack.push((next_inner_most_trait_ref, emit_vptr_on_new_entry, siblings));
171
172 continue 'outer;
174 }
175 }
176
177 return ControlFlow::Continue(());
179 }
180}
181
182fn maybe_iter<I: Iterator>(i: Option<I>) -> impl Iterator<Item = I::Item> {
184 i.into_iter().flatten()
186}
187
188fn has_own_existential_vtable_entries(tcx: TyCtxt<'_>, trait_def_id: DefId) -> bool {
189 own_existential_vtable_entries_iter(tcx, trait_def_id).next().is_some()
190}
191
192fn own_existential_vtable_entries(tcx: TyCtxt<'_>, trait_def_id: DefId) -> &[DefId] {
193 tcx.arena.alloc_from_iter(own_existential_vtable_entries_iter(tcx, trait_def_id))
194}
195
196fn own_existential_vtable_entries_iter(
197 tcx: TyCtxt<'_>,
198 trait_def_id: DefId,
199) -> impl Iterator<Item = DefId> + '_ {
200 let trait_methods = tcx
201 .associated_items(trait_def_id)
202 .in_definition_order()
203 .filter(|item| item.kind == ty::AssocKind::Fn);
204
205 let own_entries = trait_methods.filter_map(move |&trait_method| {
207 debug!("own_existential_vtable_entry: trait_method={:?}", trait_method);
208 let def_id = trait_method.def_id;
209
210 if !is_vtable_safe_method(tcx, trait_def_id, trait_method) {
212 debug!("own_existential_vtable_entry: not vtable safe");
213 return None;
214 }
215
216 Some(def_id)
217 });
218
219 own_entries
220}
221
222fn vtable_entries<'tcx>(
225 tcx: TyCtxt<'tcx>,
226 trait_ref: ty::TraitRef<'tcx>,
227) -> &'tcx [VtblEntry<'tcx>] {
228 debug_assert!(!trait_ref.has_non_region_infer() && !trait_ref.has_non_region_param());
229 debug_assert_eq!(
230 tcx.normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), trait_ref),
231 trait_ref,
232 "vtable trait ref should be normalized"
233 );
234
235 debug!("vtable_entries({:?})", trait_ref);
236
237 let mut entries = vec![];
238
239 let vtable_segment_callback = |segment| -> ControlFlow<()> {
240 match segment {
241 VtblSegment::MetadataDSA => {
242 entries.extend(TyCtxt::COMMON_VTABLE_ENTRIES);
243 }
244 VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => {
245 let existential_trait_ref = ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref);
246
247 let own_existential_entries =
249 tcx.own_existential_vtable_entries(existential_trait_ref.def_id);
250
251 let own_entries = own_existential_entries.iter().copied().map(|def_id| {
252 debug!("vtable_entries: trait_method={:?}", def_id);
253
254 let args = tcx.normalize_erasing_regions(
257 ty::TypingEnv::fully_monomorphized(),
258 GenericArgs::for_item(tcx, def_id, |param, _| match param.kind {
259 GenericParamDefKind::Lifetime => tcx.lifetimes.re_erased.into(),
260 GenericParamDefKind::Type { .. }
261 | GenericParamDefKind::Const { .. } => {
262 trait_ref.args[param.index as usize]
263 }
264 }),
265 );
266
267 let predicates = tcx.predicates_of(def_id).instantiate_own(tcx, args);
272 if impossible_predicates(
273 tcx,
274 predicates.map(|(predicate, _)| predicate).collect(),
275 ) {
276 debug!("vtable_entries: predicates do not hold");
277 return VtblEntry::Vacant;
278 }
279
280 let instance = ty::Instance::expect_resolve_for_vtable(
281 tcx,
282 ty::TypingEnv::fully_monomorphized(),
283 def_id,
284 args,
285 DUMMY_SP,
286 );
287
288 VtblEntry::Method(instance)
289 });
290
291 entries.extend(own_entries);
292
293 if emit_vptr {
294 entries.push(VtblEntry::TraitVPtr(trait_ref));
295 }
296 }
297 }
298
299 ControlFlow::Continue(())
300 };
301
302 let _ = prepare_vtable_segments(tcx, trait_ref, vtable_segment_callback);
303
304 tcx.arena.alloc_from_iter(entries)
305}
306
307pub(crate) fn first_method_vtable_slot<'tcx>(tcx: TyCtxt<'tcx>, key: ty::TraitRef<'tcx>) -> usize {
310 debug_assert!(!key.has_non_region_infer() && !key.has_non_region_param());
311 debug_assert_eq!(
312 tcx.normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), key),
313 key,
314 "vtable trait ref should be normalized"
315 );
316
317 let ty::Dynamic(source, _, _) = *key.self_ty().kind() else {
318 bug!();
319 };
320 let source_principal = tcx.instantiate_bound_regions_with_erased(
321 source.principal().unwrap().with_self_ty(tcx, key.self_ty()),
322 );
323
324 if tcx.instantiate_and_check_impossible_predicates((
326 source_principal.def_id,
327 source_principal.args,
328 )) {
329 return 0;
330 }
331
332 let target_principal = ty::ExistentialTraitRef::erase_self_ty(tcx, key);
333
334 let vtable_segment_callback = {
335 let mut vptr_offset = 0;
336 move |segment| {
337 match segment {
338 VtblSegment::MetadataDSA => {
339 vptr_offset += TyCtxt::COMMON_VTABLE_ENTRIES.len();
340 }
341 VtblSegment::TraitOwnEntries { trait_ref: vtable_principal, emit_vptr } => {
342 if ty::ExistentialTraitRef::erase_self_ty(tcx, vtable_principal)
343 == target_principal
344 {
345 return ControlFlow::Break(vptr_offset);
346 }
347
348 vptr_offset +=
349 tcx.own_existential_vtable_entries(vtable_principal.def_id).len();
350
351 if emit_vptr {
352 vptr_offset += 1;
353 }
354 }
355 }
356 ControlFlow::Continue(())
357 }
358 };
359
360 prepare_vtable_segments(tcx, source_principal, vtable_segment_callback).unwrap()
361}
362
363pub(crate) fn supertrait_vtable_slot<'tcx>(
368 tcx: TyCtxt<'tcx>,
369 key: (
370 Ty<'tcx>, Ty<'tcx>, ),
373) -> Option<usize> {
374 debug_assert!(!key.has_non_region_infer() && !key.has_non_region_param());
375 debug_assert_eq!(
376 tcx.normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), key),
377 key,
378 "upcasting trait refs should be normalized"
379 );
380
381 let (source, target) = key;
382
383 let ty::Dynamic(target_data, _, _) = *target.kind() else {
385 bug!();
386 };
387 let target_principal = tcx.instantiate_bound_regions_with_erased(target_data.principal()?);
388
389 let ty::Dynamic(source_data, _, _) = *source.kind() else {
391 bug!();
392 };
393 let source_principal = tcx.instantiate_bound_regions_with_erased(
394 source_data.principal().unwrap().with_self_ty(tcx, source),
395 );
396
397 if tcx.instantiate_and_check_impossible_predicates((
399 source_principal.def_id,
400 source_principal.args,
401 )) {
402 return None;
403 }
404
405 let vtable_segment_callback = {
406 let mut vptr_offset = 0;
407 move |segment| {
408 match segment {
409 VtblSegment::MetadataDSA => {
410 vptr_offset += TyCtxt::COMMON_VTABLE_ENTRIES.len();
411 }
412 VtblSegment::TraitOwnEntries { trait_ref: vtable_principal, emit_vptr } => {
413 vptr_offset +=
414 tcx.own_existential_vtable_entries(vtable_principal.def_id).len();
415 if ty::ExistentialTraitRef::erase_self_ty(tcx, vtable_principal)
416 == target_principal
417 {
418 if emit_vptr {
419 return ControlFlow::Break(Some(vptr_offset));
420 } else {
421 return ControlFlow::Break(None);
422 }
423 }
424
425 if emit_vptr {
426 vptr_offset += 1;
427 }
428 }
429 }
430 ControlFlow::Continue(())
431 }
432 };
433
434 prepare_vtable_segments(tcx, source_principal, vtable_segment_callback).unwrap()
435}
436
437pub(super) fn provide(providers: &mut Providers) {
438 *providers = Providers {
439 own_existential_vtable_entries,
440 vtable_entries,
441 first_method_vtable_slot,
442 supertrait_vtable_slot,
443 ..*providers
444 };
445}