From 0f64da9672d88921439f6fdb306d16fece9b9c90 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Mon, 29 Dec 2025 18:14:28 +0900 Subject: [PATCH] Make `rb_check_typeddata` and `rbimpl_check_typeddata` identical --- error.c | 53 ++++++-------- include/ruby/internal/core/rtypeddata.h | 96 +++++++++++++++++++++---- internal/error.h | 3 +- 3 files changed, 104 insertions(+), 48 deletions(-) diff --git a/error.c b/error.c index e1a01b985a..f21a682d65 100644 --- a/error.c +++ b/error.c @@ -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 */ diff --git a/include/ruby/internal/core/rtypeddata.h b/include/ruby/internal/core/rtypeddata.h index 0f430f0c6a..8952ba9181 100644 --- a/include/ruby/internal/core/rtypeddata.h +++ b/include/ruby/internal/core/rtypeddata.h @@ -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); } diff --git a/internal/error.h b/internal/error.h index cecaa5c4a8..4b41aee77b 100644 --- a/internal/error.h +++ b/internal/error.h @@ -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 {