rdl 2.0.0.rc2 → 2.0.0.rc3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.md +7 -1
  3. data/README.md +94 -20
  4. data/lib/rdl.rb +4 -1
  5. data/lib/rdl/config.rb +90 -3
  6. data/lib/rdl/info.rb +16 -0
  7. data/lib/rdl/typecheck.rb +207 -79
  8. data/lib/rdl/types/bot.rb +1 -1
  9. data/lib/rdl/types/dependent_arg.rb +17 -8
  10. data/lib/rdl/types/{finitehash.rb → finite_hash.rb} +3 -29
  11. data/lib/rdl/types/generic.rb +1 -37
  12. data/lib/rdl/types/intersection.rb +1 -0
  13. data/lib/rdl/types/lexer.rex +2 -1
  14. data/lib/rdl/types/lexer.rex.rb +4 -1
  15. data/lib/rdl/types/method.rb +2 -12
  16. data/lib/rdl/types/nominal.rb +1 -22
  17. data/lib/rdl/types/non_null.rb +50 -0
  18. data/lib/rdl/types/parser.racc +3 -1
  19. data/lib/rdl/types/parser.tab.rb +222 -190
  20. data/lib/rdl/types/singleton.rb +1 -6
  21. data/lib/rdl/types/structural.rb +1 -10
  22. data/lib/rdl/types/top.rb +1 -2
  23. data/lib/rdl/types/tuple.rb +3 -19
  24. data/lib/rdl/types/type.rb +223 -0
  25. data/lib/rdl/types/union.rb +12 -2
  26. data/lib/rdl/types/var.rb +4 -1
  27. data/lib/rdl/types/wild_query.rb +1 -0
  28. data/lib/rdl/wrap.rb +199 -169
  29. data/lib/rdl_disable.rb +41 -0
  30. data/lib/rdl_types.rb +1 -4
  31. data/{types/ruby-2.x → lib/types/core-ruby-2.x}/_aliases.rb +0 -0
  32. data/{types/ruby-2.x → lib/types/core-ruby-2.x}/abbrev.rb +0 -0
  33. data/{types/ruby-2.x → lib/types/core-ruby-2.x}/array.rb +56 -56
  34. data/{types/ruby-2.x → lib/types/core-ruby-2.x}/base64.rb +0 -0
  35. data/{types/ruby-2.x → lib/types/core-ruby-2.x}/basic_object.rb +0 -0
  36. data/{types/ruby-2.x → lib/types/core-ruby-2.x}/benchmark.rb +0 -0
  37. data/{types/ruby-2.x → lib/types/core-ruby-2.x}/bigdecimal.rb +0 -0
  38. data/{types/ruby-2.x → lib/types/core-ruby-2.x}/bigmath.rb +0 -0
  39. data/{types/ruby-2.x → lib/types/core-ruby-2.x}/bignum.rb +0 -0
  40. data/{types/ruby-2.x → lib/types/core-ruby-2.x}/class.rb +0 -0
  41. data/{types/ruby-2.x → lib/types/core-ruby-2.x}/complex.rb +0 -0
  42. data/{types/ruby-2.x → lib/types/core-ruby-2.x}/coverage.rb +0 -0
  43. data/{types/ruby-2.x → lib/types/core-ruby-2.x}/csv.rb +0 -0
  44. data/{types/ruby-2.x → lib/types/core-ruby-2.x}/date.rb +0 -0
  45. data/{types/ruby-2.x → lib/types/core-ruby-2.x}/dir.rb +0 -0
  46. data/{types/ruby-2.x → lib/types/core-ruby-2.x}/encoding.rb +0 -0
  47. data/{types/ruby-2.x → lib/types/core-ruby-2.x}/enumerable.rb +0 -0
  48. data/{types/ruby-2.x → lib/types/core-ruby-2.x}/enumerator.rb +0 -0
  49. data/{types/ruby-2.x → lib/types/core-ruby-2.x}/exception.rb +0 -0
  50. data/{types/ruby-2.x → lib/types/core-ruby-2.x}/file.rb +0 -0
  51. data/{types/ruby-2.x → lib/types/core-ruby-2.x}/fileutils.rb +0 -0
  52. data/{types/ruby-2.x → lib/types/core-ruby-2.x}/fixnum.rb +0 -0
  53. data/{types/ruby-2.x → lib/types/core-ruby-2.x}/float.rb +26 -26
  54. data/{types/ruby-2.x → lib/types/core-ruby-2.x}/gem.rb +0 -0
  55. data/{types/ruby-2.x → lib/types/core-ruby-2.x}/hash.rb +0 -0
  56. data/{types/ruby-2.x → lib/types/core-ruby-2.x}/integer.rb +8 -8
  57. data/{types/ruby-2.x → lib/types/core-ruby-2.x}/io.rb +0 -0
  58. data/{types/ruby-2.x → lib/types/core-ruby-2.x}/kernel.rb +12 -11
  59. data/{types/ruby-2.x → lib/types/core-ruby-2.x}/marshal.rb +0 -0
  60. data/{types/ruby-2.x → lib/types/core-ruby-2.x}/matchdata.rb +0 -0
  61. data/{types/ruby-2.x → lib/types/core-ruby-2.x}/math.rb +0 -0
  62. data/{types/ruby-2.x → lib/types/core-ruby-2.x}/module.rb +0 -0
  63. data/{types/ruby-2.x → lib/types/core-ruby-2.x}/nil.rb +0 -0
  64. data/{types/ruby-2.x → lib/types/core-ruby-2.x}/numeric.rb +0 -0
  65. data/lib/types/core-ruby-2.x/object.rb +75 -0
  66. data/{types/ruby-2.x → lib/types/core-ruby-2.x}/pathname.rb +0 -0
  67. data/{types/ruby-2.x → lib/types/core-ruby-2.x}/process.rb +0 -0
  68. data/{types/ruby-2.x → lib/types/core-ruby-2.x}/random.rb +0 -0
  69. data/{types/ruby-2.x → lib/types/core-ruby-2.x}/range.rb +0 -0
  70. data/{types/ruby-2.x → lib/types/core-ruby-2.x}/rational.rb +0 -0
  71. data/{types/ruby-2.x → lib/types/core-ruby-2.x}/regexp.rb +0 -0
  72. data/{types/ruby-2.x → lib/types/core-ruby-2.x}/set.rb +0 -0
  73. data/{types/ruby-2.x → lib/types/core-ruby-2.x}/string.rb +0 -0
  74. data/{types/ruby-2.x → lib/types/core-ruby-2.x}/strscan.rb +0 -0
  75. data/{types/ruby-2.x → lib/types/core-ruby-2.x}/symbol.rb +0 -0
  76. data/{types/ruby-2.x → lib/types/core-ruby-2.x}/time.rb +0 -0
  77. data/{types/ruby-2.x → lib/types/core-ruby-2.x}/uri.rb +0 -0
  78. data/{types/ruby-2.x → lib/types/core-ruby-2.x}/yaml.rb +0 -0
  79. data/lib/types/core.rb +4 -0
  80. data/rdl.gemspec +2 -2
  81. data/test/test_le.rb +77 -35
  82. data/test/test_parser.rb +75 -59
  83. data/test/test_rdl.rb +18 -0
  84. data/test/test_typecheck.rb +73 -4
  85. metadata +54 -57
  86. data/lib/rails_types.rb +0 -1
  87. data/types/rails-4.2.1/fixnum.rb +0 -3
  88. data/types/rails-4.2.1/string.rb +0 -3
  89. data/types/rails-tmp/action_dispatch.rb +0 -406
  90. data/types/rails-tmp/active_record.rb +0 -406
  91. data/types/rails-tmp/devise_contracts.rb +0 -216
  92. data/types/ruby-2.x/object.rb +0 -73
