1use hir_def::{
4 AdtId,
5 hir::ExprId,
6 signatures::{TraitFlags, TraitSignature},
7};
8use rustc_ast_ir::Mutability;
9use rustc_hash::FxHashSet;
10use rustc_type_ir::{
11 InferTy, TypeVisitableExt, UintTy, elaborate,
12 error::TypeError,
13 inherent::{BoundExistentialPredicates as _, IntoKind, Ty as _},
14};
15use stdx::never;
16
17use crate::{
18 InferenceDiagnostic,
19 db::HirDatabase,
20 infer::{AllowTwoPhase, InferenceContext, expr::ExprIsRead},
21 next_solver::{
22 BoundExistentialPredicates, ExistentialPredicate, ParamTy, Region, Ty, TyKind,
23 infer::traits::ObligationCause,
24 },
25};
26
27#[derive(Debug)]
28pub(crate) enum Int {
29 I,
30 U(UintTy),
31 Bool,
32 Char,
33 CEnum,
34 InferenceVar,
35}
36
37#[derive(Debug)]
38pub(crate) enum CastTy<'db> {
39 Int(Int),
40 Float,
41 FnPtr,
42 Ptr(Ty<'db>, Mutability),
43 }
45
46impl<'db> CastTy<'db> {
47 pub(crate) fn from_ty(db: &dyn HirDatabase, t: Ty<'db>) -> Option<Self> {
48 match t.kind() {
49 TyKind::Bool => Some(Self::Int(Int::Bool)),
50 TyKind::Char => Some(Self::Int(Int::Char)),
51 TyKind::Int(_) => Some(Self::Int(Int::I)),
52 TyKind::Uint(it) => Some(Self::Int(Int::U(it))),
53 TyKind::Infer(InferTy::IntVar(_)) => Some(Self::Int(Int::InferenceVar)),
54 TyKind::Infer(InferTy::FloatVar(_)) => Some(Self::Float),
55 TyKind::Float(_) => Some(Self::Float),
56 TyKind::Adt(..) => {
57 let (AdtId::EnumId(id), _) = t.as_adt()? else {
58 return None;
59 };
60 let enum_data = id.enum_variants(db);
61 if enum_data.is_payload_free(db) { Some(Self::Int(Int::CEnum)) } else { None }
62 }
63 TyKind::RawPtr(ty, m) => Some(Self::Ptr(ty, m)),
64 TyKind::FnPtr(..) => Some(Self::FnPtr),
65 _ => None,
66 }
67 }
68}
69
70#[derive(Debug, PartialEq, Eq, Clone, Copy)]
71pub enum CastError {
72 Unknown,
73 CastToBool,
74 CastToChar,
75 DifferingKinds,
76 SizedUnsizedCast,
77 IllegalCast,
78 IntToWideCast,
79 NeedDeref,
80 NeedViaPtr,
81 NeedViaThinPtr,
82 NeedViaInt,
83 NonScalar,
84 PtrPtrAddingAutoTraits,
85 }
89
90impl CastError {
91 fn into_diagnostic<'db>(
92 self,
93 expr: ExprId,
94 expr_ty: Ty<'db>,
95 cast_ty: Ty<'db>,
96 ) -> InferenceDiagnostic {
97 InferenceDiagnostic::InvalidCast {
98 expr,
99 error: self,
100 expr_ty: expr_ty.store(),
101 cast_ty: cast_ty.store(),
102 }
103 }
104}
105
106#[derive(Clone, Debug)]
107pub(super) struct CastCheck<'db> {
108 expr: ExprId,
109 source_expr: ExprId,
110 expr_ty: Ty<'db>,
111 cast_ty: Ty<'db>,
112}
113
114impl<'db> CastCheck<'db> {
115 pub(super) fn new(
116 expr: ExprId,
117 source_expr: ExprId,
118 expr_ty: Ty<'db>,
119 cast_ty: Ty<'db>,
120 ) -> Self {
121 Self { expr, source_expr, expr_ty, cast_ty }
122 }
123
124 pub(super) fn check(
125 &mut self,
126 ctx: &mut InferenceContext<'db>,
127 ) -> Result<(), InferenceDiagnostic> {
128 self.expr_ty =
129 ctx.table.try_structurally_resolve_type(self.source_expr.into(), self.expr_ty);
130 self.cast_ty = ctx.table.try_structurally_resolve_type(self.expr.into(), self.cast_ty);
131
132 if ctx
134 .coerce(
135 self.source_expr,
136 self.expr_ty,
137 self.cast_ty,
138 AllowTwoPhase::No,
139 ExprIsRead::Yes,
140 )
141 .is_ok()
142 {
143 ctx.result.coercion_casts.insert(self.source_expr);
144 return Ok(());
145 }
146
147 if self.expr_ty.references_non_lt_error() || self.cast_ty.references_non_lt_error() {
148 return Ok(());
149 }
150
151 if !self.cast_ty.has_infer_types() && !ctx.table.type_is_sized_modulo_regions(self.cast_ty)
152 {
153 return Err(InferenceDiagnostic::CastToUnsized {
154 expr: self.expr,
155 cast_ty: self.cast_ty.store(),
156 });
157 }
158
159 self.do_check(ctx).map_err(|e| e.into_diagnostic(self.expr, self.expr_ty, self.cast_ty))
160 }
161
162 fn do_check(&self, ctx: &mut InferenceContext<'db>) -> Result<(), CastError> {
163 let (t_from, t_cast) =
164 match (CastTy::from_ty(ctx.db, self.expr_ty), CastTy::from_ty(ctx.db, self.cast_ty)) {
165 (Some(t_from), Some(t_cast)) => (t_from, t_cast),
166 (None, Some(t_cast)) => match self.expr_ty.kind() {
167 TyKind::FnDef(..) => {
168 let sig = self.expr_ty.fn_sig(ctx.interner());
170 let fn_ptr = Ty::new_fn_ptr(ctx.interner(), sig);
171 match ctx.coerce(
172 self.source_expr,
173 self.expr_ty,
174 fn_ptr,
175 AllowTwoPhase::No,
176 ExprIsRead::Yes,
177 ) {
178 Ok(_) => {}
179 Err(TypeError::IntrinsicCast) => {
180 return Err(CastError::IllegalCast);
181 }
182 Err(_) => {
183 return Err(CastError::NonScalar);
184 }
185 }
186
187 (CastTy::FnPtr, t_cast)
188 }
189 TyKind::Ref(_, inner_ty, mutbl) => {
190 return match t_cast {
191 CastTy::Int(_) | CastTy::Float => match inner_ty.kind() {
192 TyKind::Int(_)
193 | TyKind::Uint(_)
194 | TyKind::Float(_)
195 | TyKind::Infer(InferTy::IntVar(_) | InferTy::FloatVar(_)) => {
196 Err(CastError::NeedDeref)
197 }
198
199 _ => Err(CastError::NeedViaPtr),
200 },
201 CastTy::Ptr(t, m) => {
203 let t =
204 ctx.table.try_structurally_resolve_type(self.expr.into(), t);
205 if !ctx.table.type_is_sized_modulo_regions(t) {
206 return Err(CastError::IllegalCast);
207 }
208 self.check_ref_cast(ctx, inner_ty, mutbl, t, m)
209 }
210 _ => Err(CastError::NonScalar),
211 };
212 }
213 _ => return Err(CastError::NonScalar),
214 },
215 _ => return Err(CastError::NonScalar),
216 };
217
218 match (t_from, t_cast) {
221 (_, CastTy::Int(Int::CEnum) | CastTy::FnPtr) => Err(CastError::NonScalar),
223
224 (_, CastTy::Int(Int::Bool)) => Err(CastError::CastToBool),
226
227 (CastTy::Int(Int::U(UintTy::U8)), CastTy::Int(Int::Char)) => Ok(()), (_, CastTy::Int(Int::Char)) => Err(CastError::CastToChar),
230
231 (CastTy::Int(Int::Bool | Int::CEnum | Int::Char), CastTy::Float) => {
233 Err(CastError::NeedViaInt)
234 }
235
236 (CastTy::Int(Int::Bool | Int::CEnum | Int::Char) | CastTy::Float, CastTy::Ptr(..))
237 | (CastTy::Ptr(..) | CastTy::FnPtr, CastTy::Float) => Err(CastError::IllegalCast),
238
239 (CastTy::Ptr(src, _), CastTy::Ptr(dst, _)) => self.check_ptr_ptr_cast(ctx, src, dst), (CastTy::Ptr(src, _), CastTy::Int(_)) => self.check_ptr_addr_cast(ctx, src),
244 (CastTy::FnPtr, CastTy::Int(_)) => Ok(()),
245
246 (CastTy::Int(_), CastTy::Ptr(dst, _)) => self.check_addr_ptr_cast(ctx, dst),
248
249 (CastTy::FnPtr, CastTy::Ptr(dst, _)) => self.check_fptr_ptr_cast(ctx, dst),
251
252 (CastTy::Int(Int::CEnum), CastTy::Int(_)) => Ok(()),
254 (CastTy::Int(Int::Char | Int::Bool), CastTy::Int(_)) => Ok(()),
255 (CastTy::Int(_) | CastTy::Float, CastTy::Int(_) | CastTy::Float) => Ok(()),
256 }
257 }
258
259 fn check_ref_cast(
260 &self,
261 ctx: &mut InferenceContext<'db>,
262 t_expr: Ty<'db>,
263 m_expr: Mutability,
264 t_cast: Ty<'db>,
265 m_cast: Mutability,
266 ) -> Result<(), CastError> {
267 let t_expr = ctx.table.try_structurally_resolve_type(self.expr.into(), t_expr);
268 let t_cast = ctx.table.try_structurally_resolve_type(self.expr.into(), t_cast);
269
270 if m_expr >= m_cast
271 && let TyKind::Array(ety, _) = t_expr.kind()
272 && ctx.infcx().can_eq(ctx.table.param_env, ety, t_cast)
273 {
274 let array_ptr_type = Ty::new_ptr(ctx.interner(), t_expr, m_expr);
279 if ctx
280 .coerce(
281 self.source_expr,
282 self.expr_ty,
283 array_ptr_type,
284 AllowTwoPhase::No,
285 ExprIsRead::Yes,
286 )
287 .is_ok()
288 {
289 } else {
290 never!(
291 "could not cast from reference to array to pointer to array ({:?} to {:?})",
292 self.expr_ty,
293 array_ptr_type
294 );
295 }
296
297 let _ = ctx.demand_eqtype(self.expr.into(), ety, t_cast);
299 return Ok(());
300 }
301
302 Err(CastError::IllegalCast)
303 }
304
305 fn check_ptr_ptr_cast(
306 &self,
307 ctx: &mut InferenceContext<'db>,
308 src: Ty<'db>,
309 dst: Ty<'db>,
310 ) -> Result<(), CastError> {
311 let src_kind = pointer_kind(self.expr, src, ctx).map_err(|_| CastError::Unknown)?;
312 let dst_kind = pointer_kind(self.expr, dst, ctx).map_err(|_| CastError::Unknown)?;
313
314 match (src_kind, dst_kind) {
315 (Some(PointerKind::Error), _) | (_, Some(PointerKind::Error)) => Ok(()),
316
317 (_, None) | (None, _) => Ok(()),
320
321 (_, Some(PointerKind::Thin)) => Ok(()),
323
324 (Some(PointerKind::Thin), _) => Err(CastError::SizedUnsizedCast),
326
327 (Some(PointerKind::VTable(src_tty)), Some(PointerKind::VTable(dst_tty))) => {
329 match (src_tty.principal_def_id(), dst_tty.principal_def_id()) {
330 (Some(src_principal), Some(_)) => {
339 let src_obj = Ty::new_dynamic(
347 ctx.interner(),
348 BoundExistentialPredicates::new_from_iter(
349 ctx.interner(),
350 src_tty.iter().filter(|pred| {
351 !matches!(
352 pred.skip_binder(),
353 ExistentialPredicate::AutoTrait(_)
354 )
355 }),
356 ),
357 Region::new_erased(ctx.interner()),
358 );
359 let dst_obj = Ty::new_dynamic(
360 ctx.interner(),
361 BoundExistentialPredicates::new_from_iter(
362 ctx.interner(),
363 dst_tty.iter().filter(|pred| {
364 !matches!(
365 pred.skip_binder(),
366 ExistentialPredicate::AutoTrait(_)
367 )
368 }),
369 ),
370 Region::new_erased(ctx.interner()),
371 );
372
373 if ctx
376 .table
377 .at(&ObligationCause::new(self.expr))
378 .eq(src_obj, dst_obj)
379 .map(|infer_ok| ctx.table.register_infer_ok(infer_ok))
380 .is_err()
381 {
382 return Err(CastError::DifferingKinds);
383 }
384
385 let src_auto: FxHashSet<_> = src_tty
388 .auto_traits()
389 .into_iter()
390 .chain(
391 elaborate::supertrait_def_ids(ctx.interner(), src_principal)
392 .filter(|trait_| {
393 TraitSignature::of(ctx.db, trait_.0)
394 .flags
395 .contains(TraitFlags::AUTO)
396 }),
397 )
398 .collect();
399
400 let added = dst_tty
401 .auto_traits()
402 .into_iter()
403 .any(|trait_| !src_auto.contains(&trait_));
404
405 if added {
406 return Err(CastError::PtrPtrAddingAutoTraits);
407 }
408
409 Ok(())
410 }
411
412 (None, None) => Ok(()),
414
415 (Some(_), None) => Err(CastError::DifferingKinds),
445
446 (None, Some(_)) => Err(CastError::DifferingKinds),
448 }
449 }
450
451 (Some(src_kind), Some(dst_kind)) if src_kind == dst_kind => Ok(()),
453 (_, _) => Err(CastError::DifferingKinds),
454 }
455 }
456
457 fn check_ptr_addr_cast(
458 &self,
459 ctx: &mut InferenceContext<'db>,
460 expr_ty: Ty<'db>,
461 ) -> Result<(), CastError> {
462 match pointer_kind(self.expr, expr_ty, ctx).map_err(|_| CastError::Unknown)? {
463 None => Ok(()),
465 Some(PointerKind::Error) => Ok(()),
466 Some(PointerKind::Thin) => Ok(()),
467 _ => Err(CastError::NeedViaThinPtr),
468 }
469 }
470
471 fn check_addr_ptr_cast(
472 &self,
473 ctx: &mut InferenceContext<'db>,
474 cast_ty: Ty<'db>,
475 ) -> Result<(), CastError> {
476 match pointer_kind(self.expr, cast_ty, ctx).map_err(|_| CastError::Unknown)? {
477 None => Ok(()),
479 Some(PointerKind::Error) => Ok(()),
480 Some(PointerKind::Thin) => Ok(()),
481 Some(PointerKind::VTable(_)) => Err(CastError::IntToWideCast),
482 Some(PointerKind::Length) => Err(CastError::IntToWideCast),
483 Some(PointerKind::OfAlias | PointerKind::OfParam(_)) => Err(CastError::IntToWideCast),
484 }
485 }
486
487 fn check_fptr_ptr_cast(
488 &self,
489 ctx: &mut InferenceContext<'db>,
490 cast_ty: Ty<'db>,
491 ) -> Result<(), CastError> {
492 match pointer_kind(self.expr, cast_ty, ctx).map_err(|_| CastError::Unknown)? {
493 None => Ok(()),
495 Some(PointerKind::Error) => Ok(()),
496 Some(PointerKind::Thin) => Ok(()),
497 _ => Err(CastError::IllegalCast),
498 }
499 }
500}
501
502#[derive(Debug, PartialEq, Eq)]
506enum PointerKind<'db> {
507 Thin,
509 VTable(BoundExistentialPredicates<'db>),
511 Length,
513 OfAlias,
515 OfParam(ParamTy),
517 Error,
518}
519
520fn pointer_kind<'db>(
521 expr: ExprId,
522 ty: Ty<'db>,
523 ctx: &mut InferenceContext<'db>,
524) -> Result<Option<PointerKind<'db>>, ()> {
525 let ty = ctx.table.try_structurally_resolve_type(expr.into(), ty);
526
527 if ctx.table.type_is_sized_modulo_regions(ty) {
528 return Ok(Some(PointerKind::Thin));
529 }
530
531 match ty.kind() {
532 TyKind::Slice(_) | TyKind::Str => Ok(Some(PointerKind::Length)),
533 TyKind::Dynamic(bounds, _) => Ok(Some(PointerKind::VTable(bounds))),
534 TyKind::Adt(adt_def, subst) => {
535 let id = adt_def.def_id();
536 let AdtId::StructId(id) = id else {
537 never!("`{:?}` should be sized but is not?", ty);
538 return Err(());
539 };
540
541 let struct_data = id.fields(ctx.db);
542 if let Some((last_field, _)) = struct_data.fields().iter().last() {
543 let last_field_ty = ctx.db.field_types(id.into())[last_field]
544 .ty()
545 .instantiate(ctx.interner(), subst)
546 .skip_norm_wip();
547 pointer_kind(expr, last_field_ty, ctx)
548 } else {
549 Ok(Some(PointerKind::Thin))
550 }
551 }
552 TyKind::Tuple(subst) => match subst.iter().next_back() {
553 None => Ok(Some(PointerKind::Thin)),
554 Some(ty) => pointer_kind(expr, ty, ctx),
555 },
556 TyKind::Foreign(_) => Ok(Some(PointerKind::Thin)),
557 TyKind::Alias(..) => Ok(Some(PointerKind::OfAlias)),
558 TyKind::Error(_) => Ok(Some(PointerKind::Error)),
559 TyKind::Param(idx) => Ok(Some(PointerKind::OfParam(idx))),
560 TyKind::Bound(..) | TyKind::Placeholder(..) | TyKind::Infer(..) => Ok(None),
561 TyKind::Int(_)
562 | TyKind::Uint(_)
563 | TyKind::Float(_)
564 | TyKind::Bool
565 | TyKind::Char
566 | TyKind::Array(..)
567 | TyKind::CoroutineWitness(..)
568 | TyKind::RawPtr(..)
569 | TyKind::Ref(..)
570 | TyKind::FnDef(..)
571 | TyKind::FnPtr(..)
572 | TyKind::Closure(..)
573 | TyKind::Coroutine(..)
574 | TyKind::CoroutineClosure(..)
575 | TyKind::Never => {
576 never!("`{:?}` should be sized but is not?", ty);
577 Err(())
578 }
579 TyKind::UnsafeBinder(..) | TyKind::Pat(..) => {
580 never!("we don't produce these types: {ty:?}");
581 Err(())
582 }
583 }
584}