Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 11 additions & 5 deletions doc/syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -402,13 +402,13 @@ _decl_ ::= _class-decl_ # Class declaration
| _const-decl_ # Constant declaration
| _global-decl_ # Global declaration

_class-decl_ ::= `class` _class-name_ _type-parameters_ _members_ `end`
| `class` _class-name_ _type-parameters_ `<` _class-name_ _type-arguments_ _members_ `end`
_class-decl_ ::= `class` _class-name_ _module-type-parameters_ _members_ `end`
| `class` _class-name_ _module-type-parameters_ `<` _class-name_ _type-arguments_ _members_ `end`

_module-decl_ ::= `module` _module-name_ _type-parameters_ _members_ `end`
| `module` _module-name_ _type-parameters_ `:` _class-name_ _type-arguments_ _members_ `end`
_module-decl_ ::= `module` _module-name_ _module-type-parameters_ _members_ `end`
| `module` _module-name_ _module-type-parameters_ `:` _class-name_ _type-arguments_ _members_ `end`

_interface-decl_ ::= `interface` _interface-name_ _type-parameters_ _interface-members_ `end`
_interface-decl_ ::= `interface` _interface-name_ _module-type-parameters_ _interface-members_ `end`

_interface-members_ ::= _method-member_ # Method
| _include-member_ # Mixin (include)
Expand All @@ -424,6 +424,12 @@ _global-decl_ ::= _global-name_ `:` _type_

_const-name_ ::= _namespace_ /[A-Z]\w*/
_global-name_ ::= /$[a-zA-Z]\w+/ | ...

_module-type-parameters_ ::= # Empty
| `[` _module-type-parameter_ `,` ... `]`

