Skip to main content

rustc_lint/
foreign_modules.rs

1use rustc_abi::FIRST_VARIANT;
2use rustc_attr_ir::find_attr;
3use rustc_data_structures::unord::{UnordMap, UnordSet};
4use rustc_hir as hir;
5use rustc_hir::def::DefKind;
6use rustc_lint_defs::declare_lint;
7use rustc_middle::query::Providers;
8use rustc_middle::ty::{self, AdtDef, Instance, Ty, TyCtxt};
9use rustc_span::{Span, Symbol};
10use tracing::{debug, instrument};
11
12use crate::diagnostics::{BuiltinClashingExtern, BuiltinClashingExternSub};
13use crate::{LintVec, types};
14
15pub(crate) fn provide(providers: &mut Providers) {
16    *providers = Providers { clashing_extern_declarations, ..*providers };
17}
18
19pub(crate) fn lint_vec() -> LintVec {
20    ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [CLASHING_EXTERN_DECLARATIONS]))vec![CLASHING_EXTERN_DECLARATIONS]
21}
22
23fn clashing_extern_declarations(tcx: TyCtxt<'_>, (): ()) {
24    let mut lint = ClashingExternDeclarations::new();
25    for id in tcx.hir_crate_items(()).foreign_items() {
26        lint.check_foreign_item(tcx, id);
27    }
28}
29
30#[doc =
r" The `clashing_extern_declarations` lint detects when an `extern fn`"]
#[doc = r" has been declared with the same name but different types."]
#[doc = r""]
#[doc = r" ### Example"]
#[doc = r""]
#[doc = r" ```rust"]
#[doc = r" mod m {"]
#[doc = r#"     unsafe extern "C" {"#]
#[doc = r"         fn foo();"]
#[doc = r"     }"]
#[doc = r" }"]
#[doc = r""]
#[doc = r#" unsafe extern "C" {"#]
#[doc = r"     fn foo(_: u32);"]
#[doc = r" }"]
#[doc = r" ```"]
#[doc = r""]
#[doc = r" {{produces}}"]
#[doc = r""]
#[doc = r" ### Explanation"]
#[doc = r""]
#[doc = r" Because two symbols of the same name cannot be resolved to two"]
#[doc =
r" different functions at link time, and one function cannot possibly"]
#[doc =
r" have two types, a clashing extern declaration is almost certainly a"]
#[doc =
r" mistake. Check to make sure that the `extern` definitions are correct"]
#[doc =
r" and equivalent, and possibly consider unifying them in one location."]
#[doc = r""]
#[doc = r" This lint does not run between crates because a project may have"]
#[doc =
r" dependencies which both rely on the same extern function, but declare"]
#[doc =
r" it in a different (but valid) way. For example, they may both declare"]
#[doc =
r" an opaque type for one or more of the arguments (which would end up"]
#[doc = r" distinct types), or use types that are valid conversions in the"]
#[doc =
r" language the `extern fn` is defined in. In these cases, the compiler"]
#[doc = r" can't say that the clashing declaration is incorrect."]
pub static CLASHING_EXTERN_DECLARATIONS: &::rustc_lint_defs::Lint =
    &::rustc_lint_defs::Lint {
            name: "CLASHING_EXTERN_DECLARATIONS",
            default_level: ::rustc_lint_defs::Warn,
            desc: "detects when an extern fn has been declared with the same name but different types",
            ..
        };declare_lint! {
31    /// The `clashing_extern_declarations` lint detects when an `extern fn`
32    /// has been declared with the same name but different types.
33    ///
34    /// ### Example
35    ///
36    /// ```rust
37    /// mod m {
38    ///     unsafe extern "C" {
39    ///         fn foo();
40    ///     }
41    /// }
42    ///
43    /// unsafe extern "C" {
44    ///     fn foo(_: u32);
45    /// }
46    /// ```
47    ///
48    /// {{produces}}
49    ///
50    /// ### Explanation
51    ///
52    /// Because two symbols of the same name cannot be resolved to two
53    /// different functions at link time, and one function cannot possibly
54    /// have two types, a clashing extern declaration is almost certainly a
55    /// mistake. Check to make sure that the `extern` definitions are correct
56    /// and equivalent, and possibly consider unifying them in one location.
57    ///
58    /// This lint does not run between crates because a project may have
59    /// dependencies which both rely on the same extern function, but declare
60    /// it in a different (but valid) way. For example, they may both declare
61    /// an opaque type for one or more of the arguments (which would end up
62    /// distinct types), or use types that are valid conversions in the
63    /// language the `extern fn` is defined in. In these cases, the compiler
64    /// can't say that the clashing declaration is incorrect.
65    pub CLASHING_EXTERN_DECLARATIONS,
66    Warn,
67    "detects when an extern fn has been declared with the same name but different types"
68}
69
70struct ClashingExternDeclarations {
71    /// Map of function symbol name to the first-seen hir id for that symbol name.. If seen_decls
72    /// contains an entry for key K, it means a symbol with name K has been seen by this lint and
73    /// the symbol should be reported as a clashing declaration.
74    // FIXME: Technically, we could just store a &'tcx str here without issue; however, the
75    // `impl_lint_pass` macro doesn't currently support lints parametric over a lifetime.
76    seen_decls: UnordMap<Symbol, hir::OwnerId>,
77}
78
79struct SymbolName {
80    /// The name of the symbol that will be linked against.
81    link_name: Symbol,
82    /// The span of the annotation which introduced the link name.
83    span: Option<Span>,
84}
85
86impl ClashingExternDeclarations {
87    pub(crate) fn new() -> Self {
88        ClashingExternDeclarations { seen_decls: Default::default() }
89    }
90
91    /// Insert a new foreign item into the seen set. If a symbol with the same name already exists
92    /// for the item, return its HirId without updating the set.
93    fn insert(&mut self, tcx: TyCtxt<'_>, fi: hir::ForeignItemId) -> Option<hir::OwnerId> {
94        let did = fi.owner_id.to_def_id();
95        let instance = Instance::new_raw(did, ty::GenericArgs::identity_for_item(tcx, did));
96        let name = Symbol::intern(tcx.symbol_name(instance).name);
97        if let Some(&existing_id) = self.seen_decls.get(&name) {
98            // Avoid updating the map with the new entry when we do find a collision. We want to
99            // make sure we're always pointing to the first definition as the previous declaration.
100            // This lets us avoid emitting "knock-on" diagnostics.
101            Some(existing_id)
102        } else {
103            self.seen_decls.insert(name, fi.owner_id)
104        }
105    }
106
107    {}
#[allow(clippy :: suspicious_else_formatting)]
{
    let __tracing_attr_span;
    let __tracing_attr_guard;
    if ::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::TRACE <=
                    ::tracing::level_filters::LevelFilter::current() ||
            { false } {
        __tracing_attr_span =
            {
                use ::tracing::__macro_support::Callsite as _;
                static __CALLSITE: ::tracing::callsite::DefaultCallsite =
                    {
                        static META: ::tracing::Metadata<'static> =
                            {
                                ::tracing_core::metadata::Metadata::new("check_foreign_item",
                                    "rustc_lint::foreign_modules", ::tracing::Level::TRACE,
                                    ::tracing_core::__macro_support::Option::Some("/rustc-dev/3ef0c22cdb86ac42dc6b8f0f1ec36857daa6f2a1/compiler/rustc_lint/src/foreign_modules.rs"),
                                    ::tracing_core::__macro_support::Option::Some(107u32),
                                    ::tracing_core::__macro_support::Option::Some("rustc_lint::foreign_modules"),
                                    ::tracing_core::field::FieldSet::new(&[{
                                                        const NAME:
                                                            ::tracing::__macro_support::FieldName<{
                                                                ::tracing::__macro_support::FieldName::len("this_fi")
                                                            }> =
                                                            ::tracing::__macro_support::FieldName::new("this_fi");
                                                        NAME.as_str()
                                                    }], ::tracing_core::callsite::Identifier(&__CALLSITE)),
                                    ::tracing::metadata::Kind::SPAN)
                            };
                        ::tracing::callsite::DefaultCallsite::new(&META)
                    };
                let mut interest = ::tracing::subscriber::Interest::never();
                if ::tracing::Level::TRACE <=
                                    ::tracing::level_filters::STATIC_MAX_LEVEL &&
                                ::tracing::Level::TRACE <=
                                    ::tracing::level_filters::LevelFilter::current() &&
                            { interest = __CALLSITE.interest(); !interest.is_never() }
                        &&
                        ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                            interest) {
                    let meta = __CALLSITE.metadata();
                    ::tracing::Span::new(meta,
                        &{
                                #[allow(unused_imports)]
                                use ::tracing::field::{debug, display, Value};
                                meta.fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&this_fi)
                                                            as &dyn ::tracing::field::Value))])
                            })
                } else {
                    let span =
                        ::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
                    {};
                    span
                }
            };
        __tracing_attr_guard = __tracing_attr_span.enter();
    }

    #[warn(clippy :: suspicious_else_formatting)]
    {

        #[allow(unknown_lints, unreachable_code, clippy ::
        diverging_sub_expression, clippy :: empty_loop, clippy ::
        let_unit_value, clippy :: let_with_type_underscore, clippy ::
        needless_return, clippy :: unreachable)]
        if false {
            let __tracing_attr_fake_return: () = loop {};
            return __tracing_attr_fake_return;
        }
        {
            let DefKind::Fn = tcx.def_kind(this_fi.owner_id) else { return };
            let Some(existing_did) =
                self.insert(tcx, this_fi) else { return };
            let existing_decl_ty = tcx.type_of(existing_did).skip_binder();
            let this_decl_ty =
                tcx.type_of(this_fi.owner_id).instantiate_identity().skip_norm_wip();
            {
                use ::tracing::__macro_support::Callsite as _;
                static __CALLSITE: ::tracing::callsite::DefaultCallsite =
                    {
                        static META: ::tracing::Metadata<'static> =
                            {
                                ::tracing_core::metadata::Metadata::new("event /rustc-dev/3ef0c22cdb86ac42dc6b8f0f1ec36857daa6f2a1/compiler/rustc_lint/src/foreign_modules.rs:114",
                                    "rustc_lint::foreign_modules", ::tracing::Level::DEBUG,
                                    ::tracing_core::__macro_support::Option::Some("/rustc-dev/3ef0c22cdb86ac42dc6b8f0f1ec36857daa6f2a1/compiler/rustc_lint/src/foreign_modules.rs"),
                                    ::tracing_core::__macro_support::Option::Some(114u32),
                                    ::tracing_core::__macro_support::Option::Some("rustc_lint::foreign_modules"),
                                    ::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!("ClashingExternDeclarations: Comparing existing {0:?}: {1:?} to this {2:?}: {3:?}",
                                                                existing_did, existing_decl_ty, this_fi.owner_id,
                                                                this_decl_ty) as &dyn ::tracing::field::Value))])
                        });
                } else { ; }
            };
            if !structurally_same_type(tcx,
                        ty::TypingEnv::non_body_analysis(tcx, this_fi.owner_id),
                        existing_decl_ty, this_decl_ty) {
                let orig = name_of_extern_decl(tcx, existing_did);
                let this = tcx.item_name(this_fi.owner_id.to_def_id());
                let orig = orig.link_name;
                let previous_decl_label =
                    get_relevant_span(tcx, existing_did);
                let mismatch_label = get_relevant_span(tcx, this_fi.owner_id);
                let sub =
                    BuiltinClashingExternSub {
                        tcx,
                        expected: existing_decl_ty,
                        found: this_decl_ty,
                    };
                let decorator =
                    if orig == this {
                        BuiltinClashingExtern::SameName {
                            this,
                            orig,
                            previous_decl_label,
                            mismatch_label,
                            sub,
                        }
                    } else {
                        BuiltinClashingExtern::DiffName {
                            this,
                            orig,
                            previous_decl_label,
                            mismatch_label,
                            sub,
                        }
                    };
                tcx.emit_node_span_lint(CLASHING_EXTERN_DECLARATIONS,
                    this_fi.hir_id(), mismatch_label, decorator);
            }
        }
    }
}#[instrument(level = "trace", skip(self, tcx))]
108    fn check_foreign_item<'tcx>(&mut self, tcx: TyCtxt<'tcx>, this_fi: hir::ForeignItemId) {
109        let DefKind::Fn = tcx.def_kind(this_fi.owner_id) else { return };
110        let Some(existing_did) = self.insert(tcx, this_fi) else { return };
111
112        let existing_decl_ty = tcx.type_of(existing_did).skip_binder();
113        let this_decl_ty = tcx.type_of(this_fi.owner_id).instantiate_identity().skip_norm_wip();
114        debug!(
115            "ClashingExternDeclarations: Comparing existing {:?}: {:?} to this {:?}: {:?}",
116            existing_did, existing_decl_ty, this_fi.owner_id, this_decl_ty
117        );
118
119        // Check that the declarations match.
120        if !structurally_same_type(
121            tcx,
122            ty::TypingEnv::non_body_analysis(tcx, this_fi.owner_id),
123            existing_decl_ty,
124            this_decl_ty,
125        ) {
126            let orig = name_of_extern_decl(tcx, existing_did);
127
128            // Finally, emit the diagnostic.
129            let this = tcx.item_name(this_fi.owner_id.to_def_id());
130            let orig = orig.link_name;
131            let previous_decl_label = get_relevant_span(tcx, existing_did);
132            let mismatch_label = get_relevant_span(tcx, this_fi.owner_id);
133            let sub =
134                BuiltinClashingExternSub { tcx, expected: existing_decl_ty, found: this_decl_ty };
135            let decorator = if orig == this {
136                BuiltinClashingExtern::SameName {
137                    this,
138                    orig,
139                    previous_decl_label,
140                    mismatch_label,
141                    sub,
142                }
143            } else {
144                BuiltinClashingExtern::DiffName {
145                    this,
146                    orig,
147                    previous_decl_label,
148                    mismatch_label,
149                    sub,
150                }
151            };
152            tcx.emit_node_span_lint(
153                CLASHING_EXTERN_DECLARATIONS,
154                this_fi.hir_id(),
155                mismatch_label,
156                decorator,
157            );
158        }
159    }
160}
161
162/// Get the name of the symbol that's linked against for a given extern declaration. That is,
163/// the name specified in a #[link_name = ...] attribute if one was specified, else, just the
164/// symbol's name.
165fn name_of_extern_decl(tcx: TyCtxt<'_>, fi: hir::OwnerId) -> SymbolName {
166    // FIXME if symbol_name is not set, this is likely a #[rustc_std_internal_symbol] or EII which
167    // actually have their name mangled and thus should use the mangled name here.
168    let link_name =
169        tcx.codegen_fn_attrs(fi).symbol_name.unwrap_or_else(|| tcx.item_name(fi.to_def_id()));
170    SymbolName {
171        link_name,
172        // FIXME: Instead of searching through the attributes again to get span
173        // information, we could have codegen_fn_attrs also give span information back for
174        // where the attribute was defined. However, until this is found to be a
175        // bottleneck, this does just fine.
176        span: {
    {
        'done:
            {
            for i in ::rustc_attr_ir::HasAttrs::get_attrs(fi, &tcx) {
                #[allow(unused_imports)]
                use ::rustc_attr_ir::AttributeKind::*;
                let i: &::rustc_attr_ir::Attribute = i;
                match i {
                    ::rustc_attr_ir::Attribute::Parsed(LinkName { span, .. }) =>
                        {
                        break 'done Some(*span);
                    }
                    ::rustc_attr_ir::Attribute::Tool(..) |
                        ::rustc_attr_ir::Attribute::DeriveHelper(..) =>
                        {}
                        #[deny(unreachable_patterns)]
                        _ => {}
                }
            }
            None
        }
    }
}find_attr!(tcx, fi, LinkName {span, ..} => *span),
177    }
178}
179
180/// We want to ensure that we use spans for both decls that include where the
181/// name was defined, whether that was from the link_name attribute or not.
182fn get_relevant_span(tcx: TyCtxt<'_>, fi: hir::OwnerId) -> Span {
183    if let Some(span) = name_of_extern_decl(tcx, fi).span { span } else { tcx.def_span(fi) }
184}
185
186/// Checks whether two types are structurally the same enough that the declarations shouldn't
187/// clash. We need this so we don't emit a lint when two modules both declare an extern struct,
188/// with the same members (as the declarations shouldn't clash).
189fn structurally_same_type<'tcx>(
190    tcx: TyCtxt<'tcx>,
191    typing_env: ty::TypingEnv<'tcx>,
192    a: Ty<'tcx>,
193    b: Ty<'tcx>,
194) -> bool {
195    let mut seen_types = UnordSet::default();
196    let result = structurally_same_type_impl(&mut seen_types, tcx, typing_env, a, b);
197    if truecfg!(debug_assertions) && result {
198        // Sanity-check: must have same ABI, size and alignment.
199        // `extern` blocks cannot be generic, so we'll always get a layout here.
200        let a_layout = tcx.layout_of(typing_env.as_query_input(a)).unwrap();
201        let b_layout = tcx.layout_of(typing_env.as_query_input(b)).unwrap();
202        {
    match (&a_layout.backend_repr, &b_layout.backend_repr) {
        (left_val, right_val) => {
            if !(*left_val == *right_val) {
                let kind = ::core::panicking::AssertKind::Eq;
                ::core::panicking::assert_failed(kind, &*left_val,
                    &*right_val, ::core::option::Option::None);
            }
        }
    }
};assert_eq!(a_layout.backend_repr, b_layout.backend_repr);
203        {
    match (&a_layout.size, &b_layout.size) {
        (left_val, right_val) => {
            if !(*left_val == *right_val) {
                let kind = ::core::panicking::AssertKind::Eq;
                ::core::panicking::assert_failed(kind, &*left_val,
                    &*right_val, ::core::option::Option::None);
            }
        }
    }
};assert_eq!(a_layout.size, b_layout.size);
204        {
    match (&a_layout.align, &b_layout.align) {
        (left_val, right_val) => {
            if !(*left_val == *right_val) {
                let kind = ::core::panicking::AssertKind::Eq;
                ::core::panicking::assert_failed(kind, &*left_val,
                    &*right_val, ::core::option::Option::None);
            }
        }
    }
};assert_eq!(a_layout.align, b_layout.align);
205    }
206    result
207}
208
209fn structurally_same_type_impl<'tcx>(
210    seen_types: &mut UnordSet<(Ty<'tcx>, Ty<'tcx>)>,
211    tcx: TyCtxt<'tcx>,
212    typing_env: ty::TypingEnv<'tcx>,
213    a: Ty<'tcx>,
214    b: Ty<'tcx>,
215) -> bool {
216    {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event /rustc-dev/3ef0c22cdb86ac42dc6b8f0f1ec36857daa6f2a1/compiler/rustc_lint/src/foreign_modules.rs:216",
                        "rustc_lint::foreign_modules", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("/rustc-dev/3ef0c22cdb86ac42dc6b8f0f1ec36857daa6f2a1/compiler/rustc_lint/src/foreign_modules.rs"),
                        ::tracing_core::__macro_support::Option::Some(216u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_lint::foreign_modules"),
                        ::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!("structurally_same_type_impl(tcx, a = {0:?}, b = {1:?})",
                                                    a, b) as &dyn ::tracing::field::Value))])
            });
    } else { ; }
};debug!("structurally_same_type_impl(tcx, a = {:?}, b = {:?})", a, b);
217
218    // Given a transparent newtype, reach through and grab the inner
219    // type unless the newtype makes the type non-null.
220    let non_transparent_ty = |mut ty: Ty<'tcx>| -> Ty<'tcx> {
221        loop {
222            if let ty::Adt(def, args) = *ty.kind() {
223                let is_transparent = def.repr().transparent();
224                let is_non_null = types::nonnull_optimization_guaranteed(tcx, def);
225                {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event /rustc-dev/3ef0c22cdb86ac42dc6b8f0f1ec36857daa6f2a1/compiler/rustc_lint/src/foreign_modules.rs:225",
                        "rustc_lint::foreign_modules", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("/rustc-dev/3ef0c22cdb86ac42dc6b8f0f1ec36857daa6f2a1/compiler/rustc_lint/src/foreign_modules.rs"),
                        ::tracing_core::__macro_support::Option::Some(225u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_lint::foreign_modules"),
                        ::tracing_core::field::FieldSet::new(&[{
                                            const NAME:
                                                ::tracing::__macro_support::FieldName<{
                                                    ::tracing::__macro_support::FieldName::len("ty")
                                                }> =
                                                ::tracing::__macro_support::FieldName::new("ty");
                                            NAME.as_str()
                                        },
                                        {
                                            const NAME:
                                                ::tracing::__macro_support::FieldName<{
                                                    ::tracing::__macro_support::FieldName::len("is_transparent")
                                                }> =
                                                ::tracing::__macro_support::FieldName::new("is_transparent");
                                            NAME.as_str()
                                        },
                                        {
                                            const NAME:
                                                ::tracing::__macro_support::FieldName<{
                                                    ::tracing::__macro_support::FieldName::len("is_non_null")
                                                }> =
                                                ::tracing::__macro_support::FieldName::new("is_non_null");
                                            NAME.as_str()
                                        }], ::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(&::tracing::field::debug(&ty)
                                            as &dyn ::tracing::field::Value)),
                                (::tracing::__macro_support::Option::Some(&is_transparent as
                                            &dyn ::tracing::field::Value)),
                                (::tracing::__macro_support::Option::Some(&is_non_null as
                                            &dyn ::tracing::field::Value))])
            });
    } else { ; }
};debug!(?ty, is_transparent, is_non_null);
226                if is_transparent && !is_non_null {
227                    if true {
    {
        match (&def.variants().len(), &1) {
            (left_val, right_val) => {
                if !(*left_val == *right_val) {
                    let kind = ::core::panicking::AssertKind::Eq;
                    ::core::panicking::assert_failed(kind, &*left_val,
                        &*right_val, ::core::option::Option::None);
                }
            }
        }
    };
};debug_assert_eq!(def.variants().len(), 1);
228                    let v = &def.variant(FIRST_VARIANT);
229                    // continue with `ty`'s non-ZST field,
230                    // otherwise `ty` is a ZST and we can return
231                    if let Some(field) = types::transparent_newtype_field(tcx, v) {
232                        ty = field.ty(tcx, args).skip_norm_wip();
233                        continue;
234                    }
235                }
236            }
237            {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event /rustc-dev/3ef0c22cdb86ac42dc6b8f0f1ec36857daa6f2a1/compiler/rustc_lint/src/foreign_modules.rs:237",
                        "rustc_lint::foreign_modules", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("/rustc-dev/3ef0c22cdb86ac42dc6b8f0f1ec36857daa6f2a1/compiler/rustc_lint/src/foreign_modules.rs"),
                        ::tracing_core::__macro_support::Option::Some(237u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_lint::foreign_modules"),
                        ::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!("non_transparent_ty -> {0:?}",
                                                    ty) as &dyn ::tracing::field::Value))])
            });
    } else { ; }
};debug!("non_transparent_ty -> {:?}", ty);
238            return ty;
239        }
240    };
241
242    let a = non_transparent_ty(a);
243    let b = non_transparent_ty(b);
244
245    if !seen_types.insert((a, b)) {
246        // We've encountered a cycle. There's no point going any further -- the types are
247        // structurally the same.
248        true
249    } else if a == b {
250        // All nominally-same types are structurally same, too.
251        true
252    } else {
253        // Do a full, depth-first comparison between the two.
254        let is_primitive_or_pointer =
255            |ty: Ty<'tcx>| ty.is_primitive() || #[allow(non_exhaustive_omitted_patterns)] match ty.kind() {
    ty::RawPtr(..) | ty::Ref(..) => true,
    _ => false,
}matches!(ty.kind(), ty::RawPtr(..) | ty::Ref(..));
256
257        match (a.kind(), b.kind()) {
258            (&ty::Adt(a_def, a_gen_args), &ty::Adt(b_def, b_gen_args)) => {
259                // Only `repr(C)` types can be compared structurally.
260                if !(a_def.repr().c() && b_def.repr().c()) {
261                    return false;
262                }
263                // If the types differ in their packed-ness, align, or simd-ness they conflict.
264                let repr_characteristica =
265                    |def: AdtDef<'tcx>| (def.repr().pack, def.repr().align, def.repr().simd());
266                if repr_characteristica(a_def) != repr_characteristica(b_def) {
267                    return false;
268                }
269
270                // Grab a flattened representation of all fields.
271                let a_fields = a_def.variants().iter().flat_map(|v| v.fields.iter());
272                let b_fields = b_def.variants().iter().flat_map(|v| v.fields.iter());
273
274                // Perform a structural comparison for each field.
275                a_fields.eq_by(
276                    b_fields,
277                    |&ty::FieldDef { did: a_did, .. }, &ty::FieldDef { did: b_did, .. }| {
278                        structurally_same_type_impl(
279                            seen_types,
280                            tcx,
281                            typing_env,
282                            tcx.type_of(a_did).instantiate(tcx, a_gen_args).skip_norm_wip(),
283                            tcx.type_of(b_did).instantiate(tcx, b_gen_args).skip_norm_wip(),
284                        )
285                    },
286                )
287            }
288            (ty::Array(a_ty, a_len), ty::Array(b_ty, b_len)) => {
289                // For arrays, we also check the length.
290                a_len == b_len
291                    && structurally_same_type_impl(seen_types, tcx, typing_env, *a_ty, *b_ty)
292            }
293            (ty::Slice(a_ty), ty::Slice(b_ty)) => {
294                structurally_same_type_impl(seen_types, tcx, typing_env, *a_ty, *b_ty)
295            }
296            (ty::RawPtr(a_ty, a_mutbl), ty::RawPtr(b_ty, b_mutbl)) => {
297                a_mutbl == b_mutbl
298                    && structurally_same_type_impl(seen_types, tcx, typing_env, *a_ty, *b_ty)
299            }
300            (ty::Ref(_a_region, a_ty, a_mut), ty::Ref(_b_region, b_ty, b_mut)) => {
301                // For structural sameness, we don't need the region to be same.
302                a_mut == b_mut
303                    && structurally_same_type_impl(seen_types, tcx, typing_env, *a_ty, *b_ty)
304            }
305            (ty::FnDef(..), ty::FnDef(..)) => {
306                let a_poly_sig = a.fn_sig(tcx);
307                let b_poly_sig = b.fn_sig(tcx);
308
309                // We don't compare regions, but leaving bound regions around ICEs, so
310                // we erase them.
311                let a_sig = tcx.instantiate_bound_regions_with_erased(a_poly_sig);
312                let b_sig = tcx.instantiate_bound_regions_with_erased(b_poly_sig);
313
314                // FIXME(splat): Is splatting ever repr(C)?
315                // Can two splatted functions to have the same structure?
316                // Can a splatted and non-splatted function have the same structure?
317                // For now, we require splatting to match exactly.
318                if a_sig.splatted() != b_sig.splatted() {
319                    return false;
320                }
321
322                (a_sig.abi(), a_sig.safety(), a_sig.c_variadic())
323                    == (b_sig.abi(), b_sig.safety(), b_sig.c_variadic())
324                    && a_sig.inputs().iter().eq_by(b_sig.inputs().iter(), |a, b| {
325                        structurally_same_type_impl(seen_types, tcx, typing_env, *a, *b)
326                    })
327                    && structurally_same_type_impl(
328                        seen_types,
329                        tcx,
330                        typing_env,
331                        a_sig.output(),
332                        b_sig.output(),
333                    )
334            }
335            (ty::Tuple(..), ty::Tuple(..)) => {
336                // Tuples are not `repr(C)` so these cannot be compared structurally.
337                false
338            }
339            // For these, it's not quite as easy to define structural-sameness quite so easily.
340            // For the purposes of this lint, take the conservative approach and mark them as
341            // not structurally same.
342            (ty::Dynamic(..), ty::Dynamic(..))
343            | (ty::Error(..), ty::Error(..))
344            | (ty::Closure(..), ty::Closure(..))
345            | (ty::Coroutine(..), ty::Coroutine(..))
346            | (ty::CoroutineWitness(..), ty::CoroutineWitness(..))
347            | (
348                ty::Alias(_, ty::AliasTy { kind: ty::Projection { .. }, .. }),
349                ty::Alias(_, ty::AliasTy { kind: ty::Projection { .. }, .. }),
350            )
351            | (
352                ty::Alias(_, ty::AliasTy { kind: ty::Inherent { .. }, .. }),
353                ty::Alias(_, ty::AliasTy { kind: ty::Inherent { .. }, .. }),
354            )
355            | (
356                ty::Alias(_, ty::AliasTy { kind: ty::Opaque { .. }, .. }),
357                ty::Alias(_, ty::AliasTy { kind: ty::Opaque { .. }, .. }),
358            ) => false,
359
360            // These definitely should have been caught above.
361            (ty::Bool, ty::Bool)
362            | (ty::Char, ty::Char)
363            | (ty::Never, ty::Never)
364            | (ty::Str, ty::Str) => ::core::panicking::unreachable_default_msg()unreachable!(),
365
366            // An Adt and a primitive or pointer type. This can be FFI-safe if non-null
367            // enum layout optimisation is being applied.
368            (ty::Adt(..) | ty::Pat(..), _) if is_primitive_or_pointer(b) => {
369                if let Some(a_inner) = types::repr_nullable_ptr(tcx, typing_env, a) {
370                    a_inner == b
371                } else {
372                    false
373                }
374            }
375            (_, ty::Adt(..) | ty::Pat(..)) if is_primitive_or_pointer(a) => {
376                if let Some(b_inner) = types::repr_nullable_ptr(tcx, typing_env, b) {
377                    b_inner == a
378                } else {
379                    false
380                }
381            }
382
383            _ => false,
384        }
385    }
386}