Skip to main content

rustc_hir_typeck/
intrinsicck.rs

1use hir::HirId;
2use rustc_abi::Primitive::Pointer;
3use rustc_abi::VariantIdx;
4use rustc_errors::codes::*;
5use rustc_errors::struct_span_code_err;
6use rustc_hir as hir;
7use rustc_index::Idx;
8use rustc_middle::bug;
9use rustc_middle::ty::layout::{LayoutError, SizeSkeleton};
10use rustc_middle::ty::{self, Ty, TyCtxt, Unnormalized};
11use rustc_span::ErrorGuaranteed;
12use rustc_span::def_id::LocalDefId;
13use tracing::trace;
14
15/// If the type is `Option<T>`, it will return `T`, otherwise
16/// the type itself. Works on most `Option`-like types.
17fn unpack_option_like<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> {
18    let ty::Adt(def, args) = *ty.kind() else { return ty };
19
20    if def.variants().len() == 2 && !def.repr().c() && def.repr().int.is_none() {
21        let data_idx;
22
23        let one = VariantIdx::new(1);
24        let zero = VariantIdx::ZERO;
25
26        if def.variant(zero).fields.is_empty() {
27            data_idx = one;
28        } else if def.variant(one).fields.is_empty() {
29            data_idx = zero;
30        } else {
31            return ty;
32        }
33
34        if def.variant(data_idx).fields.len() == 1 {
35            return def.variant(data_idx).single_field().ty(tcx, args).skip_norm_wip();
36        }
37    }
38
39    ty
40}
41
42/// Try to display a sensible error with as much information as possible.
43fn skeleton_string<'tcx>(
44    ty: Ty<'tcx>,
45    sk: Result<SizeSkeleton<'tcx>, &'tcx LayoutError<'tcx>>,
46) -> String {
47    match sk {
48        Ok(SizeSkeleton::Pointer { tail, .. }) => ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("pointer to `{0}`", tail))
    })format!("pointer to `{tail}`"),
49        Ok(SizeSkeleton::Known(size, _)) => {
50            if let Some(v) = u128::from(size.bytes()).checked_mul(8) {
51                ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0} bits", v))
    })format!("{v} bits")
52            } else {
53                // `u128` should definitely be able to hold the size of different architectures
54                // larger sizes should be reported as error `are too big for the target architecture`
55                // otherwise we have a bug somewhere
56                ::rustc_middle::util::bug::bug_fmt(format_args!("{0:?} overflow for u128",
        size))bug!("{:?} overflow for u128", size)
57            }
58        }
59        Err(LayoutError::TooGeneric(bad)) => {
60            if *bad == ty {
61                "this type does not have a fixed size".to_owned()
62            } else {
63                ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("size can vary because of {0}",
                bad))
    })format!("size can vary because of {bad}")
