]>
Commit | Line | Data |
---|---|---|
9346a6ac | 1 | //! Code for type-checking cast expressions. |
62682a34 SL |
2 | //! |
3 | //! A cast `e as U` is valid if one of the following holds: | |
4 | //! * `e` has type `T` and `T` coerces to `U`; *coercion-cast* | |
5 | //! * `e` has type `*T`, `U` is `*U_0`, and either `U_0: Sized` or | |
abe05a73 | 6 | //! pointer_kind(`T`) = pointer_kind(`U_0`); *ptr-ptr-cast* |
62682a34 SL |
7 | //! * `e` has type `*T` and `U` is a numeric type, while `T: Sized`; *ptr-addr-cast* |
8 | //! * `e` is an integer and `U` is `*U_0`, while `U_0: Sized`; *addr-ptr-cast* | |
9 | //! * `e` has type `T` and `T` and `U` are any numeric types; *numeric-cast* | |
10 | //! * `e` is a C-like enum and `U` is an integer type; *enum-cast* | |
11 | //! * `e` has type `bool` or `char` and `U` is an integer; *prim-int-cast* | |
12 | //! * `e` has type `u8` and `U` is `char`; *u8-char-cast* | |
13 | //! * `e` has type `&[T; n]` and `U` is `*const T`; *array-ptr-cast* | |
14 | //! * `e` is a function pointer type and `U` has type `*T`, | |
15 | //! while `T: Sized`; *fptr-ptr-cast* | |
16 | //! * `e` is a function pointer type and `U` is an integer; *fptr-addr-cast* | |
17 | //! | |
18 | //! where `&.T` and `*T` are references of either mutability, | |
abe05a73 | 19 | //! and where pointer_kind(`T`) is the kind of the unsize info |
0731742a | 20 | //! in `T` - the vtable for a trait definition (e.g., `fmt::Display` or |
62682a34 SL |
21 | //! `Iterator`, not `Iterator<Item=u8>`) or a length (or `()` if `T: Sized`). |
22 | //! | |
23 | //! Note that lengths are not adjusted when casting raw slices - | |
24 | //! `T: *const [u16] as *const [u8]` creates a slice that only includes | |
25 | //! half of the original memory. | |
26 | //! | |
27 | //! Casting is not transitive, that is, even if `e as U1 as U2` is a valid | |
28 | //! expression, `e as U2` is not necessarily so (in fact it will only be valid if | |
29 | //! `U1` coerces to `U2`). | |
9346a6ac | 30 | |
0531ce1d | 31 | use super::FnCtxt; |
9346a6ac | 32 | |
9fa01778 | 33 | use crate::hir::def_id::DefId; |
dfeec247 | 34 | use crate::type_error_struct; |
74b04a01 | 35 | use rustc_ast::ast; |
ba9703b0 | 36 | use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorReported}; |
dfeec247 | 37 | use rustc_hir as hir; |
ba9703b0 XL |
38 | use rustc_hir::lang_items; |
39 | use rustc_middle::ty::adjustment::AllowTwoPhase; | |
40 | use rustc_middle::ty::cast::{CastKind, CastTy}; | |
41 | use rustc_middle::ty::error::TypeError; | |
42 | use rustc_middle::ty::subst::SubstsRef; | |
43 | use rustc_middle::ty::{self, Ty, TypeAndMut, TypeFoldable}; | |
44 | use rustc_session::lint; | |
45 | use rustc_session::Session; | |
f035d41b | 46 | use rustc_span::symbol::sym; |
dfeec247 | 47 | use rustc_span::Span; |
ba9703b0 XL |
48 | use rustc_trait_selection::traits; |
49 | use rustc_trait_selection::traits::error_reporting::report_object_safety_error; | |
60c5eb7d | 50 | |
9346a6ac AL |
51 | /// Reifies a cast check to be checked once we have full type information for |
52 | /// a function context. | |
53 | pub struct CastCheck<'tcx> { | |
dfeec247 | 54 | expr: &'tcx hir::Expr<'tcx>, |
9346a6ac AL |
55 | expr_ty: Ty<'tcx>, |
56 | cast_ty: Ty<'tcx>, | |
a7813a04 | 57 | cast_span: Span, |
9346a6ac AL |
58 | span: Span, |
59 | } | |
60 | ||
abe05a73 XL |
61 | /// The kind of pointer and associated metadata (thin, length or vtable) - we |
62 | /// only allow casts between fat pointers if their metadata have the same | |
63 | /// kind. | |
62682a34 | 64 | #[derive(Copy, Clone, PartialEq, Eq)] |
abe05a73 XL |
65 | enum PointerKind<'tcx> { |
66 | /// No metadata attached, ie pointer to sized type or foreign type | |
67 | Thin, | |
68 | /// A trait object | |
0731742a | 69 | Vtable(Option<DefId>), |
abe05a73 | 70 | /// Slice |
62682a34 SL |
71 | Length, |
72 | /// The unsize info of this projection | |
73 | OfProjection(&'tcx ty::ProjectionTy<'tcx>), | |
b7449926 | 74 | /// The unsize info of this opaque ty |
532ac7d7 | 75 | OfOpaque(DefId, SubstsRef<'tcx>), |
62682a34 | 76 | /// The unsize info of this parameter |
c30ab7b3 | 77 | OfParam(&'tcx ty::ParamTy), |
62682a34 SL |
78 | } |
79 | ||
dc9dc135 | 80 | impl<'a, 'tcx> FnCtxt<'a, 'tcx> { |
a7813a04 | 81 | /// Returns the kind of unsize information of t, or None |
abe05a73 | 82 | /// if t is unknown. |
dfeec247 XL |
83 | fn pointer_kind( |
84 | &self, | |
85 | t: Ty<'tcx>, | |
86 | span: Span, | |
87 | ) -> Result<Option<PointerKind<'tcx>>, ErrorReported> { | |
ff7c6d11 XL |
88 | debug!("pointer_kind({:?}, {:?})", t, span); |
89 | ||
dc9dc135 | 90 | let t = self.resolve_vars_if_possible(&t); |
ff7c6d11 XL |
91 | |
92 | if t.references_error() { | |
93 | return Err(ErrorReported); | |
94 | } | |
95 | ||
0731742a | 96 | if self.type_is_known_to_be_sized_modulo_regions(t, span) { |
ff7c6d11 | 97 | return Ok(Some(PointerKind::Thin)); |
abe05a73 XL |
98 | } |
99 | ||
e74abb32 | 100 | Ok(match t.kind { |
b7449926 | 101 | ty::Slice(_) | ty::Str => Some(PointerKind::Length), |
dfeec247 XL |
102 | ty::Dynamic(ref tty, ..) => Some(PointerKind::Vtable(tty.principal_def_id())), |
103 | ty::Adt(def, substs) if def.is_struct() => match def.non_enum_variant().fields.last() { | |
104 | None => Some(PointerKind::Thin), | |
105 | Some(f) => { | |
106 | let field_ty = self.field_ty(span, f, substs); | |
107 | self.pointer_kind(field_ty, span)? | |
a7813a04 | 108 | } |
dfeec247 | 109 | }, |
b7449926 | 110 | ty::Tuple(fields) => match fields.last() { |
ff7c6d11 | 111 | None => Some(PointerKind::Thin), |
dfeec247 | 112 | Some(f) => self.pointer_kind(f.expect_ty(), span)?, |
ff7c6d11 XL |
113 | }, |
114 | ||
abe05a73 | 115 | // Pointers to foreign types are thin, despite being unsized |
b7449926 | 116 | ty::Foreign(..) => Some(PointerKind::Thin), |
a7813a04 | 117 | // We should really try to normalize here. |
b7449926 XL |
118 | ty::Projection(ref pi) => Some(PointerKind::OfProjection(pi)), |
119 | ty::Opaque(def_id, substs) => Some(PointerKind::OfOpaque(def_id, substs)), | |
120 | ty::Param(ref p) => Some(PointerKind::OfParam(p)), | |
abe05a73 | 121 | // Insufficient type information. |
a1dfa0c6 | 122 | ty::Placeholder(..) | ty::Bound(..) | ty::Infer(_) => None, |
ff7c6d11 | 123 | |
dfeec247 XL |
124 | ty::Bool |
125 | | ty::Char | |
126 | | ty::Int(..) | |
127 | | ty::Uint(..) | |
128 | | ty::Float(_) | |
129 | | ty::Array(..) | |
130 | | ty::GeneratorWitness(..) | |
131 | | ty::RawPtr(_) | |
132 | | ty::Ref(..) | |
133 | | ty::FnDef(..) | |
134 | | ty::FnPtr(..) | |
135 | | ty::Closure(..) | |
136 | | ty::Generator(..) | |
137 | | ty::Adt(..) | |
138 | | ty::Never | |
f035d41b | 139 | | ty::Error(_) => { |
dfeec247 XL |
140 | self.tcx |
141 | .sess | |
142 | .delay_span_bug(span, &format!("`{:?}` should be sized but is not?", t)); | |
ff7c6d11 XL |
143 | return Err(ErrorReported); |
144 | } | |
145 | }) | |
62682a34 SL |
146 | } |
147 | } | |
148 | ||
149 | #[derive(Copy, Clone)] | |
150 | enum CastError { | |
ff7c6d11 XL |
151 | ErrorReported, |
152 | ||
62682a34 SL |
153 | CastToBool, |
154 | CastToChar, | |
155 | DifferingKinds, | |
9fa01778 | 156 | /// Cast of thin to fat raw ptr (e.g., `*const () as *const [u8]`). |
54a0048b | 157 | SizedUnsizedCast, |
62682a34 | 158 | IllegalCast, |
476ff2be | 159 | NeedDeref, |
62682a34 | 160 | NeedViaPtr, |
e9174d1e | 161 | NeedViaThinPtr, |
62682a34 | 162 | NeedViaInt, |
62682a34 | 163 | NonScalar, |
abe05a73 XL |
164 | UnknownExprPtrKind, |
165 | UnknownCastPtrKind, | |
62682a34 SL |
166 | } |
167 | ||
ff7c6d11 XL |
168 | impl From<ErrorReported> for CastError { |
169 | fn from(ErrorReported: ErrorReported) -> Self { | |
170 | CastError::ErrorReported | |
171 | } | |
172 | } | |
173 | ||
dc9dc135 XL |
174 | fn make_invalid_casting_error<'a, 'tcx>( |
175 | sess: &'a Session, | |
176 | span: Span, | |
177 | expr_ty: Ty<'tcx>, | |
178 | cast_ty: Ty<'tcx>, | |
179 | fcx: &FnCtxt<'a, 'tcx>, | |
180 | ) -> DiagnosticBuilder<'a> { | |
dfeec247 XL |
181 | type_error_struct!( |
182 | sess, | |
183 | span, | |
184 | expr_ty, | |
185 | E0606, | |
186 | "casting `{}` as `{}` is invalid", | |
187 | fcx.ty_to_string(expr_ty), | |
188 | fcx.ty_to_string(cast_ty) | |
189 | ) | |
041b39d2 XL |
190 | } |
191 | ||
dc9dc135 XL |
192 | impl<'a, 'tcx> CastCheck<'tcx> { |
193 | pub fn new( | |
194 | fcx: &FnCtxt<'a, 'tcx>, | |
dfeec247 | 195 | expr: &'tcx hir::Expr<'tcx>, |
dc9dc135 XL |
196 | expr_ty: Ty<'tcx>, |
197 | cast_ty: Ty<'tcx>, | |
198 | cast_span: Span, | |
199 | span: Span, | |
200 | ) -> Result<CastCheck<'tcx>, ErrorReported> { | |
dfeec247 | 201 | let check = CastCheck { expr, expr_ty, cast_ty, cast_span, span }; |
a7813a04 XL |
202 | |
203 | // For better error messages, check for some obviously unsized | |
204 | // cases now. We do a more thorough check at the end, once | |
205 | // inference is more completely known. | |
e74abb32 | 206 | match cast_ty.kind { |
b7449926 | 207 | ty::Dynamic(..) | ty::Slice(..) => { |
a7813a04 XL |
208 | check.report_cast_to_unsized_type(fcx); |
209 | Err(ErrorReported) | |
210 | } | |
c30ab7b3 | 211 | _ => Ok(check), |
9346a6ac AL |
212 | } |
213 | } | |
9346a6ac | 214 | |
dc9dc135 | 215 | fn report_cast_error(&self, fcx: &FnCtxt<'a, 'tcx>, e: CastError) { |
62682a34 | 216 | match e { |
ff7c6d11 XL |
217 | CastError::ErrorReported => { |
218 | // an error has already been reported | |
219 | } | |
476ff2be | 220 | CastError::NeedDeref => { |
32a655c1 | 221 | let error_span = self.span; |
dfeec247 XL |
222 | let mut err = make_invalid_casting_error( |
223 | fcx.tcx.sess, | |
224 | self.span, | |
225 | self.expr_ty, | |
226 | self.cast_ty, | |
227 | fcx, | |
228 | ); | |
476ff2be | 229 | let cast_ty = fcx.ty_to_string(self.cast_ty); |
dfeec247 XL |
230 | err.span_label( |
231 | error_span, | |
232 | format!("cannot cast `{}` as `{}`", fcx.ty_to_string(self.expr_ty), cast_ty), | |
233 | ); | |
b7449926 | 234 | if let Ok(snippet) = fcx.sess().source_map().span_to_snippet(self.expr.span) { |
9fa01778 | 235 | err.span_suggestion( |
0731742a XL |
236 | self.expr.span, |
237 | "dereference the expression", | |
238 | format!("*{}", snippet), | |
239 | Applicability::MaybeIncorrect, | |
240 | ); | |
241 | } else { | |
242 | err.span_help(self.expr.span, "dereference the expression with `*`"); | |
476ff2be SL |
243 | } |
244 | err.emit(); | |
245 | } | |
dfeec247 XL |
246 | CastError::NeedViaThinPtr | CastError::NeedViaPtr => { |
247 | let mut err = make_invalid_casting_error( | |
248 | fcx.tcx.sess, | |
249 | self.span, | |
250 | self.expr_ty, | |
251 | self.cast_ty, | |
252 | fcx, | |
253 | ); | |
2c00a5a8 | 254 | if self.cast_ty.is_integral() { |
dfeec247 XL |
255 | err.help(&format!( |
256 | "cast through {} first", | |
257 | match e { | |
258 | CastError::NeedViaPtr => "a raw pointer", | |
259 | CastError::NeedViaThinPtr => "a thin pointer", | |
260 | _ => bug!(), | |
261 | } | |
262 | )); | |
476ff2be SL |
263 | } |
264 | err.emit(); | |
265 | } | |
266 | CastError::NeedViaInt => { | |
dfeec247 XL |
267 | make_invalid_casting_error( |
268 | fcx.tcx.sess, | |
269 | self.span, | |
270 | self.expr_ty, | |
271 | self.cast_ty, | |
272 | fcx, | |
273 | ) | |
274 | .help(&format!( | |
275 | "cast through {} first", | |
276 | match e { | |
277 | CastError::NeedViaInt => "an integer", | |
278 | _ => bug!(), | |
279 | } | |
280 | )) | |
281 | .emit(); | |
62682a34 | 282 | } |
041b39d2 | 283 | CastError::IllegalCast => { |
dfeec247 XL |
284 | make_invalid_casting_error( |
285 | fcx.tcx.sess, | |
286 | self.span, | |
287 | self.expr_ty, | |
288 | self.cast_ty, | |
289 | fcx, | |
290 | ) | |
291 | .emit(); | |
041b39d2 XL |
292 | } |
293 | CastError::DifferingKinds => { | |
dfeec247 XL |
294 | make_invalid_casting_error( |
295 | fcx.tcx.sess, | |
296 | self.span, | |
297 | self.expr_ty, | |
298 | self.cast_ty, | |
299 | fcx, | |
300 | ) | |
301 | .note("vtable kinds may not match") | |
302 | .emit(); | |
041b39d2 | 303 | } |
62682a34 | 304 | CastError::CastToBool => { |
0731742a XL |
305 | let mut err = |
306 | struct_span_err!(fcx.tcx.sess, self.span, E0054, "cannot cast as `bool`"); | |
307 | ||
308 | if self.expr_ty.is_numeric() { | |
309 | match fcx.tcx.sess.source_map().span_to_snippet(self.expr.span) { | |
310 | Ok(snippet) => { | |
9fa01778 | 311 | err.span_suggestion( |
0731742a XL |
312 | self.span, |
313 | "compare with zero instead", | |
314 | format!("{} != 0", snippet), | |
315 | Applicability::MachineApplicable, | |
316 | ); | |
317 | } | |
318 | Err(_) => { | |
319 | err.span_help(self.span, "compare with zero instead"); | |
320 | } | |
321 | } | |
322 | } else { | |
323 | err.span_label(self.span, "unsupported cast"); | |
324 | } | |
325 | ||
326 | err.emit(); | |
62682a34 SL |
327 | } |
328 | CastError::CastToChar => { | |
dfeec247 XL |
329 | type_error_struct!( |
330 | fcx.tcx.sess, | |
331 | self.span, | |
332 | self.expr_ty, | |
333 | E0604, | |
334 | "only `u8` can be cast as `char`, not `{}`", | |
335 | self.expr_ty | |
336 | ) | |
f035d41b | 337 | .span_label(self.span, "invalid cast") |
dfeec247 | 338 | .emit(); |
62682a34 SL |
339 | } |
340 | CastError::NonScalar => { | |
f035d41b | 341 | let mut err = type_error_struct!( |
dfeec247 XL |
342 | fcx.tcx.sess, |
343 | self.span, | |
344 | self.expr_ty, | |
345 | E0605, | |
346 | "non-primitive cast: `{}` as `{}`", | |
347 | self.expr_ty, | |
348 | fcx.ty_to_string(self.cast_ty) | |
f035d41b XL |
349 | ); |
350 | let mut sugg = None; | |
351 | if let ty::Ref(reg, _, mutbl) = self.cast_ty.kind { | |
352 | if fcx | |
353 | .try_coerce( | |
354 | self.expr, | |
355 | fcx.tcx.mk_ref(reg, TypeAndMut { ty: self.expr_ty, mutbl }), | |
356 | self.cast_ty, | |
357 | AllowTwoPhase::No, | |
358 | ) | |
359 | .is_ok() | |
360 | { | |
361 | sugg = Some(format!("&{}", mutbl.prefix_str())); | |
362 | } | |
363 | } | |
364 | if let Some(sugg) = sugg { | |
365 | err.span_label(self.span, "invalid cast"); | |
366 | err.span_suggestion_verbose( | |
367 | self.expr.span.shrink_to_lo(), | |
368 | "borrow the value for the cast to be valid", | |
369 | sugg, | |
370 | Applicability::MachineApplicable, | |
371 | ); | |
372 | } else if !matches!( | |
373 | self.cast_ty.kind, | |
374 | ty::FnDef(..) | ty::FnPtr(..) | ty::Closure(..) | |
375 | ) { | |
376 | let mut label = true; | |
377 | // Check `impl From<self.expr_ty> for self.cast_ty {}` for accurate suggestion: | |
378 | if let Ok(snippet) = fcx.tcx.sess.source_map().span_to_snippet(self.expr.span) { | |
379 | if let Some(from_trait) = fcx.tcx.get_diagnostic_item(sym::from_trait) { | |
380 | let ty = fcx.resolve_vars_if_possible(&self.cast_ty); | |
381 | // Erase regions to avoid panic in `prove_value` when calling | |
382 | // `type_implements_trait`. | |
383 | let ty = fcx.tcx.erase_regions(&ty); | |
384 | let expr_ty = fcx.resolve_vars_if_possible(&self.expr_ty); | |
385 | let expr_ty = fcx.tcx.erase_regions(&expr_ty); | |
386 | let ty_params = fcx.tcx.mk_substs_trait(expr_ty, &[]); | |
387 | // Check for infer types because cases like `Option<{integer}>` would | |
388 | // panic otherwise. | |
389 | if !expr_ty.has_infer_types() | |
390 | && fcx.tcx.type_implements_trait(( | |
391 | from_trait, | |
392 | ty, | |
393 | ty_params, | |
394 | fcx.param_env, | |
395 | )) | |
396 | { | |
397 | label = false; | |
398 | err.span_suggestion( | |
399 | self.span, | |
400 | "consider using the `From` trait instead", | |
401 | format!("{}::from({})", self.cast_ty, snippet), | |
402 | Applicability::MaybeIncorrect, | |
403 | ); | |
404 | } | |
405 | } | |
406 | } | |
407 | let msg = "an `as` expression can only be used to convert between primitive \ | |
408 | types or to coerce to a specific trait object"; | |
409 | if label { | |
410 | err.span_label(self.span, msg); | |
411 | } else { | |
412 | err.note(msg); | |
413 | } | |
414 | } else { | |
415 | err.span_label(self.span, "invalid cast"); | |
416 | } | |
417 | err.emit(); | |
62682a34 | 418 | } |
54a0048b | 419 | CastError::SizedUnsizedCast => { |
9fa01778 | 420 | use crate::structured_errors::{SizedUnsizedCastError, StructuredDiagnostic}; |
dfeec247 XL |
421 | SizedUnsizedCastError::new( |
422 | &fcx.tcx.sess, | |
423 | self.span, | |
424 | self.expr_ty, | |
425 | fcx.ty_to_string(self.cast_ty), | |
426 | ) | |
427 | .diagnostic() | |
428 | .emit(); | |
62682a34 | 429 | } |
dfeec247 | 430 | CastError::UnknownCastPtrKind | CastError::UnknownExprPtrKind => { |
abe05a73 XL |
431 | let unknown_cast_to = match e { |
432 | CastError::UnknownCastPtrKind => true, | |
433 | CastError::UnknownExprPtrKind => false, | |
434 | _ => bug!(), | |
435 | }; | |
dfeec247 XL |
436 | let mut err = struct_span_err!( |
437 | fcx.tcx.sess, | |
f035d41b | 438 | if unknown_cast_to { self.cast_span } else { self.span }, |
dfeec247 XL |
439 | E0641, |
440 | "cannot cast {} a pointer of an unknown kind", | |
441 | if unknown_cast_to { "to" } else { "from" } | |
442 | ); | |
abe05a73 | 443 | if unknown_cast_to { |
f035d41b XL |
444 | err.span_label(self.cast_span, "needs more type information"); |
445 | err.note( | |
446 | "the type information given here is insufficient to check whether \ | |
447 | the pointer cast is valid", | |
448 | ); | |
449 | } else { | |
450 | err.span_label( | |
451 | self.span, | |
452 | "the type information given here is insufficient to check whether \ | |
453 | the pointer cast is valid", | |
0bf4aa26 | 454 | ); |
abe05a73 XL |
455 | } |
456 | err.emit(); | |
457 | } | |
62682a34 SL |
458 | } |
459 | } | |
460 | ||
dc9dc135 | 461 | fn report_cast_to_unsized_type(&self, fcx: &FnCtxt<'a, 'tcx>) { |
c30ab7b3 | 462 | if self.cast_ty.references_error() || self.expr_ty.references_error() { |
a7813a04 XL |
463 | return; |
464 | } | |
465 | ||
466 | let tstr = fcx.ty_to_string(self.cast_ty); | |
dfeec247 XL |
467 | let mut err = type_error_struct!( |
468 | fcx.tcx.sess, | |
469 | self.span, | |
470 | self.expr_ty, | |
471 | E0620, | |
472 | "cast to unsized type: `{}` as `{}`", | |
473 | fcx.resolve_vars_if_possible(&self.expr_ty), | |
474 | tstr | |
475 | ); | |
e74abb32 | 476 | match self.expr_ty.kind { |
b7449926 | 477 | ty::Ref(_, _, mt) => { |
60c5eb7d | 478 | let mtstr = mt.prefix_str(); |
a7813a04 | 479 | if self.cast_ty.is_trait() { |
b7449926 | 480 | match fcx.tcx.sess.source_map().span_to_snippet(self.cast_span) { |
a7813a04 | 481 | Ok(s) => { |
9fa01778 | 482 | err.span_suggestion( |
0bf4aa26 XL |
483 | self.cast_span, |
484 | "try casting to a reference instead", | |
485 | format!("&{}{}", mtstr, s), | |
486 | Applicability::MachineApplicable, | |
487 | ); | |
c30ab7b3 SL |
488 | } |
489 | Err(_) => { | |
dfeec247 XL |
490 | let msg = &format!("did you mean `&{}{}`?", mtstr, tstr); |
491 | err.span_help(self.cast_span, msg); | |
c30ab7b3 | 492 | } |
a7813a04 XL |
493 | } |
494 | } else { | |
dfeec247 XL |
495 | let msg = &format!( |
496 | "consider using an implicit coercion to `&{}{}` instead", | |
497 | mtstr, tstr | |
498 | ); | |
499 | err.span_help(self.span, msg); | |
a7813a04 XL |
500 | } |
501 | } | |
b7449926 XL |
502 | ty::Adt(def, ..) if def.is_box() => { |
503 | match fcx.tcx.sess.source_map().span_to_snippet(self.cast_span) { | |
a7813a04 | 504 | Ok(s) => { |
9fa01778 | 505 | err.span_suggestion( |
0bf4aa26 | 506 | self.cast_span, |
f035d41b | 507 | "you can cast to a `Box` instead", |
0bf4aa26 XL |
508 | format!("Box<{}>", s), |
509 | Applicability::MachineApplicable, | |
510 | ); | |
c30ab7b3 | 511 | } |
dfeec247 | 512 | Err(_) => { |
f035d41b XL |
513 | err.span_help( |
514 | self.cast_span, | |
515 | &format!("you might have meant `Box<{}>`", tstr), | |
516 | ); | |
dfeec247 | 517 | } |
a7813a04 XL |
518 | } |
519 | } | |
520 | _ => { | |
dfeec247 | 521 | err.span_help(self.expr.span, "consider using a box or reference as appropriate"); |
a7813a04 XL |
522 | } |
523 | } | |
524 | err.emit(); | |
525 | } | |
526 | ||
dc9dc135 | 527 | fn trivial_cast_lint(&self, fcx: &FnCtxt<'a, 'tcx>) { |
62682a34 SL |
528 | let t_cast = self.cast_ty; |
529 | let t_expr = self.expr_ty; | |
dfeec247 XL |
530 | let type_asc_or = |
531 | if fcx.tcx.features().type_ascription { "type ascription or " } else { "" }; | |
8faf50e0 XL |
532 | let (adjective, lint) = if t_cast.is_numeric() && t_expr.is_numeric() { |
533 | ("numeric ", lint::builtin::TRIVIAL_NUMERIC_CASTS) | |
534 | } else { | |
535 | ("", lint::builtin::TRIVIAL_CASTS) | |
536 | }; | |
74b04a01 XL |
537 | fcx.tcx.struct_span_lint_hir(lint, self.expr.hir_id, self.span, |err| { |
538 | err.build(&format!( | |
dfeec247 XL |
539 | "trivial {}cast: `{}` as `{}`", |
540 | adjective, | |
541 | fcx.ty_to_string(t_expr), | |
542 | fcx.ty_to_string(t_cast) | |
74b04a01 XL |
543 | )) |
544 | .help(&format!( | |
545 | "cast can be replaced by coercion; this might \ | |
546 | require {}a temporary variable", | |
547 | type_asc_or | |
548 | )) | |
549 | .emit(); | |
550 | }); | |
9346a6ac AL |
551 | } |
552 | ||
dc9dc135 | 553 | pub fn check(mut self, fcx: &FnCtxt<'a, 'tcx>) { |
a7813a04 XL |
554 | self.expr_ty = fcx.structurally_resolved_type(self.span, self.expr_ty); |
555 | self.cast_ty = fcx.structurally_resolved_type(self.span, self.cast_ty); | |
62682a34 | 556 | |
dfeec247 | 557 | debug!("check_cast({}, {:?} as {:?})", self.expr.hir_id, self.expr_ty, self.cast_ty); |
62682a34 | 558 | |
0731742a | 559 | if !fcx.type_is_known_to_be_sized_modulo_regions(self.cast_ty, self.span) { |
a7813a04 XL |
560 | self.report_cast_to_unsized_type(fcx); |
561 | } else if self.expr_ty.references_error() || self.cast_ty.references_error() { | |
62682a34 | 562 | // No sense in giving duplicate error messages |
c30ab7b3 | 563 | } else { |
e74abb32 XL |
564 | match self.try_coercion_cast(fcx) { |
565 | Ok(()) => { | |
566 | self.trivial_cast_lint(fcx); | |
567 | debug!(" -> CoercionCast"); | |
dfeec247 | 568 | fcx.tables.borrow_mut().set_coercion_cast(self.expr.hir_id.local_id); |
e74abb32 XL |
569 | } |
570 | Err(ty::error::TypeError::ObjectUnsafeCoercion(did)) => { | |
571 | self.report_object_unsafe_cast(&fcx, did); | |
572 | } | |
573 | Err(_) => { | |
574 | match self.do_check(fcx) { | |
575 | Ok(k) => { | |
576 | debug!(" -> {:?}", k); | |
577 | } | |
578 | Err(e) => self.report_cast_error(fcx, e), | |
579 | }; | |
c30ab7b3 | 580 | } |
c30ab7b3 SL |
581 | }; |
582 | } | |
62682a34 SL |
583 | } |
584 | ||
e74abb32 | 585 | fn report_object_unsafe_cast(&self, fcx: &FnCtxt<'a, 'tcx>, did: DefId) { |
74b04a01 | 586 | let violations = fcx.tcx.object_safety_violations(did); |
dfeec247 | 587 | let mut err = report_object_safety_error(fcx.tcx, self.cast_span, did, violations); |
e74abb32 XL |
588 | err.note(&format!("required by cast to type '{}'", fcx.ty_to_string(self.cast_ty))); |
589 | err.emit(); | |
590 | } | |
591 | ||
9fa01778 | 592 | /// Checks a cast, and report an error if one exists. In some cases, this |
62682a34 SL |
593 | /// can return Ok and create type errors in the fcx rather than returning |
594 | /// directly. coercion-cast is handled in check instead of here. | |
dc9dc135 | 595 | fn do_check(&self, fcx: &FnCtxt<'a, 'tcx>) -> Result<CastKind, CastError> { |
ba9703b0 XL |
596 | use rustc_middle::ty::cast::CastTy::*; |
597 | use rustc_middle::ty::cast::IntTy::*; | |
62682a34 | 598 | |
dfeec247 XL |
599 | let (t_from, t_cast) = match (CastTy::from_ty(self.expr_ty), CastTy::from_ty(self.cast_ty)) |
600 | { | |
62682a34 | 601 | (Some(t_from), Some(t_cast)) => (t_from, t_cast), |
54a0048b SL |
602 | // Function item types may need to be reified before casts. |
603 | (None, Some(t_cast)) => { | |
dfeec247 XL |
604 | match self.expr_ty.kind { |
605 | ty::FnDef(..) => { | |
606 | // Attempt a coercion to a fn pointer type. | |
ba9703b0 XL |
607 | let f = fcx.normalize_associated_types_in( |
608 | self.expr.span, | |
609 | &self.expr_ty.fn_sig(fcx.tcx), | |
610 | ); | |
dfeec247 XL |
611 | let res = fcx.try_coerce( |
612 | self.expr, | |
613 | self.expr_ty, | |
614 | fcx.tcx.mk_fn_ptr(f), | |
615 | AllowTwoPhase::No, | |
616 | ); | |
617 | if let Err(TypeError::IntrinsicCast) = res { | |
618 | return Err(CastError::IllegalCast); | |
619 | } | |
620 | if res.is_err() { | |
621 | return Err(CastError::NonScalar); | |
622 | } | |
623 | (FnPtr, t_cast) | |
e1599b0c | 624 | } |
dfeec247 XL |
625 | // Special case some errors for references, and check for |
626 | // array-ptr-casts. `Ref` is not a CastTy because the cast | |
627 | // is split into a coercion to a pointer type, followed by | |
628 | // a cast. | |
629 | ty::Ref(_, inner_ty, mutbl) => { | |
630 | return match t_cast { | |
631 | Int(_) | Float => match inner_ty.kind { | |
632 | ty::Int(_) | |
633 | | ty::Uint(_) | |
634 | | ty::Float(_) | |
ba9703b0 XL |
635 | | ty::Infer(ty::InferTy::IntVar(_) | ty::InferTy::FloatVar(_)) => { |
636 | Err(CastError::NeedDeref) | |
637 | } | |
dfeec247 XL |
638 | _ => Err(CastError::NeedViaPtr), |
639 | }, | |
640 | // array-ptr-cast | |
641 | Ptr(mt) => { | |
642 | self.check_ref_cast(fcx, TypeAndMut { mutbl, ty: inner_ty }, mt) | |
643 | } | |
644 | _ => Err(CastError::NonScalar), | |
645 | }; | |
54a0048b | 646 | } |
dfeec247 | 647 | _ => return Err(CastError::NonScalar), |
54a0048b SL |
648 | } |
649 | } | |
c30ab7b3 | 650 | _ => return Err(CastError::NonScalar), |
62682a34 SL |
651 | }; |
652 | ||
653 | match (t_from, t_cast) { | |
654 | // These types have invariants! can't cast into them. | |
ba9703b0 | 655 | (_, Int(CEnum) | FnPtr) => Err(CastError::NonScalar), |
62682a34 SL |
656 | |
657 | // * -> Bool | |
658 | (_, Int(Bool)) => Err(CastError::CastToBool), | |
659 | ||
660 | // * -> Char | |
7453a54e | 661 | (Int(U(ast::UintTy::U8)), Int(Char)) => Ok(CastKind::U8CharCast), // u8-char-cast |
62682a34 SL |
662 | (_, Int(Char)) => Err(CastError::CastToChar), |
663 | ||
664 | // prim -> float,ptr | |
ba9703b0 | 665 | (Int(Bool) | Int(CEnum) | Int(Char), Float) => Err(CastError::NeedViaInt), |
476ff2be | 666 | |
ba9703b0 XL |
667 | (Int(Bool) | Int(CEnum) | Int(Char) | Float, Ptr(_)) | (Ptr(_) | FnPtr, Float) => { |
668 | Err(CastError::IllegalCast) | |
669 | } | |
62682a34 SL |
670 | |
671 | // ptr -> * | |
672 | (Ptr(m_e), Ptr(m_c)) => self.check_ptr_ptr_cast(fcx, m_e, m_c), // ptr-ptr-cast | |
673 | (Ptr(m_expr), Int(_)) => self.check_ptr_addr_cast(fcx, m_expr), // ptr-addr-cast | |
62682a34 | 674 | (FnPtr, Int(_)) => Ok(CastKind::FnPtrAddrCast), |
dfeec247 | 675 | |
62682a34 SL |
676 | // * -> ptr |
677 | (Int(_), Ptr(mt)) => self.check_addr_ptr_cast(fcx, mt), // addr-ptr-cast | |
678 | (FnPtr, Ptr(mt)) => self.check_fptr_ptr_cast(fcx, mt), | |
62682a34 SL |
679 | |
680 | // prim -> prim | |
f035d41b XL |
681 | (Int(CEnum), Int(_)) => { |
682 | self.cenum_impl_drop_lint(fcx); | |
683 | Ok(CastKind::EnumCast) | |
684 | } | |
ba9703b0 | 685 | (Int(Char) | Int(Bool), Int(_)) => Ok(CastKind::PrimIntCast), |
62682a34 | 686 | |
ba9703b0 | 687 | (Int(_) | Float, Int(_) | Float) => Ok(CastKind::NumericCast), |
9346a6ac AL |
688 | } |
689 | } | |
690 | ||
dc9dc135 XL |
691 | fn check_ptr_ptr_cast( |
692 | &self, | |
693 | fcx: &FnCtxt<'a, 'tcx>, | |
694 | m_expr: ty::TypeAndMut<'tcx>, | |
695 | m_cast: ty::TypeAndMut<'tcx>, | |
696 | ) -> Result<CastKind, CastError> { | |
c30ab7b3 | 697 | debug!("check_ptr_ptr_cast m_expr={:?} m_cast={:?}", m_expr, m_cast); |
62682a34 SL |
698 | // ptr-ptr cast. vtables must match. |
699 | ||
ff7c6d11 XL |
700 | let expr_kind = fcx.pointer_kind(m_expr.ty, self.span)?; |
701 | let cast_kind = fcx.pointer_kind(m_cast.ty, self.span)?; | |
abe05a73 XL |
702 | |
703 | let cast_kind = match cast_kind { | |
704 | // We can't cast if target pointer kind is unknown | |
705 | None => return Err(CastError::UnknownCastPtrKind), | |
706 | Some(cast_kind) => cast_kind, | |
707 | }; | |
708 | ||
709 | // Cast to thin pointer is OK | |
710 | if cast_kind == PointerKind::Thin { | |
62682a34 | 711 | return Ok(CastKind::PtrPtrCast); |
9346a6ac | 712 | } |
62682a34 | 713 | |
abe05a73 XL |
714 | let expr_kind = match expr_kind { |
715 | // We can't cast to fat pointer if source pointer kind is unknown | |
716 | None => return Err(CastError::UnknownExprPtrKind), | |
717 | Some(expr_kind) => expr_kind, | |
718 | }; | |
719 | ||
720 | // thin -> fat? report invalid cast (don't complain about vtable kinds) | |
721 | if expr_kind == PointerKind::Thin { | |
54a0048b | 722 | return Err(CastError::SizedUnsizedCast); |
9346a6ac | 723 | } |
9346a6ac | 724 | |
62682a34 | 725 | // vtable kinds must match |
abe05a73 XL |
726 | if cast_kind == expr_kind { |
727 | Ok(CastKind::PtrPtrCast) | |
728 | } else { | |
729 | Err(CastError::DifferingKinds) | |
9346a6ac | 730 | } |
62682a34 | 731 | } |
9346a6ac | 732 | |
dc9dc135 XL |
733 | fn check_fptr_ptr_cast( |
734 | &self, | |
735 | fcx: &FnCtxt<'a, 'tcx>, | |
736 | m_cast: ty::TypeAndMut<'tcx>, | |
737 | ) -> Result<CastKind, CastError> { | |
abe05a73 | 738 | // fptr-ptr cast. must be to thin ptr |
62682a34 | 739 | |
ff7c6d11 | 740 | match fcx.pointer_kind(m_cast.ty, self.span)? { |
abe05a73 XL |
741 | None => Err(CastError::UnknownCastPtrKind), |
742 | Some(PointerKind::Thin) => Ok(CastKind::FnPtrPtrCast), | |
743 | _ => Err(CastError::IllegalCast), | |
62682a34 SL |
744 | } |
745 | } | |
746 | ||
dc9dc135 XL |
747 | fn check_ptr_addr_cast( |
748 | &self, | |
749 | fcx: &FnCtxt<'a, 'tcx>, | |
750 | m_expr: ty::TypeAndMut<'tcx>, | |
751 | ) -> Result<CastKind, CastError> { | |
abe05a73 | 752 | // ptr-addr cast. must be from thin ptr |
62682a34 | 753 | |
ff7c6d11 | 754 | match fcx.pointer_kind(m_expr.ty, self.span)? { |
abe05a73 XL |
755 | None => Err(CastError::UnknownExprPtrKind), |
756 | Some(PointerKind::Thin) => Ok(CastKind::PtrAddrCast), | |
757 | _ => Err(CastError::NeedViaThinPtr), | |
62682a34 SL |
758 | } |
759 | } | |
760 | ||
dc9dc135 XL |
761 | fn check_ref_cast( |
762 | &self, | |
763 | fcx: &FnCtxt<'a, 'tcx>, | |
764 | m_expr: ty::TypeAndMut<'tcx>, | |
765 | m_cast: ty::TypeAndMut<'tcx>, | |
766 | ) -> Result<CastKind, CastError> { | |
62682a34 SL |
767 | // array-ptr-cast. |
768 | ||
dfeec247 | 769 | if m_expr.mutbl == hir::Mutability::Not && m_cast.mutbl == hir::Mutability::Not { |
e74abb32 | 770 | if let ty::Array(ety, _) = m_expr.ty.kind { |
62682a34 SL |
771 | // Due to the limitations of LLVM global constants, |
772 | // region pointers end up pointing at copies of | |
773 | // vector elements instead of the original values. | |
774 | // To allow raw pointers to work correctly, we | |
775 | // need to special-case obtaining a raw pointer | |
776 | // from a region pointer to a vector. | |
777 | ||
60c5eb7d XL |
778 | // Coerce to a raw pointer so that we generate AddressOf in MIR. |
779 | let array_ptr_type = fcx.tcx.mk_ptr(m_expr); | |
780 | fcx.try_coerce(self.expr, self.expr_ty, array_ptr_type, AllowTwoPhase::No) | |
f035d41b XL |
781 | .unwrap_or_else(|_| { |
782 | bug!( | |
60c5eb7d XL |
783 | "could not cast from reference to array to pointer to array ({:?} to {:?})", |
784 | self.expr_ty, | |
785 | array_ptr_type, | |
f035d41b XL |
786 | ) |
787 | }); | |
60c5eb7d | 788 | |
62682a34 | 789 | // this will report a type mismatch if needed |
a7813a04 | 790 | fcx.demand_eqtype(self.span, ety, m_cast.ty); |
62682a34 | 791 | return Ok(CastKind::ArrayPtrCast); |
9346a6ac AL |
792 | } |
793 | } | |
62682a34 SL |
794 | |
795 | Err(CastError::IllegalCast) | |
796 | } | |
797 | ||
dc9dc135 XL |
798 | fn check_addr_ptr_cast( |
799 | &self, | |
800 | fcx: &FnCtxt<'a, 'tcx>, | |
801 | m_cast: TypeAndMut<'tcx>, | |
802 | ) -> Result<CastKind, CastError> { | |
62682a34 | 803 | // ptr-addr cast. pointer must be thin. |
ff7c6d11 | 804 | match fcx.pointer_kind(m_cast.ty, self.span)? { |
abe05a73 XL |
805 | None => Err(CastError::UnknownCastPtrKind), |
806 | Some(PointerKind::Thin) => Ok(CastKind::AddrPtrCast), | |
807 | _ => Err(CastError::IllegalCast), | |
d9579d0f | 808 | } |
9346a6ac | 809 | } |
62682a34 | 810 | |
dfeec247 | 811 | fn try_coercion_cast(&self, fcx: &FnCtxt<'a, 'tcx>) -> Result<(), ty::error::TypeError<'_>> { |
e74abb32 XL |
812 | match fcx.try_coerce(self.expr, self.expr_ty, self.cast_ty, AllowTwoPhase::No) { |
813 | Ok(_) => Ok(()), | |
814 | Err(err) => Err(err), | |
815 | } | |
62682a34 | 816 | } |
f035d41b XL |
817 | |
818 | fn cenum_impl_drop_lint(&self, fcx: &FnCtxt<'a, 'tcx>) { | |
819 | if let ty::Adt(d, _) = self.expr_ty.kind { | |
820 | if d.has_dtor(fcx.tcx) { | |
821 | fcx.tcx.struct_span_lint_hir( | |
822 | lint::builtin::CENUM_IMPL_DROP_CAST, | |
823 | self.expr.hir_id, | |
824 | self.span, | |
825 | |err| { | |
826 | err.build(&format!( | |
827 | "cannot cast enum `{}` into integer `{}` because it implements `Drop`", | |
828 | self.expr_ty, self.cast_ty | |
829 | )) | |
830 | .emit(); | |
831 | }, | |
832 | ); | |
833 | } | |
834 | } | |
835 | } | |
9346a6ac | 836 | } |
a7813a04 | 837 | |
dc9dc135 | 838 | impl<'a, 'tcx> FnCtxt<'a, 'tcx> { |
0731742a | 839 | fn type_is_known_to_be_sized_modulo_regions(&self, ty: Ty<'tcx>, span: Span) -> bool { |
e1599b0c | 840 | let lang_item = self.tcx.require_lang_item(lang_items::SizedTraitLangItem, None); |
0731742a | 841 | traits::type_known_to_meet_bound_modulo_regions(self, self.param_env, ty, lang_item, span) |
a7813a04 XL |
842 | } |
843 | } |