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 = 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 =
201 tcx.associated_items(trait_def_id).in_definition_order().filter(|item| item.is_fn());
202
203 let own_entries = trait_methods.filter_map(move |&trait_method| {
205 debug!("own_existential_vtable_entry: trait_method={:?}", trait_method);
206 let def_id = trait_method.def_id;
207
208 if !is_vtable_safe_method(tcx, trait_def_id, trait_method) {
210 debug!("own_existential_vtable_entry: not vtable safe");
211 return None;
212 }
213
214 Some(def_id)
215 });
216
217 own_entries
218}
219
220fn vtable_entries<'tcx>(
223 tcx: TyCtxt<'tcx>,
224 trait_ref: ty::TraitRef<'tcx>,
225) -> &'tcx [VtblEntry<'tcx>] {
226 debug_assert!(!trait_ref.has_non_region_infer() && !trait_ref.has_non_region_param());
227 debug_assert_eq!(
228 tcx.normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), trait_ref),
229 trait_ref,
230 "vtable trait ref should be normalized"
231 );
232
233 debug!("vtable_entries({:?})", trait_ref);
234
235 let mut entries = vec![];
236
237 let vtable_segment_callback = |segment| -> ControlFlow<()> {
238 match segment {
239 VtblSegment::MetadataDSA => {
240 entries.extend(TyCtxt::COMMON_VTABLE_ENTRIES);
241 }
242 VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => {
243 let existential_trait_ref = ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref);
244
245 let own_existential_entries =
247 tcx.own_existential_vtable_entries(existential_trait_ref.def_id);
248
249 let own_entries = own_existential_entries.iter().copied().map(|def_id| {
250 debug!("vtable_entries: trait_method={:?}", def_id);
251
252 let args = tcx.normalize_erasing_regions(
255 ty::TypingEnv::fully_monomorphized(),
256 GenericArgs::for_item(tcx, def_id, |param, _| match param.kind {
257 GenericParamDefKind::Lifetime => tcx.lifetimes.re_erased.into(),
258 GenericParamDefKind::Type { .. }
259 | GenericParamDefKind::Const { .. } => {
260 trait_ref.args[param.index as usize]
261 }
262 }),
263 );
264
265 let predicates = tcx.predicates_of(def_id).instantiate_own(tcx, args);
270 if impossible_predicates(
271 tcx,
272 predicates.map(|(predicate, _)| predicate).collect(),
273 ) {
274 debug!("vtable_entries: predicates do not hold");
275 return VtblEntry::Vacant;
276 }
277
278 let instance = ty::Instance::expect_resolve_for_vtable(
279 tcx,
280 ty::TypingEnv::fully_monomorphized(),
281 def_id,
282 args,
283 DUMMY_SP,
284 );
285
286 VtblEntry::Method(instance)
287 });
288
289 entries.extend(own_entries);
290
291 if emit_vptr {
292 entries.push(VtblEntry::TraitVPtr(trait_ref));
293 }
294 }
295 }
296
297 ControlFlow::Continue(())
298 };
299
300 let _ = prepare_vtable_segments(tcx, trait_ref, vtable_segment_callback);
301
302 tcx.arena.alloc_from_iter(entries)
303}
304
305pub(crate) fn first_method_vtable_slot<'tcx>(tcx: TyCtxt<'tcx>, key: ty::TraitRef<'tcx>) -> usize {
308 debug_assert!(!key.has_non_region_infer() && !key.has_non_region_param());
309 debug_assert_eq!(
310 tcx.normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), key),
311 key,
312 "vtable trait ref should be normalized"
313 );
314
315 let ty::Dynamic(source, _, _) = *key.self_ty().kind() else {
316 bug!();
317 };
318 let source_principal = tcx.instantiate_bound_regions_with_erased(
319 source.principal().unwrap().with_self_ty(tcx, key.self_ty()),
320 );
321
322 if tcx.instantiate_and_check_impossible_predicates((
324 source_principal.def_id,
325 source_principal.args,
326 )) {
327 return 0;
328 }
329
330 let target_principal = ty::ExistentialTraitRef::erase_self_ty(tcx, key);
331
332 let vtable_segment_callback = {
333 let mut vptr_offset = 0;
334 move |segment| {
335 match segment {
336 VtblSegment::MetadataDSA => {
337 vptr_offset += TyCtxt::COMMON_VTABLE_ENTRIES.len();
338 }
339 VtblSegment::TraitOwnEntries { trait_ref: vtable_principal, emit_vptr } => {
340 if ty::ExistentialTraitRef::erase_self_ty(tcx, vtable_principal)
341 == target_principal
342 {
343 return ControlFlow::Break(vptr_offset);
344 }
345
346 vptr_offset +=
347 tcx.own_existential_vtable_entries(vtable_principal.def_id).len();
348
349 if emit_vptr {
350 vptr_offset += 1;
351 }
352 }
353 }
354 ControlFlow::Continue(())
355 }
356 };
357
358 prepare_vtable_segments(tcx, source_principal, vtable_segment_callback).unwrap()
359}
360
361pub(crate) fn supertrait_vtable_slot<'tcx>(
366 tcx: TyCtxt<'tcx>,
367 key: (
368 Ty<'tcx>, Ty<'tcx>, ),
371) -> Option<usize> {
372 debug_assert!(!key.has_non_region_infer() && !key.has_non_region_param());
373 debug_assert_eq!(
374 tcx.normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), key),
375 key,
376 "upcasting trait refs should be normalized"
377 );
378
379 let (source, target) = key;
380
381 let ty::Dynamic(target_data, _, _) = *target.kind() else {
383 bug!();
384 };
385 let target_principal = tcx.instantiate_bound_regions_with_erased(target_data.principal()?);
386
387 let ty::Dynamic(source_data, _, _) = *source.kind() else {
389 bug!();
390 };
391 let source_principal = tcx.instantiate_bound_regions_with_erased(
392 source_data.principal().unwrap().with_self_ty(tcx, source),
393 );
394
395 if tcx.instantiate_and_check_impossible_predicates((
397 source_principal.def_id,
398 source_principal.args,
399 )) {
400 return None;
401 }
402
403 let vtable_segment_callback = {
404 let mut vptr_offset = 0;
405 move |segment| {
406 match segment {
407 VtblSegment::MetadataDSA => {
408 vptr_offset += TyCtxt::COMMON_VTABLE_ENTRIES.len();
409 }
410 VtblSegment::TraitOwnEntries { trait_ref: vtable_principal, emit_vptr } => {
411 vptr_offset +=
412 tcx.own_existential_vtable_entries(vtable_principal.def_id).len();
413 if ty::ExistentialTraitRef::erase_self_ty(tcx, vtable_principal)
414 == target_principal
415 {
416 if emit_vptr {
417 return ControlFlow::Break(Some(vptr_offset));
418 } else {
419 return ControlFlow::Break(None);
420 }
421 }
422
423 if emit_vptr {
424 vptr_offset += 1;
425 }
426 }
427 }
428 ControlFlow::Continue(())
429 }
430 };
431
432 prepare_vtable_segments(tcx, source_principal, vtable_segment_callback).unwrap()
433}
434
435pub(super) fn provide(providers: &mut Providers) {
436 *providers = Providers {
437 own_existential_vtable_entries,
438 vtable_entries,
439 first_method_vtable_slot,
440 supertrait_vtable_slot,
441 ..*providers
442 };
443}