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