@@ -53,12 +53,7 @@ module RDL::Type
53
53
  end
54
54
 
55
55
  def <=(other)
56
- other = other.canonical
57
- other.instance_of?(TopType) ||
58
- (@val.nil? && (not (other.instance_of?(SingletonType)))) ||
59
- (other.instance_of?(SingletonType) && other.val == @val) ||
60
- (other.instance_of?(UnionType) && other.types.any? { |ot| self <= ot }) ||
61
- (@nominal <= other)
56
+ return Type.leq(self, other)
62
57
  end
63
58
 
64
59
  def member?(obj, *args)
@@ -34,16 +34,7 @@ module RDL::Type
34
34
  end
35
35
 
36
36
  def <=(other)
37
- other = other.canonical
38
- return true if other.instance_of? TopType
39
- # in theory a StructuralType could contain all the methods of a NominalType or GenericType,
40
- # but it seems unlikely in practice, so disallow this case.
41
- return RuntimeError, "Structural subtype can't be subtype of #{other.class}" unless other.instance_of? StructuralType
42
- # allow width subtyping
43
- other.methods.each_pair { |m, t|
44
- return false unless @methods.has_key?(m) && @methods[m] <= t
45
- }
46
- return true
37
+ return Type.leq(self, other)
47
38
  end
48
39
 
49
40
  def member?(obj, *args)
@@ -35,8 +35,7 @@ module RDL::Type
35
35
  end
36
36
 
37
37
  def <=(other)
38
- other = other.canonical
39
- other.instance_of? TopType
38
+ return Type.leq(self, other)
40
39
  end
41
40
 
42
41
  def member?(obj, *args)
@@ -3,8 +3,8 @@ module RDL::Type
3
3
  class TupleType < Type
4
4
  attr_reader :params
5
5
  attr_reader :array # either nil or array type if self has been promoted to array
6
- attr_reader :ubounds # upper bounds this tuple has been compared with using <=
7
- attr_reader :lbounds # lower bounds...
6
+ attr_accessor :ubounds # upper bounds this tuple has been compared with using <=
7
+ attr_accessor :lbounds # lower bounds...
8
8
 
9
9
  # no caching because array might be mutated
10
10
  def initialize(*params)
@@ -58,23 +58,7 @@ module RDL::Type
58
58
  end
59
59
 
60
60
  def <=(other)
61
- return @array <= other if @array
62
- other = other.canonical
63
- return true if other.instance_of? TopType
64
- other = other.array if other.instance_of?(TupleType) && other.array
65
- if other.instance_of? TupleType
66
- # Tuples are immutable, so covariant subtyping allowed
67
- return false unless @params.length == other.params.length
68
- return false unless @params.zip(other.params).all? { |left, right| left <= right }
69
- # subyping check passed
70
- ubounds << other
71
- other.lbounds << self
72
- return true
73
- elsif (other.instance_of? GenericType) && (other.base == $__rdl_array_type)
74
- r = promote!
75
- return (self <= other) && r
76
- end
77
- return false
61
+ return Type.leq(self, other)
78
62
  end
79
63
 
80
64
  def member?(obj, *args)
@@ -29,5 +29,228 @@ module RDL::Type
29
29
  return self
30
30
  end
31
31
 
