rdl 1.0.0.rc1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+