Skip to content

Commit 0f845be

Browse files
allcresoutaro
authored andcommitted
Add type arguments support to singleton types
Previously, singleton type arguments could not be supported by RBS. This adds support for them, enabling syntax like `singleton(Array)[String, Integer]`.
1 parent c3337ad commit 0f845be

12 files changed

Lines changed: 230 additions & 86 deletions

File tree

config.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -573,8 +573,11 @@ nodes:
573573
fields:
574574
- name: name
575575
c_type: rbs_type_name
576+
- name: args
577+
c_type: rbs_node_list
576578
locations:
577579
- required: name
580+
- optional: args
578581
- name: RBS::Types::Function
579582
rust_name: FunctionTypeNode
580583
expose_location: false

docs/syntax.md

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,17 @@
33
## Types
44

55
```markdown
6-
_type_ ::= _class-name_ _type-arguments_ (Class instance type)
7-
| _interface-name_ _type-arguments_ (Interface type)
8-
| _alias-name_ _type-arguments_ (Alias type)
9-
| `singleton(` _class-name_ `)` (Class singleton type)
10-
| _literal_ (Literal type)
11-
| _type_ `|` _type_ (Union type)
12-
| _type_ `&` _type_ (Intersection type)
13-
| _type_ `?` (Optional type)
14-
| `{` _record-name_ `:` _type_ `,` etc. `}` (Record type)
15-
| `[]` | `[` _type_ `,` etc. `]` (Tuples)
16-
| _type-variable_ (Type variables)
6+
_type_ ::= _class-name_ _type-arguments_ (Class instance type)
7+
| _interface-name_ _type-arguments_ (Interface type)
8+
| _alias-name_ _type-arguments_ (Alias type)
9+
| `singleton(` _class-name_ `)` _type-arguments_ (Class singleton type)
10+
| _literal_ (Literal type)
11+
| _type_ `|` _type_ (Union type)
12+
| _type_ `&` _type_ (Intersection type)
13+
| _type_ `?` (Optional type)
14+
| `{` _record-name_ `:` _type_ `,` etc. `}` (Record type)
15+
| `[]` | `[` _type_ `,` etc. `]` (Tuples)
16+
| _type-variable_ (Type variables)
1717
| `self`
1818
| `instance`
1919
| `class`
@@ -85,7 +85,8 @@ Class singleton type denotes _the type of a singleton object of a class_.
8585

8686
```rbs
8787
singleton(String)
88-
singleton(::Hash) # Class singleton type cannot be parametrized.
88+
singleton(::Hash) # Class singleton type
89+
singleton(Array)[String] # Class singleton type with type application
8990
```
9091

9192
### Literal type

ext/rbs_extension/ast_translation.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1229,10 +1229,12 @@ VALUE rbs_struct_to_ruby_value(rbs_translation_context_t ctx, rbs_node_t *instan
12291229
VALUE h = rb_hash_new();
12301230
VALUE location = rbs_location_range_to_ruby_location(ctx, node->base.location);
12311231
rbs_loc *loc = rbs_check_location(location);
1232-
rbs_loc_legacy_alloc_children(loc, 1);
1232+
rbs_loc_legacy_alloc_children(loc, 2);
12331233
rbs_loc_legacy_add_required_child(loc, rb_intern("name"), (rbs_loc_range) { .start = node->name_range.start_char, .end = node->name_range.end_char });
1234+
rbs_loc_legacy_add_optional_child(loc, rb_intern("args"), (rbs_loc_range) { .start = node->args_range.start_char, .end = node->args_range.end_char });
12341235
rb_hash_aset(h, ID2SYM(rb_intern("location")), location);
12351236
rb_hash_aset(h, ID2SYM(rb_intern("name")), rbs_struct_to_ruby_value(ctx, (rbs_node_t *) node->name)); // rbs_type_name
1237+
rb_hash_aset(h, ID2SYM(rb_intern("args")), rbs_node_list_to_ruby_array(ctx, node->args));
12361238

12371239
return CLASS_NEW_INSTANCE(
12381240
RBS_Types_ClassSingleton,

include/rbs/ast.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -772,8 +772,10 @@ typedef struct rbs_types_class_singleton {
772772
rbs_node_t base;
773773

774774
struct rbs_type_name *name;
775+
struct rbs_node_list *args;
775776

776777
rbs_location_range name_range; /* Required */
778+
rbs_location_range args_range; /* Optional */
777779
} rbs_types_class_singleton_t;
778780

779781
typedef struct rbs_types_function {
@@ -947,7 +949,7 @@ rbs_types_bases_top_t *rbs_types_bases_top_new(rbs_allocator_t *allocator, rbs_l
947949
rbs_types_bases_void_t *rbs_types_bases_void_new(rbs_allocator_t *allocator, rbs_location_range location);
948950
rbs_types_block_t *rbs_types_block_new(rbs_allocator_t *allocator, rbs_location_range location, rbs_node_t *type, bool required, rbs_node_t *self_type);
949951
rbs_types_class_instance_t *rbs_types_class_instance_new(rbs_allocator_t *allocator, rbs_location_range location, rbs_type_name_t *name, rbs_node_list_t *args, rbs_location_range name_range);
950-
rbs_types_class_singleton_t *rbs_types_class_singleton_new(rbs_allocator_t *allocator, rbs_location_range location, rbs_type_name_t *name, rbs_location_range name_range);
952+
rbs_types_class_singleton_t *rbs_types_class_singleton_new(rbs_allocator_t *allocator, rbs_location_range location, rbs_type_name_t *name, rbs_node_list_t *args, rbs_location_range name_range);
951953
rbs_types_function_t *rbs_types_function_new(rbs_allocator_t *allocator, rbs_location_range location, rbs_node_list_t *required_positionals, rbs_node_list_t *optional_positionals, rbs_node_t *rest_positionals, rbs_node_list_t *trailing_positionals, rbs_hash_t *required_keywords, rbs_hash_t *optional_keywords, rbs_node_t *rest_keywords, rbs_node_t *return_type);
952954
rbs_types_function_param_t *rbs_types_function_param_new(rbs_allocator_t *allocator, rbs_location_range location, rbs_node_t *type, rbs_ast_symbol_t *name);
953955
rbs_types_interface_t *rbs_types_interface_new(rbs_allocator_t *allocator, rbs_location_range location, rbs_type_name_t *name, rbs_node_list_t *args, rbs_location_range name_range);

lib/rbs/types.rb

Lines changed: 62 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -199,58 +199,6 @@ def with_nonreturn_void?
199199
end
200200
end
201201

202-
class ClassSingleton
203-
attr_reader :name
204-
attr_reader :location
205-
206-
def initialize(name:, location:)
207-
@name = name
208-
@location = location
209-
end
210-
211-
def ==(other)
212-
other.is_a?(ClassSingleton) && other.name == name
213-
end
214-
215-
alias eql? ==
216-
217-
def hash
218-
self.class.hash ^ name.hash
219-
end
220-
221-
include NoFreeVariables
222-
include NoSubst
223-
224-
def to_json(state = nil)
225-
{ class: :class_singleton, name: name, location: location }.to_json(state)
226-
end
227-
228-
def to_s(level = 0)
229-
"singleton(#{name})"
230-
end
231-
232-
include EmptyEachType
233-
234-
def map_type_name(&)
235-
ClassSingleton.new(
236-
name: yield(name, location, self),
237-
location: location
238-
)
239-
end
240-
241-
def has_self_type?
242-
false
243-
end
244-
245-
def has_classish_type?
246-
false
247-
end
248-
249-
def with_nonreturn_void?
250-
false
251-
end
252-
end
253-
254202
module Application
255203
attr_reader :name
256204
attr_reader :args
@@ -309,6 +257,68 @@ def with_nonreturn_void?
309257
end
310258
end
311259

260+
class ClassSingleton
261+
attr_reader :location
262+
263+
include Application
264+
265+
def initialize(name:, location:, args: [])
266+
@name = name
267+
@location = location
268+
@args = args
269+
end
270+
271+
def ==(other)
272+
other.is_a?(ClassSingleton) && other.name == name && other.args == args
273+
end
274+
275+
alias eql? ==
276+
277+
def hash
278+
self.class.hash ^ name.hash ^ args.hash
279+
end
280+
281+
def sub(s)
282+
return self if s.empty?
283+
284+
self.class.new(name: name,
285+
args: args.map {|ty| ty.sub(s) },
286+
location: location)
287+
end
288+
289+
def to_json(state = _ = nil)
290+
{ class: :class_singleton, name: name, args: args, location: location }.to_json(state)
291+
end
292+
293+
def to_s(level = 0)
294+
if args.empty?
295+
"singleton(#{name})"
296+
else
297+
"singleton(#{name})[#{args.join(", ")}]"
298+
end
299+
end
300+
301+
def map_type_name(&block)
302+
ClassSingleton.new(
303+
name: yield(name, location, self),
304+
args: args.map {|type| type.map_type_name(&block) },
305+
location: location
306+
)
307+
end
308+
309+
def map_type(&block)
310+
if block
311+
ClassSingleton.new(
312+
name: name,
313+
args: args.map {|type| yield type },
314+
location: location
315+
)
316+
else
317+
enum_for :map_type
318+
end
319+
end
320+
end
321+
312322
class Interface
313323
attr_reader :location
314324

lib/rbs/unit_test/type_assertions.rb

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -256,15 +256,17 @@ def method_defs(method)
256256
type, definition = target
257257

258258
case type
259-
when Types::ClassInstance
260-
subst = RBS::Substitution.build(definition.type_params, type.args)
261-
definition.methods[method].defs.map do |type_def|
262-
type_def.update(
263-
type: type_def.type.sub(subst)
264-
)
259+
when Types::ClassInstance, Types::ClassSingleton
260+
if type.is_a?(Types::ClassSingleton) && type.args.empty?
261+
definition.methods[method].defs
262+
else
263+
subst = RBS::Substitution.build(definition.type_params, type.args)
264+
definition.methods[method].defs.map do |type_def|
265+
type_def.update(
266+
type: type_def.type.sub(subst)
267+
)
268+
end
265269
end
266-
when Types::ClassSingleton
267-
definition.methods[method].defs
268270
else
269271
raise
270272
end

sig/types.rbs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -172,24 +172,23 @@ module RBS
172172
class ClassSingleton
173173
# singleton(::Foo)
174174
# ^^^^^ => name
175-
type loc = Location[:name, bot]
176-
177-
def initialize: (name: TypeName, location: loc?) -> void
175+
type loc = Location[:name, :args]
178176

179-
attr_reader name: TypeName
177+
def initialize: (name: TypeName, location: loc?, ?args: Array[t]) -> void
180178

181179
attr_reader location: loc?
182180

183181
include _TypeBase
184-
include NoFreeVariables
185-
include NoSubst
186-
include EmptyEachType
182+
include Application
187183

188184
def ==: (untyped other) -> bool
189185

190186
alias eql? ==
191187

192188
def hash: () -> Integer
189+
190+
def map_type: () { (t) -> t } -> ClassSingleton
191+
| () -> Enumerator[t, ClassSingleton]
193192
end
194193

195194
module Application

src/ast.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1303,7 +1303,7 @@ rbs_types_class_instance_t *rbs_types_class_instance_new(rbs_allocator_t *alloca
13031303
return instance;
13041304
}
13051305
#line 140 "prism/templates/src/ast.c.erb"
1306-
rbs_types_class_singleton_t *rbs_types_class_singleton_new(rbs_allocator_t *allocator, rbs_location_range location, rbs_type_name_t *name, rbs_location_range name_range) {
1306+
rbs_types_class_singleton_t *rbs_types_class_singleton_new(rbs_allocator_t *allocator, rbs_location_range location, rbs_type_name_t *name, rbs_node_list_t *args, rbs_location_range name_range) {
13071307
rbs_types_class_singleton_t *instance = rbs_allocator_alloc(allocator, rbs_types_class_singleton_t);
13081308

13091309
*instance = (rbs_types_class_singleton_t) {
@@ -1312,7 +1312,9 @@ rbs_types_class_singleton_t *rbs_types_class_singleton_new(rbs_allocator_t *allo
13121312
.location = location,
13131313
},
13141314
.name = name,
1315+
.args = args,
13151316
.name_range = name_range,
1317+
.args_range = RBS_LOCATION_NULL_RANGE,
13161318
};
13171319

13181320
return instance;

src/parser.c

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1042,10 +1042,10 @@ static bool parse_instance_type(rbs_parser_t *parser, bool parse_alias, rbs_node
10421042
}
10431043

10441044
/*
1045-
singleton_type ::= {`singleton`} `(` type_name <`)`>
1045+
singleton_type ::= {`singleton`} `(` type_name <`)`> type_args?
10461046
*/
10471047
NODISCARD
1048-
static bool parse_singleton_type(rbs_parser_t *parser, rbs_types_class_singleton_t **singleton) {
1048+
static bool parse_singleton_type(rbs_parser_t *parser, rbs_types_class_singleton_t **singleton, bool self_allowed, bool classish_allowed) {
10491049
ASSERT_TOKEN(parser, kSINGLETON);
10501050

10511051
rbs_range_t type_range;
@@ -1058,9 +1058,26 @@ static bool parse_singleton_type(rbs_parser_t *parser, rbs_types_class_singleton
10581058
CHECK_PARSE(parse_type_name(parser, CLASS_NAME, &name_range, &type_name));
10591059

10601060
ADVANCE_ASSERT(parser, pRPAREN);
1061+
1062+
rbs_node_list_t *types = rbs_node_list_new(ALLOCATOR());
1063+
1064+
rbs_location_range args_range = RBS_LOCATION_NULL_RANGE;
1065+
if (parser->next_token.type == pLBRACKET) {
1066+
rbs_parser_advance(parser);
1067+
args_range.start_byte = parser->current_token.range.start.byte_pos;
1068+
args_range.start_char = parser->current_token.range.start.char_pos;
1069+
CHECK_PARSE(parse_type_list(parser, pRBRACKET, types, true, self_allowed, classish_allowed));
1070+
ADVANCE_ASSERT(parser, pRBRACKET);
1071+
args_range.end_byte = parser->current_token.range.end.byte_pos;
1072+
args_range.end_char = parser->current_token.range.end.char_pos;
1073+
}
1074+
10611075
type_range.end = parser->current_token.range.end;
1076+
rbs_location_range loc = RBS_RANGE_LEX2AST(type_range);
1077+
1078+
*singleton = rbs_types_class_singleton_new(ALLOCATOR(), loc, type_name, types, RBS_RANGE_LEX2AST(name_range));
1079+
(*singleton)->args_range = args_range;
10621080

1063-
*singleton = rbs_types_class_singleton_new(ALLOCATOR(), RBS_RANGE_LEX2AST(type_range), type_name, RBS_RANGE_LEX2AST(name_range));
10641081
return true;
10651082
}
10661083

@@ -1242,7 +1259,7 @@ static bool parse_simple(rbs_parser_t *parser, rbs_node_t **type, bool void_allo
12421259
}
12431260
case kSINGLETON: {
12441261
rbs_types_class_singleton_t *singleton = NULL;
1245-
CHECK_PARSE(parse_singleton_type(parser, &singleton));
1262+
CHECK_PARSE(parse_singleton_type(parser, &singleton, self_allowed, classish_allowed));
12461263
*type = (rbs_node_t *) singleton;
12471264
return true;
12481265
}

0 commit comments

Comments
 (0)