32
+ # [+ other +] is a Type
33
+ # [+ inst +] is a Hash<Symbol, Type> representing an instantiation
34
+ # [+ ileft +] is a %bool
35
+ # if inst is nil, returns self <= other
36
+ # if inst is non-nil and ileft, returns inst(self) <= other, possibly mutating inst to make this true
37
+ # if inst is non-nil and !ileft, returns self <= inst(other), again possibly mutating inst
38
+ def self.leq(left, right, inst=nil, ileft=true)
39
+ left = inst[left.name] if inst && ileft && left.is_a?(VarType) && inst[left.name]
40
+ right = inst[right.name] if inst && !ileft && right.is_a?(VarType) && inst[right.name]
41
+ left = left.type if left.is_a? DependentArgType
42
+ right = right.type if right.is_a? DependentArgType
43
+ left = left.type if left.is_a? NonNullType # ignore nullness!
44
+ right = right.type if right.is_a? NonNullType
45
+ left = left.canonical
46
+ right = right.canonical
47
+
48
+ # top and bottom
49
+ return true if left.is_a? BotType
50
+ return true if right.is_a? TopType
51
+
52
+ # type variables
53
+ begin inst.merge!(left.name => right); return true end if inst && ileft && left.is_a?(VarType)
54
+ begin inst.merge!(right.name => left); return true end if inst && !ileft && right.is_a?(VarType)
55
+ if left.is_a?(VarType) && right.is_a?(VarType)
56
+ return left.name == right.name
57
+ end
58
+
59
+ # union
60
+ return left.types.all? { |t| leq(t, right, inst, ileft) } if left.is_a?(UnionType)
61
+ if right.instance_of?(UnionType)
62
+ right.types.each { |t|
63
+ # return true at first match, updating inst accordingly to first succeessful match
64
+ new_inst = inst.dup unless inst.nil?
65
+ if leq(left, t, new_inst, ileft)
66
+ inst.update(new_inst) unless inst.nil?
67
+ return true
68
+ end
69
+ }
70
+ return false
71
+ end
72
+
73
+ # nominal
74
+ return left.klass.ancestors.member?(right.klass) if left.is_a?(NominalType) && right.is_a?(NominalType)
75
+ if left.is_a?(NominalType) && right.is_a?(StructuralType)
76
+ right.methods.each_pair { |m, t|
77
+ return false unless left.klass.method_defined? m
78
+ types = $__rdl_info.get(left.klass, m, :type)
79
+ if types
80
+ return false unless types.all? { |tlm| leq(tlm, t, nil, ileft) }
81
+ # inst above is nil because the method types inside the class and
82
+ # inside the structural type have an implicit quantifier on them. So
83
+ # even if we're allowed to instantiate type variables we can't do that
84
+ # inside those types
85
+ end
86
+ }
87
+ return true
88
+ end
89
+
90
+ # singleton
91
+ return left.val == right.val if left.is_a?(SingletonType) && right.is_a?(SingletonType)
92
+ return true if left.is_a?(SingletonType) && left.val.nil? # right cannot be a SingletonType due to above conditional
93
+ return leq(left.nominal, right, inst, ileft) if left.is_a?(SingletonType) # fall through case---use nominal type for reasoning
94
+
95
+ # generic
96
+ if left.is_a?(GenericType) && right.is_a?(GenericType)
97
+ formals, variance, _ = $__rdl_type_params[left.base.name]
98
+ # do check here to avoid hiding errors if generic type written
99
+ # with wrong number of parameters but never checked against
100
+ # instantiated instances
101
+ raise TypeError, "No type parameters defined for #{base.name}" unless formals
102
+ return false unless left.base == right.base
103
+ return variance.zip(left.params, right.params).all? { |v, tl, tr|
104
+ case v
105
+ when :+
106
+ leq(tl, tr, inst, ileft)
107
+ when :-
108
+ leq(tr, tl, inst, !ileft)
109
+ when :~
110
+ leq(tl, tr, inst, ileft) && leq(tr, tl, inst, !ileft)
111
+ else
112
+ raise RuntimeError, "Unexpected variance #{v}" # shouldn't happen
113
+ end
114
+ }
115
+ end
116
+ if left.is_a?(GenericType) && right.is_a?(StructuralType)
117
+ # similar to logic above for leq(NominalType, StructuralType, ...)
118
+ formals, variance, _ = $__rdl_type_params[left.base.name]
119
+ raise TypeError, "No type parameters defined for #{base.name}" unless formals
120
+ base_inst = Hash[*formals.zip(left.params).flatten] # instantiation for methods in base's class
121
+ klass = left.base.klass
122
+ right.methods.each_pair { |meth, t|
123
+ return false unless klass.method_defined? meth
124
+ types = $__rdl_info.get(klass, meth, :type)
125
+ if types
126
+ return false unless types.all? { |tlm| leq(tlm.instantiate(base_inst), t, nil, ileft) }
127
+ end
128
+ }
129
+ return true
130
+ end
131
+ # Note we do not allow raw subtyping leq(GenericType, NominalType, ...)
132
+
133
+ # method
134
+ if left.is_a?(MethodType) && right.is_a?(MethodType)
135
+ return false unless left.args.size == right.args.size
136
+ return false unless left.args.zip(right.args).all? { |tl, tr| leq(tr, tl, inst, !ileft) } # contravariance
137
+ return false unless leq(left.ret, right.ret, inst, ileft) # covariance
138
+ if left.block && right.block
139
+ return leq(right.block, left.block, inst, !ileft) # contravariance
140
+ elsif left.block.nil? && right.block.nil?
141
+ return true
142
+ else
143
+ return false # one has a block and the other doesn't
144
+ end
145
+ end
146
+
147
+ # structural
148
+ if left.is_a?(StructuralType) && right.is_a?(StructuralType)
149
+ # allow width subtyping - methods of right have to be in left, but not vice-versa
150
+ return right.methods.all? { |m, t|
151
+ # in recursive call set inst to nil since those method types have implicit quantifier
152
+ left.methods.has_key?(m) && leq(left.methods[m], t, nil, ileft)
153
+ }
154
+ end
155
+ # Note we do not allow a structural type to be a subtype of a nominal type or generic type,
156
+ # even though in theory that would be possible.
157
+
158
+ # tuple
159
+ if left.is_a?(TupleType) && right.is_a?(TupleType)
160
+ # Tuples are immutable, so covariant subtyping allowed
161
+ return false unless left.params.length == right.params.length
162
+ return false unless left.params.zip(right.params).all? { |lt, rt| leq(lt, rt, inst, ileft) }
163
+ # subyping check passed
164
+ left.ubounds << right
165
+ right.lbounds << left
166
+ return true
167
+ end
168
+ if left.is_a?(TupleType) && right.is_a?(GenericType) && right.base == $__rdl_array_type
169
+ # TODO !ileft and right carries a free variable
170
+ return false unless left.promote!
171
+ return leq(left, right, inst, ileft) # recheck for promoted type
172
+ end
173
+
174
+ # finite hash
175
+ if left.is_a?(FiniteHashType) && right.is_a?(FiniteHashType)
176
+ # Like Tuples, FiniteHashes are immutable, so covariant subtyping allowed
177
+ # But note, no width subtyping allowed, to match #member?
178
+ rest = right.elts.clone # shallow copy
179
+ left.elts.each_pair { |k, tl|
180
+ return false unless rest.has_key? k
181
+ tr = rest[k]
182
+ tl = tl.type if tl.is_a? OptionalType
183
+ tr = tr.type if tr.is_a? OptionalType
184
+ return false unless leq(tl, tr, inst, ileft)
185
+ rest.delete k
186
+ }
187
+ rest.each_pair { |k, t|
188
+ return false unless t.is_a? OptionalType
189
+ }
190
+ left.ubounds << right
191
+ right.lbounds << left
192
+ return true
193
+ end
194
+ if left.is_a?(FiniteHashType) && right.is_a?(GenericType) && right.base == $__rdl_hash_type
195
+ # TODO !ileft and right carries a free variable
196
+ return false unless left.promote!
197
+ return leq(left, right, inst, ileft) # recheck for promoted type
198
+ end
199
+
200
+ return false
201
+ end
32
202
  end
