@@ -24,6 +24,7 @@ use rustc_parse::{parse_crate_from_file, parse_crate_from_source_str, validate_a
24
24
use rustc_passes:: { self , hir_stats, layout_test} ;
25
25
use rustc_plugin_impl as plugin;
26
26
use rustc_resolve:: Resolver ;
27
+ use rustc_session:: code_stats:: VTableSizeInfo ;
27
28
use rustc_session:: config:: { CrateType , Input , OutFileName , OutputFilenames , OutputType } ;
28
29
use rustc_session:: cstore:: { MetadataLoader , Untracked } ;
29
30
use rustc_session:: output:: filename_for_input;
@@ -866,6 +867,99 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> {
866
867
sess. time ( "check_lint_expectations" , || tcx. check_expectations ( None ) ) ;
867
868
} ) ;
868
869
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
+
869
963
Ok ( ( ) )
870
964
}
871
965
0 commit comments