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