203
+
204
+ # [+ a +] is an Array<Type> that may contain union types.
205
+ # returns Array<Array<Type>> containing all possible expansions of the union types.
206
+ # For example, slightly abusing notation:
207
+ #
208
+ # expand_product [A, B] #=> [[A, B]]
209
+ # expand_product [A or B, C] #=> [[A, C], [B, C]]
210
+ # expand_product [A or B, C or D] #=> [[A, C], [B, C], [A, D], [B, D]]
211
+ def self.expand_product(a)
212
+ return [[]] if a.empty? # logic below only applies if at least one element
213
+ a.map! { |t| t.canonical }
214
+ counts = a.map { |t| if t.is_a? UnionType then t.types.length - 1 else 0 end }
215
+ res = []
216
+ # now iterate through ever combination of indices
217
+ # using combinations is not quite as memory efficient as inlining that code here,
218
+ # but it's a lot easier to think about combinations separate from this code
219
+ combinations(counts).each { |inds|
220
+ tmp = []
221
+ # set tmp to be a with elts in positions in ind selected from unions
222
+ a.each_with_index { |t, i| if t.is_a? UnionType then tmp << t.types[inds[i]] else tmp << t end }
223
+ res << tmp
224
+ }
225
+ return res
226
+ # return [a]
227
+ end
228
+
229
+ private
230
+
231
+ # [+ a +] is Array<Fixnum>
232
+ # returns Array<Array<Fixnum>> containing all combinations of 0..a[i] at index i
233
+ # For example:
234
+ #
235
+ # combinations [0, 0] #=> [[0, 0]]
236
+ # combinations [1, 0] #=> [[0, 0], [1, 0]]
237
+ # combinations [1, 1] #=> [[0, 0], [0, 1][, [1, 0], [1, 1]]]
238
+ #
239
+ # yes, this is used in expand_product above!
240
+ def self.combinations(a)
241
+ cur = a.map { |x| 0 }
242
+ res = []
243
+ while ((cur <=> a) < 1) # Array#<=> uses lexicographic order, so this will repeat until cur == a
244
+ res << cur.dup
245
+ i = cur.length - 1 # start at right since want next in lexicographic order
246
+ while i >= 0
247
+ cur[i] += 1
248
+ break if (cur[i] <= a[i]) # increment did not overflow position, or it overflowed in position 0 so allow inc to break outer loop
249
+ cur[i] = 0 unless i == 0 # increment overflowed; reset to 0 and continue looping, except allow overflow to exit when i == 0
250
+ i -= 1
251
+ end
252
+ end
253
+ return res
254
+ end
255
+
33
256
  end
@@ -82,10 +82,20 @@ module RDL::Type
82
82
  end
83
83
 
84
84
  def <=(other)
85
+ return Type.leq(self, other)
86
+ end
87
+
88
+ def leq_inst(other, inst=nil, ileft=true)
85
89
  canonicalize!
86
- return @canonical <= other if @canonical
90
+ return @canonical.leq_inst(other, inst, ileft) if @canonical
91
+ other = other.type if other.is_a? DependentArgType
87
92
  other = other.canonical
88
- @types.all? { |t| t <= other }
93
+ if inst && !ileft && other.is_a?(VarType)
94
+ return leq_inst(inst[other.name], inst, ileft) if inst[other.name]
95
+ inst.merge!(other.name => self)
96
+ return true
97
+ end
98
+ return @types.all? { |t| t.leq_inst(other, inst, ileft) }
89
99
  end
90
100
 
91
101
  def member?(obj, *args)
@@ -32,7 +32,10 @@ module RDL::Type
32
32
 
33
33
  alias eql? ==
34
34
 
35
- alias <= == # hack to allow rdl_query to work with union types...
35
+ # an uninstantiated variable is only comparable to itself
36
+ def <=(other)
37
+ return Type.leq(self, other)
38
+ end
36
39
 
37
40
  def match(other)
38
41
  other = other.canonical
@@ -24,6 +24,7 @@ module RDL::Type
24
24
  alias eql? ==
25
25
 
26
26
  def <=(other)
27
+ other = other.type if other.is_a? DependentArgType
27
28
  other = other.canonical
28
29
  return self == other
29
30
  end
@@ -36,6 +36,7 @@ class RDL::Wrap
36
36
  # return if (klass.method_defined? meth_old) # now checked above by wrapped? call
37
37
  is_singleton_method = RDL::Util.has_singleton_marker(klass_str)
38
38
  full_method_name = RDL::Util.pp_klass_method(klass_str, meth)
39
+ klass_str_without_singleton = if is_singleton_method then RDL::Util.remove_singleton_marker(klass_str) else klass_str end
39
40
 
40
41
  klass.class_eval <<-RUBY, __FILE__, __LINE__
41
42
  alias_method meth_old, meth
@@ -59,18 +60,21 @@ class RDL::Wrap
59
60
  RDL::Contract::AndContract.check_array(pres, self, *args, &blk) if pres
60
61
  types = $__rdl_info.get(klass, meth, :type)
61
62
  if types
62
- matches,args,blk,bind = RDL::Type::MethodType.check_arg_types("#{full_method_name}", self, bind, types, inst, *args, &blk)
63
+ matches, args, blk, bind = RDL::Type::MethodType.check_arg_types("#{full_method_name}", self, bind, types, inst, *args, &blk)
63
64
  end
64
65
  }
