Skip to content

Commit e20d336

Browse files
committed
Add type arguments support to singleton types
1 parent 66c2c91 commit e20d336

File tree

11 files changed

+265
-71
lines changed

11 files changed

+265
-71
lines changed

config.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,8 @@ nodes:
363363
fields:
364364
- name: name
365365
c_type: rbs_type_name
366+
- name: args
367+
c_type: rbs_node_list
366368
- name: RBS::Types::Function
367369
expose_location: false
368370
fields:

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: 38 additions & 34 deletions
Large diffs are not rendered by default.

include/rbs/ast.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -555,6 +555,7 @@ typedef struct rbs_types_class_singleton {
555555
rbs_node_t base;
556556

557557
struct rbs_type_name *name;
558+
struct rbs_node_list *args;
558559
} rbs_types_class_singleton_t;
559560

560561
typedef struct rbs_types_function {
@@ -729,7 +730,7 @@ rbs_types_bases_top_t *rbs_types_bases_top_new(rbs_allocator_t *allocator, rbs_l
729730
rbs_types_bases_void_t *rbs_types_bases_void_new(rbs_allocator_t *allocator, rbs_location_t *location);
730731
rbs_types_block_t *rbs_types_block_new(rbs_allocator_t *allocator, rbs_location_t *location, rbs_node_t *type, bool required, rbs_node_t *self_type);
731732
rbs_types_class_instance_t *rbs_types_class_instance_new(rbs_allocator_t *allocator, rbs_location_t *location, rbs_type_name_t *name, rbs_node_list_t *args);
732-
rbs_types_class_singleton_t *rbs_types_class_singleton_new(rbs_allocator_t *allocator, rbs_location_t *location, rbs_type_name_t *name);
733+
rbs_types_class_singleton_t *rbs_types_class_singleton_new(rbs_allocator_t *allocator, rbs_location_t *location, rbs_type_name_t *name, rbs_node_list_t *args);
733734
rbs_types_function_t *rbs_types_function_new(rbs_allocator_t *allocator, rbs_location_t *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);
734735
rbs_types_function_param_t *rbs_types_function_param_new(rbs_allocator_t *allocator, rbs_location_t *location, rbs_node_t *type, rbs_ast_symbol_t *name);
735736
rbs_types_interface_t *rbs_types_interface_new(rbs_allocator_t *allocator, rbs_location_t *location, rbs_type_name_t *name, rbs_node_list_t *args);

lib/rbs/types.rb

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -202,38 +202,70 @@ def with_nonreturn_void?
202202
class ClassSingleton
203203
attr_reader :name
204204
attr_reader :location
205+
attr_reader :args
205206

206-
def initialize(name:, location:)
207+
def initialize(name:, location:, args: [])
207208
@name = name
208209
@location = location
210+
@args = args
209211
end
210212

211213
def ==(other)
212-
other.is_a?(ClassSingleton) && other.name == name
214+
other.is_a?(ClassSingleton) && other.name == name && other.args == args
213215
end
214216

215217
alias eql? ==
216218

217219
def hash
218-
self.class.hash ^ name.hash
220+
self.class.hash ^ name.hash ^ args.hash
219221
end
220222

221223
include NoFreeVariables
222-
include NoSubst
224+
225+
def sub(s)
226+
return self if s.empty?
227+
228+
self.class.new(name: name,
229+
args: args.map {|ty| ty.sub(s) },
230+
location: location)
231+
end
223232

224233
def to_json(state = _ = nil)
225-
{ class: :class_singleton, name: name, location: location }.to_json(state)
234+
{ class: :class_singleton, name: name, args: args, location: location }.to_json(state)
226235
end
227236

228237
def to_s(level = 0)
229-
"singleton(#{name})"
238+
if args.empty?
239+
"singleton(#{name})"
240+
else
241+
"singleton(#{name})[#{args.join(", ")}]"
242+
end
230243
end
231244

232-
include EmptyEachType
245+
def each_type(&block)
246+
if block
247+
args.each(&block)
248+
else
249+
enum_for :each_type
250+
end
251+
end
233252

234-
def map_type_name(&)
253+
def map_type(&block)
254+
if block
255+
ClassSingleton.new(
256+
name: name,
257+
args: args.map {|type| yield type },
258+
location: location
259+
)
260+
else
261+
enum_for :map_type
262+
end
263+
end
264+
265+
def map_type_name(&block)
235266
ClassSingleton.new(
236267
name: yield(name, location, self),
268+
args: args.map {|type| type.map_type_name(&block) },
237269
location: location
238270
)
239271
end

lib/rbs/unit_test/type_assertions.rb

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

233233
case type
234-
when Types::ClassInstance
235-
subst = RBS::Substitution.build(definition.type_params, type.args)
236-
definition.methods[method].defs.map do |type_def|
237-
type_def.update(
238-
type: type_def.type.sub(subst)
239-
)
234+
when Types::ClassInstance, Types::ClassSingleton
235+
if type.is_a?(Types::ClassSingleton) && type.args.empty?
236+
definition.methods[method].defs
237+
else
238+
subst = RBS::Substitution.build(definition.type_params, type.args)
239+
definition.methods[method].defs.map do |type_def|
240+
type_def.update(
241+
type: type_def.type.sub(subst)
242+
)
243+
end
240244
end
241-
when Types::ClassSingleton
242-
definition.methods[method].defs
243245
else
244246
raise
245247
end

src/ast.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1138,7 +1138,7 @@ rbs_types_class_instance_t *rbs_types_class_instance_new(rbs_allocator_t *alloca
11381138
return instance;
11391139
}
11401140
#line 156 "prism/templates/src/ast.c.erb"
1141-
rbs_types_class_singleton_t *rbs_types_class_singleton_new(rbs_allocator_t *allocator, rbs_location_t *location, rbs_type_name_t *name) {
1141+
rbs_types_class_singleton_t *rbs_types_class_singleton_new(rbs_allocator_t *allocator, rbs_location_t *location, rbs_type_name_t *name, rbs_node_list_t *args) {
11421142
rbs_types_class_singleton_t *instance = rbs_allocator_alloc(allocator, rbs_types_class_singleton_t);
11431143

11441144
*instance = (rbs_types_class_singleton_t) {
@@ -1147,6 +1147,7 @@ rbs_types_class_singleton_t *rbs_types_class_singleton_new(rbs_allocator_t *allo
11471147
.location = location,
11481148
},
11491149
.name = name,
1150+
.args = args,
11501151
};
11511152

11521153
return instance;

src/parser.c

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ static bool parse_type_name(rbs_parser_t *parser, TypeNameKind kind, rbs_range_t
208208
goto error_handling;
209209
}
210210

211-
success: {
211+
success : {
212212
if (rg) {
213213
rg->end = parser->current_token.range.end;
214214
}
@@ -220,7 +220,7 @@ success: {
220220
return true;
221221
}
222222

223-
error_handling: {
223+
error_handling : {
224224
const char *ids = NULL;
225225
if (kind & ALIAS_NAME) {
226226
ids = "alias name";
@@ -1019,7 +1019,7 @@ static bool parse_instance_type(rbs_parser_t *parser, bool parse_alias, rbs_node
10191019
}
10201020

10211021
/*
1022-
singleton_type ::= {`singleton`} `(` type_name <`)`>
1022+
singleton_type ::= {`singleton`} `(` type_name <`)`> type_args?
10231023
*/
10241024
NODISCARD
10251025
static bool parse_singleton_type(rbs_parser_t *parser, rbs_types_class_singleton_t **singleton) {
@@ -1035,13 +1035,29 @@ static bool parse_singleton_type(rbs_parser_t *parser, rbs_types_class_singleton
10351035
CHECK_PARSE(parse_type_name(parser, CLASS_NAME, &name_range, &type_name));
10361036

10371037
ADVANCE_ASSERT(parser, pRPAREN);
1038-
type_range.end = parser->current_token.range.end;
1038+
1039+
rbs_node_list_t *types = rbs_node_list_new(ALLOCATOR());
1040+
1041+
rbs_range_t args_range;
1042+
if (parser->next_token.type == pLBRACKET) {
1043+
rbs_parser_advance(parser);
1044+
args_range.start = parser->current_token.range.start;
1045+
CHECK_PARSE(parse_type_list(parser, pRBRACKET, types));
1046+
ADVANCE_ASSERT(parser, pRBRACKET);
1047+
args_range.end = parser->current_token.range.end;
1048+
type_range.end = parser->current_token.range.end;
1049+
} else {
1050+
args_range = NULL_RANGE;
1051+
type_range.end = parser->current_token.range.end;
1052+
}
10391053

10401054
rbs_location_t *loc = rbs_location_new(ALLOCATOR(), type_range);
1041-
rbs_loc_alloc_children(ALLOCATOR(), loc, 1);
1055+
rbs_loc_alloc_children(ALLOCATOR(), loc, 2);
10421056
rbs_loc_add_required_child(loc, INTERN("name"), name_range);
1057+
rbs_loc_add_optional_child(loc, INTERN("args"), args_range);
1058+
1059+
*singleton = rbs_types_class_singleton_new(ALLOCATOR(), loc, type_name, types);
10431060

1044-
*singleton = rbs_types_class_singleton_new(ALLOCATOR(), loc, type_name);
10451061
return true;
10461062
}
10471063

test/rbs/singleton_type_test.rb

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
require "test_helper"
2+
3+
class RBS::SingletonTypeTest < Test::Unit::TestCase
4+
include TestHelper
5+
6+
Parser = RBS::Parser
7+
Buffer = RBS::Buffer
8+
Types = RBS::Types
9+
TypeName = RBS::TypeName
10+
Namespace = RBS::Namespace
11+
12+
def test_singleton_type_with_arguments
13+
Parser.parse_type("singleton(Array)[String]").yield_self do |type|
14+
assert_instance_of Types::ClassSingleton, type
15+
assert_equal TypeName.new(namespace: Namespace.empty, name: :Array), type.name
16+
assert_equal 1, type.args.size
17+
assert_instance_of Types::ClassInstance, type.args[0]
18+
assert_equal TypeName.new(namespace: Namespace.empty, name: :String), type.args[0].name
19+
assert_equal "singleton(Array)[String]", type.location.source
20+
end
21+
22+
Parser.parse_type("singleton(Hash)[Symbol, Integer]").yield_self do |type|
23+
assert_instance_of Types::ClassSingleton, type
24+
assert_equal TypeName.new(namespace: Namespace.empty, name: :Hash), type.name
25+
assert_equal 2, type.args.size
26+
assert_instance_of Types::ClassInstance, type.args[0]
27+
assert_instance_of Types::ClassInstance, type.args[1]
28+
assert_equal TypeName.new(namespace: Namespace.empty, name: :Symbol), type.args[0].name
29+
assert_equal TypeName.new(namespace: Namespace.empty, name: :Integer), type.args[1].name
30+
assert_equal "singleton(Hash)[Symbol, Integer]", type.location.source
31+
end
32+
33+
Parser.parse_type("singleton(::Foo::Bar)[Baz]").yield_self do |type|
34+
assert_instance_of Types::ClassSingleton, type
35+
assert_equal TypeName.new(namespace: Namespace.parse("::Foo"), name: :Bar), type.name
36+
assert_equal 1, type.args.size
37+
assert_instance_of Types::ClassInstance, type.args[0]
38+
assert_equal TypeName.new(namespace: Namespace.empty, name: :Baz), type.args[0].name
39+
assert_equal "singleton(::Foo::Bar)[Baz]", type.location.source
40+
end
41+
end
42+
43+
def test_singleton_type_equality
44+
type1 = parse_type("singleton(Array)[String]")
45+
type2 = parse_type("singleton(Array)[String]")
46+
type3 = parse_type("singleton(Array)[Integer]")
47+
type4 = parse_type("singleton(Hash)[String]")
48+
49+
assert_equal type1, type2
50+
refute_equal type1, type3
51+
refute_equal type1, type4
52+
end
53+
54+
def test_singleton_type_hash
55+
type1 = parse_type("singleton(Array)[String]")
56+
type2 = parse_type("singleton(Array)[String]")
57+
type3 = parse_type("singleton(Array)[Integer]")
58+
59+
assert_equal type1.hash, type2.hash
60+
refute_equal type1.hash, type3.hash
61+
end
62+
63+
def test_singleton_type_sub
64+
type = parse_type("singleton(Array)[T]", variables: [:T])
65+
subst = RBS::Substitution.build([:T], [parse_type("String")])
66+
67+
result = type.sub(subst)
68+
assert_instance_of Types::ClassSingleton, result
69+
assert_equal TypeName.new(namespace: Namespace.empty, name: :Array), result.name
70+
assert_equal 1, result.args.size
71+
assert_instance_of Types::ClassInstance, result.args[0]
72+
assert_equal TypeName.new(namespace: Namespace.empty, name: :String), result.args[0].name
73+
end
74+
75+
def test_singleton_type_map_type_name
76+
type = parse_type("singleton(Array)[String]")
77+
78+
mapped = type.map_type_name do |name, _, _|
79+
TypeName.new(namespace: Namespace.empty, name: :List)
80+
end
81+
82+
assert_instance_of Types::ClassSingleton, mapped
83+
assert_equal TypeName.new(namespace: Namespace.empty, name: :List), mapped.name
84+
assert_equal 1, mapped.args.size
85+
assert_instance_of Types::ClassInstance, mapped.args[0]
86+
assert_equal TypeName.new(namespace: Namespace.empty, name: :List), mapped.args[0].name
87+
end
88+
89+
def test_singleton_type_each_type
90+
type = parse_type("singleton(Array)[String, Integer]")
91+
92+
types = []
93+
type.each_type { |t| types << t }
94+
95+
assert_equal 2, types.size
96+
assert_instance_of Types::ClassInstance, types[0]
97+
assert_instance_of Types::ClassInstance, types[1]
98+
assert_equal TypeName.new(namespace: Namespace.empty, name: :String), types[0].name
99+
assert_equal TypeName.new(namespace: Namespace.empty, name: :Integer), types[1].name
100+
end
101+
102+
def test_singleton_type_map_type
103+
type = parse_type("singleton(Array)[String, Integer]")
104+
105+
mapped = type.map_type do |t|
106+
if t.is_a?(Types::ClassInstance) && t.name.name == :String
107+
parse_type("Symbol")
108+
else
109+
t
110+
end
111+
end
112+
113+
assert_instance_of Types::ClassSingleton, mapped
114+
assert_equal TypeName.new(namespace: Namespace.empty, name: :Array), mapped.name
115+
assert_equal 2, mapped.args.size
116+
assert_instance_of Types::ClassInstance, mapped.args[0]
117+
assert_instance_of Types::ClassInstance, mapped.args[1]
118+
assert_equal TypeName.new(namespace: Namespace.empty, name: :Symbol), mapped.args[0].name
119+
assert_equal TypeName.new(namespace: Namespace.empty, name: :Integer), mapped.args[1].name
120+
end
121+
end

test/rbs/type_parsing_test.rb

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,7 @@ def test_class_singleton
257257
assert_instance_of Types::ClassSingleton, type
258258

259259
assert_equal TypeName.new(namespace: Namespace.empty, name: :Object), type.name
260+
assert_equal [], type.args
260261

261262
assert_equal "singleton(Object)", type.location.source
262263
end
@@ -265,10 +266,21 @@ def test_class_singleton
265266
assert_instance_of Types::ClassSingleton, type
266267

267268
assert_equal TypeName.new(namespace: Namespace.root, name: :Object), type.name
268-
269+
assert_equal [], type.args
269270
assert_equal "singleton(::Object)", type.location.source
270271
end
271272

273+
Parser.parse_type("singleton(Array)[String]").yield_self do |type|
274+
assert_instance_of Types::ClassSingleton, type
275+
276+
assert_equal TypeName.new(namespace: Namespace.empty, name: :Array), type.name
277+
assert_equal 1, type.args.size
278+
assert_instance_of Types::ClassInstance, type.args[0]
279+
assert_equal TypeName.new(namespace: Namespace.empty, name: :String), type.args[0].name
280+
281+
assert_equal "singleton(Array)[String]", type.location.source
282+
end
283+
272284
assert_raises RBS::ParsingError do
273285
Parser.parse_type("singleton(foo)")
274286
end

0 commit comments

Comments
 (0)