rdl 2.0.0.rc2 → 2.0.0.rc3
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.
- 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
|