65
- ret = send(#{meth_old.inspect}, *args, &blk)
66
+ ret = send(#{meth_old.inspect}, *args, &blk)
66
67
  $__rdl_wrap_switch.off {
67
68
  posts = $__rdl_info.get(klass, meth, :post)
68
69
  RDL::Contract::AndContract.check_array(posts, self, ret, *args, &blk) if posts
69
70
  if matches
70
71
  ret = RDL::Type::MethodType.check_ret_types(self, "#{full_method_name}", types, inst, matches, ret, bind, *args, &blk)
71
72
  end
73
+ if RDL::Config.instance.guess_types.include?("#{klass_str_without_singleton}".to_sym)
74
+ $__rdl_info.add(klass, meth, :otype, { args: (args.map { |arg| arg.class }), ret: ret.class, block: block_given? })
75
+ end
76
+ return ret
72
77
  }
73
- return ret
74
78
  end
75
79
  if (public_method_defined? meth_old) then public meth
76
80
  elsif (protected_method_defined? meth_old) then protected meth
@@ -117,7 +121,7 @@ RUBY
117
121
  end
118
122
 
119
123
  # [+default_class+] should be a class
120
- def self.process_type_args(default_class, *args, &blk)
124
+ def self.process_type_args(default_class, *args)
121
125
  klass = meth = type = nil
122
126
  default_class = "Object" if (default_class.is_a? Object) && (default_class.to_s == "main") # special case for main
123
127
  if args.size == 3
@@ -179,13 +183,13 @@ RUBY
179
183
  # called by Object#method_added (sing=false) and Object#singleton_method_added (sing=true)
180
184
  def self.do_method_added(the_self, sing, klass, meth)
181
185
  # Apply any deferred contracts and reset list
182
- if sing
183
- loc = the_self.singleton_method(meth).source_location
184
- else
185
- loc = the_self.instance_method(meth).source_location
186
- end
187
186
 
188
187
  if $__rdl_deferred.size > 0
188
+ if sing
189
+ loc = the_self.singleton_method(meth).source_location
190
+ else
191
+ loc = the_self.instance_method(meth).source_location
192
+ end
189
193
  $__rdl_info.set(klass, meth, :source_location, loc)
190
194
  a = $__rdl_deferred
191
195
  $__rdl_deferred = [] # Reset before doing more work to avoid infinite recursion
@@ -195,10 +199,12 @@ RUBY
195
199
  else
196
200
  tmp_klass = klass
197
201
  end
198
- raise RuntimeError, "Deferred contract from class #{prev_klass} being applied in class #{tmp_klass}" if prev_klass != tmp_klass
202
+ if (!h[:class_check] && (prev_klass != tmp_klass)) || (h[:class_check] && (h[:class_check].to_s != tmp_klass))
203
+ raise RuntimeError, "Deferred #{kind} contract from class #{prev_klass} being applied in class #{tmp_klass} to #{meth}"
204
+ end
199
205
  $__rdl_info.add(klass, meth, kind, contract)
200
206
  RDL::Wrap.wrap(klass, meth) if h[:wrap]
201
- unless $__rdl_info.set(klass, meth, :typecheck, h[:typecheck])
207
+ unless !h.has_key?(:typecheck) || $__rdl_info.set(klass, meth, :typecheck, h[:typecheck])
202
208
  raise RuntimeError, "Inconsistent typecheck flag on #{RDL::Util.pp_klass_method(klass, meth)}"
203
209
  end
204
210
  RDL::Typecheck.typecheck(klass, meth) if h[:typecheck] == :now
@@ -213,14 +219,24 @@ RUBY
213
219
  if $__rdl_to_wrap.member? [klass, meth]
214
220
  $__rdl_to_wrap.delete [klass, meth]
215
221
  RDL::Wrap.wrap(klass, meth)
222
+ if sing
223
+ loc = the_self.singleton_method(meth).source_location
224
+ else
225
+ loc = the_self.instance_method(meth).source_location
226
+ end
216
227
  $__rdl_info.set(klass, meth, :source_location, loc)
217
228
  end
218
229
 
219
- # Type check method if requested; must typecheck before wrap
230
+ # Type check method if requested
220
231
  if $__rdl_to_typecheck[:now].member? [klass, meth]
221
232
  $__rdl_to_typecheck[:now].delete [klass, meth]
222
233
  RDL::Typecheck.typecheck(klass, meth)
223
234
  end
235
+
236
+ if RDL::Config.instance.guess_types.include?(the_self.to_s.to_sym) && !$__rdl_info.has?(klass, meth, :type)
237
+ # Added a method with no type annotation from a class we want to guess types for
238
+ RDL::Wrap.wrap(klass, meth)
239
+ end
224
240
  end
225
241
  end
226
242
 
@@ -238,41 +254,39 @@ class Object
238
254
  # pre(meth) { block } = pre(self, meth, FlatContract.new { block })
239
255
  # pre(contract) = pre(self, next method, contract)
240
256
  # pre { block } = pre(self, next method, FlatContract.new { block })
241
- def pre(*args, wrap: true, &blk)
242
- $__rdl_contract_switch.off { # Don't check contracts inside RDL code itself
243
- klass, meth, contract = RDL::Wrap.process_pre_post_args(self, "Precondition", *args, &blk)
244
- if meth
245
- $__rdl_info.add(klass, meth, :pre, contract)
246
- if wrap
247
- if RDL::Util.method_defined?(klass, meth) || meth == :initialize # there is always an initialize
248
- RDL::Wrap.wrap(klass, meth)
249
- else
250
- $__rdl_to_wrap << [klass, meth]
251
- end
257
+ def pre(*args, wrap: RDL::Config.instance.pre_defaults[:wrap], &blk)
258
+ klass, meth, contract = RDL::Wrap.process_pre_post_args(self, "Precondition", *args, &blk)
259
+ if meth
260
+ $__rdl_info.add(klass, meth, :pre, contract)
261
+ if wrap
262
+ if RDL::Util.method_defined?(klass, meth) || meth == :initialize # there is always an initialize
263
+ RDL::Wrap.wrap(klass, meth)
264
+ else
265
+ $__rdl_to_wrap << [klass, meth]
252
266
  end
253
- else
254
- $__rdl_deferred << [klass, :pre, contract, {wrap: wrap}]
255
267
  end
256
- }
268
+ else
269
+ $__rdl_deferred << [klass, :pre, contract, {wrap: wrap}]
270
+ end
271
+ nil
257
272
  end
258
273
 
259
274
  # Add a postcondition to a method. Same possible invocations as pre.
260
- def post(*args, wrap: true, &blk)
261
- $__rdl_contract_switch.off {
262
- klass, meth, contract = RDL::Wrap.process_pre_post_args(self, "Postcondition", *args, &blk)
263
- if meth
264
- $__rdl_info.add(klass, meth, :post, contract)
265
- if wrap
266
- if RDL::Util.method_defined?(klass, meth) || meth == :initialize
267
- RDL::Wrap.wrap(klass, meth)
268
- else
269
- $__rdl_to_wrap << [klass, meth]
270
- end
275
+ def post(*args, wrap: RDL::Config.instance.post_defaults[:wrap], &blk)
276
+ klass, meth, contract = RDL::Wrap.process_pre_post_args(self, "Postcondition", *args, &blk)
277
+ if meth
278
+ $__rdl_info.add(klass, meth, :post, contract)
279
+ if wrap
280
+ if RDL::Util.method_defined?(klass, meth) || meth == :initialize
281
+ RDL::Wrap.wrap(klass, meth)
282
+ else
283
+ $__rdl_to_wrap << [klass, meth]
271
284
  end
272
- else
273
- $__rdl_deferred << [klass, :post, contract, {wrap: wrap}]
274
285
  end
275
- }
286
+ else
287
+ $__rdl_deferred << [klass, :post, contract, {wrap: wrap}]
288
+ end
289
+ nil
276
290
  end
277
291
 
278
292
  # [+ klass +] may be Class, Symbol, or String
@@ -288,35 +302,35 @@ class Object
288
302
  # type(klass, meth, type)
289
303
  # type(meth, type)
290
304
  # type(type)
291
- def type(*args, wrap: true, typecheck: false, &blk)
292
- $__rdl_contract_switch.off {
293
- klass, meth, type = begin
294
- RDL::Wrap.process_type_args(self, *args, &blk)
295
- rescue Racc::ParseError => err
296
- # Remove enough backtrace to only include actual source line
297
- # Warning: Adjust the -5 below if the code (or this comment) changes
298
- bt = err.backtrace
299
- bt.shift until bt[0] =~ /^#{__FILE__}:#{__LINE__-5}/
300
- bt.shift # remove $__rdl_contract_switch.off call
301
- bt.shift # remove type call itself
302
- err.set_backtrace bt
303
- raise err
304
- end
305
- if meth
305
+ def type(*args, wrap: RDL::Config.instance.type_defaults[:wrap], typecheck: RDL::Config.instance.type_defaults[:typecheck])
306
+ klass, meth, type = begin
307
+ RDL::Wrap.process_type_args(self, *args)
308
+ rescue Racc::ParseError => err
309
+ # Remove enough backtrace to only include actual source line
310
+ # Warning: Adjust the -5 below if the code (or this comment) changes
311
+ bt = err.backtrace
312
+ bt.shift until bt[0] =~ /^#{__FILE__}:#{__LINE__-5}/
313
+ bt.shift # remove $__rdl_contract_switch.off call
314
+ bt.shift # remove type call itself
315
+ err.set_backtrace bt
316
+ raise err
317
+ end
318
+ if meth
306
319
  # It turns out Ruby core/stdlib don't always follow this convention...
307
320
  # if (meth.to_s[-1] == "?") && (type.ret != $__rdl_type_bool)
308
321
  # warn "#{RDL::Util.pp_klass_method(klass, meth)}: methods that end in ? should have return type %bool"
309
322
  # end
310
- $__rdl_info.add(klass, meth, :type, type)
311
- unless $__rdl_info.set(klass, meth, :typecheck, typecheck)
312
- raise RuntimeError, "Inconsistent typecheck flag on #{RDL::Util.pp_klass_method(klass, meth)}"
313
- end
314
- if wrap
315
- if RDL::Util.method_defined?(klass, meth) || meth == :initialize
316
- $__rdl_info.set(klass, meth, :source_location, RDL::Util.to_class(klass).instance_method(meth).source_location)
317
- RDL::Typecheck.typecheck(klass, meth) if typecheck == :now
318
- RDL::Wrap.wrap(klass, meth)
319
- else
323
+ $__rdl_info.add(klass, meth, :type, type)
324
+ unless $__rdl_info.set(klass, meth, :typecheck, typecheck)
325
+ raise RuntimeError, "Inconsistent typecheck flag on #{RDL::Util.pp_klass_method(klass, meth)}"
326
+ end
327
+ if wrap || typecheck == :now
328
+ if RDL::Util.method_defined?(klass, meth) || meth == :initialize
329
+ $__rdl_info.set(klass, meth, :source_location, RDL::Util.to_class(klass).instance_method(meth).source_location)
330
+ RDL::Typecheck.typecheck(klass, meth) if typecheck == :now
331
+ RDL::Wrap.wrap(klass, meth) if wrap
332
+ else
333
+ if wrap
320
334
  $__rdl_to_wrap << [klass, meth]
321
335
  if (typecheck && typecheck != :call)
322
336
  $__rdl_to_typecheck[typecheck] = Set.new unless $__rdl_to_typecheck[typecheck]
@@ -324,11 +338,12 @@ class Object
324
338
  end
325
339
  end
326
340
  end
327
- else
328
- $__rdl_deferred << [klass, :type, type, {wrap: wrap,
329
- typecheck: typecheck}]
330
341
  end
331
- }
342
+ else
343
+ $__rdl_deferred << [klass, :type, type, {wrap: wrap,
344
+ typecheck: typecheck}]
345
+ end
346
+ nil
332
347
  end
333
348
 
334
349
  # [+ klass +] is the class containing the variable; self if omitted; ignored for local and global variables
@@ -342,6 +357,7 @@ class Object
342
357
  unless $__rdl_info.set(klass, var, :type, $__rdl_parser.scan_str("#T #{typ}"))
343
358
  raise RuntimeError, "Type already declared for #{var}"
344
359
  end
360
+ nil
345
361
  end
346
362
 
347
363
  # In the following three methods
@@ -355,6 +371,7 @@ class Object
355
371
  type name, "() -> #{typ}"
356
372
  type name.to_s + "=", "(#{typ}) -> #{typ}"
357
373
  }
374
+ nil
358
375
  end
359
376
 
360
377
  def attr_reader_type(*args)
@@ -363,6 +380,7 @@ class Object
363
380
  var_type ("@" + name.to_s), typ
364
381
  type name, "() -> #{typ}"
365
382
  }
