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:: * , struct_span_code_err, DiagMessage , SubdiagMessage } ;
45use rustc_hir as hir;
56use rustc_hir:: def:: DefKind ;
@@ -14,7 +15,9 @@ use rustc_middle::ty::{self as ty, TyCtxt};
1415use rustc_session:: { lint, parse:: feature_err} ;
1516use rustc_span:: symbol:: Ident ;
1617use rustc_span:: { sym, Span } ;
18+ use rustc_target:: abi:: VariantIdx ;
1719use rustc_target:: spec:: { abi, SanitizerSet } ;
20+ use rustc_type_ir:: inherent:: Abi ;
1821
1922use crate :: errors;
2023use crate :: target_features:: { check_target_feature_trait_unsafe, from_target_feature} ;
@@ -75,23 +78,26 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
7578 let mut link_ordinal_span = None ;
7679 let mut no_sanitize_span = None ;
7780
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+
7893 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.
8494 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 ( ) {
9197 tcx. dcx ( )
9298 . span_delayed_bug ( attr. span , "this attribute can only be applied to functions" ) ;
93- None
9499 }
100+ sig
95101 } ;
96102
97103 let Some ( Ident { name, .. } ) = attr. ident ( ) else {
@@ -610,6 +616,57 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
610616 }
611617 }
612618
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+ // TODO: presumably we 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+
613670 // If a function uses #[target_feature] it can't be inlined into general
614671 // purpose functions as they wouldn't have the right target features
615672 // enabled. For that reason we also forbid #[inline(always)] as it can't be
0 commit comments