@@ -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,99 @@ 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 unupcasted_cost = 0 ;
884+ // Number of vtable entries needed solely for upcasting
885+ let mut upcast_cost = 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+ unupcasted_cost += 3 ;
915+ } else {
916+ upcast_cost += 3 ;
917+ }
918+ }
919+
920+ traits:: vtable:: VtblSegment :: TraitOwnEntries { trait_ref, emit_vptr } => {
921+ let existential_trait_ref = trait_ref. map_bound ( |trait_ref| {
922+ ty:: ExistentialTraitRef :: erase_self_ty ( tcx, trait_ref)
923+ } ) ;
924+
925+ // Lookup the shape of vtable for the trait.
926+ let own_existential_entries =
927+ tcx. own_existential_vtable_entries ( existential_trait_ref. def_id ( ) ) ;
928+
929+ let own_entries = own_existential_entries. iter ( ) . copied ( ) . map ( |_def_id| {
930+ // The original code here ignores the method if its predicates are impossible.
931+ // We can't really do that as, for example, all not trivial bounds on generic
932+ // parameters are impossible (since we don't know the parameters...),
933+ // see the comment above.
934+
935+ 1
936+ } ) ;
937+
938+ unupcasted_cost += own_entries. sum :: < usize > ( ) ;
939+
940+ if emit_vptr {
941+ upcast_cost += 1 ;
942+ }
943+ }
944+ }
945+
946+ std:: ops:: ControlFlow :: Continue :: < std:: convert:: Infallible > ( ( ) )
947+ } ) ;
948+
949+ sess. code_stats . record_vtable_size (
950+ tr,
951+ & name,
952+ VTableSizeInfo {
953+ trait_name : name. clone ( ) ,
954+ size_words_without_upcasting : unupcasted_cost,
955+ size_words_with_upcasting : unupcasted_cost + upcast_cost,
956+ difference_words : upcast_cost,
957+ difference_percent : upcast_cost as f64 / unupcasted_cost as f64 * 100. ,
958+ } ,
959+ )
960+ }
961+ }
962+
869963 Ok ( ( ) )
870964}
871965
0 commit comments