383
+ nil
366
384
  end
367
385
 
368
386
  alias_method :attr_type, :attr_reader_type
@@ -373,46 +391,43 @@ class Object
373
391
  var_type ("@" + name.to_s), typ
374
392
  type name.to_s + "=", "(#{typ}) -> #{typ}"
375
393
  }
394
+ nil
376
395
  end
377
396
 
378
-
379
397
  def self.method_added(meth)
380
- $__rdl_contract_switch.off {
381
- klass = self.to_s
382
- klass = "Object" if (klass.is_a? Object) && (klass.to_s == "main")
383
- RDL::Wrap.do_method_added(self, false, klass, meth)
384
- }
398
+ klass = self.to_s
399
+ klass = "Object" if (klass.is_a? Object) && (klass.to_s == "main")
400
+ RDL::Wrap.do_method_added(self, false, klass, meth)
401
+ nil
385
402
  end
386
403
 
387
404
  def self.singleton_method_added(meth)
388
- $__rdl_contract_switch.off {
389
- klass = self.to_s
390
- klass = "Object" if (klass.is_a? Object) && (klass.to_s == "main")
391
- sklass = RDL::Util.add_singleton_marker(klass)
392
- RDL::Wrap.do_method_added(self, true, sklass, meth)
393
- }
405
+ klass = self.to_s
406
+ klass = "Object" if (klass.is_a? Object) && (klass.to_s == "main")
407
+ sklass = RDL::Util.add_singleton_marker(klass)
408
+ RDL::Wrap.do_method_added(self, true, sklass, meth)
409
+ nil
394
410
  end
395
411
 
396
412
  # Aliases contracts for meth_old and meth_new. Currently, this must
