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:: * , struct_span_code_err, DiagMessage , SubdiagMessage } ;
4
5
use rustc_hir as hir;
5
6
use rustc_hir:: def:: DefKind ;
@@ -13,8 +14,10 @@ use rustc_middle::query::Providers;
13
14
use rustc_middle:: ty:: { self as ty, TyCtxt } ;
14
15
use rustc_session:: { lint, parse:: feature_err} ;
15
16
use rustc_span:: symbol:: Ident ;
16
- use rustc_span:: { sym, Span } ;
17
+ use rustc_span:: { sym, Span , Symbol } ;
18
+ use rustc_target:: abi:: VariantIdx ;
17
19
use rustc_target:: spec:: { abi, SanitizerSet } ;
20
+ use rustc_type_ir:: inherent:: * ;
18
21
19
22
use crate :: errors;
20
23
use crate :: target_features:: { check_target_feature_trait_unsafe, from_target_feature} ;
@@ -75,23 +78,26 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
75
78
let mut link_ordinal_span = None ;
76
79
let mut no_sanitize_span = None ;
77
80
81
+ // In some cases, attribute are only valid on functions, but it's the `check_attr`
82
+ // pass that check that they aren't used anywhere else, rather this module.
83
+ // In these cases, we bail from performing further checks that are only meaningful for
84
+ // functions (such as calling `fn_sig`, which ICEs if given a non-function). We also
85
+ // report a delayed bug, just in case `check_attr` isn't doing its job.
86
+ let fn_sig_outer = || {
87
+ use DefKind :: * ;
88
+
89
+ let def_kind = tcx. def_kind ( did) ;
90
+ if let Fn | AssocFn | Variant | Ctor ( ..) = def_kind { Some ( tcx. fn_sig ( did) ) } else { None }
91
+ } ;
92
+
78
93
for attr in attrs. iter ( ) {
79
- // In some cases, attribute are only valid on functions, but it's the `check_attr`
80
- // pass that check that they aren't used anywhere else, rather this module.
81
- // In these cases, we bail from performing further checks that are only meaningful for
82
- // functions (such as calling `fn_sig`, which ICEs if given a non-function). We also
83
- // report a delayed bug, just in case `check_attr` isn't doing its job.
84
94
let fn_sig = || {
85
- use DefKind :: * ;
86
-
87
- let def_kind = tcx. def_kind ( did) ;
88
- if let Fn | AssocFn | Variant | Ctor ( ..) = def_kind {
89
- Some ( tcx. fn_sig ( did) )
90
- } else {
95
+ let sig = fn_sig_outer ( ) ;
96
+ if sig. is_none ( ) {
91
97
tcx. dcx ( )
92
98
. span_delayed_bug ( attr. span , "this attribute can only be applied to functions" ) ;
93
- None
94
99
}
100
+ sig
95
101
} ;
96
102
97
103
let Some ( Ident { name, .. } ) = attr. ident ( ) else {
@@ -610,6 +616,57 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
610
616
}
611
617
}
612
618
619
+ if tcx. features ( ) . struct_target_features
620
+ && let Some ( sig) = fn_sig_outer ( )
621
+ {
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: 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
+ if ty. is_ref ( ) {
638
+ reachable_types. push ( ty. builtin_deref ( false ) . unwrap ( ) ) ;
639
+ } else if matches ! ( ty. kind( ) , ty:: Tuple ( _) ) {
640
+ reachable_types. extend ( ty. tuple_fields ( ) . iter ( ) ) ;
641
+ } else if let ty:: Adt ( adt_def, args) = ty. kind ( ) {
642
+ additional_tf. extend_from_slice ( & adt_def. target_features ( ) ) ;
643
+ if adt_def. is_struct ( ) {
644
+ reachable_types. extend (
645
+ adt_def
646
+ . variant ( VariantIdx :: from_usize ( 0 ) )
647
+ . fields
648
+ . iter ( )
649
+ . map ( |field| field. ty ( tcx, args) ) ,
650
+ ) ;
651
+ }
652
+ }
653
+ }
654
+
655
+ if !additional_tf. is_empty ( ) && !sig. skip_binder ( ) . abi ( ) . is_rust ( ) {
656
+ tcx. dcx ( ) . span_err (
657
+ tcx. hir ( ) . span ( tcx. local_def_id_to_hir_id ( did) ) ,
658
+ "cannot use a struct with target features in a function with non-Rust ABI" ,
659
+ ) ;
660
+ }
661
+ if !additional_tf. is_empty ( ) && codegen_fn_attrs. inline == InlineAttr :: Always {
662
+ tcx. dcx ( ) . span_err (
663
+ tcx. hir ( ) . span ( tcx. local_def_id_to_hir_id ( did) ) ,
664
+ "cannot use a struct with target features in a #[inline(always)] function" ,
665
+ ) ;
666
+ }
667
+ codegen_fn_attrs. target_features . extend_from_slice ( & additional_tf) ;
668
+ }
669
+
613
670
// If a function uses #[target_feature] it can't be inlined into general
614
671
// purpose functions as they wouldn't have the right target features
615
672
// enabled. For that reason we also forbid #[inline(always)] as it can't be
@@ -755,6 +812,20 @@ fn check_link_name_xor_ordinal(
755
812
}
756
813
}
757
814
815
+ fn struct_target_features ( tcx : TyCtxt < ' _ > , def_id : DefId ) -> Vec < Symbol > {
816
+ let mut features = vec ! [ ] ;
817
+ let supported_features = tcx. supported_target_features ( def_id. krate ) ;
818
+ for attr in tcx. get_attrs ( def_id, sym:: target_feature) {
819
+ from_target_feature ( tcx, attr, supported_features, & mut features) ;
820
+ }
821
+ features
822
+ }
823
+
758
824
pub fn provide ( providers : & mut Providers ) {
759
- * providers = Providers { codegen_fn_attrs, should_inherit_track_caller, ..* providers } ;
825
+ * providers = Providers {
826
+ codegen_fn_attrs,
827
+ should_inherit_track_caller,
828
+ struct_target_features,
829
+ ..* providers
830
+ } ;
760
831
}
0 commit comments