// Copyright (C) 2020-2025 Free Software Foundation, Inc. // This file is part of GCC. // GCC is free software; you can redistribute it and/or modify it under // the terms of the GNU General Public License as published by the Free // Software Foundation; either version 3, or (at your option) any later // version. // GCC is distributed in the hope that it will be useful, but WITHOUT ANY // WARRANTY; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License // for more details. // You should have received a copy of the GNU General Public License // along with GCC; see the file COPYING3. If not see // . #include "rust-casts.h" #include "rust-tyty-util.h" namespace Rust { namespace Resolver { TypeCastRules::TypeCastRules (location_t locus, TyTy::TyWithLocation from, TyTy::TyWithLocation to) : locus (locus), from (from), to (to) {} TypeCoercionRules::CoercionResult TypeCastRules::resolve (location_t locus, TyTy::TyWithLocation from, TyTy::TyWithLocation to, bool emit_error) { TypeCastRules cast_rules (locus, from, to); return cast_rules.check (emit_error); } TypeCoercionRules::CoercionResult TypeCastRules::check (bool emit_error) { // try the simple cast rules auto simple_cast = cast_rules (); if (!simple_cast.is_error ()) return simple_cast; // https://github.com/rust-lang/rust/blob/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/compiler/rustc_typeck/src/check/cast.rs#L565-L582 auto possible_coercion = TypeCoercionRules::TryCoerce (from.get_ty (), to.get_ty (), locus, true /*allow-autoderef*/, true /*is_cast_site*/); if (!possible_coercion.is_error ()) { // given the attempt was ok we need to ensure we perform it so that any // inference variables are unified correctly return TypeCoercionRules::Coerce (from.get_ty (), to.get_ty (), locus, true /*allow-autoderef*/, true /*is_cast_site*/); } if (emit_error) TypeCastRules::emit_cast_error (locus, from, to); return TypeCoercionRules::CoercionResult::get_error (); } TypeCoercionRules::CoercionResult TypeCastRules::cast_rules () { // https://github.com/rust-lang/rust/blob/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/compiler/rustc_typeck/src/check/cast.rs#L596 // https://github.com/rust-lang/rust/blob/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/compiler/rustc_typeck/src/check/cast.rs#L654 TyTy::BaseType *from_type = from.get_ty ()->destructure (); rust_debug ("cast_rules from={%s} to={%s}", from_type->debug_str ().c_str (), to.get_ty ()->debug_str ().c_str ()); switch (from_type->get_kind ()) { case TyTy::TypeKind::INFER: { TyTy::InferType *from_infer = static_cast (from_type); switch (from_infer->get_infer_kind ()) { case TyTy::InferType::InferTypeKind::GENERAL: return TypeCoercionRules::CoercionResult{{}, to.get_ty ()->clone ()}; case TyTy::InferType::InferTypeKind::INTEGRAL: switch (to.get_ty ()->get_kind ()) { case TyTy::TypeKind::CHAR: { // only u8 and char bool was_uint = from.get_ty ()->get_kind () == TyTy::TypeKind::UINT; bool was_u8 = was_uint && (static_cast (from.get_ty ()) ->get_uint_kind () == TyTy::UintType::UintKind::U8); if (was_u8) return TypeCoercionRules::CoercionResult{ {}, to.get_ty ()->clone ()}; } break; case TyTy::TypeKind::USIZE: case TyTy::TypeKind::ISIZE: case TyTy::TypeKind::UINT: case TyTy::TypeKind::INT: case TyTy::TypeKind::POINTER: return TypeCoercionRules::CoercionResult{ {}, to.get_ty ()->clone ()}; case TyTy::TypeKind::INFER: { TyTy::InferType *to_infer = static_cast (to.get_ty ()); switch (to_infer->get_infer_kind ()) { case TyTy::InferType::InferTypeKind::GENERAL: case TyTy::InferType::InferTypeKind::INTEGRAL: return TypeCoercionRules::CoercionResult{ {}, to.get_ty ()->clone ()}; default: return TypeCoercionRules::CoercionResult::get_error (); } } break; default: return TypeCoercionRules::CoercionResult::get_error (); } break; case TyTy::InferType::InferTypeKind::FLOAT: switch (to.get_ty ()->get_kind ()) { case TyTy::TypeKind::USIZE: case TyTy::TypeKind::ISIZE: case TyTy::TypeKind::UINT: case TyTy::TypeKind::INT: return TypeCoercionRules::CoercionResult{ {}, to.get_ty ()->clone ()}; case TyTy::TypeKind::INFER: { TyTy::InferType *to_infer = static_cast (to.get_ty ()); switch (to_infer->get_infer_kind ()) { case TyTy::InferType::InferTypeKind::GENERAL: case TyTy::InferType::InferTypeKind::FLOAT: return TypeCoercionRules::CoercionResult{ {}, to.get_ty ()->clone ()}; default: return TypeCoercionRules::CoercionResult::get_error (); } } break; default: return TypeCoercionRules::CoercionResult::get_error (); } break; } } break; case TyTy::TypeKind::BOOL: switch (to.get_ty ()->get_kind ()) { case TyTy::TypeKind::INFER: case TyTy::TypeKind::USIZE: case TyTy::TypeKind::ISIZE: case TyTy::TypeKind::UINT: case TyTy::TypeKind::INT: return TypeCoercionRules::CoercionResult{{}, to.get_ty ()->clone ()}; default: return TypeCoercionRules::CoercionResult::get_error (); } break; case TyTy::TypeKind::CHAR: case TyTy::TypeKind::USIZE: case TyTy::TypeKind::ISIZE: case TyTy::TypeKind::UINT: case TyTy::TypeKind::INT: switch (to.get_ty ()->get_kind ()) { case TyTy::TypeKind::CHAR: { // only u8 and char bool was_uint = from.get_ty ()->get_kind () == TyTy::TypeKind::UINT; bool was_u8 = was_uint && (static_cast (from.get_ty ()) ->get_uint_kind () == TyTy::UintType::UintKind::U8); if (was_u8) return TypeCoercionRules::CoercionResult{{}, to.get_ty ()->clone ()}; } break; case TyTy::TypeKind::FLOAT: { // can only do this for number types not char bool from_char = from.get_ty ()->get_kind () == TyTy::TypeKind::CHAR; if (!from_char) return TypeCoercionRules::CoercionResult{{}, to.get_ty ()->clone ()}; } break; case TyTy::TypeKind::POINTER: { // char can't be casted as a ptr bool from_char = from.get_ty ()->get_kind () == TyTy::TypeKind::CHAR; if (!from_char) return TypeCoercionRules::CoercionResult{{}, to.get_ty ()->clone ()}; } break; case TyTy::TypeKind::INFER: case TyTy::TypeKind::USIZE: case TyTy::TypeKind::ISIZE: case TyTy::TypeKind::UINT: case TyTy::TypeKind::INT: return TypeCoercionRules::CoercionResult{{}, to.get_ty ()->clone ()}; default: return TypeCoercionRules::CoercionResult::get_error (); } break; case TyTy::TypeKind::FLOAT: switch (to.get_ty ()->get_kind ()) { case TyTy::TypeKind::USIZE: case TyTy::TypeKind::ISIZE: case TyTy::TypeKind::UINT: case TyTy::TypeKind::INT: return TypeCoercionRules::CoercionResult{{}, to.get_ty ()->clone ()}; case TyTy::TypeKind::FLOAT: return TypeCoercionRules::CoercionResult{{}, to.get_ty ()->clone ()}; case TyTy::TypeKind::INFER: { TyTy::InferType *to_infer = static_cast (to.get_ty ()); switch (to_infer->get_infer_kind ()) { case TyTy::InferType::InferTypeKind::GENERAL: case TyTy::InferType::InferTypeKind::FLOAT: return TypeCoercionRules::CoercionResult{ {}, to.get_ty ()->clone ()}; default: return TypeCoercionRules::CoercionResult::get_error (); } } break; default: return TypeCoercionRules::CoercionResult::get_error (); } break; case TyTy::TypeKind::REF: case TyTy::TypeKind::FNPTR: case TyTy::TypeKind::POINTER: switch (to.get_ty ()->get_kind ()) { case TyTy::TypeKind::USIZE: case TyTy::TypeKind::ISIZE: case TyTy::TypeKind::UINT: case TyTy::TypeKind::INT: { // refs should not cast to numeric type auto kind = from.get_ty ()->get_kind (); bool from_ptr = kind == TyTy::TypeKind::POINTER || kind == TyTy::TypeKind::FNPTR; if (from_ptr) { return TypeCoercionRules::CoercionResult{ {}, to.get_ty ()->clone ()}; } } break; case TyTy::TypeKind::REF: case TyTy::TypeKind::POINTER: return check_ptr_ptr_cast (); default: return TypeCoercionRules::CoercionResult::get_error (); } break; default: return TypeCoercionRules::CoercionResult::get_error (); } return TypeCoercionRules::CoercionResult::get_error (); } TypeCoercionRules::CoercionResult TypeCastRules::check_ptr_ptr_cast () { rust_debug ("check_ptr_ptr_cast from={%s} to={%s}", from.get_ty ()->debug_str ().c_str (), to.get_ty ()->debug_str ().c_str ()); bool from_is_ref = from.get_ty ()->get_kind () == TyTy::TypeKind::REF; bool to_is_ref = to.get_ty ()->get_kind () == TyTy::TypeKind::REF; bool from_is_ptr = from.get_ty ()->get_kind () == TyTy::TypeKind::POINTER; bool to_is_ptr = to.get_ty ()->get_kind () == TyTy::TypeKind::POINTER; if (from_is_ptr && to_is_ptr) { // mutability is ignored here as all pointer usage requires unsafe return TypeCoercionRules::CoercionResult{{}, to.get_ty ()->clone ()}; } else if (from_is_ref && to_is_ref) { const auto &from_ref = *from.get_ty ()->as (); const auto &to_ref = *to.get_ty ()->as (); if (from_ref.is_dyn_object () != to_ref.is_dyn_object ()) { // this needs to be handled by coercion logic return TypeCoercionRules::CoercionResult::get_error (); } // are the underlying types safely simple castable? const auto to_underly = to_ref.get_base (); const auto from_underly = from_ref.get_base (); auto res = resolve (locus, TyTy::TyWithLocation (from_underly), TyTy::TyWithLocation (to_underly), false); if (res.is_error ()) { // this needs to be handled by coercion logic return TypeCoercionRules::CoercionResult::get_error (); } // mutability must be coerceable TyTy::ReferenceType &f = static_cast (*from.get_ty ()); TyTy::ReferenceType &t = static_cast (*to.get_ty ()); if (TypeCoercionRules::coerceable_mutability (f.mutability (), t.mutability ())) { return TypeCoercionRules::CoercionResult{{}, to.get_ty ()->clone ()}; } } return TypeCoercionRules::CoercionResult::get_error (); } void TypeCastRules::emit_cast_error (location_t locus, TyTy::TyWithLocation from, TyTy::TyWithLocation to) { rich_location r (line_table, locus); r.add_range (from.get_locus ()); r.add_range (to.get_locus ()); ErrorCode error_code; std::string error_msg; switch (to.get_ty ()->get_kind ()) { case TyTy::TypeKind::BOOL: error_msg = "cannot cast %qs as %qs"; error_code = ErrorCode::E0054; break; case TyTy::TypeKind::CHAR: error_msg += "cannot cast %qs as %qs, only % can be cast as %"; error_code = ErrorCode::E0604; break; case TyTy::TypeKind::SLICE: error_msg = "cast to unsized type: %qs as %qs"; error_code = ErrorCode::E0620; break; default: error_msg = "casting %qs as %qs is invalid"; error_code = ErrorCode::E0606; break; } rust_error_at (r, error_code, error_msg.c_str (), from.get_ty ()->get_name ().c_str (), to.get_ty ()->get_name ().c_str ()); } } // namespace Resolver } // namespace Rust