rdl 1.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (89) hide show
  1. checksums.yaml +7 -0
  2. data/lib/rails_types.rb +1 -0
  3. data/lib/rdl.rb +56 -0
  4. data/lib/rdl/config.rb +121 -0
  5. data/lib/rdl/contracts/and.rb +29 -0
  6. data/lib/rdl/contracts/contract.rb +7 -0
  7. data/lib/rdl/contracts/flat.rb +31 -0
  8. data/lib/rdl/contracts/or.rb +25 -0
  9. data/lib/rdl/contracts/proc.rb +24 -0
  10. data/lib/rdl/switch.rb +20 -0
  11. data/lib/rdl/types/annotated_arg.rb +41 -0
  12. data/lib/rdl/types/finitehash.rb +81 -0
  13. data/lib/rdl/types/generic.rb +100 -0
  14. data/lib/rdl/types/intersection.rb +66 -0
  15. data/lib/rdl/types/lexer.rex +39 -0
  16. data/lib/rdl/types/lexer.rex.rb +148 -0
  17. data/lib/rdl/types/method.rb +219 -0
  18. data/lib/rdl/types/nil.rb +50 -0
  19. data/lib/rdl/types/nominal.rb +80 -0
  20. data/lib/rdl/types/optional.rb +54 -0
  21. data/lib/rdl/types/parser.racc +150 -0
  22. data/lib/rdl/types/parser.tab.rb +654 -0
  23. data/lib/rdl/types/singleton.rb +62 -0
  24. data/lib/rdl/types/structural.rb +72 -0
  25. data/lib/rdl/types/top.rb +50 -0
  26. data/lib/rdl/types/tuple.rb +61 -0
  27. data/lib/rdl/types/type.rb +24 -0
  28. data/lib/rdl/types/type_inferencer.rb +73 -0
  29. data/lib/rdl/types/union.rb +74 -0
  30. data/lib/rdl/types/var.rb +51 -0
  31. data/lib/rdl/types/vararg.rb +54 -0
  32. data/lib/rdl/types/wild.rb +26 -0
  33. data/lib/rdl/util.rb +56 -0
  34. data/lib/rdl/wrap.rb +505 -0
  35. data/lib/rdl_types.rb +2 -0
  36. data/types/other/chronic.rb +5 -0
  37. data/types/other/paperclip_attachment.rb +7 -0
  38. data/types/other/securerandom.rb +4 -0
  39. data/types/rails-4.2.1/fixnum.rb +3 -0
  40. data/types/rails-4.2.1/string.rb +3 -0
  41. data/types/rails-tmp/action_dispatch.rb +406 -0
  42. data/types/rails-tmp/active_record.rb +406 -0
  43. data/types/rails-tmp/devise_contracts.rb +216 -0
  44. data/types/ruby-2.2.0/_aliases.rb +4 -0
  45. data/types/ruby-2.2.0/abbrev.rb +5 -0
  46. data/types/ruby-2.2.0/array.rb +137 -0
  47. data/types/ruby-2.2.0/base64.rb +10 -0
  48. data/types/ruby-2.2.0/basic_object.rb +13 -0
  49. data/types/ruby-2.2.0/benchmark.rb +11 -0
  50. data/types/ruby-2.2.0/bigdecimal.rb +15 -0
  51. data/types/ruby-2.2.0/bigmath.rb +12 -0
  52. data/types/ruby-2.2.0/class.rb +17 -0
  53. data/types/ruby-2.2.0/complex.rb +42 -0
  54. data/types/ruby-2.2.0/coverage.rb +6 -0
  55. data/types/ruby-2.2.0/csv.rb +5 -0
  56. data/types/ruby-2.2.0/date.rb +6 -0
  57. data/types/ruby-2.2.0/dir.rb +38 -0
  58. data/types/ruby-2.2.0/encoding.rb +23 -0
  59. data/types/ruby-2.2.0/enumerable.rb +98 -0
  60. data/types/ruby-2.2.0/enumerator.rb +26 -0
  61. data/types/ruby-2.2.0/exception.rb +15 -0
  62. data/types/ruby-2.2.0/file.rb +124 -0
  63. data/types/ruby-2.2.0/fileutils.rb +6 -0
  64. data/types/ruby-2.2.0/fixnum.rb +45 -0
  65. data/types/ruby-2.2.0/float.rb +54 -0
  66. data/types/ruby-2.2.0/gem.rb +245 -0
  67. data/types/ruby-2.2.0/hash.rb +72 -0
  68. data/types/ruby-2.2.0/integer.rb +31 -0
  69. data/types/ruby-2.2.0/io.rb +103 -0
  70. data/types/ruby-2.2.0/kernel.rb +89 -0
  71. data/types/ruby-2.2.0/marshal.rb +5 -0
  72. data/types/ruby-2.2.0/matchdata.rb +26 -0
  73. data/types/ruby-2.2.0/math.rb +53 -0
  74. data/types/ruby-2.2.0/numeric.rb +46 -0
  75. data/types/ruby-2.2.0/object.rb +73 -0
  76. data/types/ruby-2.2.0/pathname.rb +106 -0
  77. data/types/ruby-2.2.0/process.rb +127 -0
  78. data/types/ruby-2.2.0/random.rb +15 -0
  79. data/types/ruby-2.2.0/range.rb +38 -0
  80. data/types/ruby-2.2.0/rational.rb +31 -0
  81. data/types/ruby-2.2.0/regexp.rb +30 -0
  82. data/types/ruby-2.2.0/set.rb +58 -0
  83. data/types/ruby-2.2.0/string.rb +145 -0
  84. data/types/ruby-2.2.0/strscan.rb +7 -0
  85. data/types/ruby-2.2.0/symbol.rb +29 -0
  86. data/types/ruby-2.2.0/time.rb +68 -0
  87. data/types/ruby-2.2.0/uri.rb +18 -0
  88. data/types/ruby-2.2.0/yaml.rb +5 -0
  89. metadata +131 -0