_module-type-parameter_ ::= _variance_ _type-variable_
_variance_ ::= `out` | `in`
```

### Class declaration
Expand Down
53 changes: 53 additions & 0 deletions lib/ruby/signature/ast/declarations.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,59 @@ module Ruby
module Signature
module AST
module Declarations
class ModuleTypeParams
attr_reader :params

TypeParam = Struct.new(:name, :variance, :skip_validation, keyword_init: true)

def initialize()
@params = []
end

def add(param)
params << param
self
end

def ==(other)
other.is_a?(ModuleTypeParams) && other.params == params
end

def [](name)
params.find {|p| p.name == name }
end

def to_json(*a)
{
params: params
}.to_json(*a)
end

def each(&block)
params.each(&block)
end

def self.empty
new
end

def variance(name)
self[name].variance
end

def skip_validation?(name)
self[name].skip_validation
end

def empty?
params.empty?
end

def size
params.size
end
end

class Class
class Super
attr_reader :name
Expand Down
4 changes: 2 additions & 2 deletions lib/ruby/signature/cli.rb
Original file line number Diff line number Diff line change
Expand Up @@ -144,9 +144,9 @@ def run_ancestors(args, options)
if env.class?(type_name)
ancestor = case kind
when :instance
definition = env.find_class(type_name)
decl = env.find_class(type_name)
Definition::Ancestor::Instance.new(name: type_name,
args: Types::Variable.build(definition.type_params))
args: Types::Variable.build(decl.type_params.each.map(&:name)))
when :singleton
Definition::Ancestor::Singleton.new(name: type_name)
end
Expand Down
9 changes: 9 additions & 0 deletions lib/ruby/signature/definition.rb
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,15 @@ def type_params
@self_type.args.map(&:name)
end

def type_params_decl
case declaration
when AST::Declarations::Extension
nil
else
declaration.type_params
end
end

def each_type(&block)
if block_given?
methods.each_value do |method|
Expand Down
18 changes: 9 additions & 9 deletions lib/ruby/signature/definition_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,16 @@ def build_ancestors(self_ancestor, ancestors: [], building_ancestors: [], locati
case self_ancestor
when Definition::Ancestor::Instance
args = self_ancestor.args
params = decl.type_params
param_names = decl.type_params.each.map(&:name)

InvalidTypeApplicationError.check!(
type_name: self_ancestor.name,
args: args,
params: params,
params: decl.type_params,
location: location || decl.location
)

sub = Substitution.build(params, args)
sub = Substitution.build(param_names, args)

case decl
when AST::Declarations::Class
Expand Down Expand Up @@ -203,7 +203,7 @@ def build_instance(type_name)
try_cache type_name, cache: instance_cache do
decl = env.find_class(type_name)
self_ancestor = Definition::Ancestor::Instance.new(name: type_name,
args: Types::Variable.build(decl.type_params))
args: Types::Variable.build(decl.type_params.each.map(&:name)))
self_type = Types::ClassInstance.new(name: type_name, args: self_ancestor.args, location: nil)

case decl
Expand Down Expand Up @@ -323,7 +323,7 @@ def build_one_instance(type_name, extension_name: nil)
case decl
when AST::Declarations::Class, AST::Declarations::Module
self_type = Types::ClassInstance.new(name: type_name,
args: Types::Variable.build(decl.type_params),
args: Types::Variable.build(decl.type_params.each.map(&:name)),
location: nil)
ancestors = [Definition::Ancestor::Instance.new(name: type_name, args: self_type.args)]
when AST::Declarations::Extension
Expand Down Expand Up @@ -462,7 +462,7 @@ def build_one_instance(type_name, extension_name: nil)
InvalidTypeApplicationError.check!(
type_name: absolute_name,
args: absolute_args,
params: interface_definition.type_params,
params: interface_definition.type_params_decl,
location: member.location
)

Expand Down Expand Up @@ -584,7 +584,7 @@ def build_one_singleton(type_name, extension_name: nil)
InvalidTypeApplicationError.check!(
type_name: absolute_name,
args: absolute_args,
params: interface_definition.type_params,
params: interface_definition.type_params_decl,
location: member.location
)

Expand Down Expand Up @@ -705,7 +705,7 @@ def try_cache(type_name, cache:)
def build_interface(type_name, declaration)
self_type = Types::Interface.new(
name: type_name,
args: declaration.type_params.map {|x| Types::Variable.new(name: x, location: nil) },
args: declaration.type_params.each.map {|p| Types::Variable.new(name: p.name, location: nil) },
location: nil
)

Expand All @@ -728,7 +728,7 @@ def build_interface(type_name, declaration)
location: member.location
)

sub = Substitution.build(type_params, args)
sub = Substitution.build(type_params.each.map(&:name), args)
mixin.methods.each do |name, method|
definition.methods[name] = method.sub(sub)
end
Expand Down
2 changes: 1 addition & 1 deletion lib/ruby/signature/errors.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ def initialize(type_name:, args:, params:, location:)
@args = args
@params = params
@location = location
super "#{Location.to_string location}: #{type_name} expects parameters [#{params.join(", ")}], but given args [#{args.join(", ")}]"
super "#{Location.to_string location}: #{type_name} expects parameters [#{params.each.map(&:name).join(", ")}], but given args [#{args.join(", ")}]"
end

def self.check!(type_name:, args:, params:, location:)
Expand Down
56 changes: 47 additions & 9 deletions lib/ruby/signature/parser.y
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class Ruby::Signature::Parser
kINTERFACE kEND kINCLUDE kEXTEND kATTRREADER kATTRWRITER kATTRACCESSOR tOPERATOR tQUOTEDMETHOD
kPREPEND kEXTENSION kINCOMPATIBLE
type_TYPE type_SIGNATURE type_METHODTYPE tEOF
kOUT kIN kUNCHECKED

prechigh
nonassoc kQUESTION
Expand Down Expand Up @@ -78,13 +79,13 @@ rule
extension_name: tUIDENT | tLIDENT

class_decl:
annotations kCLASS start_new_scope class_name type_params super_class class_members kEND {
annotations kCLASS start_new_scope class_name module_type_params super_class class_members kEND {
reset_variable_scope

location = val[1].location + val[7].location
result = Declarations::Class.new(
name: val[3].value,
type_params: val[4]&.value || [],
type_params: val[4]&.value || Declarations::ModuleTypeParams.empty,
super_class: val[5],
members: val[6],
annotations: val[0],
Expand All @@ -105,13 +106,13 @@ rule
}

module_decl:
annotations kMODULE start_new_scope class_name type_params module_self_type class_members kEND {
annotations kMODULE start_new_scope class_name module_type_params module_self_type class_members kEND {
reset_variable_scope

location = val[1].location + val[7].location
result = Declarations::Module.new(
name: val[3].value,
type_params: val[4]&.value || [],
type_params: val[4]&.value || Declarations::ModuleTypeParams.empty,
self_type: val[5],
members: val[6],
annotations: val[0],
Expand All @@ -125,7 +126,7 @@ rule
location = val[1].location + val[6].location
result = Declarations::Module.new(
name: val[3].value,
type_params: [],
type_params: Declarations::ModuleTypeParams.empty,
self_type: val[4],
members: val[5],
annotations: val[0],
Expand Down Expand Up @@ -272,13 +273,13 @@ rule
}

interface_decl:
annotations kINTERFACE start_new_scope interface_name type_params interface_members kEND {
annotations kINTERFACE start_new_scope interface_name module_type_params interface_members kEND {
reset_variable_scope

location = val[1].location + val[6].location
result = Declarations::Interface.new(
name: val[3].value,
type_params: val[4]&.value || [],
type_params: val[4]&.value || Declarations::ModuleTypeParams.empty,
members: val[5],
annotations: val[0],
location: location,
Expand Down Expand Up @@ -477,7 +478,7 @@ rule

method_name:
tOPERATOR
| kAMP | kHAT | kSTAR | kLT | kEXCLAMATION | kSTAR2 | kBAR
| kAMP | kHAT | kSTAR | kLT | kEXCLAMATION | kSTAR2 | kBAR | kOUT | kIN
| method_name0
| method_name0 kQUESTION {
unless val[0].location.pred?(val[1].location)
Expand All @@ -504,6 +505,40 @@ rule
kCLASS | kVOID | kNIL | kANY | kTOP | kBOT | kINSTANCE | kBOOL | kSINGLETON
| kTYPE | kMODULE | kPRIVATE | kPUBLIC | kEND | kINCLUDE | kEXTEND | kPREPEND
| kATTRREADER | kATTRACCESSOR | kATTRWRITER | kDEF | kEXTENSION | kSELF | kINCOMPATIBLE
| kUNCHECKED

module_type_params:
{ result = nil }
| kLBRACKET module_type_params0 kRBRACKET {
val[1].each {|p| insert_bound_variable(p.name) }

result = LocatedValue.new(value: val[1], location: val[0].location + val[2].location)
}

module_type_params0:
module_type_param {
result = Declarations::ModuleTypeParams.new()
result.add(val[0])
}
| module_type_params0 kCOMMA module_type_param {
result = val[0].add(val[2])
}

module_type_param:
type_param_check type_param_variance tUIDENT {
result = Declarations::ModuleTypeParams::TypeParam.new(name: val[2].value.to_sym,
variance: val[1],
skip_validation: val[0])
}

type_param_variance:
{ result = :invariant }
| kOUT { result = :covariant }
| kIN { result = :contravariant }

type_param_check:
{ result = false }
| kUNCHECKED { result = true }

type_params:
{ result = nil }
Expand Down Expand Up @@ -1126,7 +1161,10 @@ KEYWORDS = {
"private" => :kPRIVATE,
"alias" => :kALIAS,
"extension" => :kEXTENSION,
"incompatible" => :kINCOMPATIBLE
"incompatible" => :kINCOMPATIBLE,
"unchecked" => :kUNCHECKED,
"out" => :kOUT,
"in" => :kIN,
}
KEYWORDS_RE = /#{Regexp.union(*KEYWORDS.keys)}\b/

Expand Down
20 changes: 16 additions & 4 deletions lib/ruby/signature/scaffold/rbi.rb
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def push_class(name, super_class, comment:)
modules.push AST::Declarations::Class.new(
name: nested_name(name),
super_class: super_class && AST::Declarations::Class::Super.new(name: const_to_name(super_class), args: []),
type_params: [],
type_params: AST::Declarations::ModuleTypeParams.empty,
members: [],
annotations: [],
location: nil,
Expand All @@ -66,7 +66,7 @@ def push_class(name, super_class, comment:)
def push_module(name, comment:)
modules.push AST::Declarations::Module.new(
name: nested_name(name),
type_params: [],
type_params: AST::Declarations::ModuleTypeParams.empty,
members: [],
annotations: [],
location: nil,
Expand Down Expand Up @@ -204,7 +204,19 @@ def process(node, outer: [], comments:)
node.type == :HASH &&
each_arg(node.children[0]).each_slice(2).any? {|a, _| a.type == :LIT && a.children[0] == :fixed }
}
current_module.type_params << node.children[0]
if (a0 = each_arg(send.children[1]).to_a[0])&.type == :LIT
variance = case a0.children[0]
when :out
:covariant
when :in
:contravariant
end
end

current_module.type_params.add(
AST::Declarations::ModuleTypeParams::TypeParam.new(name: node.children[0],
variance: variance || :invariant,
skip_validation: false))
end
else
name = node.children[0].yield_self do |n|
Expand Down Expand Up @@ -418,7 +430,7 @@ def type_of(type_node, variables:)
def type_of0(type_node, variables:)
case
when type_node.type == :CONST
if variables.include?(type_node.children[0])
if variables.each.include?(type_node.children[0])
Types::Variable.new(name: type_node.children[0], location: nil)
else
Types::ClassInstance.new(name: const_to_name(type_node), args: [], location: nil)
Expand Down
2 changes: 2 additions & 0 deletions lib/ruby/signature/types.rb
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,8 @@ def self.build(v)
new(name: v, location: nil)
when Array
v.map {|x| new(name: x, location: nil) }
else
raise
end
end

Expand Down
Loading