@@ -177,6 +177,30 @@ VALUE rbs_type_param_variance_to_ruby(enum rbs_type_param_variance value) {
177177 rb_class_new_instance(argc, argv, receiver)
178178#endif
179179
180+ // Route Namespace / TypeName construction through the Ruby-side
181+ // flyweight cache (`RBS::Namespace.[]` / `RBS::TypeName.[]`) so that
182+ // structurally equal values produced by the parser share canonical
183+ // instances. An in-C trie walk was tried but did not beat
184+ // `rb_funcallv` on Ruby 4.0+, where method dispatch is well optimized.
185+ static ID id_intern_brackets ;
186+
187+ static inline ID intern_brackets (void ) {
188+ if (!id_intern_brackets ) id_intern_brackets = rb_intern ("[]" );
189+ return id_intern_brackets ;
190+ }
191+
192+ static VALUE rbs_intern_namespace (rbs_translation_context_t ctx , rbs_namespace_t * node ) {
193+ VALUE args [2 ];
194+ args [0 ] = rbs_node_list_to_ruby_array (ctx , node -> path );
195+ args [1 ] = node -> absolute ? Qtrue : Qfalse ;
196+ return rb_funcallv (RBS_Namespace , intern_brackets (), 2 , args );
197+ }
198+
199+ static VALUE rbs_intern_type_name (VALUE namespace , VALUE name ) {
200+ VALUE args [2 ] = { namespace , name };
201+ return rb_funcallv (RBS_TypeName , intern_brackets (), 2 , args );
202+ }
203+
180204VALUE rbs_struct_to_ruby_value (rbs_translation_context_t ctx , rbs_node_t * instance ) {
181205 if (instance == NULL ) return Qnil ;
182206
@@ -1378,19 +1402,7 @@ VALUE rbs_struct_to_ruby_value(rbs_translation_context_t ctx, rbs_node_t *instan
13781402 return CLASS_NEW_INSTANCE (RBS_MethodType , 1 , & h );
13791403 }
13801404 case RBS_NAMESPACE : {
1381- rbs_namespace_t * node = (rbs_namespace_t * ) instance ;
1382-
1383- // Compute child VALUEs into locals variables first, before any recursion into `rbs_struct_to_ruby_value()`.
1384- VALUE arg_path = rbs_node_list_to_ruby_array (ctx , node -> path );
1385- VALUE arg_absolute = node -> absolute ? Qtrue : Qfalse ;
1386-
1387- // Claim the shared kwargs hash, clear it, fill it, and hand it to `.new`.
1388- // Must not recurse between `rb_hash_clear()` and `CLASS_NEW_INSTANCE()`.
1389- VALUE h = ctx .reusable_kwargs_hash ;
1390- rb_hash_clear (h );
1391- rb_hash_aset (h , ID2SYM (rb_intern ("path" )), arg_path );
1392- rb_hash_aset (h , ID2SYM (rb_intern ("absolute" )), arg_absolute );
1393- return CLASS_NEW_INSTANCE (RBS_Namespace , 1 , & h );
1405+ return rbs_intern_namespace (ctx , (rbs_namespace_t * ) instance );
13941406 }
13951407 case RBS_SIGNATURE : {
13961408 rbs_signature_t * signature = (rbs_signature_t * ) instance ;
@@ -1402,18 +1414,9 @@ VALUE rbs_struct_to_ruby_value(rbs_translation_context_t ctx, rbs_node_t *instan
14021414 }
14031415 case RBS_TYPE_NAME : {
14041416 rbs_type_name_t * node = (rbs_type_name_t * ) instance ;
1405-
1406- // Compute child VALUEs into locals variables first, before any recursion into `rbs_struct_to_ruby_value()`.
1407- VALUE arg_namespace = rbs_struct_to_ruby_value (ctx , (rbs_node_t * ) node -> rbs_namespace ); // rbs_namespace
1408- VALUE arg_name = rbs_struct_to_ruby_value (ctx , (rbs_node_t * ) node -> name ); // rbs_ast_symbol
1409-
1410- // Claim the shared kwargs hash, clear it, fill it, and hand it to `.new`.
1411- // Must not recurse between `rb_hash_clear()` and `CLASS_NEW_INSTANCE()`.
1412- VALUE h = ctx .reusable_kwargs_hash ;
1413- rb_hash_clear (h );
1414- rb_hash_aset (h , ID2SYM (rb_intern ("namespace" )), arg_namespace );
1415- rb_hash_aset (h , ID2SYM (rb_intern ("name" )), arg_name );
1416- return CLASS_NEW_INSTANCE (RBS_TypeName , 1 , & h );
1417+ VALUE ns = rbs_struct_to_ruby_value (ctx , (rbs_node_t * ) node -> rbs_namespace );
1418+ VALUE name = rbs_struct_to_ruby_value (ctx , (rbs_node_t * ) node -> name );
1419+ return rbs_intern_type_name (ns , name );
14171420 }
14181421 case RBS_TYPES_ALIAS : {
14191422 rbs_types_alias_t * node = (rbs_types_alias_t * ) instance ;
0 commit comments