@@ -0,0 +1,100 @@
1
+ require_relative 'type'
2
+
3
+ module RDL::Type
4
+ # A type that is parameterized on one or more other types. The base type
5
+ # must be a NominalType, while the parameters should be strings or symbols
6
+ class GenericType < Type
7
+ attr_reader :base
8
+ attr_reader :params
9
+
10
+ @@cache = {}
11
+
12
+ class << self
13
+ alias :__new__ :new
14
+ end
15
+
16
+ def self.new(base, *params)
17
+ t = @@cache[[base, params]]
18
+ return t if t
19
+ raise RuntimeError, "Attempt to create generic type with non-type param" unless params.all? { |p| p.is_a? Type }
20
+ t = GenericType.__new__(base, params)
21
+ return (@@cache[[base, params]] = t) # assignment evaluates to t
22
+ end
23
+
24
+ def initialize(base, params)
25
+ raise "base must be NominalType" unless base.instance_of? NominalType
26
+
27
+ @base = base
28
+ @params = params
29
+ super()
30
+ end
31
+
32
+ def to_s
33
+ "#{@base}<#{@params.map { |t| t.to_s }.join(', ')}>"
34
+ end
35
+
36
+ def eql?(other)
37
+ self == other
38
+ end
39
+
40
+ def ==(other) # :nodoc:
41
+ return (other.instance_of? GenericType) && (other.base == @base) && (other.params == @params)
42
+ end
43
+
44
+ def <=(other)
45
+ formals, variance, check = $__rdl_type_params[base.name]
46
+ # do check here to avoid hiding errors if generic type written
47
+ # with wrong number of parameters but never checked against
48
+ # instantiated instances
49
+ raise TypeError, "No type parameters defined for #{base.name}" unless formals
50
+ return true if other.instance_of? TopType
51
+ # return (@base <= other) if other.instance_of?(NominalType) # raw subtyping not allowed
52
+ if other.instance_of? GenericType
53
+ return false unless @base == other.base
54
+ return variance.zip(params, other.params).all? { |v, self_t, other_t|
55
+ case v
56
+ when :+
57
+ self_t <= other_t
58
+ when :-
59
+ other_t <= self_t
60
+ when :~
61
+ self_t == other_t
62
+ else
63
+ raise RuntimeError, "Unexpected variance #{v}" # shouldn't happen
64
+ end
65
+ }
66
+ end
67
+ if other.instance_of? StructuralType
68
+ # similar logic in NominalType
69
+ inst = Hash[*formals.zip(params).flatten]
70
+ k = base.klass
71
+ other.methods.each_pair { |m, t|
72
+ return false unless k.method_defined? m
73
+ if RDL::Wrap.has_contracts?(k, m, :type)
74
+ types = RDL::Wrap.get_contracts(k, m, :type)
75
+ return false unless types.all? { |t_self| t_self.instantiate(inst) <= t }
76
+ end
77
+ }
78
+ return true
79
+ end
80
+ return false
81
+ end
82
+
83
+ def member?(obj, *args)
84
+ raise "No type parameters defined for #{base.name}" unless $__rdl_type_params[base.name]
85
+ formals = $__rdl_type_params[base.name][0]
86
+ t = RDL::Util.rdl_type obj
87
+ return t <= self if t
88
+ return false unless base.member?(obj, *args)
89
+ return true
90
+ end
91
+
92
+ def instantiate(inst)
93
+ GenericType.new(base, *params.map { |t| t.instantiate(inst) })
94
+ end
95
+
96
+ def hash
97
+ h = (61 + @base.hash) * @params.hash
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,66 @@
1
+ require_relative 'type'
2
+
3
+ module RDL::Type
4
+ class IntersectionType < Type
5
+ attr_reader :types
6
+
7
+ @@cache = {}
8
+
9
+ class << self
10
+ alias :__new__ :new
11
+ end
12
+
13
+ def self.new(*types)
14
+ ts = []
15
+ types.each { |t|
16
+ if t.instance_of? NilType
17
+ next
18
+ elsif t.instance_of? IntersectionType
19
+ ts.concat t.types
20
+ else
21
+ raise RuntimeError, "Attempt to create intersection type with non-type" unless t.is_a? Type
22
+ ts << t
23
+ end
24
+ }
25
+ ts.sort! { |a,b| a.object_id <=> b.object_id }
26
+ ts.uniq!
27
+
28
+ return NilType.new if ts.size == 0
29
+ return ts[0] if ts.size == 1
30
+
31
+ t = @@cache[ts]
32
+ return t if t
33
+ t = IntersectionType.__new__(ts)
34
+ return (@@cache[ts] = t) # assignment evaluates to t
35
+ end
36
+
37
+ def initialize(types)
38
+ @types = types
39
+ super()
40
+ end
41
+
42
+ def to_s # :nodoc:
43
+ "(#{@types.map { |t| t.to_s }.join(' and ')})"
44
+ end
45
+
46
+ def eql?(other)
47
+ self == other
48
+ end
49
+
50
+ def ==(other) # :nodoc:
51
+ return (other.instance_of? IntersectionType) && (other.types == @types)
52
+ end
53
+
54
+ def member?(obj, *args)
55
+ @types.all? { |t| t.member?(obj, *args) }
56
+ end
57
+
58
+ def instantiate(inst)
59
+ return IntersectionType.new(*(@types.map { |t| t.instantiate(inst) }))
60
+ end
61
+
62
+ def hash # :nodoc:
63
+ 47 + @types.hash
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,39 @@
1
+ module RDL::Type
2
+ class Parser
3
+
4
+ macro
5
+ ID (\w|\:\:)+
6
+ SYMBOL :\w+
7
+ SPECIAL_ID %\w+
8
+ FIXNUM -?(\d)+
9
+ FLOAT -?\d\.\d+
10
+
11
+ rule
12
+ \s # skip
13
+ or { [:OR, text] }
14
+ -> { [:RARROW, text] }
15
+ => { [:RASSOC, text] }
16
+ \( { [:LPAREN, text] }
17
+ \) { [:RPAREN, text] }
18
+ \{ { [:LBRACE, text] }
19
+ \} { [:RBRACE, text] }
20
+ \[ { [:LBRACKET, text] }
21
+ \] { [:RBRACKET, text] }
22
+ < { [:LESS, text] }
23
+ > { [:GREATER, text] }
24
+ , { [:COMMA, text] }
25
+ \? { [:QUERY, text] }
26
+ \* { [:STAR, text] }
27
+ \#\# { [:DOUBLE_HASH, text] }
28
+ \$\{ { [:CONST_BEGIN, text] }
29
+ {FLOAT} { [:FLOAT, text] } # Must go before FIXNUM
30
+ {FIXNUM} { [:FIXNUM, text] }
31
+ {ID} { [:ID, text] }
32
+ {SYMBOL} { [:SYMBOL, text[1..-1]] }
33
+ \: { [:COLON, text] } # Must come after SYMBOL
34
+ {SPECIAL_ID} { [:SPECIAL_ID, text] }
35
+ '[^']*' { [:STRING, text.gsub("'", "")] }
36
+ "[^"]*" { [:STRING, text.gsub('"', "")] }
37
+
38
+ end
39
+ end
@@ -0,0 +1,148 @@
1
+ #--
2
+ # DO NOT MODIFY!!!!
3
+ # This file is automatically generated by rex 1.0.5
4
+ # from lexical definition file "lexer.rex".
5
+ #++
6
+
7
+ require 'racc/parser'
8
+ module RDL::Type
9
+ class Parser < Racc::Parser
10
+ require 'strscan'
11
+
12
+ class ScanError < StandardError ; end
13
+
14
+ attr_reader :lineno
15
+ attr_reader :filename
16
+ attr_accessor :state
17
+
18
+ def scan_setup(str)
19
+ @ss = StringScanner.new(str)
20
+ @lineno = 1
21
+ @state = nil
22
+ end
23
+
24
+ def action
25
+ yield
26
+ end
27
+
28
+ def scan_str(str)
29
+ scan_setup(str)
30
+ do_parse
31
+ end
32
+ alias :scan :scan_str
33
+
34
+ def load_file( filename )
35
+ @filename = filename
36
+ open(filename, "r") do |f|
37
+ scan_setup(f.read)
38
+ end
39
+ end
40
+
41
+ def scan_file( filename )
42
+ load_file(filename)
43
+ do_parse
44
+ end
45
+
46
+
47
+ def next_token
48
+ return if @ss.eos?
49
+
50
+ # skips empty actions
51
+ until token = _next_token or @ss.eos?; end
52
+ token
53
+ end
54
+
55
+ def _next_token
56
+ text = @ss.peek(1)
57
+ @lineno += 1 if text == "\n"
58
+ token = case @state
59
+ when nil
60
+ case
61
+ when (text = @ss.scan(/\s/))
62
+ ;
63
+
64
+ when (text = @ss.scan(/or/))
65
+ action { [:OR, text] }
66
+
67
+ when (text = @ss.scan(/->/))
68
+ action { [:RARROW, text] }
69
+
70
+ when (text = @ss.scan(/=>/))
71
+ action { [:RASSOC, text] }
72
+
73
+ when (text = @ss.scan(/\(/))
74
+ action { [:LPAREN, text] }
75
+
76
+ when (text = @ss.scan(/\)/))
77
+ action { [:RPAREN, text] }
78
+
79
+ when (text = @ss.scan(/\{/))
80
+ action { [:LBRACE, text] }
81
+
82
+ when (text = @ss.scan(/\}/))
83
+ action { [:RBRACE, text] }
84
+
85
+ when (text = @ss.scan(/\[/))
86
+ action { [:LBRACKET, text] }
87
+
88
+ when (text = @ss.scan(/\]/))
89
+ action { [:RBRACKET, text] }
90
+
91
+ when (text = @ss.scan(/</))
92
+ action { [:LESS, text] }
93
+
94
+ when (text = @ss.scan(/>/))
95
+ action { [:GREATER, text] }
96
+
97
+ when (text = @ss.scan(/,/))
98
+ action { [:COMMA, text] }
99
+
100
+ when (text = @ss.scan(/\?/))
101
+ action { [:QUERY, text] }
102
+
103
+ when (text = @ss.scan(/\*/))
104
+ action { [:STAR, text] }
105
+
106
+ when (text = @ss.scan(/\#\#/))
107
+ action { [:DOUBLE_HASH, text] }
108
+
109
+ when (text = @ss.scan(/\$\{/))
110
+ action { [:CONST_BEGIN, text] }
111
+
112
+ when (text = @ss.scan(/-?\d\.\d+/))
113
+ action { [:FLOAT, text] } # Must go before FIXNUM
114
+
115
+ when (text = @ss.scan(/-?(\d)+/))
116
+ action { [:FIXNUM, text] }
117
+
118
+ when (text = @ss.scan(/(\w|\:\:)+/))
119
+ action { [:ID, text] }
120
+
121
+ when (text = @ss.scan(/:\w+/))
122
+ action { [:SYMBOL, text[1..-1]] }
123
+
124
+ when (text = @ss.scan(/\:/))
125
+ action { [:COLON, text] } # Must come after SYMBOL
126
+
127
+ when (text = @ss.scan(/%\w+/))
128
+ action { [:SPECIAL_ID, text] }
129
+
130
+ when (text = @ss.scan(/'[^']*'/))
131
+ action { [:STRING, text.gsub("'", "")] }
132
+
133
+ when (text = @ss.scan(/"[^"]*"/))
134
+ action { [:STRING, text.gsub('"', "")] }
135
+
136
+ else
137
+ text = @ss.string[@ss.pos .. -1]
138
+ raise ScanError, "can not match: '" + text + "'"
139
+ end # if
140
+
141
+ else
142
+ raise ScanError, "undefined state: '" + state.to_s + "'"
143
+ end # case state
144
+ token
145
+ end # def _next_token
146
+
147
+ end # class
148
+ end
@@ -0,0 +1,219 @@
1
+ require_relative 'type'
2
+
3
+ module RDL::Type
4
+
5
+ # A type representing some method or block. MethodType has subcomponent
6
+ # types for arguments (zero or more), block (optional) and return value
7
+ # (exactly one).
8
+ class MethodType < Type
9
+ attr_reader :args
10
+ attr_reader :block
11
+ attr_reader :ret
12
+
13
+ @@contract_cache = {}
14
+
15
+ # Create a new MethodType
16
+ #
17
+ # [+args+] List of types of the arguments of the procedure (use [] for no args).
18
+ # [+block+] The type of the block passed to this method, if it takes one.
19
+ # [+ret+] The type that the procedure returns.
20
+ def initialize(args, block, ret)
21
+ # First check argument types have form (any number of required
22
+ # or optional args, at most one vararg, any number of named arguments)
23
+ state = :required
24
+ args.each { |arg|
25
+ arg = arg.type if arg.instance_of? RDL::Type::AnnotatedArgType
26
+ case arg
27
+ when OptionalType
28
+ raise "Optional arguments not allowed after varargs" if state == :vararg
29
+ raise "Optional arguments not allowed after named arguments" if state == :hash
30
+ state = :optional
31
+ when VarargType
32
+ raise "Multiple varargs not allowed" if state == :vararg
33
+ raise "Varargs not allowed after named arguments" if state == :hash
34
+ state = :vararg
35
+ when FiniteHashType
36
+ raise "Only one set of named arguments allowed" if state == :hash
37
+ state = :hash
38
+ else
39
+ raise "Attempt to create method type with non-type arg" unless arg.is_a? Type
40
+ raise "Required arguments not allowed after varargs" if state == :vararg
41
+ raise "Required arguments not allowed after named arguments" if state == :hash
42
+ end
43
+ }
44
+ @args = *args
45
+
46
+ raise "Block must be MethodType" unless (not block) or (block.instance_of? MethodType)
47
+ @block = block
48
+
49
+ raise "Attempt to create method type with non-type ret" unless ret.is_a? Type
50
+ @ret = ret
51
+
52
+ super()
53
+ end
54
+
55
+ def le(other, h={})
56
+ raise RuntimeError, "should not be called"
57
+ end
58
+
59
+ # TODO: Check blk
60
+ def pre_cond?(inst, *args, &blk)
61
+ states = [[0, 0]] # [position in @arg, position in args]
62
+ until states.empty?
63
+ formal, actual = states.pop
64
+ return true if formal == @args.size && actual == args.size # Matched all actuals, no formals left over
65
+ next if formal >= @args.size # Too many actuals to match
66
+ t = @args[formal]
67
+ t = t.type if t.instance_of? AnnotatedArgType
68
+ case t
69
+ when OptionalType
70
+ t = t.type.instantiate(inst)
71
+ if actual == args.size
72
+ states << [formal+1, actual] # skip to allow extra formal optionals at end
73
+ elsif t.member?(args[actual], vars_wild: true)
74
+ states << [formal+1, actual+1] # match
75
+ states << [formal+1, actual] # skip
76
+ else
77
+ states << [formal+1, actual] # type doesn't match; must skip this formal
78
+ end
79
+ when VarargType
80
+ t = t.type.instantiate(inst)
81
+ if actual == args.size
82
+ states << [formal+1, actual] # skip to allow empty vararg at end
83
+ elsif t.member?(args[actual], vars_wild: true)
84
+ states << [formal, actual+1] # match, more varargs coming
85
+ states << [formal+1, actual+1] # match, no more varargs
86
+ # states << [formal+1, actual] # skip - can't happen, varargs have to be at end
87
+ else
88
+ states << [formal+1, actual] # skip
89
+ end
90
+ else
91
+ t = t.instantiate(inst)
92
+ the_actual = nil
93
+ if actual == args.size
94
+ next unless t.instance_of? FiniteHashType
95
+ if t.member?({}, vars_wild: true) # try matching against the empty hash
96
+ states << [formal+1, actual]
97
+ end
98
+ elsif t.member?(args[actual], vars_wild: true)
99
+ states << [formal+1, actual+1] # match
100
+ # no else case; if there is no match, this is a dead end
101
+ end
102
+ end
103
+ end
104
+ return false
105
+ end
106
+
107
+ def post_cond?(inst, ret, *args)
108
+ method_name = method_name ? method_name + ": " : ""
109
+ return @ret.instantiate(inst).member?(ret, vars_wild: true)
110
+ end
111
+
112
+ def to_contract(inst: nil)
113
+ c = @@contract_cache[self]
114
+ return c if c
115
+
116
+ # slf.ret, slf.args are the formals
117
+ # ret, args are the actuals
118
+ slf = self # Bind self so it's captured in a closure, since contracts are executed
119
+ # with self bound to the receiver method's self
120
+ prec = RDL::Contract::FlatContract.new { |*args, &blk|
121
+ raise TypeError, "Arguments #{args} do not match argument types #{slf}" unless slf.pre_cond?(inst, *args, &blk)
122
+ true
123
+ }
124
+ postc = RDL::Contract::FlatContract.new { |ret, *args|
125
+ raise TypeError, "Return #{ret} does not match return type #{slf}" unless slf.post_cond?(inst, ret, *args)
126
+ true
127
+ }
128
+ c = RDL::Contract::ProcContract.new(pre_cond: prec, post_cond: postc)
129
+ return (@@contract_cache[self] = c) # assignment evaluates to c
130
+ end
131
+
132
+ # [+types+] is an array of method types. Checks that [+args+] and
133
+ # [+blk+] match at least one arm of the intersection type;
134
+ # otherwise raises exception. Returns array of method types that
135
+ # matched [+args+] and [+blk+]
136
+ def self.check_arg_types(method_name, types, inst, *args, &blk)
137
+ $__rdl_contract_switch.off {
138
+ matches = [] # types that matched args
139
+ types.each_with_index { |t, i| matches << i if t.pre_cond?(inst, *args, &blk) }
140
+ return matches if matches.size > 0
141
+ method_name = method_name ? method_name + ": " : ""
142
+ raise TypeError, <<RUBY
143
+ #{method_name}Argument type error.
144
+ Method type:
145
+ #{ types.map { |t| " " + t.to_s }.join("\n") }
146
+ Actual argument type#{args.size > 1 ? "s" : ""}:
147
+ (#{args.map { |arg| RDL::Util.rdl_type_or_class(arg) }.join(', ')}) #{if blk then blk.to_s end}
148
+ Actual argument values (one per line):
149
+ #{ args.map { |arg| " " + arg.inspect }.join("\n") }
150
+ RUBY
151
+ }
152
+ end
153
+
154
+ def self.check_ret_types(method_name, types, inst, matches, ret, *args, &blk)
155
+ $__rdl_contract_switch.off {
156
+ matches.each { |i| return true if types[i].post_cond?(inst, ret, *args) }
157
+ method_name = method_name ? method_name + ": " : ""
158
+ raise TypeError, <<RUBY
159
+ #{method_name}Return type error. *'s indicate argument lists that matched.
160
+ Method type:
161
+ #{types.each_with_index.map { |t,i| " " + (matches.member?(i) ? "*" : " ") + t.to_s }.join("\n") }
162
+ Actual return type:
163
+ #{ RDL::Util.rdl_type_or_class(ret)}
164
+ Actual return value:
165
+ #{ ret.inspect }
166
+ RUBY
167
+ }
168
+ end
169
+
170
+ def to_s # :nodoc:
171
+ if @block
172
+ return "(#{@args.map { |arg| arg.to_s }.join(', ')}) {#{@block.to_s}} -> #{@ret.to_s}"
173
+ elsif @args
174
+ return "(#{@args.map { |arg| arg.to_s }.join(', ')}) -> #{@ret.to_s}"
175
+ else
176
+ return "() -> #{@ret.to_s}"
177
+ end
178
+ end
179
+
180
+ def <=(other)
181
+ return false unless other.instance_of? MethodType # only comparable to method types
182
+ return false unless other.args.size == @args.size
183
+ return false unless @args.zip(other.args).all? { |left, right| right <= left } # contravariance
184
+ return false unless @ret <= other.ret # covariance
185
+ if @block && other.block
186
+ return (other.block <= @block) # contravariance
187
+ elsif @block.nil? && other.block.nil?
188
+ return true
189
+ else
190
+ return false # one has a block and the other doesn't
191
+ end
192
+ end
193
+
194
+ def instantiate(inst)
195
+ return MethodType.new(@args.map { |arg| arg.instantiate(inst) },
196
+ @block ? @block.instantiate(inst) : nil,
197
+ @ret.instantiate(inst))
198
+ end
199
+
200
+ def eql?(other)
201
+ self == other
202
+ end
203
+
204
+ # Return +true+ if +other+ is the same type
205
+ def ==(other)
206
+ return (other.instance_of? MethodType) &&
207
+ (other.args == @args) &&
208
+ (other.block == @block) &&
209
+ (other.ret == @ret)
210
+ end
211
+
212
+ def hash # :nodoc:
213
+ h = (37 + @ret.hash) * 41 + @args.hash
214
+ h = h * 31 + @block.hash if @block
215
+ return h
216
+ end
217
+ end
218
+ end
219
+