1 use rustc
::hir
::def
::{Res, DefKind}
;
2 use rustc
::hir
::def_id
::DefId
;
3 use rustc
::ty
::{self, Ty, TyCtxt}
;
4 use rustc
::ty
::layout
::{LayoutError, Pointer, SizeSkeleton, VariantIdx}
;
5 use rustc
::ty
::query
::Providers
;
7 use rustc_target
::spec
::abi
::Abi
::RustIntrinsic
;
8 use rustc_index
::vec
::Idx
;
9 use syntax_pos
::{Span, sym}
;
10 use rustc
::hir
::intravisit
::{self, Visitor, NestedVisitorMap}
;
13 use rustc_error_codes
::*;
15 fn check_mod_intrinsics(tcx
: TyCtxt
<'_
>, module_def_id
: DefId
) {
16 tcx
.hir().visit_item_likes_in_module(
18 &mut ItemVisitor { tcx }
.as_deep_visitor()
22 pub fn provide(providers
: &mut Providers
<'_
>) {
23 *providers
= Providers
{
29 struct ItemVisitor
<'tcx
> {
33 struct ExprVisitor
<'tcx
> {
35 tables
: &'tcx ty
::TypeckTables
<'tcx
>,
36 param_env
: ty
::ParamEnv
<'tcx
>,
39 /// If the type is `Option<T>`, it will return `T`, otherwise
40 /// the type itself. Works on most `Option`-like types.
41 fn unpack_option_like
<'tcx
>(tcx
: TyCtxt
<'tcx
>, ty
: Ty
<'tcx
>) -> Ty
<'tcx
> {
42 let (def
, substs
) = match ty
.kind
{
43 ty
::Adt(def
, substs
) => (def
, substs
),
47 if def
.variants
.len() == 2 && !def
.repr
.c() && def
.repr
.int
.is_none() {
50 let one
= VariantIdx
::new(1);
51 let zero
= VariantIdx
::new(0);
53 if def
.variants
[zero
].fields
.is_empty() {
55 } else if def
.variants
[one
].fields
.is_empty() {
61 if def
.variants
[data_idx
].fields
.len() == 1 {
62 return def
.variants
[data_idx
].fields
[0].ty(tcx
, substs
);
69 impl ExprVisitor
<'tcx
> {
70 fn def_id_is_transmute(&self, def_id
: DefId
) -> bool
{
71 self.tcx
.fn_sig(def_id
).abi() == RustIntrinsic
&&
72 self.tcx
.item_name(def_id
) == sym
::transmute
75 fn check_transmute(&self, span
: Span
, from
: Ty
<'tcx
>, to
: Ty
<'tcx
>) {
76 let sk_from
= SizeSkeleton
::compute(from
, self.tcx
, self.param_env
);
77 let sk_to
= SizeSkeleton
::compute(to
, self.tcx
, self.param_env
);
79 // Check for same size using the skeletons.
80 if let (Ok(sk_from
), Ok(sk_to
)) = (sk_from
, sk_to
) {
81 if sk_from
.same_size(sk_to
) {
85 // Special-case transmutting from `typeof(function)` and
86 // `Option<typeof(function)>` to present a clearer error.
87 let from
= unpack_option_like(self.tcx
, from
);
88 if let (&ty
::FnDef(..), SizeSkeleton
::Known(size_to
)) = (&from
.kind
, sk_to
) {
89 if size_to
== Pointer
.size(&self.tcx
) {
90 struct_span_err
!(self.tcx
.sess
, span
, E0591
,
91 "can't transmute zero-sized type")
92 .note(&format
!("source type: {}", from
))
93 .note(&format
!("target type: {}", to
))
94 .help("cast with `as` to a pointer instead")
101 // Try to display a sensible error with as much information as possible.
102 let skeleton_string
= |ty
: Ty
<'tcx
>, sk
| {
104 Ok(SizeSkeleton
::Known(size
)) => {
105 format
!("{} bits", size
.bits())
107 Ok(SizeSkeleton
::Pointer { tail, .. }
) => {
108 format
!("pointer to `{}`", tail
)
110 Err(LayoutError
::Unknown(bad
)) => {
112 "this type does not have a fixed size".to_owned()
114 format
!("size can vary because of {}", bad
)
117 Err(err
) => err
.to_string()
121 let mut err
= struct_span_err
!(self.tcx
.sess
, span
, E0512
,
122 "cannot transmute between types of different sizes, \
123 or dependently-sized types");
125 err
.note(&format
!("`{}` does not have a fixed size", from
));
127 err
.note(&format
!("source type: `{}` ({})", from
, skeleton_string(from
, sk_from
)))
128 .note(&format
!("target type: `{}` ({})", to
, skeleton_string(to
, sk_to
)));
134 impl Visitor
<'tcx
> for ItemVisitor
<'tcx
> {
135 fn nested_visit_map
<'this
>(&'this
mut self) -> NestedVisitorMap
<'this
, 'tcx
> {
136 NestedVisitorMap
::None
139 fn visit_nested_body(&mut self, body_id
: hir
::BodyId
) {
140 let owner_def_id
= self.tcx
.hir().body_owner_def_id(body_id
);
141 let body
= self.tcx
.hir().body(body_id
);
142 let param_env
= self.tcx
.param_env(owner_def_id
);
143 let tables
= self.tcx
.typeck_tables_of(owner_def_id
);
144 ExprVisitor { tcx: self.tcx, param_env, tables }
.visit_body(body
);
145 self.visit_body(body
);
149 impl Visitor
<'tcx
> for ExprVisitor
<'tcx
> {
150 fn nested_visit_map
<'this
>(&'this
mut self) -> NestedVisitorMap
<'this
, 'tcx
> {
151 NestedVisitorMap
::None
154 fn visit_expr(&mut self, expr
: &'tcx hir
::Expr
) {
155 let res
= if let hir
::ExprKind
::Path(ref qpath
) = expr
.kind
{
156 self.tables
.qpath_res(qpath
, expr
.hir_id
)
160 if let Res
::Def(DefKind
::Fn
, did
) = res
{
161 if self.def_id_is_transmute(did
) {
162 let typ
= self.tables
.node_type(expr
.hir_id
);
163 let sig
= typ
.fn_sig(self.tcx
);
164 let from
= sig
.inputs().skip_binder()[0];
165 let to
= *sig
.output().skip_binder();
166 self.check_transmute(expr
.span
, from
, to
);
170 intravisit
::walk_expr(self, expr
);