1 // Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 //! Code for type-checking cast expressions.
13 //! A cast `e as U` is valid if one of the following holds:
14 //! * `e` has type `T` and `T` coerces to `U`; *coercion-cast*
15 //! * `e` has type `*T`, `U` is `*U_0`, and either `U_0: Sized` or
16 //! unsize_kind(`T`) = unsize_kind(`U_0`); *ptr-ptr-cast*
17 //! * `e` has type `*T` and `U` is a numeric type, while `T: Sized`; *ptr-addr-cast*
18 //! * `e` is an integer and `U` is `*U_0`, while `U_0: Sized`; *addr-ptr-cast*
19 //! * `e` has type `T` and `T` and `U` are any numeric types; *numeric-cast*
20 //! * `e` is a C-like enum and `U` is an integer type; *enum-cast*
21 //! * `e` has type `bool` or `char` and `U` is an integer; *prim-int-cast*
22 //! * `e` has type `u8` and `U` is `char`; *u8-char-cast*
23 //! * `e` has type `&[T; n]` and `U` is `*const T`; *array-ptr-cast*
24 //! * `e` is a function pointer type and `U` has type `*T`,
25 //! while `T: Sized`; *fptr-ptr-cast*
26 //! * `e` is a function pointer type and `U` is an integer; *fptr-addr-cast*
28 //! where `&.T` and `*T` are references of either mutability,
29 //! and where unsize_kind(`T`) is the kind of the unsize info
30 //! in `T` - the vtable for a trait definition (e.g. `fmt::Display` or
31 //! `Iterator`, not `Iterator<Item=u8>`) or a length (or `()` if `T: Sized`).
33 //! Note that lengths are not adjusted when casting raw slices -
34 //! `T: *const [u16] as *const [u8]` creates a slice that only includes
35 //! half of the original memory.
37 //! Casting is not transitive, that is, even if `e as U1 as U2` is a valid
38 //! expression, `e as U2` is not necessarily so (in fact it will only be valid if
39 //! `U1` coerces to `U2`).
44 use super::structurally_resolved_type
;
47 use hir
::def_id
::DefId
;
48 use rustc
::ty
::{self, Ty, TypeFoldable}
;
49 use rustc
::ty
::cast
::{CastKind, CastTy}
;
50 use syntax
::codemap
::Span
;
55 /// Reifies a cast check to be checked once we have full type information for
56 /// a function context.
57 pub struct CastCheck
<'tcx
> {
58 expr
: &'tcx hir
::Expr
,
64 /// The kind of the unsize info (length or vtable) - we only allow casts between
65 /// fat pointers if their unsize-infos have the same kind.
66 #[derive(Copy, Clone, PartialEq, Eq)]
67 enum UnsizeKind
<'tcx
> {
70 /// The unsize info of this projection
71 OfProjection(&'tcx ty
::ProjectionTy
<'tcx
>),
72 /// The unsize info of this parameter
73 OfParam(&'tcx ty
::ParamTy
)
76 /// Returns the kind of unsize information of t, or None
77 /// if t is sized or it is unknown.
78 fn unsize_kind
<'a
,'tcx
>(fcx
: &FnCtxt
<'a
, 'tcx
>,
80 -> Option
<UnsizeKind
<'tcx
>> {
82 ty
::TySlice(_
) | ty
::TyStr
=> Some(UnsizeKind
::Length
),
83 ty
::TyTrait(ref tty
) => Some(UnsizeKind
::Vtable(tty
.principal_def_id())),
84 ty
::TyStruct(def
, substs
) => {
85 // FIXME(arielb1): do some kind of normalization
86 match def
.struct_variant().fields
.last() {
88 Some(f
) => unsize_kind(fcx
, f
.ty(fcx
.tcx(), substs
))
91 // We should really try to normalize here.
92 ty
::TyProjection(ref pi
) => Some(UnsizeKind
::OfProjection(pi
)),
93 ty
::TyParam(ref p
) => Some(UnsizeKind
::OfParam(p
)),
98 #[derive(Copy, Clone)]
103 /// Cast of thin to fat raw ptr (eg. `*const () as *const [u8]`)
113 impl<'tcx
> CastCheck
<'tcx
> {
114 pub fn new(expr
: &'tcx hir
::Expr
, expr_ty
: Ty
<'tcx
>, cast_ty
: Ty
<'tcx
>, span
: Span
)
124 fn report_cast_error
<'a
>(&self, fcx
: &FnCtxt
<'a
, 'tcx
>,
127 CastError
::NeedViaPtr
|
128 CastError
::NeedViaThinPtr
|
129 CastError
::NeedViaInt
|
130 CastError
::NeedViaUsize
=> {
131 fcx
.type_error_struct(self.span
, |actual
| {
132 format
!("casting `{}` as `{}` is invalid",
134 fcx
.infcx().ty_to_string(self.cast_ty
))
135 }, self.expr_ty
, None
)
136 .fileline_help(self.span
,
137 &format
!("cast through {} first", match e
{
138 CastError
::NeedViaPtr
=> "a raw pointer",
139 CastError
::NeedViaThinPtr
=> "a thin pointer",
140 CastError
::NeedViaInt
=> "an integer",
141 CastError
::NeedViaUsize
=> "a usize",
146 CastError
::CastToBool
=> {
147 struct_span_err
!(fcx
.tcx().sess
, self.span
, E0054
, "cannot cast as `bool`")
148 .fileline_help(self.span
, "compare with zero instead")
151 CastError
::CastToChar
=> {
152 fcx
.type_error_message(self.span
, |actual
| {
153 format
!("only `u8` can be cast as `char`, not `{}`", actual
)
154 }, self.expr_ty
, None
);
156 CastError
::NonScalar
=> {
157 fcx
.type_error_message(self.span
, |actual
| {
158 format
!("non-scalar cast: `{}` as `{}`",
160 fcx
.infcx().ty_to_string(self.cast_ty
))
161 }, self.expr_ty
, None
);
163 CastError
::IllegalCast
=> {
164 fcx
.type_error_message(self.span
, |actual
| {
165 format
!("casting `{}` as `{}` is invalid",
167 fcx
.infcx().ty_to_string(self.cast_ty
))
168 }, self.expr_ty
, None
);
170 CastError
::SizedUnsizedCast
=> {
171 fcx
.type_error_message(self.span
, |actual
| {
172 format
!("cannot cast thin pointer `{}` to fat pointer `{}`",
174 fcx
.infcx().ty_to_string(self.cast_ty
))
175 }, self.expr_ty
, None
)
177 CastError
::DifferingKinds
=> {
178 fcx
.type_error_struct(self.span
, |actual
| {
179 format
!("casting `{}` as `{}` is invalid",
181 fcx
.infcx().ty_to_string(self.cast_ty
))
182 }, self.expr_ty
, None
)
183 .fileline_note(self.span
, "vtable kinds may not match")
189 fn trivial_cast_lint
<'a
>(&self, fcx
: &FnCtxt
<'a
, 'tcx
>) {
190 let t_cast
= self.cast_ty
;
191 let t_expr
= self.expr_ty
;
192 if t_cast
.is_numeric() && t_expr
.is_numeric() {
193 fcx
.tcx().sess
.add_lint(lint
::builtin
::TRIVIAL_NUMERIC_CASTS
,
196 format
!("trivial numeric cast: `{}` as `{}`. Cast can be \
197 replaced by coercion, this might require type \
198 ascription or a temporary variable",
199 fcx
.infcx().ty_to_string(t_expr
),
200 fcx
.infcx().ty_to_string(t_cast
)));
202 fcx
.tcx().sess
.add_lint(lint
::builtin
::TRIVIAL_CASTS
,
205 format
!("trivial cast: `{}` as `{}`. Cast can be \
206 replaced by coercion, this might require type \
207 ascription or a temporary variable",
208 fcx
.infcx().ty_to_string(t_expr
),
209 fcx
.infcx().ty_to_string(t_cast
)));
214 pub fn check
<'a
>(mut self, fcx
: &FnCtxt
<'a
, 'tcx
>) {
215 self.expr_ty
= structurally_resolved_type(fcx
, self.span
, self.expr_ty
);
216 self.cast_ty
= structurally_resolved_type(fcx
, self.span
, self.cast_ty
);
218 debug
!("check_cast({}, {:?} as {:?})", self.expr
.id
, self.expr_ty
,
221 if self.expr_ty
.references_error() || self.cast_ty
.references_error() {
222 // No sense in giving duplicate error messages
223 } else if self.try_coercion_cast(fcx
) {
224 self.trivial_cast_lint(fcx
);
225 debug
!(" -> CoercionCast");
226 fcx
.tcx().cast_kinds
.borrow_mut().insert(self.expr
.id
,
227 CastKind
::CoercionCast
);
228 } else { match self.do_check(fcx
) {
230 debug
!(" -> {:?}", k
);
231 fcx
.tcx().cast_kinds
.borrow_mut().insert(self.expr
.id
, k
);
233 Err(e
) => self.report_cast_error(fcx
, e
)
237 /// Check a cast, and report an error if one exists. In some cases, this
238 /// can return Ok and create type errors in the fcx rather than returning
239 /// directly. coercion-cast is handled in check instead of here.
240 fn do_check
<'a
>(&self, fcx
: &FnCtxt
<'a
, 'tcx
>) -> Result
<CastKind
, CastError
> {
241 use rustc
::ty
::cast
::IntTy
::*;
242 use rustc
::ty
::cast
::CastTy
::*;
244 let (t_from
, t_cast
) = match (CastTy
::from_ty(self.expr_ty
),
245 CastTy
::from_ty(self.cast_ty
)) {
246 (Some(t_from
), Some(t_cast
)) => (t_from
, t_cast
),
247 // Function item types may need to be reified before casts.
248 (None
, Some(t_cast
)) => {
249 if let ty
::TyFnDef(_
, _
, f
) = self.expr_ty
.sty
{
250 // Attempt a coercion to a fn pointer type.
251 let res
= coercion
::try(fcx
, self.expr
,
252 fcx
.tcx().mk_ty(ty
::TyFnPtr(f
)));
254 return Err(CastError
::NonScalar
);
258 return Err(CastError
::NonScalar
);
262 return Err(CastError
::NonScalar
)
266 match (t_from
, t_cast
) {
267 // These types have invariants! can't cast into them.
268 (_
, RPtr(_
)) | (_
, Int(CEnum
)) | (_
, FnPtr
) => Err(CastError
::NonScalar
),
271 (_
, Int(Bool
)) => Err(CastError
::CastToBool
),
274 (Int(U(ast
::UintTy
::U8
)), Int(Char
)) => Ok(CastKind
::U8CharCast
), // u8-char-cast
275 (_
, Int(Char
)) => Err(CastError
::CastToChar
),
278 (Int(Bool
), Float
) | (Int(CEnum
), Float
) | (Int(Char
), Float
)
279 => Err(CastError
::NeedViaInt
),
280 (Int(Bool
), Ptr(_
)) | (Int(CEnum
), Ptr(_
)) | (Int(Char
), Ptr(_
))
281 => Err(CastError
::NeedViaUsize
),
284 (Ptr(m_e
), Ptr(m_c
)) => self.check_ptr_ptr_cast(fcx
, m_e
, m_c
), // ptr-ptr-cast
285 (Ptr(m_expr
), Int(_
)) => self.check_ptr_addr_cast(fcx
, m_expr
), // ptr-addr-cast
286 (Ptr(_
), Float
) | (FnPtr
, Float
) => Err(CastError
::NeedViaUsize
),
287 (FnPtr
, Int(_
)) => Ok(CastKind
::FnPtrAddrCast
),
288 (RPtr(_
), Int(_
)) | (RPtr(_
), Float
) => Err(CastError
::NeedViaPtr
),
290 (Int(_
), Ptr(mt
)) => self.check_addr_ptr_cast(fcx
, mt
), // addr-ptr-cast
291 (FnPtr
, Ptr(mt
)) => self.check_fptr_ptr_cast(fcx
, mt
),
292 (Float
, Ptr(_
)) => Err(CastError
::NeedViaUsize
),
293 (RPtr(rmt
), Ptr(mt
)) => self.check_ref_cast(fcx
, rmt
, mt
), // array-ptr-cast
296 (Int(CEnum
), Int(_
)) => Ok(CastKind
::EnumCast
),
297 (Int(Char
), Int(_
)) | (Int(Bool
), Int(_
)) => Ok(CastKind
::PrimIntCast
),
302 (Float
, Float
) => Ok(CastKind
::NumericCast
),
307 fn check_ptr_ptr_cast
<'a
>(&self,
308 fcx
: &FnCtxt
<'a
, 'tcx
>,
309 m_expr
: &'tcx ty
::TypeAndMut
<'tcx
>,
310 m_cast
: &'tcx ty
::TypeAndMut
<'tcx
>)
311 -> Result
<CastKind
, CastError
>
313 debug
!("check_ptr_ptr_cast m_expr={:?} m_cast={:?}",
315 // ptr-ptr cast. vtables must match.
317 // Cast to sized is OK
318 if fcx
.type_is_known_to_be_sized(m_cast
.ty
, self.span
) {
319 return Ok(CastKind
::PtrPtrCast
);
322 // sized -> unsized? report invalid cast (don't complain about vtable kinds)
323 if fcx
.type_is_known_to_be_sized(m_expr
.ty
, self.span
) {
324 return Err(CastError
::SizedUnsizedCast
);
327 // vtable kinds must match
328 match (unsize_kind(fcx
, m_cast
.ty
), unsize_kind(fcx
, m_expr
.ty
)) {
329 (Some(a
), Some(b
)) if a
== b
=> Ok(CastKind
::PtrPtrCast
),
330 _
=> Err(CastError
::DifferingKinds
)
334 fn check_fptr_ptr_cast
<'a
>(&self,
335 fcx
: &FnCtxt
<'a
, 'tcx
>,
336 m_cast
: &'tcx ty
::TypeAndMut
<'tcx
>)
337 -> Result
<CastKind
, CastError
>
339 // fptr-ptr cast. must be to sized ptr
341 if fcx
.type_is_known_to_be_sized(m_cast
.ty
, self.span
) {
342 Ok(CastKind
::FnPtrPtrCast
)
344 Err(CastError
::IllegalCast
)
348 fn check_ptr_addr_cast
<'a
>(&self,
349 fcx
: &FnCtxt
<'a
, 'tcx
>,
350 m_expr
: &'tcx ty
::TypeAndMut
<'tcx
>)
351 -> Result
<CastKind
, CastError
>
353 // ptr-addr cast. must be from sized ptr
355 if fcx
.type_is_known_to_be_sized(m_expr
.ty
, self.span
) {
356 Ok(CastKind
::PtrAddrCast
)
358 Err(CastError
::NeedViaThinPtr
)
362 fn check_ref_cast
<'a
>(&self,
363 fcx
: &FnCtxt
<'a
, 'tcx
>,
364 m_expr
: &'tcx ty
::TypeAndMut
<'tcx
>,
365 m_cast
: &'tcx ty
::TypeAndMut
<'tcx
>)
366 -> Result
<CastKind
, CastError
>
370 if m_expr
.mutbl
== hir
::MutImmutable
&& m_cast
.mutbl
== hir
::MutImmutable
{
371 if let ty
::TyArray(ety
, _
) = m_expr
.ty
.sty
{
372 // Due to the limitations of LLVM global constants,
373 // region pointers end up pointing at copies of
374 // vector elements instead of the original values.
375 // To allow raw pointers to work correctly, we
376 // need to special-case obtaining a raw pointer
377 // from a region pointer to a vector.
379 // this will report a type mismatch if needed
380 demand
::eqtype(fcx
, self.span
, ety
, m_cast
.ty
);
381 return Ok(CastKind
::ArrayPtrCast
);
385 Err(CastError
::IllegalCast
)
388 fn check_addr_ptr_cast
<'a
>(&self,
389 fcx
: &FnCtxt
<'a
, 'tcx
>,
390 m_cast
: &'tcx ty
::TypeAndMut
<'tcx
>)
391 -> Result
<CastKind
, CastError
>
393 // ptr-addr cast. pointer must be thin.
394 if fcx
.type_is_known_to_be_sized(m_cast
.ty
, self.span
) {
395 Ok(CastKind
::AddrPtrCast
)
397 Err(CastError
::IllegalCast
)
401 fn try_coercion_cast
<'a
>(&self, fcx
: &FnCtxt
<'a
, 'tcx
>) -> bool
{
402 coercion
::try(fcx
, self.expr
, self.cast_ty
).is_ok()