@@ -13,7 +13,7 @@ use rustc_middle::middle::codegen_fn_attrs::{
1313} ;
1414use rustc_middle:: mir:: mono:: Linkage ;
1515use rustc_middle:: query:: Providers ;
16- use rustc_middle:: ty:: { self as ty, TyCtxt } ;
16+ use rustc_middle:: ty:: { self as ty, Ty , TyCtxt } ;
1717use rustc_session:: lint;
1818use rustc_session:: parse:: feature_err;
1919use rustc_span:: symbol:: Ident ;
@@ -619,89 +619,26 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
619619 }
620620
621621 if let Some ( sig) = fn_sig_outer ( ) {
622- // Collect target features from types reachable from arguments.
623- // We define a type as "reachable" if:
624- // - it is a function argument
625- // - it is a field of a reachable struct
626- // - there is a reachable reference to it
627- // FIXME(struct_target_features): we may want to cache the result of this computation.
628- let mut visited_types = FxHashSet :: default ( ) ;
629- let mut reachable_types: Vec < _ > = sig. skip_binder ( ) . inputs ( ) . skip_binder ( ) . to_owned ( ) ;
630- let mut additional_tf = vec ! [ ] ;
631-
632- while let Some ( ty) = reachable_types. pop ( ) {
633- if visited_types. contains ( & ty) {
634- continue ;
622+ for ty in sig. skip_binder ( ) . inputs ( ) . skip_binder ( ) {
623+ let additional_tf =
624+ tcx. struct_reachable_target_features ( tcx. param_env ( did. to_def_id ( ) ) . and ( * ty) ) ;
625+ // FIXME(struct_target_features): is this really necessary?
626+ if !additional_tf. is_empty ( ) && sig. skip_binder ( ) . abi ( ) != abi:: Abi :: Rust {
627+ tcx. dcx ( ) . span_err (
628+ tcx. hir ( ) . span ( tcx. local_def_id_to_hir_id ( did) ) ,
629+ "cannot use a struct with target features in a function with non-Rust ABI" ,
630+ ) ;
635631 }
636- visited_types. insert ( ty) ;
637- match ty. kind ( ) {
638- ty:: Alias ( ..) => {
639- if let Ok ( t) =
640- tcx. try_normalize_erasing_regions ( tcx. param_env ( did. to_def_id ( ) ) , ty)
641- {
642- reachable_types. push ( t)
643- }
644- }
645-
646- ty:: Ref ( _, inner, _) => reachable_types. push ( * inner) ,
647- ty:: Tuple ( tys) => reachable_types. extend ( tys. iter ( ) ) ,
648- ty:: Adt ( adt_def, args) => {
649- additional_tf. extend_from_slice ( tcx. struct_target_features ( adt_def. did ( ) ) ) ;
650- // This only recurses into structs as i.e. an Option<TargetFeature> is an ADT
651- // that doesn't actually always contain a TargetFeature.
652- if adt_def. is_struct ( ) {
653- reachable_types. extend (
654- adt_def
655- . variant ( VariantIdx :: from_usize ( 0 ) )
656- . fields
657- . iter ( )
658- . map ( |field| field. ty ( tcx, args) ) ,
659- ) ;
660- }
661- }
662- ty:: Bool
663- | ty:: Char
664- | ty:: Int ( ..)
665- | ty:: Uint ( ..)
666- | ty:: Float ( ..)
667- | ty:: Foreign ( ..)
668- | ty:: Str
669- | ty:: Array ( ..)
670- | ty:: Pat ( ..)
671- | ty:: Slice ( ..)
672- | ty:: RawPtr ( ..)
673- | ty:: FnDef ( ..)
674- | ty:: FnPtr ( ..)
675- | ty:: Dynamic ( ..)
676- | ty:: Closure ( ..)
677- | ty:: CoroutineClosure ( ..)
678- | ty:: Coroutine ( ..)
679- | ty:: CoroutineWitness ( ..)
680- | ty:: Never
681- | ty:: Param ( ..)
682- | ty:: Bound ( ..)
683- | ty:: Placeholder ( ..)
684- | ty:: Infer ( ..)
685- | ty:: Error ( ..) => ( ) ,
632+ if !additional_tf. is_empty ( ) && codegen_fn_attrs. inline == InlineAttr :: Always {
633+ tcx. dcx ( ) . span_err (
634+ tcx. hir ( ) . span ( tcx. local_def_id_to_hir_id ( did) ) ,
635+ "cannot use a struct with target features in a #[inline(always)] function" ,
636+ ) ;
686637 }
638+ codegen_fn_attrs
639+ . target_features
640+ . extend ( additional_tf. iter ( ) . map ( |tf| TargetFeature { implied : true , ..* tf } ) ) ;
687641 }
688-
689- // FIXME(struct_target_features): is this really necessary?
690- if !additional_tf. is_empty ( ) && sig. skip_binder ( ) . abi ( ) != abi:: Abi :: Rust {
691- tcx. dcx ( ) . span_err (
692- tcx. hir ( ) . span ( tcx. local_def_id_to_hir_id ( did) ) ,
693- "cannot use a struct with target features in a function with non-Rust ABI" ,
694- ) ;
695- }
696- if !additional_tf. is_empty ( ) && codegen_fn_attrs. inline == InlineAttr :: Always {
697- tcx. dcx ( ) . span_err (
698- tcx. hir ( ) . span ( tcx. local_def_id_to_hir_id ( did) ) ,
699- "cannot use a struct with target features in a #[inline(always)] function" ,
700- ) ;
701- }
702- codegen_fn_attrs
703- . target_features
704- . extend ( additional_tf. iter ( ) . map ( |tf| TargetFeature { implied : true , ..* tf } ) ) ;
705642 }
706643
707644 // If a function uses non-default target_features it can't be inlined into general
@@ -858,11 +795,82 @@ fn struct_target_features(tcx: TyCtxt<'_>, def_id: LocalDefId) -> &[TargetFeatur
858795 tcx. arena . alloc_slice ( & features)
859796}
860797
798+ fn struct_reachable_target_features < ' tcx > (
799+ tcx : TyCtxt < ' tcx > ,
800+ env : ty:: ParamEnvAnd < ' tcx , Ty < ' tcx > > ,
801+ ) -> & ' tcx [ TargetFeature ] {
802+ // Collect target features from types reachable from `env.value`.
803+ // We define a type as "reachable" if:
804+ // - it is a function argument
805+ // - it is a field of a reachable struct
806+ // - there is a reachable reference to it
807+ let mut visited_types = FxHashSet :: default ( ) ;
808+ let mut reachable_types = vec ! [ env. value] ;
809+ let mut reachable_tf = vec ! [ ] ;
810+
811+ while let Some ( ty) = reachable_types. pop ( ) {
812+ if visited_types. contains ( & ty) {
813+ continue ;
814+ }
815+ visited_types. insert ( ty) ;
816+ match ty. kind ( ) {
817+ ty:: Alias ( ..) => {
818+ if let Ok ( t) = tcx. try_normalize_erasing_regions ( env. param_env , ty) {
819+ reachable_types. push ( t)
820+ }
821+ }
822+
823+ ty:: Ref ( _, inner, _) => reachable_types. push ( * inner) ,
824+ ty:: Tuple ( tys) => reachable_types. extend ( tys. iter ( ) ) ,
825+ ty:: Adt ( adt_def, args) => {
826+ reachable_tf. extend_from_slice ( tcx. struct_target_features ( adt_def. did ( ) ) ) ;
827+ // This only recurses into structs as i.e. an Option<TargetFeature> is an ADT
828+ // that doesn't actually always contain a TargetFeature.
829+ if adt_def. is_struct ( ) {
830+ reachable_types. extend (
831+ adt_def
832+ . variant ( VariantIdx :: from_usize ( 0 ) )
833+ . fields
834+ . iter ( )
835+ . map ( |field| field. ty ( tcx, args) ) ,
836+ ) ;
837+ }
838+ }
839+ ty:: Bool
840+ | ty:: Char
841+ | ty:: Int ( ..)
842+ | ty:: Uint ( ..)
843+ | ty:: Float ( ..)
844+ | ty:: Foreign ( ..)
845+ | ty:: Str
846+ | ty:: Array ( ..)
847+ | ty:: Pat ( ..)
848+ | ty:: Slice ( ..)
849+ | ty:: RawPtr ( ..)
850+ | ty:: FnDef ( ..)
851+ | ty:: FnPtr ( ..)
852+ | ty:: Dynamic ( ..)
853+ | ty:: Closure ( ..)
854+ | ty:: CoroutineClosure ( ..)
855+ | ty:: Coroutine ( ..)
856+ | ty:: CoroutineWitness ( ..)
857+ | ty:: Never
858+ | ty:: Param ( ..)
859+ | ty:: Bound ( ..)
860+ | ty:: Placeholder ( ..)
861+ | ty:: Infer ( ..)
862+ | ty:: Error ( ..) => ( ) ,
863+ }
864+ }
865+ tcx. arena . alloc_slice ( & reachable_tf)
866+ }
867+
861868pub fn provide ( providers : & mut Providers ) {
862869 * providers = Providers {
863870 codegen_fn_attrs,
864871 should_inherit_track_caller,
865872 struct_target_features,
873+ struct_reachable_target_features,
866874 ..* providers
867875 } ;
868876}
0 commit comments