11use rustc_ast:: { ast, attr, MetaItemKind , NestedMetaItem } ;
22use rustc_attr:: { list_contains_name, InlineAttr , InstructionSetAttr , OptimizeAttr } ;
3+ use rustc_data_structures:: fx:: FxHashSet ;
34use rustc_errors:: codes:: * ;
45use rustc_errors:: { struct_span_code_err, DiagMessage , SubdiagMessage } ;
56use rustc_hir as hir;
@@ -8,7 +9,7 @@ use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE};
89use rustc_hir:: weak_lang_items:: WEAK_LANG_ITEMS ;
910use rustc_hir:: { lang_items, LangItem } ;
1011use rustc_middle:: middle:: codegen_fn_attrs:: {
11- CodegenFnAttrFlags , CodegenFnAttrs , PatchableFunctionEntry ,
12+ CodegenFnAttrFlags , CodegenFnAttrs , PatchableFunctionEntry , TargetFeature ,
1213} ;
1314use rustc_middle:: mir:: mono:: Linkage ;
1415use rustc_middle:: query:: Providers ;
@@ -17,6 +18,7 @@ use rustc_session::lint;
1718use rustc_session:: parse:: feature_err;
1819use rustc_span:: symbol:: Ident ;
1920use rustc_span:: { sym, Span } ;
21+ use rustc_target:: abi:: VariantIdx ;
2022use rustc_target:: spec:: { abi, SanitizerSet } ;
2123
2224use crate :: errors;
@@ -78,23 +80,26 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
7880 let mut link_ordinal_span = None ;
7981 let mut no_sanitize_span = None ;
8082
83+ let fn_sig_outer = || {
84+ use DefKind :: * ;
85+
86+ let def_kind = tcx. def_kind ( did) ;
87+ if let Fn | AssocFn | Variant | Ctor ( ..) = def_kind { Some ( tcx. fn_sig ( did) ) } else { None }
88+ } ;
89+
8190 for attr in attrs. iter ( ) {
8291 // In some cases, attribute are only valid on functions, but it's the `check_attr`
8392 // pass that check that they aren't used anywhere else, rather this module.
8493 // In these cases, we bail from performing further checks that are only meaningful for
8594 // functions (such as calling `fn_sig`, which ICEs if given a non-function). We also
8695 // report a delayed bug, just in case `check_attr` isn't doing its job.
8796 let fn_sig = || {
88- use DefKind :: * ;
89-
90- let def_kind = tcx. def_kind ( did) ;
91- if let Fn | AssocFn | Variant | Ctor ( ..) = def_kind {
92- Some ( tcx. fn_sig ( did) )
93- } else {
97+ let sig = fn_sig_outer ( ) ;
98+ if sig. is_none ( ) {
9499 tcx. dcx ( )
95100 . span_delayed_bug ( attr. span , "this attribute can only be applied to functions" ) ;
96- None
97101 }
102+ sig
98103 } ;
99104
100105 let Some ( Ident { name, .. } ) = attr. ident ( ) else {
@@ -613,7 +618,93 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
613618 }
614619 }
615620
616- // If a function uses #[target_feature] it can't be inlined into general
621+ 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 ;
635+ }
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 ( ..) => ( ) ,
686+ }
687+ }
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 } ) ) ;
705+ }
706+
707+ // If a function uses non-default target_features it can't be inlined into general
617708 // purpose functions as they wouldn't have the right target features
618709 // enabled. For that reason we also forbid #[inline(always)] as it can't be
619710 // respected.
@@ -758,6 +849,20 @@ fn check_link_name_xor_ordinal(
758849 }
759850}
760851
852+ fn struct_target_features ( tcx : TyCtxt < ' _ > , def_id : LocalDefId ) -> & [ TargetFeature ] {
853+ let mut features = vec ! [ ] ;
854+ let supported_features = tcx. supported_target_features ( LOCAL_CRATE ) ;
855+ for attr in tcx. get_attrs ( def_id, sym:: target_feature) {
856+ from_target_feature ( tcx, attr, supported_features, & mut features) ;
857+ }
858+ tcx. arena . alloc_slice ( & features)
859+ }
860+
761861pub fn provide ( providers : & mut Providers ) {
762- * providers = Providers { codegen_fn_attrs, should_inherit_track_caller, ..* providers } ;
862+ * providers = Providers {
863+ codegen_fn_attrs,
864+ should_inherit_track_caller,
865+ struct_target_features,
866+ ..* providers
867+ } ;
763868}
0 commit comments