64            }
65        }
66        Err(err) => err.to_string(),
67    }
68}
69
70fn check_transmute<'tcx>(
71    tcx: TyCtxt<'tcx>,
72    typing_env: ty::TypingEnv<'tcx>,
73    from: Ty<'tcx>,
74    to: Ty<'tcx>,
75    hir_id: HirId,
76) -> Result<(), ErrorGuaranteed> {
77    let span = tcx.hir_span(hir_id);
78    let normalize = |ty| {
79        if let Ok(ty) = tcx.try_normalize_erasing_regions(typing_env, Unnormalized::new_wip(ty)) {
80            ty
81        } else {
82            Ty::new_error_with_message(
83                tcx,
84                span,
85                ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("tried to normalize non-wf type {0:#?} in check_transmute",
                ty))
    })format!("tried to normalize non-wf type {ty:#?} in check_transmute"),
86            )
87        }
88    };
89
90    let from = normalize(from);
91    let to = normalize(to);
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_hir_typeck/src/intrinsicck.rs:92",
                        "rustc_hir_typeck::intrinsicck", ::tracing::Level::TRACE,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_hir_typeck/src/intrinsicck.rs"),
                        ::tracing_core::__macro_support::Option::Some(92u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_hir_typeck::intrinsicck"),
                        ::tracing_core::field::FieldSet::new(&["from", "to"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::TRACE <=
                    ::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};
                let mut iter = __CALLSITE.metadata().fields().iter();
                __CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&debug(&from) as
                                            &dyn Value)),
                                (&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&debug(&to) as
                                            &dyn Value))])
            });
    } else { ; }
};trace!(?from, ?to);
93
94    // Transmutes that are only changing lifetimes are always ok.
95    if from == to {
96        return Ok(());
97    }
98
99    let sk_from = SizeSkeleton::compute(from, tcx, typing_env, span);
100    let sk_to = SizeSkeleton::compute(to, tcx, typing_env, span);
101    {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_hir_typeck/src/intrinsicck.rs:101",
                        "rustc_hir_typeck::intrinsicck", ::tracing::Level::TRACE,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_hir_typeck/src/intrinsicck.rs"),
                        ::tracing_core::__macro_support::Option::Some(101u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_hir_typeck::intrinsicck"),
                        ::tracing_core::field::FieldSet::new(&["sk_from", "sk_to"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::TRACE <=
                    ::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};
                let mut iter = __CALLSITE.metadata().fields().iter();
                __CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&debug(&sk_from) as
                                            &dyn Value)),
                                (&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&debug(&sk_to) as
                                            &dyn Value))])
            });
    } else { ; }
};trace!(?sk_from, ?sk_to);
102
103    // Check for same size using the skeletons.
104    if let Ok(sk_from) = sk_from
105        && let Ok(sk_to) = sk_to
106    {
107        if sk_from.same_size(sk_to) {
108            return Ok(());
109        }
110
111        // Special-case transmuting from `typeof(function)` and
112        // `Option<typeof(function)>` to present a clearer error.
113        let from = unpack_option_like(tcx, from);
114        if let ty::FnDef(..) = from.kind()
115            && let SizeSkeleton::Known(size_to, _) = sk_to
116            && size_to == Pointer(tcx.data_layout.instruction_address_space).size(&tcx)
117        {
118            {
    tcx.sess.dcx().struct_span_err(span,
            ::alloc::__export::must_use({
                    ::alloc::fmt::format(format_args!("can\'t transmute zero-sized type"))
                })).with_code(E0591)
}struct_span_code_err!(tcx.sess.dcx(), span, E0591, "can't transmute zero-sized type")
119                .with_note(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("source type: {0}", from))
    })format!("source type: {from}"))
120                .with_note(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("target type: {0}", to))
    })format!("target type: {to}"))
121                .with_help("cast with `as` to a pointer instead")
122                .emit();
123            return Ok(());
124        }
125    }
126
127    let mut err = {
    tcx.sess.dcx().struct_span_err(span,
            ::alloc::__export::must_use({
                    ::alloc::fmt::format(format_args!("cannot transmute between types of different sizes, or dependently-sized types"))
                })).with_code(E0512)
}struct_span_code_err!(
128        tcx.sess.dcx(),
129        span,
130        E0512,
131        "cannot transmute between types of different sizes, or dependently-sized types"
132    );
133    if from == to {
134        err.note(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("`{0}` does not have a fixed size",
                from))
    })format!("`{from}` does not have a fixed size"));
135        Err(err.emit())
136    } else {
137        err.note(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("source type: `{0}` ({1})", from,
                skeleton_string(from, sk_from)))
    })format!("source type: `{}` ({})", from, skeleton_string(from, sk_from)));
138        err.note(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("target type: `{0}` ({1})", to,
                skeleton_string(to, sk_to)))
    })format!("target type: `{}` ({})", to, skeleton_string(to, sk_to)));
139        Err(err.emit())
140    }
141}
142
143pub(crate) fn check_transmutes(tcx: TyCtxt<'_>, owner: LocalDefId) -> Result<(), ErrorGuaranteed> {
144    if !!tcx.is_typeck_child(owner.to_def_id()) {
    ::core::panicking::panic("assertion failed: !tcx.is_typeck_child(owner.to_def_id())")
};assert!(!tcx.is_typeck_child(owner.to_def_id()));
145    let typeck_results = tcx.typeck(owner);
146    if let Some(e) = typeck_results.tainted_by_errors {
147        return Err(e);
148    };
149
150    let typing_env = ty::TypingEnv::post_analysis(tcx, owner);
151    let mut result = Ok(());
152    for &(from, to, hir_id) in &typeck_results.transmutes_to_check {
153        result = result.and(check_transmute(tcx, typing_env, from, to, hir_id));
154    }
155    result
156}