Skip to content

Conversation

@soutaro
Copy link
Member

@soutaro soutaro commented Jan 23, 2026

The parser currently uses global constant pool and updates the pool during parsing through RBS_KEYWORD node. This is not thread-safe and we cannot run the parser in parallel. This PR is to remove the global constant pool and RBS_KEYWORD node by:

  1. Simplify the location structure from the nested one to flat struct attributes
  2. Introducing C enums for symbol literal unions (for example, method/attribute visibility is :public | :private)

This is not a breaking change in Ruby library -- the RBS::Location structure nor the union type attributes are not changed -- but introduces breaking changes in C level API.

Flat location attributes

The nested location attributes are expanded to flat struct attributes.

Instead of having one location attribute and sub-locations are stored under it, node structs directly have sub-locations are its attributes.

    // before: sub-locations are saved under location attribute.
    rbs_location_t *location;

    // after: The main location is saved as `location`, but sub-locations are saved to their own attributes.
    rbs_location _range location;
    rbs_location_range keyword_range;     /* Required */
    rbs_location_range name_range;        /* Required */
    rbs_location_range end_range;         /* Required */
    rbs_location_range type_params_range; /* Optional */
    rbs_location_range colon_range;       /* Optional */
    rbs_location_range self_types_range;  /* Optional */

The nested locations are then reconstructed in ast_translation.c.

        VALUE location = rbs_location_range_to_ruby_location(ctx, node->base.location);
        rbs_loc *loc = rbs_check_location(location);
        rbs_loc_legacy_alloc_children(loc, 5);
        rbs_loc_legacy_add_required_child(loc, rb_intern("keyword"), (rbs_loc_range) { .start = node->keyword_range.start, .end = node->keyword_range.end });
        rbs_loc_legacy_add_required_child(loc, rb_intern("name"), (rbs_loc_range) { .start = node->name_range.start, .end = node->name_range.end });
        rbs_loc_legacy_add_required_child(loc, rb_intern("end"), (rbs_loc_range) { .start = node->end_range.start, .end = node->end_range.end });
        rbs_loc_legacy_add_optional_child(loc, rb_intern("type_params"), (rbs_loc_range) { .start = node->type_params_range.start, .end = node->type_params_range.end });
        rbs_loc_legacy_add_optional_child(loc, rb_intern("lt"), (rbs_loc_range) { .start = node->lt_range.start, .end = node->lt_range.end });
        rb_hash_aset(h, ID2SYM(rb_intern("location")), location);

C enums for symbol literal unions

The union attributes have their own enum types.

enum rbs_attribute_visibility {
    RBS_ATTRIBUTE_VISIBILITY_UNSPECIFIED, /* unspecified (nil) */
    RBS_ATTRIBUTE_VISIBILITY_PUBLIC,      /* public (:public) */
    RBS_ATTRIBUTE_VISIBILITY_PRIVATE,     /* private (:private) */
};

The values are translated to Ruby symbols (and nil).

@soutaro soutaro added this to the RBS 4.0 milestone Jan 23, 2026
#[must_use]
pub fn start(&self) -> i32 {
unsafe { (*self.pointer).rg.start.byte_pos }
self.range.start
Copy link
Member Author

@soutaro soutaro Jan 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • This should be fixed. The rbs_location_range has character index, but should have byte position here.


use rbs_encoding_type_t::RBS_ENCODING_UTF_8;

static INIT: Once = Once::new();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can remove this now since we no longer use it.

rbs_constant_pool_init(RBS_GLOBAL_CONSTANT_POOL, 26);
});
}
fn setup() {}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can also remove this as it's empty.

}
}

pub struct RBSLocation {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since rbs_location_t has been renamed to rbs_location_range_t, we can rename this to RBSLocationRange. It still needs to hold a pointer.

Comment on lines +509 to +514
"alias_kind"
| "attribute_kind"
| "attribute_visibility"
| "method_definition_kind"
| "method_definition_visibility"
| "type_param_variance" => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should create Rust enums and return the enums for these instead of u32. This would allow us to leverage Rust's type system and provide better ergonomics.

}
}

pub struct RBSLocationListIter {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can remove this struct since we now use RBSLocationRangeList

writeln!(file, " #[must_use]")?;
writeln!(
file,
" pub fn {}(&self) -> Option<rbs_location_range> {{",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should return the Rust RBSLocationRange struct.

writeln!(file, " #[must_use]")?;
writeln!(
file,
" pub fn {}(&self) -> rbs_location_range {{",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should also return the Rust RBSLocationRange struct.

writeln!(file)?;
}
"rbs_location_range_list" => {
write_node_field_accessor(&mut file, field, "RBSLocationRangeList")?;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The write_node_field_accessor helper function is meant to add accessors to other nodes that need the parser. Location doesn't need it, so we need to treat it differently.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants