Skip to main content

rustc_middle/middle/codegen_fn_attrs/
ferrocene.rs

1use rustc_hir::def::DefKind;
2use rustc_hir::def_id::{DefId, LocalDefId};
3use rustc_hir::{
4    ForeignItem, ForeignItemKind, Item, ItemKind, Node, TraitFn, TraitItem, TraitItemKind,
5};
6use rustc_macros::{StableHash, TyDecodable, TyEncodable};
7use rustc_middle::ty::{self, TyCtxt};
8use rustc_span::{Span, Symbol, sym};
9use tracing::{info, instrument};
10
11#[derive(#[automatically_derived]
impl ::core::clone::Clone for Validated {
    #[inline]
    fn clone(&self) -> Validated { Validated {} }
}Clone, const _: () =
    {
        impl<'tcx, __E: ::rustc_middle::ty::codec::TyEncoder<'tcx>>
            ::rustc_serialize::Encodable<__E> for Validated {
            fn encode(&self, __encoder: &mut __E) {
                match *self { Validated {} => {} }
            }
        }
    };TyEncodable, const _: () =
    {
        impl<'tcx, __D: ::rustc_middle::ty::codec::TyDecoder<'tcx>>
            ::rustc_serialize::Decodable<__D> for Validated {
            fn decode(__decoder: &mut __D) -> Self { Validated {} }
        }
    };TyDecodable, const _: () =
    {
        impl ::rustc_data_structures::stable_hash::StableHash for Validated {
            #[inline]
            fn stable_hash<__Hcx: ::rustc_data_structures::stable_hash::StableHashCtxt>(&self,
                __hcx: &mut __Hcx,
                __hasher:
                    &mut ::rustc_data_structures::stable_hash::StableHasher) {
                match *self { Validated {} => {} }
            }
        }
    };StableHash, #[automatically_derived]
