spinel_rbs_extract β supported RBS subset
spinel_rbs_extract (tools/spinel_rbs_extract.c) reads *.rbs files
and emits a line-oriented seed file. When spinel --rbs DIR is given,
the driver runs the extractor over DIR, hands the seed file to the
compiler through the SPINEL_RBS_SEED environment variable, and the
analyzer applies it before the inference fixpoint. The seeded types are
advisory: inference still runs on top, and the analyzer widens on
observed contradiction, so the seed file is never load-bearing β a wrong
or unrepresentable seed is at worst a no-op.
Anything outside the subset below is silently dropped: the seed line isn't emitted, and the analyzer falls back to its normal inference for that method or ivar.
Supported
Type vocabulary
| RBS | Spinel tag |
|---|---|
Integer | int |
Float | float |
String | string |
Symbol | symbol |
TrueClass, FalseClass | bool |
NilClass, nil | nil |
bool | bool |
void (return only) | nil |
Foo, Foo::Bar (nominal) | obj_Foo, obj_Foo_Bar |
Array[Integer] | int_array |
Array[Float] | float_array |
Array[String] | str_array |
Array[Symbol] | sym_array |
Array[Foo] | obj_Foo_ptr_array |
Array[<other>] | poly_array |
Hash[String, Integer] | str_int_hash |
Hash[String, String] | str_str_hash |
Hash[String, <other>] | str_poly_hash |
Hash[Symbol, Integer] | sym_int_hash |
Hash[Symbol, String] | sym_str_hash |
Hash[Symbol, <other>] | sym_poly_hash |
T? | <T>? (recursive) |
T | nil / nil | T | <T>? |
Members emitted
def name: (...) -> Rβ instance method (meth)def self.name: (...) -> Rβ class method (cmeth)def self?.name: (...) -> Rβ emits bothmethandcmethattr_accessor,attr_reader,attr_writerβ emitsivar
Unqualified type resolution
Inside module Foo; class Bar, an unqualified reference like
def record: () -> Base is resolved to obj_Foo_Base (single-level
parent fallback). A wrong guess is a no-op because the analyzer
silently drops seeds for unknown types β no symbol table is required.
Covers the common sibling-in-module pattern; full lexical lookup is
not implemented.
Dropped (silently skipped)
Method signature shapes
- Multiple overloads β only the first overload is considered; if the first is itself out-of-subset the whole method is skipped.
- Optional positional params (
?String) - Rest positional params (
*args) - Trailing positional params
- Required keyword params (
name: T) - Optional keyword params (
?name: T) - Rest keyword params (
**rest) - Block params (
{ ... },?{ ... }) - Generic method params (
[X]) - Proc / untyped function types
A signature that touches any of the above is dropped wholesale rather than emitted partially.
Param-level β any one of these in any required positional kills the whole signature
- A type that doesn't reduce to a supported tag.
- A generic container that isn't
Array[T]orHash[K, V]. - A union that isn't
T | nil.
Type-level
self,instance,class,top,bot,untyped/any- Interfaces (
_Foo) - Intersections (
T & U) - Literal types (
:foo,42,"x") - Type aliases
- Type variables (generics with parameters)
- Records (
{ name: T, ... }) - Tuples (
[T, U]) - Proc types (
^(T) -> U) Hash[K, V]where K is notStringorSymbol
Members
include,extend,prependβ mixin ancestry not modeledpublic,privatealias@ivar/@@cvardeclarations (useattr_*for ivars)
Seed file format
Read and applied by apply_rbs_seeds in src/analyze.c (it both parses
the seed file and pins the named params / returns / ivars before the
inference fixpoint):
class <QualifiedName> # enter class scope; nested names use `_`
meth <name> <ret> <ptypes> # `-` means "leave alone"; ptypes is
cmeth <name> <ret> <ptypes> # comma-separated, or `-` for nullary
ivar <name> <type>
Lines whose first token isn't a keyword are treated as comments.
See experiments/rbs/box.seed for a worked example.
Follow-up
Tracked as out-of-scope for the initial spike (see matz/spinel#7 + OriPekelman/tep#6):
- Multi-overload resolution
- Generics with type variables
- Mixin ancestry from
include/extend - Return-type pinning where the body would otherwise cascade-widen