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.
- checksums.yaml +4 -4
- data/CHANGES.md +7 -1
- data/README.md +94 -20
- data/lib/rdl.rb +4 -1
- data/lib/rdl/config.rb +90 -3
- data/lib/rdl/info.rb +16 -0
- data/lib/rdl/typecheck.rb +207 -79
- data/lib/rdl/types/bot.rb +1 -1
- data/lib/rdl/types/dependent_arg.rb +17 -8
- data/lib/rdl/types/{finitehash.rb → finite_hash.rb} +3 -29
- data/lib/rdl/types/generic.rb +1 -37
- data/lib/rdl/types/intersection.rb +1 -0
- data/lib/rdl/types/lexer.rex +2 -1
- data/lib/rdl/types/lexer.rex.rb +4 -1
- data/lib/rdl/types/method.rb +2 -12
- data/lib/rdl/types/nominal.rb +1 -22
- data/lib/rdl/types/non_null.rb +50 -0
- data/lib/rdl/types/parser.racc +3 -1
- data/lib/rdl/types/parser.tab.rb +222 -190
- data/lib/rdl/types/singleton.rb +1 -6
- data/lib/rdl/types/structural.rb +1 -10
- data/lib/rdl/types/top.rb +1 -2
- data/lib/rdl/types/tuple.rb +3 -19
- data/lib/rdl/types/type.rb +223 -0
- data/lib/rdl/types/union.rb +12 -2
- data/lib/rdl/types/var.rb +4 -1
- data/lib/rdl/types/wild_query.rb +1 -0
- data/lib/rdl/wrap.rb +199 -169
- data/lib/rdl_disable.rb +41 -0
- data/lib/rdl_types.rb +1 -4
- data/{types/ruby-2.x → lib/types/core-ruby-2.x}/_aliases.rb +0 -0
- data/{types/ruby-2.x → lib/types/core-ruby-2.x}/abbrev.rb +0 -0
- data/{types/ruby-2.x → lib/types/core-ruby-2.x}/array.rb +56 -56
- data/{types/ruby-2.x → lib/types/core-ruby-2.x}/base64.rb +0 -0
- data/{types/ruby-2.x → lib/types/core-ruby-2.x}/basic_object.rb +0 -0
- data/{types/ruby-2.x → lib/types/core-ruby-2.x}/benchmark.rb +0 -0
- data/{types/ruby-2.x → lib/types/core-ruby-2.x}/bigdecimal.rb +0 -0
- data/{types/ruby-2.x → lib/types/core-ruby-2.x}/bigmath.rb +0 -0
- data/{types/ruby-2.x → lib/types/core-ruby-2.x}/bignum.rb +0 -0
- data/{types/ruby-2.x → lib/types/core-ruby-2.x}/class.rb +0 -0
- data/{types/ruby-2.x → lib/types/core-ruby-2.x}/complex.rb +0 -0
- data/{types/ruby-2.x → lib/types/core-ruby-2.x}/coverage.rb +0 -0
- data/{types/ruby-2.x → lib/types/core-ruby-2.x}/csv.rb +0 -0
- data/{types/ruby-2.x → lib/types/core-ruby-2.x}/date.rb +0 -0
- data/{types/ruby-2.x → lib/types/core-ruby-2.x}/dir.rb +0 -0
- data/{types/ruby-2.x → lib/types/core-ruby-2.x}/encoding.rb +0 -0
- data/{types/ruby-2.x → lib/types/core-ruby-2.x}/enumerable.rb +0 -0
- data/{types/ruby-2.x → lib/types/core-ruby-2.x}/enumerator.rb +0 -0
- data/{types/ruby-2.x → lib/types/core-ruby-2.x}/exception.rb +0 -0
- data/{types/ruby-2.x → lib/types/core-ruby-2.x}/file.rb +0 -0
- data/{types/ruby-2.x → lib/types/core-ruby-2.x}/fileutils.rb +0 -0
- data/{types/ruby-2.x → lib/types/core-ruby-2.x}/fixnum.rb +0 -0
- data/{types/ruby-2.x → lib/types/core-ruby-2.x}/float.rb +26 -26
- data/{types/ruby-2.x → lib/types/core-ruby-2.x}/gem.rb +0 -0
- data/{types/ruby-2.x → lib/types/core-ruby-2.x}/hash.rb +0 -0
- data/{types/ruby-2.x → lib/types/core-ruby-2.x}/integer.rb +8 -8
- data/{types/ruby-2.x → lib/types/core-ruby-2.x}/io.rb +0 -0
- data/{types/ruby-2.x → lib/types/core-ruby-2.x}/kernel.rb +12 -11
- data/{types/ruby-2.x → lib/types/core-ruby-2.x}/marshal.rb +0 -0
- data/{types/ruby-2.x → lib/types/core-ruby-2.x}/matchdata.rb +0 -0
- data/{types/ruby-2.x → lib/types/core-ruby-2.x}/math.rb +0 -0
- data/{types/ruby-2.x → lib/types/core-ruby-2.x}/module.rb +0 -0
- data/{types/ruby-2.x → lib/types/core-ruby-2.x}/nil.rb +0 -0
- data/{types/ruby-2.x → lib/types/core-ruby-2.x}/numeric.rb +0 -0
- data/lib/types/core-ruby-2.x/object.rb +75 -0
- data/{types/ruby-2.x → lib/types/core-ruby-2.x}/pathname.rb +0 -0
- data/{types/ruby-2.x → lib/types/core-ruby-2.x}/process.rb +0 -0
- data/{types/ruby-2.x → lib/types/core-ruby-2.x}/random.rb +0 -0
- data/{types/ruby-2.x → lib/types/core-ruby-2.x}/range.rb +0 -0
- data/{types/ruby-2.x → lib/types/core-ruby-2.x}/rational.rb +0 -0
- data/{types/ruby-2.x → lib/types/core-ruby-2.x}/regexp.rb +0 -0
- data/{types/ruby-2.x → lib/types/core-ruby-2.x}/set.rb +0 -0
- data/{types/ruby-2.x → lib/types/core-ruby-2.x}/string.rb +0 -0
- data/{types/ruby-2.x → lib/types/core-ruby-2.x}/strscan.rb +0 -0
- data/{types/ruby-2.x → lib/types/core-ruby-2.x}/symbol.rb +0 -0
- data/{types/ruby-2.x → lib/types/core-ruby-2.x}/time.rb +0 -0
- data/{types/ruby-2.x → lib/types/core-ruby-2.x}/uri.rb +0 -0
- data/{types/ruby-2.x → lib/types/core-ruby-2.x}/yaml.rb +0 -0
- data/lib/types/core.rb +4 -0
- data/rdl.gemspec +2 -2
- data/test/test_le.rb +77 -35
- data/test/test_parser.rb +75 -59
- data/test/test_rdl.rb +18 -0
- data/test/test_typecheck.rb +73 -4
- metadata +54 -57
- data/lib/rails_types.rb +0 -1
- data/types/rails-4.2.1/fixnum.rb +0 -3
- data/types/rails-4.2.1/string.rb +0 -3
- data/types/rails-tmp/action_dispatch.rb +0 -406
- data/types/rails-tmp/active_record.rb +0 -406
- data/types/rails-tmp/devise_contracts.rb +0 -216
- data/types/ruby-2.x/object.rb +0 -73
data/lib/rdl/types/singleton.rb
CHANGED
@@ -53,12 +53,7 @@ module RDL::Type
|
|
53
53
|
end
|
54
54
|
|
55
55
|
def <=(other)
|
56
|
-
|
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)
|
data/lib/rdl/types/structural.rb
CHANGED
@@ -34,16 +34,7 @@ module RDL::Type
|
|
34
34
|
end
|
35
35
|
|
36
36
|
def <=(other)
|
37
|
-
|
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)
|
data/lib/rdl/types/top.rb
CHANGED
data/lib/rdl/types/tuple.rb
CHANGED
@@ -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
|
-
|
7
|
-
|
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
|
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)
|
data/lib/rdl/types/type.rb
CHANGED
@@ -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
|
data/lib/rdl/types/union.rb
CHANGED
@@ -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
|
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
|
-
|
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)
|
data/lib/rdl/types/var.rb
CHANGED
@@ -32,7 +32,10 @@ module RDL::Type
|
|
32
32
|
|
33
33
|
alias eql? ==
|
34
34
|
|
35
|
-
|
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
|
data/lib/rdl/types/wild_query.rb
CHANGED
data/lib/rdl/wrap.rb
CHANGED
@@ -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
|
-
|
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
|
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
|
-
|
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
|
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:
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
if
|
247
|
-
|
248
|
-
|
249
|
-
|
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:
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
if
|
266
|
-
|
267
|
-
|
268
|
-
|
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:
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
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
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
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
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
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
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
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
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
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
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
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
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
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
|
-
|
456
|
-
|
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
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
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
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
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
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
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
|
-
$
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
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
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
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
|