impl ::core::fmt::Debug for Validated {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f, "Validated")
    }
}Debug)]
12pub struct Validated {
13    // prevalidated: bool,
14    // FIXME: exported_constraint, validated, standards, qualification levels, entrypoint
15}
16
17const VALIDATED_ATTR: &[Symbol; 2] = &[sym::ferrocene, sym::prevalidated];
18
19#[derive(#[automatically_derived]
impl ::core::fmt::Debug for ValidatedStatus {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        match self {
            ValidatedStatus::Validated {
                annotation: __self_0, inherited: __self_1 } =>
                ::core::fmt::Formatter::debug_struct_field2_finish(f,
                    "Validated", "annotation", __self_0, "inherited",
                    &__self_1),
            ValidatedStatus::Unvalidated =>
                ::core::fmt::Formatter::write_str(f, "Unvalidated"),
            ValidatedStatus::WorkaroundDelegationBugs =>
                ::core::fmt::Formatter::write_str(f,
                    "WorkaroundDelegationBugs"),
        }
    }
}Debug)]
20pub enum ValidatedStatus {
21    Validated {
22        /// `annotation` is None if this is the `main` entrypoint, or if it was synthesized by
23        /// the compiler.
24        annotation: Option<Span>,
25        /// We want to distinguish between items that are *directly* annotated and items that are
26        /// *indirectly* annotated.
27        /// Indirecty annotated items can sometimes have no meaning: macros, `use` statements, etc.
28        /// But we may wish to give them a meaning in the future if they're directly annotated.
29        /// So: only consider indirect annotations for certain kinds of items.
30        inherited: bool,
31    },
32    Unvalidated,
33    WorkaroundDelegationBugs,
34}
35
36impl ValidatedStatus {
37    pub fn allowed_in_certified_build(self) -> bool {
38        match self {
39            ValidatedStatus::Validated { .. } => true,
40            ValidatedStatus::WorkaroundDelegationBugs | ValidatedStatus::Unvalidated => false,
41        }
42    }
43
44    pub fn needs_test(self) -> bool {
45        match self {
46            ValidatedStatus::Validated { annotation, inherited } => {
47                annotation.is_some() || inherited
48            }
49            _ => false,
50        }
51    }
52}
53
54fn implied_validation(tcx: TyCtxt<'_>, local: LocalDefId) -> Option<ValidatedStatus> {
55    match tcx.hir_node_by_def_id(local) {
56        // Skip intrinsics, extern functions, and associated functions with no default.
57        // We only have to do this for local DefIds; nothing makes it to post-mono without a body unless
58        // it's an intrinsic.
59        Node::Item(Item { kind: ItemKind::Fn { has_body: false, .. }, .. })
60            // FIXME: ForeignItems should be an exported_constraint
61            | Node::ForeignItem(ForeignItem { kind: ForeignItemKind::Fn(..), .. })
62            | Node::TraitItem(TraitItem { kind: TraitItemKind::Fn(_, TraitFn::Required(_)), .. }) => {
63                {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_middle/src/middle/codegen_fn_attrs/ferrocene.rs:63",
                        "rustc_middle::middle::codegen_fn_attrs::ferrocene",
                        ::tracing::Level::INFO,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_middle/src/middle/codegen_fn_attrs/ferrocene.rs"),
                        ::tracing_core::__macro_support::Option::Some(63u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_middle::middle::codegen_fn_attrs::ferrocene"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::INFO <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::INFO <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("skipping item {0:?}",
                                                    local) as &dyn ::tracing::field::Value))])
            });
    } else { ; }
};info!("skipping item {local:?}");
64                return Some(ValidatedStatus::Validated { annotation: None, inherited: false, });
65            }
66        Node::ImplItem(..) => {}
67        _ => return None,
68    }
69
70    // Check if this is an associated function from a `derive`.
71    let parent = tcx.parent(local.into());
72    {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_middle/src/middle/codegen_fn_attrs/ferrocene.rs:72",
                        "rustc_middle::middle::codegen_fn_attrs::ferrocene",
                        ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_middle/src/middle/codegen_fn_attrs/ferrocene.rs"),
                        ::tracing_core::__macro_support::Option::Some(72u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_middle::middle::codegen_fn_attrs::ferrocene"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("parent={1:?}, kind={0:?}",
                                                    tcx.def_kind(parent), parent) as
                                            &dyn ::tracing::field::Value))])
            });
    } else { ; }
};tracing::debug!("parent={parent:?}, kind={:?}", tcx.def_kind(parent));
73    #[allow(deprecated)]
74    if #[allow(non_exhaustive_omitted_patterns)] match tcx.def_kind(parent) {
    DefKind::Impl { .. } => true,
    _ => false,
}matches!(tcx.def_kind(parent), DefKind::Impl { .. }) {
75        {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_middle/src/middle/codegen_fn_attrs/ferrocene.rs:75",
                        "rustc_middle::middle::codegen_fn_attrs::ferrocene",
                        ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_middle/src/middle/codegen_fn_attrs/ferrocene.rs"),
                        ::tracing_core::__macro_support::Option::Some(75u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_middle::middle::codegen_fn_attrs::ferrocene"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("attrs={0:?}",
                                                    tcx.get_all_attrs(parent)) as
                                            &dyn ::tracing::field::Value))])
            });
    } else { ; }
};tracing::debug!("attrs={:?}", tcx.get_all_attrs(parent));
76    }
77
78    let derived = #[allow(non_exhaustive_omitted_patterns)] match tcx.def_kind(parent) {
    DefKind::Impl { .. } => true,
    _ => false,
}matches!(tcx.def_kind(parent), DefKind::Impl { .. })
79        && tcx.is_automatically_derived(parent);
80
81    if !derived {
82        return None;
83    }
84
85    // Builtin macros are covered under our compiler qualification and considered verified.
86    // We currently don't support proc-macros; assume them to be unverified.
87    if !tcx.is_builtin_derived(parent) {
88        return Some(ValidatedStatus::Unvalidated);
89    }
90
91    let self_ty = tcx.type_of(parent);
92    {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_middle/src/middle/codegen_fn_attrs/ferrocene.rs:92",
                        "rustc_middle::middle::codegen_fn_attrs::ferrocene",
                        ::tracing::Level::INFO,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_middle/src/middle/codegen_fn_attrs/ferrocene.rs"),
                        ::tracing_core::__macro_support::Option::Some(92u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_middle::middle::codegen_fn_attrs::ferrocene"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::INFO <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::INFO <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("checking {0:?}",
                                                    self_ty) as &dyn ::tracing::field::Value))])
            });
    } else { ; }
};info!("checking {self_ty:?}");
93    // FIXME: need to try to normalize this type so we handle aliases and associated types
94    let unwrapped_ty = self_ty.skip_binder().peel_refs();
95    match unwrapped_ty.kind() {
96        ty::Adt(adt_def, _) => {
97            let self_id = adt_def.did();
98            {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_middle/src/middle/codegen_fn_attrs/ferrocene.rs:98",
                        "rustc_middle::middle::codegen_fn_attrs::ferrocene",
                        ::tracing::Level::INFO,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_middle/src/middle/codegen_fn_attrs/ferrocene.rs"),
                        ::tracing_core::__macro_support::Option::Some(98u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_middle::middle::codegen_fn_attrs::ferrocene"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::INFO <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::INFO <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("{0} is marked #[automatically_derived], checking {1}",
                                                    tcx.def_path_str(local), tcx.def_path_str(self_id)) as
                                            &dyn ::tracing::field::Value))])
            });
    } else { ; }
};info!(
99                "{} is marked #[automatically_derived], checking {}",
100                tcx.def_path_str(local),
101                tcx.def_path_str(self_id)
102            );
103            return Some(item_is_validated(tcx, self_id));
104        }
105        ty::Error(..) => None,
106        _ if unwrapped_ty.is_primitive_ty() => None,
107        // We don't know how to resolve this back to a type.
108        // For now, just assume it's unvalidated.
109        _ => Some(ValidatedStatus::Unvalidated),
110    }
111}
112
113/// Shared between `rustc_lint` and `rustc_codegen_ssa` attr parsing.
114///
115/// This analysis needs to be conservative. If you don't have enough information to determine the
116/// status, assume it's unvalidated.
117x;#[instrument(skip(tcx), ret)]
118pub fn item_is_validated(tcx: TyCtxt<'_>, def_id: DefId) -> ValidatedStatus {
119    // A closure is validated if the function it's defined in is validated.
120    let owner = tcx.typeck_root_def_id(def_id);
121
122    // Skip items synthesized by the compiler.
123    let synthetic = match tcx.def_kind(owner) {
124        DefKind::Ctor(..) | DefKind::SyntheticCoroutineBody => true,
125        // NOTE: intrinsics might have a "fallback body" that is used as a default if the codegen
126        // backend doesn't override it.
127        _ => tcx.intrinsic(owner).is_some_and(|def| def.must_be_overridden),
128    };
129    if synthetic {
130        info!("skipping synthetic item {owner:?}");
131        return ValidatedStatus::Validated { annotation: None, inherited: false };
132    }
133
134    if let Some(local) = owner.as_local()
135        && let Some(status) = implied_validation(tcx, local)
136    {
137        return status;
138    }
139
140    if let Some(annotation) = any_parent_is_validated(tcx, owner) {
141        return annotation;
142    }
143
144    // HACK: `feature(delegation)` is horribly buggy and causes infinite cycles within the query
145    // system itself. While we wait for upstream to fix it, work around the issue.
146    // See https://github.com/rust-lang/rust/pull/154387 for more information.
147    let delegation_enabled = tcx.dcx().has_errors().is_some() || tcx.features().fn_delegation();
148    let is_main = delegation_enabled || tcx.entry_fn(()).map_or(false, |(main, _)| main == owner);
149    // We normally assume main functions are validated, but not for the
150    // shim generated by `rustc --test`.
151    let main_is_validated = is_main && !tcx.sess.opts.test;
152
153    if delegation_enabled {
154        // Avoid trying to run our lint on items with delegation errors.
155        ValidatedStatus::WorkaroundDelegationBugs
156    } else if main_is_validated {
157        ValidatedStatus::Validated { annotation: None, inherited: false }
158    } else {
159        ValidatedStatus::Unvalidated
160    }
161}
162
163/// Check if this item or any of its parents are validated.
164fn any_parent_is_validated(tcx: TyCtxt<'_>, item: DefId) -> Option<ValidatedStatus> {
165    let mut current = item;
166    loop {
167        // Check if it's possible for this item to have attributes.
168        // If not, skip it so `get_attrs` doesn't panic.
169        match tcx.def_kind(current) {
170            DefKind::ForeignMod => {
171                current = tcx.parent(current);
172                continue;
173            }
174            _ => {}
175        }
176        // Check if the current item has an annotation
177        if let Some(attr) = tcx.get_attrs_by_path(current, VALIDATED_ATTR).next() {
178            return Some(ValidatedStatus::Validated {
179                annotation: Some(attr.span()),
180                inherited: current != item,
181            });
182        }
183
184        if current.is_crate_root() {
185            return None;
186        }
187
188        current = tcx.parent(current);
189    }
190}