blob: f06d9ed24e8e7d876bfee6c902894b944b3a31c0 [file] [log] [blame]
// 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
// <http://www.gnu.org/licenses/>.
#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<TyTy::InferType *> (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<TyTy::UintType *> (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<TyTy::InferType *> (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<TyTy::InferType *> (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<TyTy::UintType *> (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<TyTy::InferType *> (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::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
bool from_ptr
= from.get_ty ()->get_kind () == TyTy::TypeKind::POINTER;
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<TyTy::ReferenceType> ();
const auto &to_ref = *to.get_ty ()->as<TyTy::ReferenceType> ();
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<TyTy::ReferenceType &> (*from.get_ty ());
TyTy::ReferenceType &t
= static_cast<TyTy::ReferenceType &> (*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 %<u8%> can be cast as %<char%>";
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