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(src_tty.iter().filter(
349 |pred| {
350 !matches!(
351 pred.skip_binder(),
352 ExistentialPredicate::AutoTrait(_)
353 )
354 },
355 )),
356 Region::new_erased(ctx.interner()),
357 );
358 let dst_obj = Ty::new_dynamic(
359 ctx.interner(),
360 BoundExistentialPredicates::new_from_iter(dst_tty.iter().filter(
361 |pred| {
362 !matches!(
363 pred.skip_binder(),
364 ExistentialPredicate::AutoTrait(_)
365 )
366 },
367 )),
368 Region::new_erased(ctx.interner()),
369 );
370
371 if ctx
374 .table
375 .at(&ObligationCause::new(self.expr))
376 .eq(src_obj, dst_obj)
377 .map(|infer_ok| ctx.table.register_infer_ok(infer_ok))
378 .is_err()
379 {
380 return Err(CastError::DifferingKinds);
381 }
382
383 let src_auto: FxHashSet<_> = src_tty
386 .auto_traits()
387 .into_iter()
388 .chain(
389 elaborate::supertrait_def_ids(ctx.interner(), src_principal)
390 .filter(|trait_| {
391 TraitSignature::of(ctx.db, trait_.0)
392 .flags
393 .contains(TraitFlags::AUTO)
394 }),
395 )
396 .collect();
397
398 let added = dst_tty
399 .auto_traits()
400 .into_iter()
401 .any(|trait_| !src_auto.contains(&trait_));
402
403 if added {
404 return Err(CastError::PtrPtrAddingAutoTraits);
405 }
406
407 Ok(())
408 }
409
410 (None, None) => Ok(()),
412
413 (Some(_), None) => Err(CastError::DifferingKinds),
443
444 (None, Some(_)) => Err(CastError::DifferingKinds),
446 }
447 }
448
449 (Some(src_kind), Some(dst_kind)) if src_kind == dst_kind => Ok(()),
451 (_, _) => Err(CastError::DifferingKinds),
452 }
453 }
454
455 fn check_ptr_addr_cast(
456 &self,
457 ctx: &mut InferenceContext<'db>,
458 expr_ty: Ty<'db>,
459 ) -> Result<(), CastError> {
460 match pointer_kind(self.expr, expr_ty, ctx).map_err(|_| CastError::Unknown)? {
461 None => Ok(()),
463 Some(PointerKind::Error) => Ok(()),
464 Some(PointerKind::Thin) => Ok(()),
465 _ => Err(CastError::NeedViaThinPtr),
466 }
467 }
468
469 fn check_addr_ptr_cast(
470 &self,
471 ctx: &mut InferenceContext<'db>,
472 cast_ty: Ty<'db>,
473 ) -> Result<(), CastError> {
474 match pointer_kind(self.expr, cast_ty, ctx).map_err(|_| CastError::Unknown)? {
475 None => Ok(()),
477 Some(PointerKind::Error) => Ok(()),
478 Some(PointerKind::Thin) => Ok(()),
479 Some(PointerKind::VTable(_)) => Err(CastError::IntToWideCast),
480 Some(PointerKind::Length) => Err(CastError::IntToWideCast),
481 Some(PointerKind::OfAlias | PointerKind::OfParam(_)) => Err(CastError::IntToWideCast),
482 }
483 }
484
485 fn check_fptr_ptr_cast(
486 &self,
487 ctx: &mut InferenceContext<'db>,
488 cast_ty: Ty<'db>,
489 ) -> Result<(), CastError> {
490 match pointer_kind(self.expr, cast_ty, ctx).map_err(|_| CastError::Unknown)? {
491 None => Ok(()),
493 Some(PointerKind::Error) => Ok(()),
494 Some(PointerKind::Thin) => Ok(()),
495 _ => Err(CastError::IllegalCast),
496 }
497 }
498}
499
500#[derive(Debug, PartialEq, Eq)]
504enum PointerKind<'db> {
505 Thin,
507 VTable(BoundExistentialPredicates<'db>),
509 Length,
511 OfAlias,
513 OfParam(ParamTy),
515 Error,
516}
517
518fn pointer_kind<'db>(
519 expr: ExprId,
520 ty: Ty<'db>,
521 ctx: &mut InferenceContext<'db>,
522) -> Result<Option<PointerKind<'db>>, ()> {
523 let ty = ctx.table.try_structurally_resolve_type(expr.into(), ty);
524
525 if ctx.table.type_is_sized_modulo_regions(ty) {
526 return Ok(Some(PointerKind::Thin));
527 }
528
529 match ty.kind() {
530 TyKind::Slice(_) | TyKind::Str => Ok(Some(PointerKind::Length)),
531 TyKind::Dynamic(bounds, _) => Ok(Some(PointerKind::VTable(bounds))),
532 TyKind::Adt(adt_def, subst) => {
533 let id = adt_def.def_id();
534 let AdtId::StructId(id) = id else {
535 never!("`{:?}` should be sized but is not?", ty);
536 return Err(());
537 };
538
539 let struct_data = id.fields(ctx.db);
540 if let Some((last_field, _)) = struct_data.fields().iter().last() {
541 let last_field_ty = ctx.db.field_types(id.into())[last_field]
542 .ty()
543 .instantiate(ctx.interner(), subst)
544 .skip_norm_wip();
545 pointer_kind(expr, last_field_ty, ctx)
546 } else {
547 Ok(Some(PointerKind::Thin))
548 }
549 }
550 TyKind::Tuple(subst) => match subst.iter().next_back() {
551 None => Ok(Some(PointerKind::Thin)),
552 Some(ty) => pointer_kind(expr, ty, ctx),
553 },
554 TyKind::Foreign(_) => Ok(Some(PointerKind::Thin)),
555 TyKind::Alias(..) => Ok(Some(PointerKind::OfAlias)),
556 TyKind::Error(_) => Ok(Some(PointerKind::Error)),
557 TyKind::Param(idx) => Ok(Some(PointerKind::OfParam(idx))),
558 TyKind::Bound(..) | TyKind::Placeholder(..) | TyKind::Infer(..) => Ok(None),
559 TyKind::Int(_)
560 | TyKind::Uint(_)
561 | TyKind::Float(_)
562 | TyKind::Bool
563 | TyKind::Char
564 | TyKind::Array(..)
565 | TyKind::CoroutineWitness(..)
566 | TyKind::RawPtr(..)
567 | TyKind::Ref(..)
568 | TyKind::FnDef(..)
569 | TyKind::FnPtr(..)
570 | TyKind::Closure(..)
571 | TyKind::Coroutine(..)
572 | TyKind::CoroutineClosure(..)
573 | TyKind::Never => {
574 never!("`{:?}` should be sized but is not?", ty);
575 Err(())
576 }
577 TyKind::UnsafeBinder(..) | TyKind::Pat(..) => {
578 never!("we don't produce these types: {ty:?}");
579 Err(())
580 }
581 }
582}