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