]>
Commit | Line | Data |
---|---|---|
9346a6ac AL |
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. | |
4 | // | |
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. | |
10 | ||
11 | //! Code for type-checking cast expressions. | |
62682a34 SL |
12 | //! |
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* | |
27 | //! | |
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`). | |
32 | //! | |
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. | |
36 | //! | |
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`). | |
9346a6ac AL |
40 | |
41 | use super::coercion; | |
42 | use super::demand; | |
43 | use super::FnCtxt; | |
44 | use super::structurally_resolved_type; | |
45 | ||
46 | use lint; | |
62682a34 | 47 | use middle::cast::{CastKind, CastTy}; |
9346a6ac AL |
48 | use middle::ty; |
49 | use middle::ty::Ty; | |
50 | use syntax::ast; | |
62682a34 | 51 | use syntax::ast::UintTy::{TyU8}; |
9346a6ac AL |
52 | use syntax::codemap::Span; |
53 | ||
54 | /// Reifies a cast check to be checked once we have full type information for | |
55 | /// a function context. | |
56 | pub struct CastCheck<'tcx> { | |
57 | expr: ast::Expr, | |
58 | expr_ty: Ty<'tcx>, | |
59 | cast_ty: Ty<'tcx>, | |
60 | span: Span, | |
61 | } | |
62 | ||
62682a34 SL |
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> { | |
67 | Vtable(ast::DefId), | |
68 | Length, | |
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) | |
73 | } | |
74 | ||
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>, | |
78 | t: Ty<'tcx>) | |
79 | -> Option<UnsizeKind<'tcx>> { | |
80 | match t.sty { | |
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() { | |
85 | None => None, | |
86 | Some(f) => unsize_kind(fcx, f.mt.ty) | |
87 | } | |
88 | } | |
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)), | |
92 | _ => None | |
93 | } | |
94 | } | |
95 | ||
96 | #[derive(Copy, Clone)] | |
97 | enum CastError { | |
98 | CastToBool, | |
99 | CastToChar, | |
100 | DifferingKinds, | |
101 | IllegalCast, | |
102 | NeedViaPtr, | |
103 | NeedViaInt, | |
104 | NeedViaUsize, | |
105 | NonScalar, | |
106 | } | |
107 | ||
9346a6ac AL |
108 | impl<'tcx> CastCheck<'tcx> { |
109 | pub fn new(expr: ast::Expr, expr_ty: Ty<'tcx>, cast_ty: Ty<'tcx>, span: Span) | |
110 | -> CastCheck<'tcx> { | |
111 | CastCheck { | |
112 | expr: expr, | |
113 | expr_ty: expr_ty, | |
114 | cast_ty: cast_ty, | |
115 | span: span, | |
116 | } | |
117 | } | |
9346a6ac | 118 | |
62682a34 SL |
119 | fn report_cast_error<'a>(&self, fcx: &FnCtxt<'a, 'tcx>, |
120 | e: CastError) { | |
121 | match e { | |
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 `{}`", | |
127 | match e { | |
128 | CastError::NeedViaPtr => "a raw pointer", | |
129 | CastError::NeedViaInt => "an integer", | |
130 | CastError::NeedViaUsize => "a usize", | |
131 | _ => unreachable!() | |
132 | }, | |
133 | actual, | |
134 | fcx.infcx().ty_to_string(self.cast_ty)) | |
135 | }, self.expr_ty, None) | |
136 | } | |
137 | CastError::CastToBool => { | |
138 | span_err!(fcx.tcx().sess, self.span, E0054, | |
139 | "cannot cast as `bool`, compare with zero instead"); | |
140 | } | |
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); | |
145 | } | |
146 | CastError::NonScalar => { | |
147 | fcx.type_error_message(self.span, |actual| { | |
148 | format!("non-scalar cast: `{}` as `{}`", | |
149 | actual, | |
150 | fcx.infcx().ty_to_string(self.cast_ty)) | |
151 | }, self.expr_ty, None); | |
152 | } | |
153 | CastError::IllegalCast => { | |
154 | fcx.type_error_message(self.span, |actual| { | |
155 | format!("illegal cast: `{}` as `{}`", | |
156 | actual, | |
157 | fcx.infcx().ty_to_string(self.cast_ty)) | |
158 | }, self.expr_ty, None); | |
159 | } | |
160 | CastError::DifferingKinds => { | |
161 | fcx.type_error_message(self.span, |actual| { | |
162 | format!("illegal cast: `{}` as `{}`; vtable kinds may not match", | |
163 | actual, | |
164 | fcx.infcx().ty_to_string(self.cast_ty)) | |
165 | }, self.expr_ty, None); | |
166 | } | |
167 | } | |
168 | } | |
169 | ||
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, | |
175 | self.expr.id, | |
176 | self.span, | |
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))); | |
182 | } else { | |
183 | fcx.tcx().sess.add_lint(lint::builtin::TRIVIAL_CASTS, | |
184 | self.expr.id, | |
185 | self.span, | |
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))); | |
191 | } | |
192 | ||
9346a6ac AL |
193 | } |
194 | ||
62682a34 SL |
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); | |
198 | ||
199 | debug!("check_cast({}, {:?} as {:?})", self.expr.id, self.expr_ty, | |
200 | self.cast_ty); | |
201 | ||
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) { | |
210 | Ok(k) => { | |
211 | debug!(" -> {:?}", k); | |
212 | fcx.tcx().cast_kinds.borrow_mut().insert(self.expr.id, k); | |
9346a6ac | 213 | } |
62682a34 SL |
214 | Err(e) => self.report_cast_error(fcx, e) |
215 | };} | |
216 | } | |
217 | ||
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::*; | |
224 | ||
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), | |
228 | _ => { | |
229 | return Err(CastError::NonScalar) | |
230 | } | |
231 | }; | |
232 | ||
233 | match (t_from, t_cast) { | |
234 | // These types have invariants! can't cast into them. | |
235 | (_, RPtr(_)) | (_, Int(CEnum)) | (_, FnPtr) => Err(CastError::NonScalar), | |
236 | ||
237 | // * -> Bool | |
238 | (_, Int(Bool)) => Err(CastError::CastToBool), | |
239 | ||
240 | // * -> Char | |
241 | (Int(U(ast::TyU8)), Int(Char)) => Ok(CastKind::U8CharCast), // u8-char-cast | |
242 | (_, Int(Char)) => Err(CastError::CastToChar), | |
243 | ||
244 | // prim -> float,ptr | |
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), | |
249 | ||
250 | // ptr -> * | |
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), | |
256 | // * -> ptr | |
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 | |
261 | ||
262 | // prim -> prim | |
263 | (Int(CEnum), Int(_)) => Ok(CastKind::EnumCast), | |
264 | (Int(Char), Int(_)) | (Int(Bool), Int(_)) => Ok(CastKind::PrimIntCast), | |
265 | ||
266 | (Int(_), Int(_)) | | |
267 | (Int(_), Float) | | |
268 | (Float, Int(_)) | | |
269 | (Float, Float) => Ok(CastKind::NumericCast), | |
270 | ||
9346a6ac AL |
271 | } |
272 | } | |
273 | ||
62682a34 SL |
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> | |
279 | { | |
280 | debug!("check_ptr_ptr_cast m_expr={:?} m_cast={:?}", | |
281 | m_expr, m_cast); | |
282 | // ptr-ptr cast. vtables must match. | |
283 | ||
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); | |
9346a6ac | 287 | } |
62682a34 SL |
288 | |
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); | |
9346a6ac | 292 | } |
9346a6ac | 293 | |
62682a34 SL |
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) | |
9346a6ac | 298 | } |
62682a34 | 299 | } |
9346a6ac | 300 | |
62682a34 SL |
301 | fn check_fptr_ptr_cast<'a>(&self, |
302 | fcx: &FnCtxt<'a, 'tcx>, | |
303 | m_cast: &'tcx ty::mt<'tcx>) | |
304 | -> Result<CastKind, CastError> | |
305 | { | |
306 | // fptr-ptr cast. must be to sized ptr | |
307 | ||
308 | if fcx.type_is_known_to_be_sized(m_cast.ty, self.span) { | |
309 | Ok(CastKind::FnPtrPtrCast) | |
310 | } else { | |
311 | Err(CastError::IllegalCast) | |
312 | } | |
313 | } | |
314 | ||
315 | fn check_ptr_addr_cast<'a>(&self, | |
316 | fcx: &FnCtxt<'a, 'tcx>, | |
317 | m_expr: &'tcx ty::mt<'tcx>) | |
318 | -> Result<CastKind, CastError> | |
319 | { | |
320 | // ptr-addr cast. must be from sized ptr | |
321 | ||
322 | if fcx.type_is_known_to_be_sized(m_expr.ty, self.span) { | |
323 | Ok(CastKind::PtrAddrCast) | |
324 | } else { | |
325 | Err(CastError::NeedViaPtr) | |
326 | } | |
327 | } | |
328 | ||
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> | |
334 | { | |
335 | // array-ptr-cast. | |
336 | ||
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. | |
345 | ||
346 | // this will report a type mismatch if needed | |
347 | demand::eqtype(fcx, self.span, ety, m_cast.ty); | |
348 | return Ok(CastKind::ArrayPtrCast); | |
9346a6ac AL |
349 | } |
350 | } | |
62682a34 SL |
351 | |
352 | Err(CastError::IllegalCast) | |
353 | } | |
354 | ||
355 | fn check_addr_ptr_cast<'a>(&self, | |
356 | fcx: &FnCtxt<'a, 'tcx>, | |
357 | m_cast: &'tcx ty::mt<'tcx>) | |
358 | -> Result<CastKind, CastError> | |
359 | { | |
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) | |
363 | } else { | |
364 | Err(CastError::IllegalCast) | |
d9579d0f | 365 | } |
9346a6ac | 366 | } |
62682a34 SL |
367 | |
368 | fn try_coercion_cast<'a>(&self, fcx: &FnCtxt<'a, 'tcx>) -> bool { | |
369 | if let Ok(()) = coercion::mk_assignty(fcx, | |
370 | &self.expr, | |
371 | self.expr_ty, | |
372 | self.cast_ty) { | |
373 | true | |
374 | } else { | |
375 | false | |
376 | } | |
377 | } | |
378 | ||
9346a6ac | 379 | } |