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;
@@ -16,8 +17,10 @@ use rustc_middle::ty::{self as ty, TyCtxt};
1617use rustc_session:: lint;
1718use rustc_session:: parse:: feature_err;
1819use rustc_span:: symbol:: Ident ;
19- use rustc_span:: { sym, Span } ;
20+ use rustc_span:: { sym, Span , Symbol } ;
21+ use rustc_target:: abi:: VariantIdx ;
2022use rustc_target:: spec:: { abi, SanitizerSet } ;
23+ use rustc_type_ir:: inherent:: * ;
2124
2225use crate :: errors;
2326use crate :: target_features:: { check_target_feature_trait_unsafe, from_target_feature} ;
@@ -78,23 +81,26 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
7881 let mut link_ordinal_span = None ;
7982 let mut no_sanitize_span = None ;
8083
84+ let fn_sig_outer = || {
85+ use DefKind :: * ;
86+
87+ let def_kind = tcx. def_kind ( did) ;
88+ if let Fn | AssocFn | Variant | Ctor ( ..) = def_kind { Some ( tcx. fn_sig ( did) ) } else { None }
89+ } ;
90+
8191 for attr in attrs. iter ( ) {
8292 // In some cases, attribute are only valid on functions, but it's the `check_attr`
8393 // pass that check that they aren't used anywhere else, rather this module.
8494 // In these cases, we bail from performing further checks that are only meaningful for
8595 // functions (such as calling `fn_sig`, which ICEs if given a non-function). We also
8696 // report a delayed bug, just in case `check_attr` isn't doing its job.
8797 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 {
98+ let sig = fn_sig_outer ( ) ;
99+ if sig. is_none ( ) {
94100 tcx. dcx ( )
95101 . span_delayed_bug ( attr. span , "this attribute can only be applied to functions" ) ;
96- None
97102 }
103+ sig
98104 } ;
99105
100106 let Some ( Ident { name, .. } ) = attr. ident ( ) else {
@@ -613,6 +619,82 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
613619 }
614620 }
615621
622+ if tcx. features ( ) . struct_target_features
623+ && let Some ( sig) = fn_sig_outer ( )
624+ {
625+ // Collect target features from types reachable from arguments.
626+ // We define a type as "reachable" if:
627+ // - it is a function argument
628+ // - it is a field of a reachable struct
629+ // - there is a reachable reference to it
630+ // FIXME: we may want to cache the result of this computation.
631+ let mut visited_types = FxHashSet :: default ( ) ;
632+ let mut reachable_types: Vec < _ > = sig. skip_binder ( ) . inputs ( ) . skip_binder ( ) . to_owned ( ) ;
633+ let mut additional_tf = vec ! [ ] ;
634+
635+ while let Some ( ty) = reachable_types. pop ( ) {
636+ if visited_types. contains ( & ty) {
637+ continue ;
638+ }
639+ visited_types. insert ( ty) ;
640+ match ty. kind ( ) {
641+ ty:: Ref ( ..) => reachable_types. push ( ty. builtin_deref ( false ) . unwrap ( ) ) ,
642+ ty:: Tuple ( ..) => reachable_types. extend ( ty. tuple_fields ( ) . iter ( ) ) ,
643+ ty:: Adt ( adt_def, args) => {
644+ additional_tf. extend_from_slice ( tcx. struct_target_features ( adt_def. did ( ) ) ) ;
645+ if adt_def. is_struct ( ) {
646+ reachable_types. extend (
647+ adt_def
648+ . variant ( VariantIdx :: from_usize ( 0 ) )
649+ . fields
650+ . iter ( )
651+ . map ( |field| field. ty ( tcx, args) ) ,
652+ ) ;
653+ }
654+ }
655+ ty:: Bool
656+ | ty:: Char
657+ | ty:: Int ( ..)
658+ | ty:: Uint ( ..)
659+ | ty:: Float ( ..)
660+ | ty:: Foreign ( ..)
661+ | ty:: Str
662+ | ty:: Array ( ..)
663+ | ty:: Pat ( ..)
664+ | ty:: Slice ( ..)
665+ | ty:: RawPtr ( ..)
666+ | ty:: FnDef ( ..)
667+ | ty:: FnPtr ( ..)
668+ | ty:: Dynamic ( ..)
669+ | ty:: Closure ( ..)
670+ | ty:: CoroutineClosure ( ..)
671+ | ty:: Coroutine ( ..)
672+ | ty:: CoroutineWitness ( ..)
673+ | ty:: Never
674+ | ty:: Alias ( ..)
675+ | ty:: Param ( ..)
676+ | ty:: Bound ( ..)
677+ | ty:: Placeholder ( ..)
678+ | ty:: Infer ( ..)
679+ | ty:: Error ( ..) => ( ) ,
680+ }
681+ }
682+
683+ if !additional_tf. is_empty ( ) && !sig. skip_binder ( ) . abi ( ) . is_rust ( ) {
684+ tcx. dcx ( ) . span_err (
685+ tcx. hir ( ) . span ( tcx. local_def_id_to_hir_id ( did) ) ,
686+ "cannot use a struct with target features in a function with non-Rust ABI" ,
687+ ) ;
688+ }
689+ if !additional_tf. is_empty ( ) && codegen_fn_attrs. inline == InlineAttr :: Always {
690+ tcx. dcx ( ) . span_err (
691+ tcx. hir ( ) . span ( tcx. local_def_id_to_hir_id ( did) ) ,
692+ "cannot use a struct with target features in a #[inline(always)] function" ,
693+ ) ;
694+ }
695+ codegen_fn_attrs. target_features . extend_from_slice ( & additional_tf) ;
696+ }
697+
616698 // If a function uses #[target_feature] it can't be inlined into general
617699 // purpose functions as they wouldn't have the right target features
618700 // enabled. For that reason we also forbid #[inline(always)] as it can't be
@@ -758,6 +840,20 @@ fn check_link_name_xor_ordinal(
758840 }
759841}
760842
843+ fn struct_target_features ( tcx : TyCtxt < ' _ > , def_id : LocalDefId ) -> & [ Symbol ] {
844+ let mut features = vec ! [ ] ;
845+ let supported_features = tcx. supported_target_features ( LOCAL_CRATE ) ;
846+ for attr in tcx. get_attrs ( def_id, sym:: target_feature) {
847+ from_target_feature ( tcx, attr, supported_features, & mut features) ;
848+ }
849+ tcx. arena . alloc_slice ( & features)
850+ }
851+
761852pub fn provide ( providers : & mut Providers ) {
762- * providers = Providers { codegen_fn_attrs, should_inherit_track_caller, ..* providers } ;
853+ * providers = Providers {
854+ codegen_fn_attrs,
855+ should_inherit_track_caller,
856+ struct_target_features,
857+ ..* providers
858+ } ;
763859}
0 commit comments