Make rb_check_typeddata and rbimpl_check_typeddata identical

This commit is contained in:
Nobuyoshi Nakada 2025-12-29 18:14:28 +09:00
parent 72627d85e3
commit 0f64da9672
No known key found for this signature in database
GPG Key ID: 3582D74E1FEE4465
Notes: git 2025-12-29 10:47:40 +00:00
3 changed files with 104 additions and 48 deletions

53
error.c
View File

@ -1313,6 +1313,20 @@ rb_builtin_class_name(VALUE x)
COLDFUNC NORETURN(static void unexpected_type(VALUE, int, int));
#define UNDEF_LEAKED "undef leaked to the Ruby space"
void
rb_unexpected_typeddata(const rb_data_type_t *actual, const rb_data_type_t *expected)
{
rb_raise(rb_eTypeError, "wrong argument type %s (expected %s)",
actual->wrap_struct_name, expected->wrap_struct_name);
}
void
rb_unexpected_object_type(VALUE obj, const char *expected)
{
rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (expected %s)",
displaying_class_of(obj), expected);
}
static void
unexpected_type(VALUE x, int xt, int t)
{
@ -1320,9 +1334,7 @@ unexpected_type(VALUE x, int xt, int t)
VALUE mesg, exc = rb_eFatal;
if (tname) {
mesg = rb_sprintf("wrong argument type %"PRIsVALUE" (expected %s)",
displaying_class_of(x), tname);
exc = rb_eTypeError;
rb_unexpected_object_type(x, tname);
}
else if (xt > T_MASK && xt <= 0x3f) {
mesg = rb_sprintf("unknown type 0x%x (0x%x given, probably comes"
@ -1367,24 +1379,18 @@ rb_unexpected_type(VALUE x, int t)
unexpected_type(x, TYPE(x), t);
}
#undef rb_typeddata_inherited_p
int
rb_typeddata_inherited_p(const rb_data_type_t *child, const rb_data_type_t *parent)
{
while (child) {
if (child == parent) return 1;
child = child->parent;
}
return 0;
return rb_typeddata_inherited_p_inline(child, parent);
}
#undef rb_typeddata_is_kind_of
int
rb_typeddata_is_kind_of(VALUE obj, const rb_data_type_t *data_type)
{
if (!RB_TYPE_P(obj, T_DATA) ||
!RTYPEDDATA_P(obj) || !rb_typeddata_inherited_p(RTYPEDDATA_TYPE(obj), data_type)) {
return 0;
}
return 1;
return rb_typeddata_is_kind_of_inline(obj, data_type);
}
#undef rb_typeddata_is_instance_of
@ -1397,26 +1403,7 @@ rb_typeddata_is_instance_of(VALUE obj, const rb_data_type_t *data_type)
void *
rb_check_typeddata(VALUE obj, const rb_data_type_t *data_type)
{
VALUE actual;
if (!RB_TYPE_P(obj, T_DATA)) {
actual = displaying_class_of(obj);
}
else if (!RTYPEDDATA_P(obj)) {
actual = displaying_class_of(obj);
}
else if (!rb_typeddata_inherited_p(RTYPEDDATA_TYPE(obj), data_type)) {
const char *name = RTYPEDDATA_TYPE(obj)->wrap_struct_name;
actual = rb_str_new_cstr(name); /* or rb_fstring_cstr? not sure... */
}
else {
return RTYPEDDATA_GET_DATA(obj);
}
const char *expected = data_type->wrap_struct_name;
rb_raise(rb_eTypeError, "wrong argument type %"PRIsVALUE" (expected %s)",
actual, expected);
UNREACHABLE_RETURN(NULL);
return rbimpl_check_typeddata(obj, data_type);
}
/* exception classes */

View File

@ -450,6 +450,36 @@ RBIMPL_ATTR_NONNULL((2))
* @post Upon successful return `obj`'s type is guaranteed `data_type`.
*/
void *rb_check_typeddata(VALUE obj, const rb_data_type_t *data_type);
RBIMPL_ATTR_NORETURN()
RBIMPL_ATTR_NONNULL((2))
/**
* @private
*
* Fails with the given object's type incompatibility to the type.
*
* This is an implementation detail of Check_Type. People don't use it
* directly.
*
* @param[in] obj The object in question.
* @param[in] expected Name of expected data type of `obj`.
*/
void rb_unexpected_object_type(VALUE obj, const char *expected);
RBIMPL_ATTR_NORETURN()
RBIMPL_ATTR_NONNULL(())
/**
* @private
*
* Fails with the given object's type incompatibility to the type.
*
* This is an implementation detail of #TypedData_Make_Struct. People don't
* use it directly.
*
* @param[in] actual Actual data type.
* @param[in] expected Expected data type.
*/
void rb_unexpected_typeddata(const rb_data_type_t *actual, const rb_data_type_t *expected);
RBIMPL_SYMBOL_EXPORT_END()
/**
@ -565,6 +595,27 @@ rbimpl_rtypeddata_p(VALUE obj)
return FL_TEST_RAW(obj, RUBY_TYPED_FL_IS_TYPED_DATA);
}
RBIMPL_ATTR_PURE()
RBIMPL_ATTR_ARTIFICIAL()
/**
* @private
*
* Identical to rbimpl_rtypeddata_p(), except it is allowed to call on non-data
* objects.
*
* This is an implementation detail of inline functions defined in this file.
* People don't use it directly.
*
* @param[in] obj Object in question
* @retval true `obj` is an instance of ::RTypedData.
* @retval false `obj` is not an instance of ::RTypedData
*/
static inline bool
rbimpl_obj_typeddata_p(VALUE obj)
{
return RB_TYPE_P(obj, RUBY_T_DATA) && rbimpl_rtypeddata_p(obj);
}
RBIMPL_ATTR_PURE_UNLESS_DEBUG()
RBIMPL_ATTR_ARTIFICIAL()
/**
@ -615,6 +666,29 @@ RTYPEDDATA_TYPE(VALUE obj)
}
RBIMPL_ATTR_ARTIFICIAL()
RBIMPL_ATTR_NONNULL(())
static inline bool
rb_typeddata_inherited_p_inline(const rb_data_type_t *child, const rb_data_type_t *parent)
{
do {
if (RB_LIKELY(child == parent)) return true;
} while ((child = child->parent) != NULL);
return false;
}
#define rb_typeddata_inherited_p rb_typeddata_inherited_p_inline
RBIMPL_ATTR_ARTIFICIAL()
RBIMPL_ATTR_NONNULL((2))
static inline bool
rb_typeddata_is_kind_of_inline(VALUE obj, const rb_data_type_t *data_type)
{
if (RB_UNLIKELY(!rbimpl_obj_typeddata_p(obj))) return false;
return rb_typeddata_inherited_p(RTYPEDDATA_TYPE(obj), data_type);
}
#define rb_typeddata_is_kind_of rb_typeddata_is_kind_of_inline
RBIMPL_ATTR_ARTIFICIAL()
RBIMPL_ATTR_NONNULL((2))
/**
* @private
*
@ -624,22 +698,16 @@ RBIMPL_ATTR_ARTIFICIAL()
static inline void *
rbimpl_check_typeddata(VALUE obj, const rb_data_type_t *expected_type)
{
if (RB_LIKELY(RB_TYPE_P(obj, T_DATA) && RTYPEDDATA_P(obj))) {
const rb_data_type_t *actual_type = RTYPEDDATA_TYPE(obj);
void *data = RTYPEDDATA_GET_DATA(obj);
if (RB_LIKELY(actual_type == expected_type)) {
return data;
}
while (actual_type) {
actual_type = actual_type->parent;
if (actual_type == expected_type) {
return data;
}
}
if (RB_UNLIKELY(!rbimpl_obj_typeddata_p(obj))) {
rb_unexpected_object_type(obj, expected_type->wrap_struct_name);
}
return rb_check_typeddata(obj, expected_type);
const rb_data_type_t *actual_type = RTYPEDDATA_TYPE(obj);
if (RB_UNLIKELY(!rb_typeddata_inherited_p(actual_type, expected_type))){
rb_unexpected_typeddata(actual_type, expected_type);
}
return RTYPEDDATA_GET_DATA(obj);
}

View File

@ -235,10 +235,11 @@ rb_key_err_raise(VALUE mesg, VALUE recv, VALUE name)
rb_exc_raise(exc);
}
RBIMPL_ATTR_NONNULL((2))
static inline bool
rb_typeddata_is_instance_of_inline(VALUE obj, const rb_data_type_t *data_type)
{
return RB_TYPE_P(obj, T_DATA) && RTYPEDDATA_P(obj) && (RTYPEDDATA_TYPE(obj) == data_type);
return rbimpl_obj_typeddata_p(obj) && (RTYPEDDATA_TYPE(obj) == data_type);
}
typedef enum {