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 middle
::cast
::{CastKind, CastTy}
;
51 use syntax
::ast
::UintTy
::{TyU8}
;
52 use syntax
::codemap
::Span
;
54 /// Reifies a cast check to be checked once we have full type information for
55 /// a function context.
56 pub struct CastCheck
<'tcx
> {
63 /// The kind of the unsize info (length or vtable) - we only allow casts between
64 /// fat pointers if their unsize-infos have the same kind.
65 #[derive(Copy, Clone, PartialEq, Eq)]
66 enum UnsizeKind
<'tcx
> {
69 /// The unsize info of this projection
70 OfProjection(&'tcx ty
::ProjectionTy
<'tcx
>),
71 /// The unsize info of this parameter
72 OfParam(&'tcx ty
::ParamTy
)
75 /// Returns the kind of unsize information of t, or None
76 /// if t is sized or it is unknown.
77 fn unsize_kind
<'a
,'tcx
>(fcx
: &FnCtxt
<'a
, 'tcx
>,
79 -> Option
<UnsizeKind
<'tcx
>> {
81 ty
::TySlice(_
) | ty
::TyStr
=> Some(UnsizeKind
::Length
),
82 ty
::TyTrait(ref tty
) => Some(UnsizeKind
::Vtable(tty
.principal_def_id())),
83 ty
::TyStruct(did
, substs
) => {
84 match ty
::struct_fields(fcx
.tcx(), did
, substs
).pop() {
86 Some(f
) => unsize_kind(fcx
, f
.mt
.ty
)
89 // We should really try to normalize here.
90 ty
::TyProjection(ref pi
) => Some(UnsizeKind
::OfProjection(pi
)),
91 ty
::TyParam(ref p
) => Some(UnsizeKind
::OfParam(p
)),
96 #[derive(Copy, Clone)]
108 impl<'tcx
> CastCheck
<'tcx
> {
109 pub fn new(expr
: ast
::Expr
, expr_ty
: Ty
<'tcx
>, cast_ty
: Ty
<'tcx
>, span
: Span
)
119 fn report_cast_error
<'a
>(&self, fcx
: &FnCtxt
<'a
, 'tcx
>,
122 CastError
::NeedViaPtr
|
123 CastError
::NeedViaInt
|
124 CastError
::NeedViaUsize
=> {
125 fcx
.type_error_message(self.span
, |actual
| {
126 format
!("illegal cast; cast through {} first: `{}` as `{}`",
128 CastError
::NeedViaPtr
=> "a raw pointer",
129 CastError
::NeedViaInt
=> "an integer",
130 CastError
::NeedViaUsize
=> "a usize",
134 fcx
.infcx().ty_to_string(self.cast_ty
))
135 }, self.expr_ty
, None
)
137 CastError
::CastToBool
=> {
138 span_err
!(fcx
.tcx().sess
, self.span
, E0054
,
139 "cannot cast as `bool`, compare with zero instead");
141 CastError
::CastToChar
=> {
142 fcx
.type_error_message(self.span
, |actual
| {
143 format
!("only `u8` can be cast as `char`, not `{}`", actual
)
144 }, self.expr_ty
, None
);
146 CastError
::NonScalar
=> {
147 fcx
.type_error_message(self.span
, |actual
| {
148 format
!("non-scalar cast: `{}` as `{}`",
150 fcx
.infcx().ty_to_string(self.cast_ty
))
151 }, self.expr_ty
, None
);
153 CastError
::IllegalCast
=> {
154 fcx
.type_error_message(self.span
, |actual
| {
155 format
!("illegal cast: `{}` as `{}`",
157 fcx
.infcx().ty_to_string(self.cast_ty
))
158 }, self.expr_ty
, None
);
160 CastError
::DifferingKinds
=> {
161 fcx
.type_error_message(self.span
, |actual
| {
162 format
!("illegal cast: `{}` as `{}`; vtable kinds may not match",
164 fcx
.infcx().ty_to_string(self.cast_ty
))
165 }, self.expr_ty
, None
);
170 fn trivial_cast_lint
<'a
>(&self, fcx
: &FnCtxt
<'a
, 'tcx
>) {
171 let t_cast
= self.cast_ty
;
172 let t_expr
= self.expr_ty
;
173 if ty
::type_is_numeric(t_cast
) && ty
::type_is_numeric(t_expr
) {
174 fcx
.tcx().sess
.add_lint(lint
::builtin
::TRIVIAL_NUMERIC_CASTS
,
177 format
!("trivial numeric cast: `{}` as `{}`. Cast can be \
178 replaced by coercion, this might require type \
179 ascription or a temporary variable",
180 fcx
.infcx().ty_to_string(t_expr
),
181 fcx
.infcx().ty_to_string(t_cast
)));
183 fcx
.tcx().sess
.add_lint(lint
::builtin
::TRIVIAL_CASTS
,
186 format
!("trivial cast: `{}` as `{}`. Cast can be \
187 replaced by coercion, this might require type \
188 ascription or a temporary variable",
189 fcx
.infcx().ty_to_string(t_expr
),
190 fcx
.infcx().ty_to_string(t_cast
)));
195 pub fn check
<'a
>(mut self, fcx
: &FnCtxt
<'a
, 'tcx
>) {
196 self.expr_ty
= structurally_resolved_type(fcx
, self.span
, self.expr_ty
);
197 self.cast_ty
= structurally_resolved_type(fcx
, self.span
, self.cast_ty
);
199 debug
!("check_cast({}, {:?} as {:?})", self.expr
.id
, self.expr_ty
,
202 if ty
::type_is_error(self.expr_ty
) || ty
::type_is_error(self.cast_ty
) {
203 // No sense in giving duplicate error messages
204 } else if self.try_coercion_cast(fcx
) {
205 self.trivial_cast_lint(fcx
);
206 debug
!(" -> CoercionCast");
207 fcx
.tcx().cast_kinds
.borrow_mut().insert(self.expr
.id
,
208 CastKind
::CoercionCast
);
209 } else { match self.do_check(fcx
) {
211 debug
!(" -> {:?}", k
);
212 fcx
.tcx().cast_kinds
.borrow_mut().insert(self.expr
.id
, k
);
214 Err(e
) => self.report_cast_error(fcx
, e
)
218 /// Check a cast, and report an error if one exists. In some cases, this
219 /// can return Ok and create type errors in the fcx rather than returning
220 /// directly. coercion-cast is handled in check instead of here.
221 fn do_check
<'a
>(&self, fcx
: &FnCtxt
<'a
, 'tcx
>) -> Result
<CastKind
, CastError
> {
222 use middle
::cast
::IntTy
::*;
223 use middle
::cast
::CastTy
::*;
225 let (t_from
, t_cast
) = match (CastTy
::from_ty(fcx
.tcx(), self.expr_ty
),
226 CastTy
::from_ty(fcx
.tcx(), self.cast_ty
)) {
227 (Some(t_from
), Some(t_cast
)) => (t_from
, t_cast
),
229 return Err(CastError
::NonScalar
)
233 match (t_from
, t_cast
) {
234 // These types have invariants! can't cast into them.
235 (_
, RPtr(_
)) | (_
, Int(CEnum
)) | (_
, FnPtr
) => Err(CastError
::NonScalar
),
238 (_
, Int(Bool
)) => Err(CastError
::CastToBool
),
241 (Int(U(ast
::TyU8
)), Int(Char
)) => Ok(CastKind
::U8CharCast
), // u8-char-cast
242 (_
, Int(Char
)) => Err(CastError
::CastToChar
),
245 (Int(Bool
), Float
) | (Int(CEnum
), Float
) | (Int(Char
), Float
)
246 => Err(CastError
::NeedViaInt
),
247 (Int(Bool
), Ptr(_
)) | (Int(CEnum
), Ptr(_
)) | (Int(Char
), Ptr(_
))
248 => Err(CastError
::NeedViaUsize
),
251 (Ptr(m_e
), Ptr(m_c
)) => self.check_ptr_ptr_cast(fcx
, m_e
, m_c
), // ptr-ptr-cast
252 (Ptr(m_expr
), Int(_
)) => self.check_ptr_addr_cast(fcx
, m_expr
), // ptr-addr-cast
253 (Ptr(_
), Float
) | (FnPtr
, Float
) => Err(CastError
::NeedViaUsize
),
254 (FnPtr
, Int(_
)) => Ok(CastKind
::FnPtrAddrCast
),
255 (RPtr(_
), Int(_
)) | (RPtr(_
), Float
) => Err(CastError
::NeedViaPtr
),
257 (Int(_
), Ptr(mt
)) => self.check_addr_ptr_cast(fcx
, mt
), // addr-ptr-cast
258 (FnPtr
, Ptr(mt
)) => self.check_fptr_ptr_cast(fcx
, mt
),
259 (Float
, Ptr(_
)) => Err(CastError
::NeedViaUsize
),
260 (RPtr(rmt
), Ptr(mt
)) => self.check_ref_cast(fcx
, rmt
, mt
), // array-ptr-cast
263 (Int(CEnum
), Int(_
)) => Ok(CastKind
::EnumCast
),
264 (Int(Char
), Int(_
)) | (Int(Bool
), Int(_
)) => Ok(CastKind
::PrimIntCast
),
269 (Float
, Float
) => Ok(CastKind
::NumericCast
),
274 fn check_ptr_ptr_cast
<'a
>(&self,
275 fcx
: &FnCtxt
<'a
, 'tcx
>,
276 m_expr
: &'tcx ty
::mt
<'tcx
>,
277 m_cast
: &'tcx ty
::mt
<'tcx
>)
278 -> Result
<CastKind
, CastError
>
280 debug
!("check_ptr_ptr_cast m_expr={:?} m_cast={:?}",
282 // ptr-ptr cast. vtables must match.
284 // Cast to sized is OK
285 if fcx
.type_is_known_to_be_sized(m_cast
.ty
, self.span
) {
286 return Ok(CastKind
::PtrPtrCast
);
289 // sized -> unsized? report illegal cast (don't complain about vtable kinds)
290 if fcx
.type_is_known_to_be_sized(m_expr
.ty
, self.span
) {
291 return Err(CastError
::IllegalCast
);
294 // vtable kinds must match
295 match (unsize_kind(fcx
, m_cast
.ty
), unsize_kind(fcx
, m_expr
.ty
)) {
296 (Some(a
), Some(b
)) if a
== b
=> Ok(CastKind
::PtrPtrCast
),
297 _
=> Err(CastError
::DifferingKinds
)
301 fn check_fptr_ptr_cast
<'a
>(&self,
302 fcx
: &FnCtxt
<'a
, 'tcx
>,
303 m_cast
: &'tcx ty
::mt
<'tcx
>)
304 -> Result
<CastKind
, CastError
>
306 // fptr-ptr cast. must be to sized ptr
308 if fcx
.type_is_known_to_be_sized(m_cast
.ty
, self.span
) {
309 Ok(CastKind
::FnPtrPtrCast
)
311 Err(CastError
::IllegalCast
)
315 fn check_ptr_addr_cast
<'a
>(&self,
316 fcx
: &FnCtxt
<'a
, 'tcx
>,
317 m_expr
: &'tcx ty
::mt
<'tcx
>)
318 -> Result
<CastKind
, CastError
>
320 // ptr-addr cast. must be from sized ptr
322 if fcx
.type_is_known_to_be_sized(m_expr
.ty
, self.span
) {
323 Ok(CastKind
::PtrAddrCast
)
325 Err(CastError
::NeedViaPtr
)
329 fn check_ref_cast
<'a
>(&self,
330 fcx
: &FnCtxt
<'a
, 'tcx
>,
331 m_expr
: &'tcx ty
::mt
<'tcx
>,
332 m_cast
: &'tcx ty
::mt
<'tcx
>)
333 -> Result
<CastKind
, CastError
>
337 if m_expr
.mutbl
== ast
::MutImmutable
&& m_cast
.mutbl
== ast
::MutImmutable
{
338 if let ty
::TyArray(ety
, _
) = m_expr
.ty
.sty
{
339 // Due to the limitations of LLVM global constants,
340 // region pointers end up pointing at copies of
341 // vector elements instead of the original values.
342 // To allow raw pointers to work correctly, we
343 // need to special-case obtaining a raw pointer
344 // from a region pointer to a vector.
346 // this will report a type mismatch if needed
347 demand
::eqtype(fcx
, self.span
, ety
, m_cast
.ty
);
348 return Ok(CastKind
::ArrayPtrCast
);
352 Err(CastError
::IllegalCast
)
355 fn check_addr_ptr_cast
<'a
>(&self,
356 fcx
: &FnCtxt
<'a
, 'tcx
>,
357 m_cast
: &'tcx ty
::mt
<'tcx
>)
358 -> Result
<CastKind
, CastError
>
360 // ptr-addr cast. pointer must be thin.
361 if fcx
.type_is_known_to_be_sized(m_cast
.ty
, self.span
) {
362 Ok(CastKind
::AddrPtrCast
)
364 Err(CastError
::IllegalCast
)
368 fn try_coercion_cast
<'a
>(&self, fcx
: &FnCtxt
<'a
, 'tcx
>) -> bool
{
369 if let Ok(()) = coercion
::mk_assignty(fcx
,