1
1
use rustc_ast:: { ast, attr, MetaItemKind , NestedMetaItem } ;
2
2
use rustc_attr:: { list_contains_name, InlineAttr , InstructionSetAttr , OptimizeAttr } ;
3
+ use rustc_data_structures:: fx:: FxHashSet ;
3
4
use rustc_errors:: codes:: * ;
4
5
use rustc_errors:: { struct_span_code_err, DiagMessage , SubdiagMessage } ;
5
6
use rustc_hir as hir;
@@ -8,15 +9,16 @@ use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE};
8
9
use rustc_hir:: weak_lang_items:: WEAK_LANG_ITEMS ;
9
10
use rustc_hir:: { lang_items, LangItem } ;
10
11
use rustc_middle:: middle:: codegen_fn_attrs:: {
11
- CodegenFnAttrFlags , CodegenFnAttrs , PatchableFunctionEntry ,
12
+ CodegenFnAttrFlags , CodegenFnAttrs , PatchableFunctionEntry , TargetFeature ,
12
13
} ;
13
14
use rustc_middle:: mir:: mono:: Linkage ;
14
15
use rustc_middle:: query:: Providers ;
15
- use rustc_middle:: ty:: { self as ty, TyCtxt } ;
16
+ use rustc_middle:: ty:: { self as ty, Ty , TyCtxt } ;
16
17
use rustc_session:: lint;
17
18
use rustc_session:: parse:: feature_err;
18
19
use rustc_span:: symbol:: Ident ;
19
20
use rustc_span:: { sym, Span } ;
21
+ use rustc_target:: abi:: VariantIdx ;
20
22
use rustc_target:: spec:: { abi, SanitizerSet } ;
21
23
22
24
use crate :: errors;
@@ -78,23 +80,26 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
78
80
let mut link_ordinal_span = None ;
79
81
let mut no_sanitize_span = None ;
80
82
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
+
81
90
for attr in attrs. iter ( ) {
82
91
// In some cases, attribute are only valid on functions, but it's the `check_attr`
83
92
// pass that check that they aren't used anywhere else, rather this module.
84
93
// In these cases, we bail from performing further checks that are only meaningful for
85
94
// functions (such as calling `fn_sig`, which ICEs if given a non-function). We also
86
95
// report a delayed bug, just in case `check_attr` isn't doing its job.
87
96
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 ( ) {
94
99
tcx. dcx ( )
95
100
. span_delayed_bug ( attr. span , "this attribute can only be applied to functions" ) ;
96
- None
97
101
}
102
+ sig
98
103
} ;
99
104
100
105
let Some ( Ident { name, .. } ) = attr. ident ( ) else {
@@ -613,7 +618,30 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
613
618
}
614
619
}
615
620
616
- // If a function uses #[target_feature] it can't be inlined into general
621
+ if let Some ( sig) = fn_sig_outer ( ) {
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
+ ) ;
631
+ }
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
+ ) ;
637
+ }
638
+ codegen_fn_attrs
639
+ . target_features
640
+ . extend ( additional_tf. iter ( ) . map ( |tf| TargetFeature { implied : true , ..* tf } ) ) ;
641
+ }
642
+ }
643
+
644
+ // If a function uses non-default target_features it can't be inlined into general
617
645
// purpose functions as they wouldn't have the right target features
618
646
// enabled. For that reason we also forbid #[inline(always)] as it can't be
619
647
// respected.
@@ -758,6 +786,91 @@ fn check_link_name_xor_ordinal(
758
786
}
759
787
}
760
788
789
+ fn struct_target_features ( tcx : TyCtxt < ' _ > , def_id : LocalDefId ) -> & [ TargetFeature ] {
790
+ let mut features = vec ! [ ] ;
791
+ let supported_features = tcx. supported_target_features ( LOCAL_CRATE ) ;
792
+ for attr in tcx. get_attrs ( def_id, sym:: target_feature) {
793
+ from_target_feature ( tcx, attr, supported_features, & mut features) ;
794
+ }
795
+ tcx. arena . alloc_slice ( & features)
796
+ }
797
+
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
+
761
868
pub fn provide ( providers : & mut Providers ) {
762
- * providers = Providers { codegen_fn_attrs, should_inherit_track_caller, ..* providers } ;
869
+ * providers = Providers {
870
+ codegen_fn_attrs,
871
+ should_inherit_track_caller,
872
+ struct_target_features,
873
+ struct_reachable_target_features,
874
+ ..* providers
875
+ } ;
763
876
}
0 commit comments