@@ -24,6 +24,7 @@ use rustc_parse::{parse_crate_from_file, parse_crate_from_source_str, validate_a
2424use rustc_passes:: { self , hir_stats, layout_test} ;
2525use rustc_plugin_impl as plugin;
2626use rustc_resolve:: Resolver ;
27+ use rustc_session:: code_stats:: VTableSizeInfo ;
2728use rustc_session:: config:: { CrateType , Input , OutFileName , OutputFilenames , OutputType } ;
2829use rustc_session:: cstore:: { MetadataLoader , Untracked } ;
2930use rustc_session:: output:: filename_for_input;
@@ -866,6 +867,92 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> {
866867 sess. time ( "check_lint_expectations" , || tcx. check_expectations ( None ) ) ;
867868 } ) ;
868869
870+ if sess. opts . unstable_opts . print_vtable_sizes {
871+ let traits = tcx. traits ( LOCAL_CRATE ) ;
872+
873+ for & tr in traits {
874+ if !tcx. check_is_object_safe ( tr) {
875+ continue ;
876+ }
877+
878+ let name = ty:: print:: with_no_trimmed_paths!( tcx. def_path_str( tr) ) ;
879+
880+ let mut first_dsa = true ;
881+
882+ // Number of vtable entries, if we didn't have upcasting
883+ let mut entries_ignoring_upcasting = 0 ;
884+ // Number of vtable entries needed solely for upcasting
885+ let mut entries_for_upcasting = 0 ;
886+
887+ let trait_ref = ty:: Binder :: dummy ( ty:: TraitRef :: identity ( tcx, tr) ) ;
888+
889+ // A slightly edited version of the code in `rustc_trait_selection::traits::vtable::vtable_entries`,
890+ // that works without self type and just counts number of entries.
891+ //
892+ // Note that this is technically wrong, for traits which have associated types in supertraits:
893+ //
894+ // trait A: AsRef<Self::T> + AsRef<()> { type T; }
895+ //
896+ // Without self type we can't normalize `Self::T`, so we can't know if `AsRef<Self::T>` and
897+ // `AsRef<()>` are the same trait, thus we assume that those are different, and potentially
898+ // over-estimate how many vtable entries there are.
899+ //
900+ // Similarly this is wrong for traits that have methods with possibly-impossible bounds.
901+ // For example:
902+ //
903+ // trait B<T> { fn f(&self) where T: Copy; }
904+ //
905+ // Here `dyn B<u8>` will have 4 entries, while `dyn B<String>` will only have 3.
906+ // However, since we don't know `T`, we can't know if `T: Copy` holds or not,
907+ // thus we lean on the bigger side and say it has 4 entries.
908+ traits:: vtable:: prepare_vtable_segments ( tcx, trait_ref, |segment| {
909+ match segment {
910+ traits:: vtable:: VtblSegment :: MetadataDSA => {
911+ // If this is the first dsa, it would be included either way,
912+ // otherwise it's needed for upcasting
913+ if std:: mem:: take ( & mut first_dsa) {
914+ entries_ignoring_upcasting += 3 ;
915+ } else {
916+ entries_for_upcasting += 3 ;
917+ }
918+ }
919+
920+ traits:: vtable:: VtblSegment :: TraitOwnEntries { trait_ref, emit_vptr } => {
921+ // Lookup the shape of vtable for the trait.
922+ let own_existential_entries =
923+ tcx. own_existential_vtable_entries ( trait_ref. def_id ( ) ) ;
924+
925+ // The original code here ignores the method if its predicates are impossible.
926+ // We can't really do that as, for example, all not trivial bounds on generic
927+ // parameters are impossible (since we don't know the parameters...),
928+ // see the comment above.
929+ entries_ignoring_upcasting += own_existential_entries. len ( ) ;
930+
931+ if emit_vptr {
932+ entries_for_upcasting += 1 ;
933+ }
934+ }
935+ }
936+
937+ std:: ops:: ControlFlow :: Continue :: < std:: convert:: Infallible > ( ( ) )
938+ } ) ;
939+
940+ sess. code_stats . record_vtable_size (
941+ tr,
942+ & name,
943+ VTableSizeInfo {
944+ trait_name : name. clone ( ) ,
945+ entries : entries_ignoring_upcasting + entries_for_upcasting,
946+ entries_ignoring_upcasting,
947+ entries_for_upcasting,
948+ upcasting_cost_percent : entries_for_upcasting as f64
949+ / entries_ignoring_upcasting as f64
950+ * 100. ,
951+ } ,
952+ )
953+ }
954+ }
955+
869956 Ok ( ( ) )
870957}
871958
0 commit comments