397
413
  # be called for any aliases or they will not be wrapped with
398
414
  # contracts. Only creates aliases in the current class.
399
415
  def rdl_alias(new_name, old_name)
400
- $__rdl_contract_switch.off {
401
- klass = self.to_s
402
- klass = "Object" if (klass.is_a? Object) && (klass.to_s == "main")
403
- $__rdl_aliases[klass] = {} unless $__rdl_aliases[klass]
404
- if $__rdl_aliases[klass][new_name]
405
- raise RuntimeError,
406
- "Tried to alias #{new_name}, already aliased to #{$__rdl_aliases[klass][new_name]}"
407
- end
408
- $__rdl_aliases[klass][new_name] = old_name
416
+ klass = self.to_s
417
+ klass = "Object" if (klass.is_a? Object) && (klass.to_s == "main")
418
+ $__rdl_aliases[klass] = {} unless $__rdl_aliases[klass]
419
+ if $__rdl_aliases[klass][new_name]
420
+ raise RuntimeError,
421
+ "Tried to alias #{new_name}, already aliased to #{$__rdl_aliases[klass][new_name]}"
422
+ end
423
+ $__rdl_aliases[klass][new_name] = old_name
409
424
 
410
- if self.method_defined? new_name
411
- RDL::Wrap.wrap(klass, new_name)
412
- else
413
- $__rdl_to_wrap << [klass, old_name]
414
- end
415
- }
425
+ if self.method_defined? new_name
426
+ RDL::Wrap.wrap(klass, new_name)
427
+ else
428
+ $__rdl_to_wrap << [klass, old_name]
429
+ end
430
+ nil
416
431
  end
417
432
 
418
433
  # [+params+] is an array of symbols or strings that are the
@@ -428,85 +443,78 @@ class Object
428
443
  # parameters of the class, and the block should return true if and
429
444
  # only if self is a member of self.class<typs>.
430
445
  def type_params(params, all, variance: nil, &blk)
