]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_hir_typeck/src/cast.rs
Rollup merge of #105724 - notriddle:notriddle/scrape-example-src-line-numbers, r...
[rust.git] / compiler / rustc_hir_typeck / src / cast.rs
1 //! Code for type-checking cast expressions.
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
6 //!    pointer_kind(`T`) = pointer_kind(`U_0`); *ptr-ptr-cast*
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,
19 //! and where pointer_kind(`T`) is the kind of the unsize info
20 //! in `T` - the vtable for a trait definition (e.g., `fmt::Display` or
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`).
30
31 use super::FnCtxt;
32
33 use crate::type_error_struct;
34 use rustc_errors::{struct_span_err, Applicability, DelayDm, DiagnosticBuilder, ErrorGuaranteed};
35 use rustc_hir as hir;
36 use rustc_macros::{TypeFoldable, TypeVisitable};
37 use rustc_middle::mir::Mutability;
38 use rustc_middle::ty::adjustment::AllowTwoPhase;
39 use rustc_middle::ty::cast::{CastKind, CastTy};
40 use rustc_middle::ty::error::TypeError;
41 use rustc_middle::ty::{self, Ty, TypeAndMut, TypeVisitable, VariantDef};
42 use rustc_session::lint;
43 use rustc_session::Session;
44 use rustc_span::def_id::{DefId, LOCAL_CRATE};
45 use rustc_span::symbol::sym;
46 use rustc_span::Span;
47 use rustc_trait_selection::infer::InferCtxtExt;
48
49 /// Reifies a cast check to be checked once we have full type information for
50 /// a function context.
51 #[derive(Debug)]
52 pub struct CastCheck<'tcx> {
53     /// The expression whose value is being casted
54     expr: &'tcx hir::Expr<'tcx>,
55     /// The source type for the cast expression
56     expr_ty: Ty<'tcx>,
57     expr_span: Span,
58     /// The target type. That is, the type we are casting to.
59     cast_ty: Ty<'tcx>,
60     cast_span: Span,
61     span: Span,
62     /// whether the cast is made in a const context or not.
63     pub constness: hir::Constness,
64 }
65
66 /// The kind of pointer and associated metadata (thin, length or vtable) - we
67 /// only allow casts between fat pointers if their metadata have the same
68 /// kind.
69 #[derive(Debug, Copy, Clone, PartialEq, Eq, TypeVisitable, TypeFoldable)]
70 enum PointerKind<'tcx> {
71     /// No metadata attached, ie pointer to sized type or foreign type
72     Thin,
73     /// A trait object
74     VTable(Option<DefId>),
75     /// Slice
76     Length,
77     /// The unsize info of this projection or opaque type
78     OfAlias(ty::AliasTy<'tcx>),
79     /// The unsize info of this parameter
80     OfParam(ty::ParamTy),
81 }
82
83 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
84     /// Returns the kind of unsize information of t, or None
85     /// if t is unknown.
86     fn pointer_kind(
87         &self,
88         t: Ty<'tcx>,
89         span: Span,
90     ) -> Result<Option<PointerKind<'tcx>>, ErrorGuaranteed> {
91         debug!("pointer_kind({:?}, {:?})", t, span);
92
93         let t = self.resolve_vars_if_possible(t);
94         t.error_reported()?;
95
96         if self.type_is_sized_modulo_regions(self.param_env, t, span) {
97             return Ok(Some(PointerKind::Thin));
98         }
99
100         Ok(match *t.kind() {
101             ty::Slice(_) | ty::Str => Some(PointerKind::Length),
102             ty::Dynamic(ref tty, _, ty::Dyn) => 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)?
108                 }
109             },
110             ty::Tuple(fields) => match fields.last() {
111                 None => Some(PointerKind::Thin),
112                 Some(&f) => self.pointer_kind(f, span)?,
113             },
114
115             // Pointers to foreign types are thin, despite being unsized
116             ty::Foreign(..) => Some(PointerKind::Thin),
117             // We should really try to normalize here.
118             ty::Alias(_, pi) => Some(PointerKind::OfAlias(pi)),
119             ty::Param(p) => Some(PointerKind::OfParam(p)),
120             // Insufficient type information.
121             ty::Placeholder(..) | ty::Bound(..) | ty::Infer(_) => None,
122
123             ty::Bool
124             | ty::Char
125             | ty::Int(..)
126             | ty::Uint(..)
127             | ty::Float(_)
128             | ty::Array(..)
129             | ty::GeneratorWitness(..)
130             | ty::RawPtr(_)
131             | ty::Ref(..)
132             | ty::FnDef(..)
133             | ty::FnPtr(..)
134             | ty::Closure(..)
135             | ty::Generator(..)
136             | ty::Adt(..)
137             | ty::Never
138             | ty::Dynamic(_, _, ty::DynStar)
139             | ty::Error(_) => {
140                 let reported = self
141                     .tcx
142                     .sess
143                     .delay_span_bug(span, &format!("`{:?}` should be sized but is not?", t));
144                 return Err(reported);
145             }
146         })
147     }
148 }
149
150 #[derive(Copy, Clone)]
151 pub enum CastError {
152     ErrorGuaranteed,
153
154     CastToBool,
155     CastToChar,
156     DifferingKinds,
157     /// Cast of thin to fat raw ptr (e.g., `*const () as *const [u8]`).
158     SizedUnsizedCast,
159     IllegalCast,
160     NeedDeref,
161     NeedViaPtr,
162     NeedViaThinPtr,
163     NeedViaInt,
164     NonScalar,
165     UnknownExprPtrKind,
166     UnknownCastPtrKind,
167     /// Cast of int to (possibly) fat raw pointer.
168     ///
169     /// Argument is the specific name of the metadata in plain words, such as "a vtable"
170     /// or "a length". If this argument is None, then the metadata is unknown, for example,
171     /// when we're typechecking a type parameter with a ?Sized bound.
172     IntToFatCast(Option<&'static str>),
173     ForeignNonExhaustiveAdt,
174 }
175
176 impl From<ErrorGuaranteed> for CastError {
177     fn from(_: ErrorGuaranteed) -> Self {
178         CastError::ErrorGuaranteed
179     }
180 }
181
182 fn make_invalid_casting_error<'a, 'tcx>(
183     sess: &'a Session,
184     span: Span,
185     expr_ty: Ty<'tcx>,
186     cast_ty: Ty<'tcx>,
187     fcx: &FnCtxt<'a, 'tcx>,
188 ) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
189     type_error_struct!(
190         sess,
191         span,
192         expr_ty,
193         E0606,
194         "casting `{}` as `{}` is invalid",
195         fcx.ty_to_string(expr_ty),
196         fcx.ty_to_string(cast_ty)
197     )
198 }
199
200 impl<'a, 'tcx> CastCheck<'tcx> {
201     pub fn new(
202         fcx: &FnCtxt<'a, 'tcx>,
203         expr: &'tcx hir::Expr<'tcx>,
204         expr_ty: Ty<'tcx>,
205         cast_ty: Ty<'tcx>,
206         cast_span: Span,
207         span: Span,
208         constness: hir::Constness,
209     ) -> Result<CastCheck<'tcx>, ErrorGuaranteed> {
210         let expr_span = expr.span.find_ancestor_inside(span).unwrap_or(expr.span);
211         let check = CastCheck { expr, expr_ty, expr_span, cast_ty, cast_span, span, constness };
212
213         // For better error messages, check for some obviously unsized
214         // cases now. We do a more thorough check at the end, once
215         // inference is more completely known.
216         match cast_ty.kind() {
217             ty::Dynamic(_, _, ty::Dyn) | ty::Slice(..) => {
218                 Err(check.report_cast_to_unsized_type(fcx))
219             }
220             _ => Ok(check),
221         }
222     }
223
224     fn report_cast_error(&self, fcx: &FnCtxt<'a, 'tcx>, e: CastError) {
225         match e {
226             CastError::ErrorGuaranteed => {
227                 // an error has already been reported
228             }
229             CastError::NeedDeref => {
230                 let error_span = self.span;
231                 let mut err = make_invalid_casting_error(
232                     fcx.tcx.sess,
233                     self.span,
234                     self.expr_ty,
235                     self.cast_ty,
236                     fcx,
237                 );
238                 let cast_ty = fcx.ty_to_string(self.cast_ty);
239                 err.span_label(
240                     error_span,
241                     format!("cannot cast `{}` as `{}`", fcx.ty_to_string(self.expr_ty), cast_ty),
242                 );
243                 if let Ok(snippet) = fcx.sess().source_map().span_to_snippet(self.expr_span) {
244                     err.span_suggestion(
245                         self.expr_span,
246                         "dereference the expression",
247                         format!("*{}", snippet),
248                         Applicability::MaybeIncorrect,
249                     );
250                 } else {
251                     err.span_help(self.expr_span, "dereference the expression with `*`");
252                 }
253                 err.emit();
254             }
255             CastError::NeedViaThinPtr | CastError::NeedViaPtr => {
256                 let mut err = make_invalid_casting_error(
257                     fcx.tcx.sess,
258                     self.span,
259                     self.expr_ty,
260                     self.cast_ty,
261                     fcx,
262                 );
263                 if self.cast_ty.is_integral() {
264                     err.help(&format!(
265                         "cast through {} first",
266                         match e {
267                             CastError::NeedViaPtr => "a raw pointer",
268                             CastError::NeedViaThinPtr => "a thin pointer",
269                             _ => bug!(),
270                         }
271                     ));
272                 }
273                 err.emit();
274             }
275             CastError::NeedViaInt => {
276                 make_invalid_casting_error(
277                     fcx.tcx.sess,
278                     self.span,
279                     self.expr_ty,
280                     self.cast_ty,
281                     fcx,
282                 )
283                 .help(&format!(
284                     "cast through {} first",
285                     match e {
286                         CastError::NeedViaInt => "an integer",
287                         _ => bug!(),
288                     }
289                 ))
290                 .emit();
291             }
292             CastError::IllegalCast => {
293                 make_invalid_casting_error(
294                     fcx.tcx.sess,
295                     self.span,
296                     self.expr_ty,
297                     self.cast_ty,
298                     fcx,
299                 )
300                 .emit();
301             }
302             CastError::DifferingKinds => {
303                 make_invalid_casting_error(
304                     fcx.tcx.sess,
305                     self.span,
306                     self.expr_ty,
307                     self.cast_ty,
308                     fcx,
309                 )
310                 .note("vtable kinds may not match")
311                 .emit();
312             }
313             CastError::CastToBool => {
314                 let mut err =
315                     struct_span_err!(fcx.tcx.sess, self.span, E0054, "cannot cast as `bool`");
316
317                 if self.expr_ty.is_numeric() {
318                     match fcx.tcx.sess.source_map().span_to_snippet(self.expr_span) {
319                         Ok(snippet) => {
320                             err.span_suggestion(
321                                 self.span,
322                                 "compare with zero instead",
323                                 format!("{snippet} != 0"),
324                                 Applicability::MachineApplicable,
325                             );
326                         }
327                         Err(_) => {
328                             err.span_help(self.span, "compare with zero instead");
329                         }
330                     }
331                 } else {
332                     err.span_label(self.span, "unsupported cast");
333                 }
334
335                 err.emit();
336             }
337             CastError::CastToChar => {
338                 let mut err = type_error_struct!(
339                     fcx.tcx.sess,
340                     self.span,
341                     self.expr_ty,
342                     E0604,
343                     "only `u8` can be cast as `char`, not `{}`",
344                     self.expr_ty
345                 );
346                 err.span_label(self.span, "invalid cast");
347                 if self.expr_ty.is_numeric() {
348                     if self.expr_ty == fcx.tcx.types.u32 {
349                         match fcx.tcx.sess.source_map().span_to_snippet(self.expr.span) {
350                             Ok(snippet) => err.span_suggestion(
351                                 self.span,
352                                 "try `char::from_u32` instead",
353                                 format!("char::from_u32({snippet})"),
354                                 Applicability::MachineApplicable,
355                             ),
356
357                             Err(_) => err.span_help(self.span, "try `char::from_u32` instead"),
358                         };
359                     } else if self.expr_ty == fcx.tcx.types.i8 {
360                         err.span_help(self.span, "try casting from `u8` instead");
361                     } else {
362                         err.span_help(self.span, "try `char::from_u32` instead (via a `u32`)");
363                     };
364                 }
365                 err.emit();
366             }
367             CastError::NonScalar => {
368                 let mut err = type_error_struct!(
369                     fcx.tcx.sess,
370                     self.span,
371                     self.expr_ty,
372                     E0605,
373                     "non-primitive cast: `{}` as `{}`",
374                     self.expr_ty,
375                     fcx.ty_to_string(self.cast_ty)
376                 );
377                 let mut sugg = None;
378                 let mut sugg_mutref = false;
379                 if let ty::Ref(reg, cast_ty, mutbl) = *self.cast_ty.kind() {
380                     if let ty::RawPtr(TypeAndMut { ty: expr_ty, .. }) = *self.expr_ty.kind()
381                         && fcx
382                             .try_coerce(
383                                 self.expr,
384                                 fcx.tcx.mk_ref(
385                                     fcx.tcx.lifetimes.re_erased,
386                                     TypeAndMut { ty: expr_ty, mutbl },
387                                 ),
388                                 self.cast_ty,
389                                 AllowTwoPhase::No,
390                                 None,
391                             )
392                             .is_ok()
393                     {
394                         sugg = Some((format!("&{}*", mutbl.prefix_str()), cast_ty == expr_ty));
395                     } else if let ty::Ref(expr_reg, expr_ty, expr_mutbl) = *self.expr_ty.kind()
396                         && expr_mutbl == Mutability::Not
397                         && mutbl == Mutability::Mut
398                         && fcx
399                             .try_coerce(
400                                 self.expr,
401                                 fcx.tcx.mk_ref(
402                                     expr_reg,
403                                     TypeAndMut { ty: expr_ty, mutbl: Mutability::Mut },
404                                 ),
405                                 self.cast_ty,
406                                 AllowTwoPhase::No,
407                                 None,
408                             )
409                             .is_ok()
410                     {
411                         sugg_mutref = true;
412                     }
413
414                     if !sugg_mutref
415                         && sugg == None
416                         && fcx
417                             .try_coerce(
418                                 self.expr,
419                                 fcx.tcx.mk_ref(reg, TypeAndMut { ty: self.expr_ty, mutbl }),
420                                 self.cast_ty,
421                                 AllowTwoPhase::No,
422                                 None,
423                             )
424                             .is_ok()
425                     {
426                         sugg = Some((format!("&{}", mutbl.prefix_str()), false));
427                     }
428                 } else if let ty::RawPtr(TypeAndMut { mutbl, .. }) = *self.cast_ty.kind()
429                     && fcx
430                         .try_coerce(
431                             self.expr,
432                             fcx.tcx.mk_ref(
433                                 fcx.tcx.lifetimes.re_erased,
434                                 TypeAndMut { ty: self.expr_ty, mutbl },
435                             ),
436                             self.cast_ty,
437                             AllowTwoPhase::No,
438                             None,
439                         )
440                         .is_ok()
441                 {
442                     sugg = Some((format!("&{}", mutbl.prefix_str()), false));
443                 }
444                 if sugg_mutref {
445                     err.span_label(self.span, "invalid cast");
446                     err.span_note(self.expr_span, "this reference is immutable");
447                     err.span_note(self.cast_span, "trying to cast to a mutable reference type");
448                 } else if let Some((sugg, remove_cast)) = sugg {
449                     err.span_label(self.span, "invalid cast");
450
451                     let has_parens = fcx
452                         .tcx
453                         .sess
454                         .source_map()
455                         .span_to_snippet(self.expr_span)
456                         .map_or(false, |snip| snip.starts_with('('));
457
458                     // Very crude check to see whether the expression must be wrapped
459                     // in parentheses for the suggestion to work (issue #89497).
460                     // Can/should be extended in the future.
461                     let needs_parens =
462                         !has_parens && matches!(self.expr.kind, hir::ExprKind::Cast(..));
463
464                     let mut suggestion = vec![(self.expr_span.shrink_to_lo(), sugg)];
465                     if needs_parens {
466                         suggestion[0].1 += "(";
467                         suggestion.push((self.expr_span.shrink_to_hi(), ")".to_string()));
468                     }
469                     if remove_cast {
470                         suggestion.push((
471                             self.expr_span.shrink_to_hi().to(self.cast_span),
472                             String::new(),
473                         ));
474                     }
475
476                     err.multipart_suggestion_verbose(
477                         "consider borrowing the value",
478                         suggestion,
479                         Applicability::MachineApplicable,
480                     );
481                 } else if !matches!(
482                     self.cast_ty.kind(),
483                     ty::FnDef(..) | ty::FnPtr(..) | ty::Closure(..)
484                 ) {
485                     let mut label = true;
486                     // Check `impl From<self.expr_ty> for self.cast_ty {}` for accurate suggestion:
487                     if let Ok(snippet) = fcx.tcx.sess.source_map().span_to_snippet(self.expr_span)
488                         && let Some(from_trait) = fcx.tcx.get_diagnostic_item(sym::From)
489                     {
490                         let ty = fcx.resolve_vars_if_possible(self.cast_ty);
491                         // Erase regions to avoid panic in `prove_value` when calling
492                         // `type_implements_trait`.
493                         let ty = fcx.tcx.erase_regions(ty);
494                         let expr_ty = fcx.resolve_vars_if_possible(self.expr_ty);
495                         let expr_ty = fcx.tcx.erase_regions(expr_ty);
496                         if fcx
497                             .infcx
498                             .type_implements_trait(from_trait, [ty, expr_ty], fcx.param_env)
499                             .must_apply_modulo_regions()
500                         {
501                             label = false;
502                             err.span_suggestion(
503                                 self.span,
504                                 "consider using the `From` trait instead",
505                                 format!("{}::from({})", self.cast_ty, snippet),
506                                 Applicability::MaybeIncorrect,
507                             );
508                         }
509                     }
510                     let msg = "an `as` expression can only be used to convert between primitive \
511                                types or to coerce to a specific trait object";
512                     if label {
513                         err.span_label(self.span, msg);
514                     } else {
515                         err.note(msg);
516                     }
517                 } else {
518                     err.span_label(self.span, "invalid cast");
519                 }
520                 err.emit();
521             }
522             CastError::SizedUnsizedCast => {
523                 use rustc_hir_analysis::structured_errors::{
524                     SizedUnsizedCast, StructuredDiagnostic,
525                 };
526
527                 SizedUnsizedCast {
528                     sess: &fcx.tcx.sess,
529                     span: self.span,
530                     expr_ty: self.expr_ty,
531                     cast_ty: fcx.ty_to_string(self.cast_ty),
532                 }
533                 .diagnostic()
534                 .emit();
535             }
536             CastError::IntToFatCast(known_metadata) => {
537                 let mut err = struct_span_err!(
538                     fcx.tcx.sess,
539                     self.cast_span,
540                     E0606,
541                     "cannot cast `{}` to a pointer that {} wide",
542                     fcx.ty_to_string(self.expr_ty),
543                     if known_metadata.is_some() { "is" } else { "may be" }
544                 );
545
546                 err.span_label(
547                     self.cast_span,
548                     format!(
549                         "creating a `{}` requires both an address and {}",
550                         self.cast_ty,
551                         known_metadata.unwrap_or("type-specific metadata"),
552                     ),
553                 );
554
555                 if fcx.tcx.sess.is_nightly_build() {
556                     err.span_label(
557                         self.expr_span,
558                         "consider casting this expression to `*const ()`, \
559                         then using `core::ptr::from_raw_parts`",
560                     );
561                 }
562
563                 err.emit();
564             }
565             CastError::UnknownCastPtrKind | CastError::UnknownExprPtrKind => {
566                 let unknown_cast_to = match e {
567                     CastError::UnknownCastPtrKind => true,
568                     CastError::UnknownExprPtrKind => false,
569                     _ => bug!(),
570                 };
571                 let mut err = struct_span_err!(
572                     fcx.tcx.sess,
573                     if unknown_cast_to { self.cast_span } else { self.span },
574                     E0641,
575                     "cannot cast {} a pointer of an unknown kind",
576                     if unknown_cast_to { "to" } else { "from" }
577                 );
578                 if unknown_cast_to {
579                     err.span_label(self.cast_span, "needs more type information");
580                     err.note(
581                         "the type information given here is insufficient to check whether \
582                         the pointer cast is valid",
583                     );
584                 } else {
585                     err.span_label(
586                         self.span,
587                         "the type information given here is insufficient to check whether \
588                         the pointer cast is valid",
589                     );
590                 }
591                 err.emit();
592             }
593             CastError::ForeignNonExhaustiveAdt => {
594                 make_invalid_casting_error(
595                     fcx.tcx.sess,
596                     self.span,
597                     self.expr_ty,
598                     self.cast_ty,
599                     fcx,
600                 )
601                 .note("cannot cast an enum with a non-exhaustive variant when it's defined in another crate")
602                 .emit();
603             }
604         }
605     }
606
607     fn report_cast_to_unsized_type(&self, fcx: &FnCtxt<'a, 'tcx>) -> ErrorGuaranteed {
608         if let Err(err) = self.cast_ty.error_reported() {
609             return err;
610         }
611         if let Err(err) = self.expr_ty.error_reported() {
612             return err;
613         }
614
615         let tstr = fcx.ty_to_string(self.cast_ty);
616         let mut err = type_error_struct!(
617             fcx.tcx.sess,
618             self.span,
619             self.expr_ty,
620             E0620,
621             "cast to unsized type: `{}` as `{}`",
622             fcx.resolve_vars_if_possible(self.expr_ty),
623             tstr
624         );
625         match self.expr_ty.kind() {
626             ty::Ref(_, _, mt) => {
627                 let mtstr = mt.prefix_str();
628                 if self.cast_ty.is_trait() {
629                     match fcx.tcx.sess.source_map().span_to_snippet(self.cast_span) {
630                         Ok(s) => {
631                             err.span_suggestion(
632                                 self.cast_span,
633                                 "try casting to a reference instead",
634                                 format!("&{}{}", mtstr, s),
635                                 Applicability::MachineApplicable,
636                             );
637                         }
638                         Err(_) => {
639                             let msg = &format!("did you mean `&{}{}`?", mtstr, tstr);
640                             err.span_help(self.cast_span, msg);
641                         }
642                     }
643                 } else {
644                     let msg =
645                         &format!("consider using an implicit coercion to `&{mtstr}{tstr}` instead");
646                     err.span_help(self.span, msg);
647                 }
648             }
649             ty::Adt(def, ..) if def.is_box() => {
650                 match fcx.tcx.sess.source_map().span_to_snippet(self.cast_span) {
651                     Ok(s) => {
652                         err.span_suggestion(
653                             self.cast_span,
654                             "you can cast to a `Box` instead",
655                             format!("Box<{s}>"),
656                             Applicability::MachineApplicable,
657                         );
658                     }
659                     Err(_) => {
660                         err.span_help(
661                             self.cast_span,
662                             &format!("you might have meant `Box<{tstr}>`"),
663                         );
664                     }
665                 }
666             }
667             _ => {
668                 err.span_help(self.expr_span, "consider using a box or reference as appropriate");
669             }
670         }
671         err.emit()
672     }
673
674     fn trivial_cast_lint(&self, fcx: &FnCtxt<'a, 'tcx>) {
675         let t_cast = self.cast_ty;
676         let t_expr = self.expr_ty;
677         let type_asc_or =
678             if fcx.tcx.features().type_ascription { "type ascription or " } else { "" };
679         let (adjective, lint) = if t_cast.is_numeric() && t_expr.is_numeric() {
680             ("numeric ", lint::builtin::TRIVIAL_NUMERIC_CASTS)
681         } else {
682             ("", lint::builtin::TRIVIAL_CASTS)
683         };
684         fcx.tcx.struct_span_lint_hir(
685             lint,
686             self.expr.hir_id,
687             self.span,
688             DelayDm(|| {
689                 format!(
690                     "trivial {}cast: `{}` as `{}`",
691                     adjective,
692                     fcx.ty_to_string(t_expr),
693                     fcx.ty_to_string(t_cast)
694                 )
695             }),
696             |lint| {
697                 lint.help(format!(
698                     "cast can be replaced by coercion; this might \
699                      require {type_asc_or}a temporary variable"
700                 ))
701             },
702         );
703     }
704
705     #[instrument(skip(fcx), level = "debug")]
706     pub fn check(mut self, fcx: &FnCtxt<'a, 'tcx>) {
707         self.expr_ty = fcx.structurally_resolved_type(self.expr_span, self.expr_ty);
708         self.cast_ty = fcx.structurally_resolved_type(self.cast_span, self.cast_ty);
709
710         debug!("check_cast({}, {:?} as {:?})", self.expr.hir_id, self.expr_ty, self.cast_ty);
711
712         if !fcx.type_is_sized_modulo_regions(fcx.param_env, self.cast_ty, self.span)
713             && !self.cast_ty.has_infer_types()
714         {
715             self.report_cast_to_unsized_type(fcx);
716         } else if self.expr_ty.references_error() || self.cast_ty.references_error() {
717             // No sense in giving duplicate error messages
718         } else {
719             match self.try_coercion_cast(fcx) {
720                 Ok(()) => {
721                     self.trivial_cast_lint(fcx);
722                     debug!(" -> CoercionCast");
723                     fcx.typeck_results.borrow_mut().set_coercion_cast(self.expr.hir_id.local_id);
724                 }
725                 Err(_) => {
726                     match self.do_check(fcx) {
727                         Ok(k) => {
728                             debug!(" -> {:?}", k);
729                         }
730                         Err(e) => self.report_cast_error(fcx, e),
731                     };
732                 }
733             };
734         }
735     }
736     /// Checks a cast, and report an error if one exists. In some cases, this
737     /// can return Ok and create type errors in the fcx rather than returning
738     /// directly. coercion-cast is handled in check instead of here.
739     pub fn do_check(&self, fcx: &FnCtxt<'a, 'tcx>) -> Result<CastKind, CastError> {
740         use rustc_middle::ty::cast::CastTy::*;
741         use rustc_middle::ty::cast::IntTy::*;
742
743         let (t_from, t_cast) = match (CastTy::from_ty(self.expr_ty), CastTy::from_ty(self.cast_ty))
744         {
745             (Some(t_from), Some(t_cast)) => (t_from, t_cast),
746             // Function item types may need to be reified before casts.
747             (None, Some(t_cast)) => {
748                 match *self.expr_ty.kind() {
749                     ty::FnDef(..) => {
750                         // Attempt a coercion to a fn pointer type.
751                         let f = fcx.normalize(self.expr_span, self.expr_ty.fn_sig(fcx.tcx));
752                         let res = fcx.try_coerce(
753                             self.expr,
754                             self.expr_ty,
755                             fcx.tcx.mk_fn_ptr(f),
756                             AllowTwoPhase::No,
757                             None,
758                         );
759                         if let Err(TypeError::IntrinsicCast) = res {
760                             return Err(CastError::IllegalCast);
761                         }
762                         if res.is_err() {
763                             return Err(CastError::NonScalar);
764                         }
765                         (FnPtr, t_cast)
766                     }
767                     // Special case some errors for references, and check for
768                     // array-ptr-casts. `Ref` is not a CastTy because the cast
769                     // is split into a coercion to a pointer type, followed by
770                     // a cast.
771                     ty::Ref(_, inner_ty, mutbl) => {
772                         return match t_cast {
773                             Int(_) | Float => match *inner_ty.kind() {
774                                 ty::Int(_)
775                                 | ty::Uint(_)
776                                 | ty::Float(_)
777                                 | ty::Infer(ty::InferTy::IntVar(_) | ty::InferTy::FloatVar(_)) => {
778                                     Err(CastError::NeedDeref)
779                                 }
780                                 _ => Err(CastError::NeedViaPtr),
781                             },
782                             // array-ptr-cast
783                             Ptr(mt) => {
784                                 self.check_ref_cast(fcx, TypeAndMut { mutbl, ty: inner_ty }, mt)
785                             }
786                             _ => Err(CastError::NonScalar),
787                         };
788                     }
789                     _ => return Err(CastError::NonScalar),
790                 }
791             }
792             _ => return Err(CastError::NonScalar),
793         };
794
795         if let ty::Adt(adt_def, _) = *self.expr_ty.kind() {
796             if adt_def.did().krate != LOCAL_CRATE {
797                 if adt_def.variants().iter().any(VariantDef::is_field_list_non_exhaustive) {
798                     return Err(CastError::ForeignNonExhaustiveAdt);
799                 }
800             }
801         }
802
803         match (t_from, t_cast) {
804             // These types have invariants! can't cast into them.
805             (_, Int(CEnum) | FnPtr) => Err(CastError::NonScalar),
806
807             // * -> Bool
808             (_, Int(Bool)) => Err(CastError::CastToBool),
809
810             // * -> Char
811             (Int(U(ty::UintTy::U8)), Int(Char)) => Ok(CastKind::U8CharCast), // u8-char-cast
812             (_, Int(Char)) => Err(CastError::CastToChar),
813
814             // prim -> float,ptr
815             (Int(Bool) | Int(CEnum) | Int(Char), Float) => Err(CastError::NeedViaInt),
816
817             (Int(Bool) | Int(CEnum) | Int(Char) | Float, Ptr(_)) | (Ptr(_) | FnPtr, Float) => {
818                 Err(CastError::IllegalCast)
819             }
820
821             // ptr -> *
822             (Ptr(m_e), Ptr(m_c)) => self.check_ptr_ptr_cast(fcx, m_e, m_c), // ptr-ptr-cast
823
824             // ptr-addr-cast
825             (Ptr(m_expr), Int(t_c)) => {
826                 self.lossy_provenance_ptr2int_lint(fcx, t_c);
827                 self.check_ptr_addr_cast(fcx, m_expr)
828             }
829             (FnPtr, Int(_)) => {
830                 // FIXME(#95489): there should eventually be a lint for these casts
831                 Ok(CastKind::FnPtrAddrCast)
832             }
833             // addr-ptr-cast
834             (Int(_), Ptr(mt)) => {
835                 self.fuzzy_provenance_int2ptr_lint(fcx);
836                 self.check_addr_ptr_cast(fcx, mt)
837             }
838             // fn-ptr-cast
839             (FnPtr, Ptr(mt)) => self.check_fptr_ptr_cast(fcx, mt),
840
841             // prim -> prim
842             (Int(CEnum), Int(_)) => {
843                 self.cenum_impl_drop_lint(fcx);
844                 Ok(CastKind::EnumCast)
845             }
846             (Int(Char) | Int(Bool), Int(_)) => Ok(CastKind::PrimIntCast),
847
848             (Int(_) | Float, Int(_) | Float) => Ok(CastKind::NumericCast),
849
850             (_, DynStar) | (DynStar, _) => {
851                 if fcx.tcx.features().dyn_star {
852                     bug!("should be handled by `try_coerce`")
853                 } else {
854                     Err(CastError::IllegalCast)
855                 }
856             }
857         }
858     }
859
860     fn check_ptr_ptr_cast(
861         &self,
862         fcx: &FnCtxt<'a, 'tcx>,
863         m_expr: ty::TypeAndMut<'tcx>,
864         m_cast: ty::TypeAndMut<'tcx>,
865     ) -> Result<CastKind, CastError> {
866         debug!("check_ptr_ptr_cast m_expr={:?} m_cast={:?}", m_expr, m_cast);
867         // ptr-ptr cast. vtables must match.
868
869         let expr_kind = fcx.pointer_kind(m_expr.ty, self.span)?;
870         let cast_kind = fcx.pointer_kind(m_cast.ty, self.span)?;
871
872         let Some(cast_kind) = cast_kind else {
873             // We can't cast if target pointer kind is unknown
874             return Err(CastError::UnknownCastPtrKind);
875         };
876
877         // Cast to thin pointer is OK
878         if cast_kind == PointerKind::Thin {
879             return Ok(CastKind::PtrPtrCast);
880         }
881
882         let Some(expr_kind) = expr_kind else {
883             // We can't cast to fat pointer if source pointer kind is unknown
884             return Err(CastError::UnknownExprPtrKind);
885         };
886
887         // thin -> fat? report invalid cast (don't complain about vtable kinds)
888         if expr_kind == PointerKind::Thin {
889             return Err(CastError::SizedUnsizedCast);
890         }
891
892         // vtable kinds must match
893         if fcx.tcx.erase_regions(cast_kind) == fcx.tcx.erase_regions(expr_kind) {
894             Ok(CastKind::PtrPtrCast)
895         } else {
896             Err(CastError::DifferingKinds)
897         }
898     }
899
900     fn check_fptr_ptr_cast(
901         &self,
902         fcx: &FnCtxt<'a, 'tcx>,
903         m_cast: ty::TypeAndMut<'tcx>,
904     ) -> Result<CastKind, CastError> {
905         // fptr-ptr cast. must be to thin ptr
906
907         match fcx.pointer_kind(m_cast.ty, self.span)? {
908             None => Err(CastError::UnknownCastPtrKind),
909             Some(PointerKind::Thin) => Ok(CastKind::FnPtrPtrCast),
910             _ => Err(CastError::IllegalCast),
911         }
912     }
913
914     fn check_ptr_addr_cast(
915         &self,
916         fcx: &FnCtxt<'a, 'tcx>,
917         m_expr: ty::TypeAndMut<'tcx>,
918     ) -> Result<CastKind, CastError> {
919         // ptr-addr cast. must be from thin ptr
920
921         match fcx.pointer_kind(m_expr.ty, self.span)? {
922             None => Err(CastError::UnknownExprPtrKind),
923             Some(PointerKind::Thin) => Ok(CastKind::PtrAddrCast),
924             _ => Err(CastError::NeedViaThinPtr),
925         }
926     }
927
928     fn check_ref_cast(
929         &self,
930         fcx: &FnCtxt<'a, 'tcx>,
931         m_expr: ty::TypeAndMut<'tcx>,
932         m_cast: ty::TypeAndMut<'tcx>,
933     ) -> Result<CastKind, CastError> {
934         // array-ptr-cast: allow mut-to-mut, mut-to-const, const-to-const
935         if m_expr.mutbl >= m_cast.mutbl {
936             if let ty::Array(ety, _) = m_expr.ty.kind() {
937                 // Due to the limitations of LLVM global constants,
938                 // region pointers end up pointing at copies of
939                 // vector elements instead of the original values.
940                 // To allow raw pointers to work correctly, we
941                 // need to special-case obtaining a raw pointer
942                 // from a region pointer to a vector.
943
944                 // Coerce to a raw pointer so that we generate AddressOf in MIR.
945                 let array_ptr_type = fcx.tcx.mk_ptr(m_expr);
946                 fcx.try_coerce(self.expr, self.expr_ty, array_ptr_type, AllowTwoPhase::No, None)
947                     .unwrap_or_else(|_| {
948                         bug!(
949                         "could not cast from reference to array to pointer to array ({:?} to {:?})",
950                         self.expr_ty,
951                         array_ptr_type,
952                     )
953                     });
954
955                 // this will report a type mismatch if needed
956                 fcx.demand_eqtype(self.span, *ety, m_cast.ty);
957                 return Ok(CastKind::ArrayPtrCast);
958             }
959         }
960
961         Err(CastError::IllegalCast)
962     }
963
964     fn check_addr_ptr_cast(
965         &self,
966         fcx: &FnCtxt<'a, 'tcx>,
967         m_cast: TypeAndMut<'tcx>,
968     ) -> Result<CastKind, CastError> {
969         // ptr-addr cast. pointer must be thin.
970         match fcx.pointer_kind(m_cast.ty, self.span)? {
971             None => Err(CastError::UnknownCastPtrKind),
972             Some(PointerKind::Thin) => Ok(CastKind::AddrPtrCast),
973             Some(PointerKind::VTable(_)) => Err(CastError::IntToFatCast(Some("a vtable"))),
974             Some(PointerKind::Length) => Err(CastError::IntToFatCast(Some("a length"))),
975             Some(PointerKind::OfAlias(_) | PointerKind::OfParam(_)) => {
976                 Err(CastError::IntToFatCast(None))
977             }
978         }
979     }
980
981     fn try_coercion_cast(&self, fcx: &FnCtxt<'a, 'tcx>) -> Result<(), ty::error::TypeError<'tcx>> {
982         match fcx.try_coerce(self.expr, self.expr_ty, self.cast_ty, AllowTwoPhase::No, None) {
983             Ok(_) => Ok(()),
984             Err(err) => Err(err),
985         }
986     }
987
988     fn cenum_impl_drop_lint(&self, fcx: &FnCtxt<'a, 'tcx>) {
989         if let ty::Adt(d, _) = self.expr_ty.kind()
990             && d.has_dtor(fcx.tcx)
991         {
992             fcx.tcx.struct_span_lint_hir(
993                 lint::builtin::CENUM_IMPL_DROP_CAST,
994                 self.expr.hir_id,
995                 self.span,
996                 DelayDm(|| format!(
997                     "cannot cast enum `{}` into integer `{}` because it implements `Drop`",
998                     self.expr_ty, self.cast_ty
999                 )),
1000                 |lint| {
1001                     lint
1002                 },
1003             );
1004         }
1005     }
1006
1007     fn lossy_provenance_ptr2int_lint(&self, fcx: &FnCtxt<'a, 'tcx>, t_c: ty::cast::IntTy) {
1008         fcx.tcx.struct_span_lint_hir(
1009             lint::builtin::LOSSY_PROVENANCE_CASTS,
1010             self.expr.hir_id,
1011             self.span,
1012             DelayDm(|| format!(
1013                     "under strict provenance it is considered bad style to cast pointer `{}` to integer `{}`",
1014                     self.expr_ty, self.cast_ty
1015                 )),
1016             |lint| {
1017                 let msg = "use `.addr()` to obtain the address of a pointer";
1018
1019                 let expr_prec = self.expr.precedence().order();
1020                 let needs_parens = expr_prec < rustc_ast::util::parser::PREC_POSTFIX;
1021
1022                 let scalar_cast = match t_c {
1023                     ty::cast::IntTy::U(ty::UintTy::Usize) => String::new(),
1024                     _ => format!(" as {}", self.cast_ty),
1025                 };
1026
1027                 let cast_span = self.expr_span.shrink_to_hi().to(self.cast_span);
1028
1029                 if needs_parens {
1030                     let suggestions = vec![
1031                         (self.expr_span.shrink_to_lo(), String::from("(")),
1032                         (cast_span, format!(").addr(){scalar_cast}")),
1033                     ];
1034
1035                     lint.multipart_suggestion(msg, suggestions, Applicability::MaybeIncorrect);
1036                 } else {
1037                     lint.span_suggestion(
1038                         cast_span,
1039                         msg,
1040                         format!(".addr(){scalar_cast}"),
1041                         Applicability::MaybeIncorrect,
1042                     );
1043                 }
1044
1045                 lint.help(
1046                     "if you can't comply with strict provenance and need to expose the pointer \
1047                     provenance you can use `.expose_addr()` instead"
1048                 );
1049
1050                 lint
1051             },
1052         );
1053     }
1054
1055     fn fuzzy_provenance_int2ptr_lint(&self, fcx: &FnCtxt<'a, 'tcx>) {
1056         fcx.tcx.struct_span_lint_hir(
1057             lint::builtin::FUZZY_PROVENANCE_CASTS,
1058             self.expr.hir_id,
1059             self.span,
1060             DelayDm(|| format!(
1061                 "strict provenance disallows casting integer `{}` to pointer `{}`",
1062                 self.expr_ty, self.cast_ty
1063             )),
1064             |lint| {
1065                 let msg = "use `.with_addr()` to adjust a valid pointer in the same allocation, to this address";
1066                 let suggestions = vec![
1067                     (self.expr_span.shrink_to_lo(), String::from("(...).with_addr(")),
1068                     (self.expr_span.shrink_to_hi().to(self.cast_span), String::from(")")),
1069                 ];
1070
1071                 lint.multipart_suggestion(msg, suggestions, Applicability::MaybeIncorrect);
1072                 lint.help(
1073                     "if you can't comply with strict provenance and don't have a pointer with \
1074                     the correct provenance you can use `std::ptr::from_exposed_addr()` instead"
1075                  );
1076
1077                 lint
1078             },
1079         );
1080     }
1081 }