431
- $__rdl_contract_switch.off {
432
- raise RuntimeError, "Empty type parameters not allowed" if params.empty?
433
- klass = self.to_s
434
- klass = "Object" if (klass.is_a? Object) && (klass.to_s == "main")
435
- if $__rdl_type_params[klass]
436
- raise RuntimeError, "#{klass} already has type parameters #{$__rdl_type_params[klass]}"
437
- end
438
- params = params.map { |v|
439
- raise RuntimeError, "Type parameter #{v.inspect} is not symbol or string" unless v.class == String || v.class == Symbol
440
- v.to_sym
441
- }
442
- raise RuntimeError, "Duplicate type parameters not allowed" unless params.uniq.size == params.size
443
- raise RuntimeError, "Expecting #{params.size} variance annotations, got #{variance.size}" if variance && params.size != variance.size
444
- raise RuntimeError, "Only :+, +-, and :~ are allowed variance annotations" unless (not variance) || variance.all? { |v| [:+, :-, :~].member? v }
445
- raise RuntimeError, "Can't pass both all and a block" if all && blk
446
- raise RuntimeError, "all must be a symbol" unless (not all) || (all.instance_of? Symbol)
447
- chk = all || blk
448
- raise RuntimeError, "At least one of {all, blk} required" unless chk
449
- variance = params.map { |p| :~ } unless variance # default to invariant
450
- $__rdl_type_params[klass] = [params, variance, chk]
446
+ raise RuntimeError, "Empty type parameters not allowed" if params.empty?
447
+ klass = self.to_s
448
+ klass = "Object" if (klass.is_a? Object) && (klass.to_s == "main")
449
+ if $__rdl_type_params[klass]
450
+ raise RuntimeError, "#{klass} already has type parameters #{$__rdl_type_params[klass]}"
451
+ end
452
+ params = params.map { |v|
453
+ raise RuntimeError, "Type parameter #{v.inspect} is not symbol or string" unless v.class == String || v.class == Symbol
454
+ v.to_sym
451
455
  }
456
+ raise RuntimeError, "Duplicate type parameters not allowed" unless params.uniq.size == params.size
457
+ raise RuntimeError, "Expecting #{params.size} variance annotations, got #{variance.size}" if variance && params.size != variance.size
458
+ raise RuntimeError, "Only :+, +-, and :~ are allowed variance annotations" unless (not variance) || variance.all? { |v| [:+, :-, :~].member? v }
459
+ raise RuntimeError, "Can't pass both all and a block" if all && blk
460
+ raise RuntimeError, "all must be a symbol" unless (not all) || (all.instance_of? Symbol)
461
+ chk = all || blk
462
+ raise RuntimeError, "At least one of {all, blk} required" unless chk
463
+ variance = params.map { |p| :~ } unless variance # default to invariant
464
+ $__rdl_type_params[klass] = [params, variance, chk]
465
+ nil
452
466
  end
453
467
 
454
468
  def rdl_nowrap
455
- $__rdl_contract_switch.off {
456
- RDL.config { |config| config.add_nowrap(self, self.singleton_class) }
457
- }
469
+ RDL.config { |config| config.add_nowrap(self, self.singleton_class) }
470
+ nil
458
471
  end
459
472
 
460
473
  # [+typs+] is an array of types, classes, symbols, or strings to instantiate
461
474
  # the type parameters. If a class, symbol, or string is given, it is
462
475
  # converted to a NominalType.
463
476
  def instantiate!(*typs)
464
- $__rdl_contract_switch.off {
465
- klass = self.class.to_s
466
- klass = "Object" if (klass.is_a? Object) && (klass.to_s == "main")
467
- formals, _, all = $__rdl_type_params[klass]
468
- raise RuntimeError, "Receiver is of class #{klass}, which is not parameterized" unless formals
469
- raise RuntimeError, "Expecting #{params.size} type parameters, got #{typs.size}" unless formals.size == typs.size
470
- raise RuntimeError, "Instance already has type instantiation" if (defined? @__rdl_type) && @rdl_type
471
- new_typs = typs.map { |t| if t.is_a? RDL::Type::Type then t else $__rdl_parser.scan_str "#T #{t}" end }
472
- t = RDL::Type::GenericType.new(RDL::Type::NominalType.new(klass), *new_typs)
473
- if all.instance_of? Symbol
474
- self.send(all) { |*objs|
475
- new_typs.zip(objs).each { |nt, obj|
476
- if nt.instance_of? RDL::Type::GenericType # require obj to be instantiated
477
- t_obj = RDL::Util.rdl_type(obj)
478
- raise RDL::Type::TypeError, "Expecting element of type #{nt.to_s}, but got uninstantiated object #{obj.inspect}" unless t_obj
479
- raise RDL::Type::TypeError, "Expecting type #{nt.to_s}, got type #{t_obj.to_s}" unless t_obj <= nt
480
- else
481
- raise RDL::Type::TypeError, "Expecting type #{nt.to_s}, got #{obj.inspect}" unless nt.member? obj
482
- end
483
- }
477
+ klass = self.class.to_s
478
+ klass = "Object" if (klass.is_a? Object) && (klass.to_s == "main")
479
+ formals, _, all = $__rdl_type_params[klass]
480
+ raise RuntimeError, "Receiver is of class #{klass}, which is not parameterized" unless formals
481
+ raise RuntimeError, "Expecting #{params.size} type parameters, got #{typs.size}" unless formals.size == typs.size
482
+ raise RuntimeError, "Instance already has type instantiation" if (defined? @__rdl_type) && @rdl_type
483
+ new_typs = typs.map { |t| if t.is_a? RDL::Type::Type then t else $__rdl_parser.scan_str "#T #{t}" end }
484
+ t = RDL::Type::GenericType.new(RDL::Type::NominalType.new(klass), *new_typs)
485
+ if all.instance_of? Symbol
486
+ self.send(all) { |*objs|
487
+ new_typs.zip(objs).each { |nt, obj|
488
+ if nt.instance_of? RDL::Type::GenericType # require obj to be instantiated
489
+ t_obj = RDL::Util.rdl_type(obj)
490
+ raise RDL::Type::TypeError, "Expecting element of type #{nt.to_s}, but got uninstantiated object #{obj.inspect}" unless t_obj
491
+ raise RDL::Type::TypeError, "Expecting type #{nt.to_s}, got type #{t_obj.to_s}" unless t_obj <= nt
492
+ else
493
+ raise RDL::Type::TypeError, "Expecting type #{nt.to_s}, got #{obj.inspect}" unless nt.member? obj
494
+ end
484
495
  }
485
- else
486
- raise RDL::Type::TypeError, "Not an instance of #{t}" unless instance_exec(*new_typs, &all)
487
- end
488
- @__rdl_type = t
489
- self
490
- }
496
+ }
497
+ else
498
+ raise RDL::Type::TypeError, "Not an instance of #{t}" unless instance_exec(*new_typs, &all)
499
+ end
500
+ @__rdl_type = t
501
+ self
491
502
  end
492
503
 
493
504
  def deinstantiate!
494
- $__rdl_contract_switch.off {
495
- raise RuntimeError, "Class #{self.to_s} is not parameterized" unless $__rdl_type_params[klass]
496
- raise RuntimeError, "Instance is not instantiated" unless @__rdl_type && @@__rdl_type.instance_of?(RDL::Type::GenericType)
497
- @__rdl_type = nil
498
- }
505
+ raise RuntimeError, "Class #{self.to_s} is not parameterized" unless $__rdl_type_params[klass]
506
+ raise RuntimeError, "Instance is not instantiated" unless @__rdl_type && @@__rdl_type.instance_of?(RDL::Type::GenericType)
507
+ @__rdl_type = nil
508
+ self
499
509
  end
500
510
 
501
511
  # Returns a new object that wraps self in a type cast. If force is true this cast is *unchecked*, so use with caution
502
512
  def type_cast(typ, force: false)
503
- $__rdl_contract_switch.off {
504
- new_typ = if typ.is_a? RDL::Type::Type then typ else $__rdl_parser.scan_str "#T #{typ}" end
505
- raise RuntimeError, "type cast error: self not a member of #{new_typ}" unless force || typ.member?(self)
506
- obj = SimpleDelegator.new(self)
507
- obj.instance_variable_set('@__rdl_type', new_typ)
508
- return obj
509
- }
513
+ new_typ = if typ.is_a? RDL::Type::Type then typ else $__rdl_parser.scan_str "#T #{typ}" end
514
+ raise RuntimeError, "type cast error: self not a member of #{new_typ}" unless force || typ.member?(self)
515
+ obj = SimpleDelegator.new(self)
516
+ obj.instance_variable_set('@__rdl_type', new_typ)
517
+ obj
510
518
  end
511
519
 
512
520
  # Add a new type alias.
@@ -514,18 +522,17 @@ class Object
514
522
  # [+typ+] can be either a string, in which case it will be parsed
515
523
  # into a type, or a Type.
516
524
  def type_alias(name, typ)
517
- $__rdl_contract_switch.off {
518
- raise RuntimeError, "Attempt to redefine type #{name}" if $__rdl_special_types[name]
519
- case typ
520
- when String
521
- t = $__rdl_parser.scan_str "#T #{typ}"
522
- $__rdl_special_types[name] = t
523
- when RDL::Type::Type
524
- $__rdl_special_types[name] = typ
525
- else
526
- raise RuntimeError, "Unexpected typ argument #{typ.inspect}"
527
- end
528
- }
525
+ raise RuntimeError, "Attempt to redefine type #{name}" if $__rdl_special_types[name]
526
+ case typ
527
+ when String
528
+ t = $__rdl_parser.scan_str "#T #{typ}"
529
+ $__rdl_special_types[name] = t
530
+ when RDL::Type::Type
531
+ $__rdl_special_types[name] = typ
532
+ else
533
+ raise RuntimeError, "Unexpected typ argument #{typ.inspect}"
534
+ end
535
+ nil
529
536
  end
530
537
 
531
538
  # Type check all methods that had annotation `typecheck: sym' at type call
@@ -534,6 +541,29 @@ class Object
534
541
  RDL::Typecheck.typecheck(klass, meth)
535
542
  }
536
543
  $__rdl_to_typecheck[sym] = Array.new
544
+ nil
545
+ end
546
+
547
+ # Does nothing at run time
548
+ def rdl_note_type(x)
549
+ return x
550
+ end
551
+
552
+ def rdl_remove_type(klass, meth)
553
+ raise RuntimeError, "No existing type for #{RDL::Util.pp_klass_method(klass, meth)}" unless $__rdl_info.has? klass, meth, :type
554
+ $__rdl_info.remove klass, meth, :type
555
+ nil
537
556
  end
538
557
 
539
558
  end
559
+
560
+ # method_added for Object doesn't get called on module methods...bug?
561
+ # class Module
562
+ # def method_added(meth)
563
+ # $__rdl_contract_switch.off {
564
+ # klass = self.to_s
565
+ # RDL::Wrap.do_method_added(self, false, klass, meth)
566
+ # nil
567
+ # }
568
+ # end
569
+ # end