rdl 2.0.1 → 2.1.0
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/.travis.yml +13 -0
- data/CHANGES.md +35 -0
- data/README.md +153 -116
- data/bin/rdl_query +1 -1
- data/extras/type_tests/typetests.rb +905 -0
- data/lib/rdl.rb +0 -1
- data/lib/rdl/boot.rb +108 -77
- data/lib/rdl/boot_rails.rb +2 -8
- data/lib/rdl/config.rb +44 -17
- data/lib/rdl/contracts/flat.rb +1 -1
- data/lib/rdl/info.rb +3 -3
- data/lib/rdl/query.rb +11 -11
- data/lib/rdl/typecheck.rb +399 -136
- data/lib/rdl/types/finite_hash.rb +3 -2
- data/lib/rdl/types/generic.rb +7 -6
- data/lib/rdl/types/intersection.rb +3 -2
- data/lib/rdl/types/lexer.rex +2 -3
- data/lib/rdl/types/lexer.rex.rb +1 -4
- data/lib/rdl/types/method.rb +7 -6
- data/lib/rdl/types/nominal.rb +10 -1
- data/lib/rdl/types/parser.racc +7 -8
- data/lib/rdl/types/parser.tab.rb +108 -109
- data/lib/rdl/types/structural.rb +1 -0
- data/lib/rdl/types/tuple.rb +2 -2
- data/lib/rdl/types/type.rb +8 -8
- data/lib/rdl/types/type_inferencer.rb +1 -1
- data/lib/rdl/types/union.rb +2 -1
- data/lib/rdl/util.rb +28 -3
- data/lib/rdl/wrap.rb +216 -165
- data/lib/rdl_disable.rb +22 -15
- data/lib/types/core.rb +2 -4
- data/lib/types/core/_aliases.rb +14 -0
- data/lib/types/core/abbrev.rb +3 -0
- data/lib/types/core/array.rb +139 -0
- data/lib/types/core/base64.rb +8 -0
- data/lib/types/core/basic_object.rb +12 -0
- data/lib/types/core/benchmark.rb +9 -0
- data/lib/types/core/bigdecimal.rb +223 -0
- data/lib/types/core/bigmath.rb +10 -0
- data/lib/types/core/bignum.rb +214 -0
- data/lib/types/core/class.rb +15 -0
- data/lib/types/core/complex.rb +123 -0
- data/lib/types/core/coverage.rb +4 -0
- data/lib/types/core/csv.rb +3 -0
- data/lib/types/core/date.rb +4 -0
- data/lib/types/core/dir.rb +37 -0
- data/lib/types/core/encoding.rb +21 -0
- data/lib/types/core/enumerable.rb +96 -0
- data/lib/types/core/enumerator.rb +24 -0
- data/lib/types/core/exception.rb +15 -0
- data/lib/types/core/file.rb +125 -0
- data/lib/types/core/fileutils.rb +4 -0
- data/lib/types/core/fixnum.rb +213 -0
- data/lib/types/core/float.rb +199 -0
- data/lib/types/core/gem.rb +19 -0
- data/lib/types/core/hash.rb +72 -0
- data/lib/types/core/integer.rb +194 -0
- data/lib/types/core/io.rb +101 -0
- data/lib/types/core/kernel.rb +89 -0
- data/lib/types/core/marshal.rb +3 -0
- data/lib/types/core/matchdata.rb +24 -0
- data/lib/types/core/math.rb +50 -0
- data/lib/types/core/module.rb +81 -0
- data/lib/types/core/nil.rb +11 -0
- data/lib/types/core/numeric.rb +56 -0
- data/lib/types/core/object.rb +73 -0
- data/lib/types/core/pathname.rb +104 -0
- data/lib/types/core/proc.rb +12 -0
- data/lib/types/core/process.rb +110 -0
- data/lib/types/core/random.rb +13 -0
- data/lib/types/core/range.rb +37 -0
- data/lib/types/core/rational.rb +207 -0
- data/lib/types/core/regexp.rb +28 -0
- data/lib/types/core/set.rb +56 -0
- data/lib/types/core/string.rb +140 -0
- data/lib/types/core/strscan.rb +6 -0
- data/lib/types/core/symbol.rb +27 -0
- data/lib/types/core/time.rb +66 -0
- data/lib/types/core/uri.rb +18 -0
- data/lib/types/core/yaml.rb +3 -0
- data/lib/types/devise.rb +1 -0
- data/lib/types/devise/controller_helpers.rb +3 -0
- data/lib/types/devise/parameter_sanitizer.rb +2 -0
- data/lib/types/pundit.rb +2 -0
- data/lib/types/rails/_helpers.rb +50 -0
- data/lib/types/rails/abstract_controller/translation.rb +2 -0
- data/lib/types/rails/action_controller/base.rb +3 -0
- data/lib/types/rails/action_controller/instrumentation.rb +6 -0
- data/lib/types/rails/action_controller/metal.rb +3 -0
- data/lib/types/rails/action_controller/mime_responds.rb +13 -0
- data/lib/types/rails/action_controller/parameters.rb +3 -0
- data/lib/types/{rails-5.x → rails}/action_controller/strong_parameters.rb +4 -8
- data/lib/types/rails/action_dispatch/flashhash.rb +8 -0
- data/lib/types/rails/action_dispatch/routing.rb +10 -0
- data/lib/types/rails/action_mailer/base.rb +2 -0
- data/lib/types/rails/action_mailer/message_delivery.rb +2 -0
- data/lib/types/rails/action_view/helpers_sanitizehelper.rb +2 -0
- data/lib/types/rails/action_view/helpers_urlhelper.rb +5 -0
- data/lib/types/rails/active_model/errors.rb +14 -0
- data/lib/types/rails/active_model/validations.rb +2 -0
- data/lib/types/rails/active_record/associations.rb +208 -0
- data/lib/types/rails/active_record/base.rb +2 -0
- data/lib/types/rails/active_record/core.rb +2 -0
- data/lib/types/rails/active_record/finder_methods.rb +2 -0
- data/lib/types/rails/active_record/model_schema.rb +37 -0
- data/lib/types/rails/active_record/relation.rb +11 -0
- data/lib/types/rails/active_record/schema_types.rb +51 -0
- data/lib/types/rails/active_record/validations.rb +2 -0
- data/lib/types/rails/active_support/base.rb +2 -0
- data/lib/types/rails/active_support/logger.rb +3 -0
- data/lib/types/rails/active_support/tagged_logging.rb +2 -0
- data/lib/types/rails/active_support/time_with_zone.rb +13 -0
- data/lib/types/rails/active_support/time_zone.rb +2 -0
- data/lib/types/rails/fixnum.rb +2 -0
- data/lib/types/rails/integer.rb +2 -0
- data/lib/types/rails/rack/request.rb +2 -0
- data/lib/types/rails/string.rb +3 -0
- data/lib/types/rails/time.rb +1 -0
- data/rdl.gemspec +2 -2
- data/test/disabled_test_rdoc.rb +8 -8
- data/test/test_alias.rb +1 -0
- data/test/test_dsl.rb +4 -4
- data/test/test_generic.rb +45 -38
- data/test/test_intersection.rb +10 -10
- data/test/test_le.rb +103 -102
- data/test/test_member.rb +33 -33
- data/test/test_parser.rb +101 -96
- data/test/test_query.rb +84 -84
- data/test/test_rdl.rb +87 -52
- data/test/test_rdl_type.rb +26 -9
- data/test/test_type_contract.rb +32 -31
- data/test/test_typecheck.rb +802 -436
- data/test/test_types.rb +39 -39
- data/test/test_wrap.rb +3 -2
- metadata +91 -120
- data/extras/type_tests/%.rb +0 -171
- data/extras/type_tests/&.rb +0 -159
- data/extras/type_tests/**.rb +0 -222
- data/extras/type_tests/*.rb +0 -177
- data/extras/type_tests/+.rb +0 -170
- data/extras/type_tests/-.rb +0 -171
- data/extras/type_tests/1scomp.rb +0 -157
- data/extras/type_tests/<.rb +0 -170
- data/extras/type_tests/<<.rb +0 -159
- data/extras/type_tests/>>.rb +0 -159
- data/extras/type_tests/[].rb +0 -163
- data/extras/type_tests/^.rb +0 -159
- data/extras/type_tests/abs.rb +0 -155
- data/extras/type_tests/abs2.rb +0 -164
- data/extras/type_tests/angle.rb +0 -157
- data/extras/type_tests/arg.rb +0 -157
- data/extras/type_tests/bit_length.rb +0 -157
- data/extras/type_tests/ceil.rb +0 -157
- data/extras/type_tests/ceilRational.rb +0 -160
- data/extras/type_tests/conj.rb +0 -158
- data/extras/type_tests/defwhere.rb +0 -86
- data/extras/type_tests/denominator.rb +0 -157
- data/extras/type_tests/div.rb +0 -172
- data/extras/type_tests/divslash.rb +0 -179
- data/extras/type_tests/even?.rb +0 -157
- data/extras/type_tests/fdiv.rb +0 -244
- data/extras/type_tests/finite?.rb +0 -157
- data/extras/type_tests/floor.rb +0 -157
- data/extras/type_tests/floorRational.rb +0 -161
- data/extras/type_tests/hash.rb +0 -157
- data/extras/type_tests/imag.rb +0 -158
- data/extras/type_tests/infinite?.rb +0 -157
- data/extras/type_tests/modulo.rb +0 -171
- data/extras/type_tests/nan?.rb +0 -157
- data/extras/type_tests/neg.rb +0 -155
- data/extras/type_tests/next.rb +0 -157
- data/extras/type_tests/next_float.rb +0 -157
- data/extras/type_tests/numerator.rb +0 -157
- data/extras/type_tests/phase.rb +0 -157
- data/extras/type_tests/prev_float.rb +0 -157
- data/extras/type_tests/quo.rb +0 -179
- data/extras/type_tests/rationalize.rb +0 -157
- data/extras/type_tests/rationalizeArg.rb +0 -198
- data/extras/type_tests/real.rb +0 -157
- data/extras/type_tests/real?.rb +0 -157
- data/extras/type_tests/round.rb +0 -157
- data/extras/type_tests/roundArg.rb +0 -169
- data/extras/type_tests/size.rb +0 -157
- data/extras/type_tests/to_c.rb +0 -157
- data/extras/type_tests/to_f.rb +0 -155
- data/extras/type_tests/to_i.rb +0 -157
- data/extras/type_tests/to_r.rb +0 -157
- data/extras/type_tests/to_s.rb +0 -157
- data/extras/type_tests/truncate.rb +0 -157
- data/extras/type_tests/truncateArg.rb +0 -166
- data/extras/type_tests/type tests +0 -1
- data/extras/type_tests/zero?.rb +0 -155
- data/extras/type_tests/|.rb +0 -159
- data/lib/types/core-ruby-2.x/_aliases.rb +0 -15
- data/lib/types/core-ruby-2.x/abbrev.rb +0 -5
- data/lib/types/core-ruby-2.x/array.rb +0 -137
- data/lib/types/core-ruby-2.x/base64.rb +0 -10
- data/lib/types/core-ruby-2.x/basic_object.rb +0 -14
- data/lib/types/core-ruby-2.x/benchmark.rb +0 -11
- data/lib/types/core-ruby-2.x/bigdecimal.rb +0 -224
- data/lib/types/core-ruby-2.x/bigmath.rb +0 -12
- data/lib/types/core-ruby-2.x/bignum.rb +0 -214
- data/lib/types/core-ruby-2.x/class.rb +0 -17
- data/lib/types/core-ruby-2.x/complex.rb +0 -124
- data/lib/types/core-ruby-2.x/coverage.rb +0 -6
- data/lib/types/core-ruby-2.x/csv.rb +0 -5
- data/lib/types/core-ruby-2.x/date.rb +0 -6
- data/lib/types/core-ruby-2.x/dir.rb +0 -38
- data/lib/types/core-ruby-2.x/encoding.rb +0 -23
- data/lib/types/core-ruby-2.x/enumerable.rb +0 -98
- data/lib/types/core-ruby-2.x/enumerator.rb +0 -26
- data/lib/types/core-ruby-2.x/exception.rb +0 -17
- data/lib/types/core-ruby-2.x/file.rb +0 -126
- data/lib/types/core-ruby-2.x/fileutils.rb +0 -6
- data/lib/types/core-ruby-2.x/fixnum.rb +0 -213
- data/lib/types/core-ruby-2.x/float.rb +0 -199
- data/lib/types/core-ruby-2.x/gem.rb +0 -247
- data/lib/types/core-ruby-2.x/hash.rb +0 -72
- data/lib/types/core-ruby-2.x/integer.rb +0 -197
- data/lib/types/core-ruby-2.x/io.rb +0 -103
- data/lib/types/core-ruby-2.x/kernel.rb +0 -90
- data/lib/types/core-ruby-2.x/marshal.rb +0 -5
- data/lib/types/core-ruby-2.x/matchdata.rb +0 -26
- data/lib/types/core-ruby-2.x/math.rb +0 -53
- data/lib/types/core-ruby-2.x/module.rb +0 -83
- data/lib/types/core-ruby-2.x/nil.rb +0 -12
- data/lib/types/core-ruby-2.x/numeric.rb +0 -56
- data/lib/types/core-ruby-2.x/object.rb +0 -75
- data/lib/types/core-ruby-2.x/pathname.rb +0 -106
- data/lib/types/core-ruby-2.x/proc.rb +0 -16
- data/lib/types/core-ruby-2.x/process.rb +0 -127
- data/lib/types/core-ruby-2.x/random.rb +0 -17
- data/lib/types/core-ruby-2.x/range.rb +0 -39
- data/lib/types/core-ruby-2.x/rational.rb +0 -209
- data/lib/types/core-ruby-2.x/regexp.rb +0 -30
- data/lib/types/core-ruby-2.x/set.rb +0 -58
- data/lib/types/core-ruby-2.x/string.rb +0 -143
- data/lib/types/core-ruby-2.x/strscan.rb +0 -7
- data/lib/types/core-ruby-2.x/symbol.rb +0 -29
- data/lib/types/core-ruby-2.x/time.rb +0 -68
- data/lib/types/core-ruby-2.x/uri.rb +0 -20
- data/lib/types/core-ruby-2.x/yaml.rb +0 -5
- data/lib/types/rails-5.x/_helpers.rb +0 -52
- data/lib/types/rails-5.x/action_controller/mime_responds.rb +0 -11
- data/lib/types/rails-5.x/action_dispatch/routing.rb +0 -10
- data/lib/types/rails-5.x/active_model/errors.rb +0 -15
- data/lib/types/rails-5.x/active_model/validations.rb +0 -5
- data/lib/types/rails-5.x/active_record/associations.rb +0 -190
- data/lib/types/rails-5.x/active_record/core.rb +0 -3
- data/lib/types/rails-5.x/active_record/model_schema.rb +0 -39
- data/lib/types/rails-5.x/fixnum.rb +0 -3
data/lib/rdl/types/structural.rb
CHANGED
|
@@ -59,6 +59,7 @@ module RDL::Type
|
|
|
59
59
|
other = other.canonical
|
|
60
60
|
other = other.type if other.instance_of? AnnotatedArgType
|
|
61
61
|
return true if other.instance_of? WildQuery
|
|
62
|
+
return false unless other.instance_of? StructuralType
|
|
62
63
|
return (@methods.length == other.methods.length &&
|
|
63
64
|
@methods.all? { |k, v| (other.methods.has_key? k) && (v.match(other.methods[k]))})
|
|
64
65
|
end
|
data/lib/rdl/types/tuple.rb
CHANGED
|
@@ -41,12 +41,12 @@ module RDL::Type
|
|
|
41
41
|
other = other.canonical
|
|
42
42
|
other = other.type if other.instance_of? AnnotatedArgType
|
|
43
43
|
return true if other.instance_of? WildQuery
|
|
44
|
-
return @params.length == other.params.length && @params.zip(other.params).all? { |t,o| t.match(o) }
|
|
44
|
+
return (other.instance_of? TupleType) && (@params.length == other.params.length) && (@params.zip(other.params).all? { |t,o| t.match(o) })
|
|
45
45
|
end
|
|
46
46
|
|
|
47
47
|
def promote!
|
|
48
48
|
return false if @cant_promote
|
|
49
|
-
@array = GenericType.new(
|
|
49
|
+
@array = GenericType.new(RDL::Globals.types[:array], UnionType.new(*@params))
|
|
50
50
|
# note since we promoted this, lbounds and ubounds will be ignored in future constraints, which
|
|
51
51
|
# is good because otherwise we'd get infinite loops
|
|
52
52
|
return (@lbounds.all? { |lbound| lbound <= self }) && (@ubounds.all? { |ubound| self <= ubound })
|
data/lib/rdl/types/type.rb
CHANGED
|
@@ -75,7 +75,7 @@ module RDL::Type
|
|
|
75
75
|
if left.is_a?(NominalType) && right.is_a?(StructuralType)
|
|
76
76
|
right.methods.each_pair { |m, t|
|
|
77
77
|
return false unless left.klass.method_defined? m
|
|
78
|
-
types =
|
|
78
|
+
types = RDL::Globals.info.get(left.klass, m, :type)
|
|
79
79
|
if types
|
|
80
80
|
return false unless types.all? { |tlm| leq(tlm, t, nil, ileft) }
|
|
81
81
|
# inst above is nil because the method types inside the class and
|
|
@@ -94,7 +94,7 @@ module RDL::Type
|
|
|
94
94
|
|
|
95
95
|
# generic
|
|
96
96
|
if left.is_a?(GenericType) && right.is_a?(GenericType)
|
|
97
|
-
formals, variance, _ =
|
|
97
|
+
formals, variance, _ = RDL::Globals.type_params[left.base.name]
|
|
98
98
|
# do check here to avoid hiding errors if generic type written
|
|
99
99
|
# with wrong number of parameters but never checked against
|
|
100
100
|
# instantiated instances
|
|
@@ -115,13 +115,13 @@ module RDL::Type
|
|
|
115
115
|
end
|
|
116
116
|
if left.is_a?(GenericType) && right.is_a?(StructuralType)
|
|
117
117
|
# similar to logic above for leq(NominalType, StructuralType, ...)
|
|
118
|
-
formals, variance, _ =
|
|
118
|
+
formals, variance, _ = RDL::Globals.type_params[left.base.name]
|
|
119
119
|
raise TypeError, "No type parameters defined for #{base.name}" unless formals
|
|
120
120
|
base_inst = Hash[*formals.zip(left.params).flatten] # instantiation for methods in base's class
|
|
121
121
|
klass = left.base.klass
|
|
122
122
|
right.methods.each_pair { |meth, t|
|
|
123
123
|
return false unless klass.method_defined? meth
|
|
124
|
-
types =
|
|
124
|
+
types = RDL::Globals.info.get(klass, meth, :type)
|
|
125
125
|
if types
|
|
126
126
|
return false unless types.all? { |tlm| leq(tlm.instantiate(base_inst), t, nil, ileft) }
|
|
127
127
|
end
|
|
@@ -166,7 +166,7 @@ module RDL::Type
|
|
|
166
166
|
right.lbounds << left
|
|
167
167
|
return true
|
|
168
168
|
end
|
|
169
|
-
if left.is_a?(TupleType) && right.is_a?(GenericType) && right.base ==
|
|
169
|
+
if left.is_a?(TupleType) && right.is_a?(GenericType) && right.base == RDL::Globals.types[:array]
|
|
170
170
|
# TODO !ileft and right carries a free variable
|
|
171
171
|
return false unless left.promote!
|
|
172
172
|
return leq(left, right, inst, ileft) # recheck for promoted type
|
|
@@ -200,7 +200,7 @@ module RDL::Type
|
|
|
200
200
|
right.lbounds << left
|
|
201
201
|
return true
|
|
202
202
|
end
|
|
203
|
-
if left.is_a?(FiniteHashType) && right.is_a?(GenericType) && right.base ==
|
|
203
|
+
if left.is_a?(FiniteHashType) && right.is_a?(GenericType) && right.base == RDL::Globals.types[:hash]
|
|
204
204
|
# TODO !ileft and right carries a free variable
|
|
205
205
|
return false unless left.promote!
|
|
206
206
|
return leq(left, right, inst, ileft) # recheck for promoted type
|
|
@@ -237,8 +237,8 @@ module RDL::Type
|
|
|
237
237
|
|
|
238
238
|
private
|
|
239
239
|
|
|
240
|
-
# [+ a +] is Array<
|
|
241
|
-
# returns Array<Array<
|
|
240
|
+
# [+ a +] is Array<Integer>
|
|
241
|
+
# returns Array<Array<Integer>> containing all combinations of 0..a[i] at index i
|
|
242
242
|
# For example:
|
|
243
243
|
#
|
|
244
244
|
# combinations [0, 0] #=> [[0, 0]]
|
data/lib/rdl/types/union.rb
CHANGED
|
@@ -7,7 +7,7 @@ module RDL::Type
|
|
|
7
7
|
end
|
|
8
8
|
|
|
9
9
|
def self.new(*types)
|
|
10
|
-
return
|
|
10
|
+
return RDL::Globals.types[:bot] if types.size == 0
|
|
11
11
|
ts = []
|
|
12
12
|
# flatten nested unions, check that all args are types
|
|
13
13
|
types.each { |t|
|
|
@@ -80,6 +80,7 @@ module RDL::Type
|
|
|
80
80
|
other = other.canonical
|
|
81
81
|
other = other.type if other.instance_of? AnnotatedArgType
|
|
82
82
|
return true if other.instance_of? WildQuery
|
|
83
|
+
return false unless other.instance_of? UnionType
|
|
83
84
|
return false if @types.length != other.types.length
|
|
84
85
|
@types.all? { |t| other.types.any? { |ot| t.match(ot) } }
|
|
85
86
|
end
|
data/lib/rdl/util.rb
CHANGED
|
@@ -15,6 +15,21 @@ class RDL::Util
|
|
|
15
15
|
return c
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
+
def self.singleton_class_to_class(cls)
|
|
19
|
+
cls_str = cls.to_s
|
|
20
|
+
cls_str = cls_str.split('(')[0] + '>' if cls_str['(']
|
|
21
|
+
to_class cls_str[8..-2]
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def self.to_class_str(cls)
|
|
25
|
+
cls_str = cls.to_s
|
|
26
|
+
if cls_str.start_with? '#<Class:'
|
|
27
|
+
cls_str = cls_str.split('(')[0] + '>' if cls_str['(']
|
|
28
|
+
cls_str = RDL::Util.add_singleton_marker(cls_str[8..-2])
|
|
29
|
+
end
|
|
30
|
+
cls_str
|
|
31
|
+
end
|
|
32
|
+
|
|
18
33
|
def self.has_singleton_marker(klass)
|
|
19
34
|
return (klass =~ /^#{SINGLETON_MARKER_REGEXP}/)
|
|
20
35
|
end
|
|
@@ -43,12 +58,22 @@ class RDL::Util
|
|
|
43
58
|
|
|
44
59
|
def self.method_defined?(klass, method)
|
|
45
60
|
begin
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
61
|
+
sk = self.to_class klass
|
|
62
|
+
msym = method.to_sym
|
|
63
|
+
mstr = method.to_s
|
|
64
|
+
sk.instance_method msym
|
|
49
65
|
rescue NameError
|
|
50
66
|
return false
|
|
51
67
|
end
|
|
68
|
+
klass_str = RDL::Util.to_class_str(klass).hash
|
|
69
|
+
if mstr.start_with?('__rdl') and mstr.end_with?('_old_#{klass_str}')
|
|
70
|
+
mstr0 = RDL::Wrap.unwrapped_name(klass, mstr)
|
|
71
|
+
owner0 = sk.instance_method(mstr0).owner
|
|
72
|
+
owner = sk.instance_method(mstr).owner
|
|
73
|
+
return false if owner0 != owner
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
true
|
|
52
77
|
end
|
|
53
78
|
|
|
54
79
|
# Returns the @__rdl_type field of [+obj+]
|
data/lib/rdl/wrap.rb
CHANGED
|
@@ -6,18 +6,18 @@ class RDL::Wrap
|
|
|
6
6
|
def self.resolve_alias(klass, meth)
|
|
7
7
|
klass = klass.to_s
|
|
8
8
|
meth = meth.to_sym
|
|
9
|
-
while
|
|
10
|
-
if
|
|
9
|
+
while RDL::Globals.aliases[klass] && RDL::Globals.aliases[klass][meth]
|
|
10
|
+
if RDL::Globals.info.has_any?(klass, meth, [:pre, :post, :type])
|
|
11
11
|
raise RuntimeError, "Alias #{RDL::Util.pp_klass_method(klass, meth)} has contracts. Contracts are only allowed on methods, not aliases."
|
|
12
12
|
end
|
|
13
|
-
meth =
|
|
13
|
+
meth = RDL::Globals.aliases[klass][meth]
|
|
14
14
|
end
|
|
15
15
|
return meth
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
def self.get_type_params(klass)
|
|
19
19
|
klass = klass.to_s
|
|
20
|
-
|
|
20
|
+
RDL::Globals.type_params[klass]
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
# [+klass+] may be a Class, String, or Symbol
|
|
@@ -26,11 +26,11 @@ class RDL::Wrap
|
|
|
26
26
|
# Wraps klass#method to check contracts and types. Does not rewrap
|
|
27
27
|
# if already wrapped. Also records source location of method.
|
|
28
28
|
def self.wrap(klass_str, meth)
|
|
29
|
-
|
|
29
|
+
RDL::Globals.wrap_switch.off {
|
|
30
30
|
klass_str = klass_str.to_s
|
|
31
31
|
klass = RDL::Util.to_class klass_str
|
|
32
32
|
return if wrapped? klass, meth
|
|
33
|
-
return if RDL::Config.instance.nowrap.member?
|
|
33
|
+
return if RDL::Config.instance.nowrap.member? klass_str.to_sym
|
|
34
34
|
raise ArgumentError, "Attempt to wrap #{RDL::Util.pp_klass_method(klass, meth)}" if klass.to_s =~ /^RDL::/
|
|
35
35
|
meth_old = wrapped_name(klass, meth) # meth_old is a symbol
|
|
36
36
|
# return if (klass.method_defined? meth_old) # now checked above by wrapped? call
|
|
@@ -46,32 +46,32 @@ class RDL::Wrap
|
|
|
46
46
|
bind = binding
|
|
47
47
|
inst = nil
|
|
48
48
|
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
RDL::Globals.wrap_switch.off {
|
|
50
|
+
RDL::Globals.wrapped_calls["#{full_method_name}"] += 1 if RDL::Config.instance.gather_stats
|
|
51
51
|
inst = nil
|
|
52
|
-
inst = @
|
|
53
|
-
inst = Hash[
|
|
52
|
+
inst = @__rdl_type.to_inst if ((defined? @__rdl_type) && @__rdl_type.is_a?(RDL::Type::GenericType))
|
|
53
|
+
inst = Hash[RDL::Globals.type_params[klass][0].zip []] if (not(inst) && RDL::Globals.type_params[klass])
|
|
54
54
|
inst = {} if not inst
|
|
55
55
|
#{if not(is_singleton_method) then "inst[:self] = RDL::Type::NominalType.new(self.class)" end}
|
|
56
56
|
# puts "Intercepted #{full_method_name}(\#{args.join(", ")}) { \#{blk} }, inst = \#{inst.inspect}"
|
|
57
57
|
meth = RDL::Wrap.resolve_alias(klass, #{meth.inspect})
|
|
58
|
-
RDL::Typecheck.typecheck(klass, meth) if
|
|
59
|
-
pres =
|
|
58
|
+
RDL::Typecheck.typecheck(klass, meth) if RDL::Globals.info.get(klass, meth, :typecheck) == :call
|
|
59
|
+
pres = RDL::Globals.info.get(klass, meth, :pre)
|
|
60
60
|
RDL::Contract::AndContract.check_array(pres, self, *args, &blk) if pres
|
|
61
|
-
types =
|
|
61
|
+
types = RDL::Globals.info.get(klass, meth, :type)
|
|
62
62
|
if types
|
|
63
63
|
matches, args, blk, bind = RDL::Type::MethodType.check_arg_types("#{full_method_name}", self, bind, types, inst, *args, &blk)
|
|
64
64
|
end
|
|
65
65
|
}
|
|
66
66
|
ret = send(#{meth_old.inspect}, *args, &blk)
|
|
67
|
-
|
|
68
|
-
posts =
|
|
67
|
+
RDL::Globals.wrap_switch.off {
|
|
68
|
+
posts = RDL::Globals.info.get(klass, meth, :post)
|
|
69
69
|
RDL::Contract::AndContract.check_array(posts, self, ret, *args, &blk) if posts
|
|
70
70
|
if matches
|
|
71
71
|
ret = RDL::Type::MethodType.check_ret_types(self, "#{full_method_name}", types, inst, matches, ret, bind, *args, &blk)
|
|
72
72
|
end
|
|
73
73
|
if RDL::Config.instance.guess_types.include?("#{klass_str_without_singleton}".to_sym)
|
|
74
|
-
|
|
74
|
+
RDL::Globals.info.add(klass, meth, :otype, { args: (args.map { |arg| arg.class }), ret: ret.class, block: block_given? })
|
|
75
75
|
end
|
|
76
76
|
return ret
|
|
77
77
|
}
|
|
@@ -112,7 +112,7 @@ RUBY
|
|
|
112
112
|
klass = default_class.to_s
|
|
113
113
|
contract = RDL::Contract::FlatContract.new(name, &blk)
|
|
114
114
|
else
|
|
115
|
-
raise ArgumentError, "Invalid arguments"
|
|
115
|
+
raise ArgumentError, "Invalid arguments to `pre` or `post`"
|
|
116
116
|
end
|
|
117
117
|
raise ArgumentError, "#{contract.class} received where Contract expected" unless contract.class < RDL::Contract::Contract
|
|
118
118
|
# meth = :initialize if meth && meth.to_sym == :new # actually wrap constructor
|
|
@@ -136,7 +136,7 @@ RUBY
|
|
|
136
136
|
klass = default_class.to_s
|
|
137
137
|
type = type_to_type args[0]
|
|
138
138
|
else
|
|
139
|
-
raise ArgumentError, "Invalid arguments"
|
|
139
|
+
raise ArgumentError, "Invalid arguments to `type`"
|
|
140
140
|
end
|
|
141
141
|
raise ArgumentError, "Excepting method type, got #{type.class} instead" if type.class != RDL::Type::MethodType
|
|
142
142
|
# meth = :initialize if meth && slf && meth.to_sym == :new # actually wrap constructor
|
|
@@ -147,12 +147,22 @@ RUBY
|
|
|
147
147
|
private
|
|
148
148
|
|
|
149
149
|
def self.wrapped_name(klass, meth)
|
|
150
|
-
|
|
150
|
+
klass_str = RDL::Util.to_class_str(klass).hash
|
|
151
|
+
"__rdl_#{meth.to_s}_old_#{klass_str}".to_sym
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def self.unwrapped_name(s)
|
|
155
|
+
if not s.start_with?('__rdl_') and s.include?('_old_')
|
|
156
|
+
raise Exception, "cannot get unwrapped name for #{s}"
|
|
157
|
+
end
|
|
158
|
+
klass_str = RDL::Util.to_class_str(klass).hash.to_s
|
|
159
|
+
s = klass_str.split("_#{klass_str}")
|
|
160
|
+
s[6..-5]
|
|
151
161
|
end
|
|
152
162
|
|
|
153
163
|
def self.class_to_string(klass)
|
|
154
164
|
case klass
|
|
155
|
-
when Class
|
|
165
|
+
when Class, Module
|
|
156
166
|
return klass.to_s
|
|
157
167
|
when String
|
|
158
168
|
return klass
|
|
@@ -176,7 +186,7 @@ RUBY
|
|
|
176
186
|
when RDL::Type::Type
|
|
177
187
|
return type
|
|
178
188
|
when String
|
|
179
|
-
return
|
|
189
|
+
return RDL::Globals.parser.scan_str type
|
|
180
190
|
end
|
|
181
191
|
end
|
|
182
192
|
|
|
@@ -184,15 +194,15 @@ RUBY
|
|
|
184
194
|
def self.do_method_added(the_self, sing, klass, meth)
|
|
185
195
|
# Apply any deferred contracts and reset list
|
|
186
196
|
|
|
187
|
-
if
|
|
197
|
+
if RDL::Globals.deferred.size > 0
|
|
188
198
|
if sing
|
|
189
199
|
loc = the_self.singleton_method(meth).source_location
|
|
190
200
|
else
|
|
191
201
|
loc = the_self.instance_method(meth).source_location
|
|
192
202
|
end
|
|
193
|
-
|
|
194
|
-
a =
|
|
195
|
-
|
|
203
|
+
RDL::Globals.info.set(klass, meth, :source_location, loc)
|
|
204
|
+
a = RDL::Globals.deferred
|
|
205
|
+
RDL::Globals.deferred = [] # Reset before doing more work to avoid infinite recursion
|
|
196
206
|
a.each { |prev_klass, kind, contract, h|
|
|
197
207
|
if RDL::Util.has_singleton_marker(klass)
|
|
198
208
|
tmp_klass = RDL::Util.remove_singleton_marker(klass)
|
|
@@ -202,50 +212,51 @@ RUBY
|
|
|
202
212
|
if (!h[:class_check] && (prev_klass != tmp_klass)) || (h[:class_check] && (h[:class_check].to_s != tmp_klass))
|
|
203
213
|
raise RuntimeError, "Deferred #{kind} contract from class #{prev_klass} being applied in class #{tmp_klass} to #{meth}"
|
|
204
214
|
end
|
|
205
|
-
|
|
215
|
+
RDL::Globals.info.add(klass, meth, kind, contract)
|
|
206
216
|
RDL::Wrap.wrap(klass, meth) if h[:wrap]
|
|
207
|
-
unless !h.has_key?(:typecheck) ||
|
|
217
|
+
unless !h.has_key?(:typecheck) || RDL::Globals.info.set(klass, meth, :typecheck, h[:typecheck])
|
|
208
218
|
raise RuntimeError, "Inconsistent typecheck flag on #{RDL::Util.pp_klass_method(klass, meth)}"
|
|
209
219
|
end
|
|
210
220
|
RDL::Typecheck.typecheck(klass, meth) if h[:typecheck] == :now
|
|
211
221
|
if (h[:typecheck] && h[:typecheck] != :call)
|
|
212
|
-
|
|
213
|
-
|
|
222
|
+
RDL::Globals.to_typecheck[h[:typecheck]] = Set.new unless RDL::Globals.to_typecheck[h[:typecheck]]
|
|
223
|
+
RDL::Globals.to_typecheck[h[:typecheck]].add([klass, meth])
|
|
214
224
|
end
|
|
215
225
|
}
|
|
216
226
|
end
|
|
217
227
|
|
|
218
228
|
# Wrap method if there was a prior contract for it.
|
|
219
|
-
if
|
|
220
|
-
|
|
221
|
-
RDL::Wrap.wrap(klass, meth)
|
|
229
|
+
if RDL::Globals.to_wrap.member? [klass, meth]
|
|
230
|
+
RDL::Globals.to_wrap.delete [klass, meth]
|
|
222
231
|
if sing
|
|
223
232
|
loc = the_self.singleton_method(meth).source_location
|
|
224
233
|
else
|
|
225
234
|
loc = the_self.instance_method(meth).source_location
|
|
226
235
|
end
|
|
227
|
-
|
|
236
|
+
RDL::Globals.info.set(klass, meth, :source_location, loc)
|
|
237
|
+
RDL::Wrap.wrap(klass, meth)
|
|
228
238
|
end
|
|
229
239
|
|
|
230
240
|
# Type check method if requested
|
|
231
|
-
if
|
|
232
|
-
|
|
241
|
+
if RDL::Globals.to_typecheck[:now].member? [klass, meth]
|
|
242
|
+
RDL::Globals.to_typecheck[:now].delete [klass, meth]
|
|
233
243
|
RDL::Typecheck.typecheck(klass, meth)
|
|
234
244
|
end
|
|
235
245
|
|
|
236
|
-
if RDL::Config.instance.guess_types.include?(the_self.to_s.to_sym) &&
|
|
246
|
+
if RDL::Config.instance.guess_types.include?(the_self.to_s.to_sym) && !RDL::Globals.info.has?(klass, meth, :type)
|
|
237
247
|
# Added a method with no type annotation from a class we want to guess types for
|
|
238
248
|
RDL::Wrap.wrap(klass, meth)
|
|
239
249
|
end
|
|
240
250
|
end
|
|
241
251
|
end
|
|
242
252
|
|
|
243
|
-
|
|
244
|
-
|
|
253
|
+
module RDL::Annotate
|
|
245
254
|
# [+klass+] may be Class, Symbol, or String
|
|
246
255
|
# [+method+] may be Symbol or String
|
|
247
256
|
# [+contract+] must be a Contract
|
|
248
257
|
# [+wrap+] indicates whether the contract should be enforced (true) or just recorded (false)
|
|
258
|
+
# [+ version +] is a rubygems version requirement string (or array of such requirement strings)
|
|
259
|
+
# if the current Ruby version does not satisfy the version, the type call will be ignored
|
|
249
260
|
#
|
|
250
261
|
# Add a precondition to a method. Possible invocations:
|
|
251
262
|
# pre(klass, meth, contract)
|
|
@@ -254,37 +265,39 @@ class Object
|
|
|
254
265
|
# pre(meth) { block } = pre(self, meth, FlatContract.new { block })
|
|
255
266
|
# pre(contract) = pre(self, next method, contract)
|
|
256
267
|
# pre { block } = pre(self, next method, FlatContract.new { block })
|
|
257
|
-
def pre(*args, wrap: RDL::Config.instance.pre_defaults[:wrap], &blk)
|
|
268
|
+
def pre(*args, wrap: RDL::Config.instance.pre_defaults[:wrap], version: nil, &blk)
|
|
269
|
+
return if version && !(Gem::Requirement.new(version).satisfied_by? Gem.ruby_version)
|
|
258
270
|
klass, meth, contract = RDL::Wrap.process_pre_post_args(self, "Precondition", *args, &blk)
|
|
259
271
|
if meth
|
|
260
|
-
|
|
272
|
+
RDL::Globals.info.add(klass, meth, :pre, contract)
|
|
261
273
|
if wrap
|
|
262
274
|
if RDL::Util.method_defined?(klass, meth) || meth == :initialize # there is always an initialize
|
|
263
275
|
RDL::Wrap.wrap(klass, meth)
|
|
264
276
|
else
|
|
265
|
-
|
|
277
|
+
RDL::Globals.to_wrap << [klass, meth]
|
|
266
278
|
end
|
|
267
279
|
end
|
|
268
280
|
else
|
|
269
|
-
|
|
281
|
+
RDL::Globals.deferred << [klass, :pre, contract, {wrap: wrap}]
|
|
270
282
|
end
|
|
271
283
|
nil
|
|
272
284
|
end
|
|
273
285
|
|
|
274
286
|
# Add a postcondition to a method. Same possible invocations as pre.
|
|
275
|
-
def post(*args, wrap: RDL::Config.instance.post_defaults[:wrap], &blk)
|
|
287
|
+
def post(*args, wrap: RDL::Config.instance.post_defaults[:wrap], version: nil, &blk)
|
|
288
|
+
return if version && !(Gem::Requirement.new(version).satisfied_by? Gem.ruby_version)
|
|
276
289
|
klass, meth, contract = RDL::Wrap.process_pre_post_args(self, "Postcondition", *args, &blk)
|
|
277
290
|
if meth
|
|
278
|
-
|
|
291
|
+
RDL::Globals.info.add(klass, meth, :post, contract)
|
|
279
292
|
if wrap
|
|
280
293
|
if RDL::Util.method_defined?(klass, meth) || meth == :initialize
|
|
281
294
|
RDL::Wrap.wrap(klass, meth)
|
|
282
295
|
else
|
|
283
|
-
|
|
296
|
+
RDL::Globals.to_wrap << [klass, meth]
|
|
284
297
|
end
|
|
285
298
|
end
|
|
286
299
|
else
|
|
287
|
-
|
|
300
|
+
RDL::Globals.deferred << [klass, :post, contract, {wrap: wrap}]
|
|
288
301
|
end
|
|
289
302
|
nil
|
|
290
303
|
end
|
|
@@ -297,12 +310,15 @@ class Object
|
|
|
297
310
|
# if :call, indicates method should be typechecked when called
|
|
298
311
|
# if :now, indicates method should be typechecked immediately
|
|
299
312
|
# if other-symbol, indicates method should be typechecked when rdl_do_typecheck(other-symbol) is called
|
|
313
|
+
# [+ version +] is a rubygems version requirement string (or array of such requirement strings)
|
|
314
|
+
# if the current Ruby version does not satisfy the version, the type call will be ignored
|
|
300
315
|
#
|
|
301
316
|
# Set a method's type. Possible invocations:
|
|
302
317
|
# type(klass, meth, type)
|
|
303
318
|
# type(meth, type)
|
|
304
319
|
# type(type)
|
|
305
|
-
def type(*args, wrap: RDL::Config.instance.type_defaults[:wrap], typecheck: RDL::Config.instance.type_defaults[:typecheck])
|
|
320
|
+
def type(*args, wrap: RDL::Config.instance.type_defaults[:wrap], typecheck: RDL::Config.instance.type_defaults[:typecheck], version: nil)
|
|
321
|
+
return if version && !(Gem::Requirement.new(version).satisfied_by? Gem.ruby_version)
|
|
306
322
|
klass, meth, type = begin
|
|
307
323
|
RDL::Wrap.process_type_args(self, *args)
|
|
308
324
|
rescue Racc::ParseError => err
|
|
@@ -310,37 +326,42 @@ class Object
|
|
|
310
326
|
# Warning: Adjust the -5 below if the code (or this comment) changes
|
|
311
327
|
bt = err.backtrace
|
|
312
328
|
bt.shift until bt[0] =~ /^#{__FILE__}:#{__LINE__-5}/
|
|
313
|
-
bt.shift # remove
|
|
329
|
+
bt.shift # remove RDL::Globals.contract_switch.off call
|
|
314
330
|
bt.shift # remove type call itself
|
|
315
331
|
err.set_backtrace bt
|
|
316
332
|
raise err
|
|
317
333
|
end
|
|
318
334
|
if meth
|
|
319
335
|
# It turns out Ruby core/stdlib don't always follow this convention...
|
|
320
|
-
# if (meth.to_s[-1] == "?") && (type.ret !=
|
|
336
|
+
# if (meth.to_s[-1] == "?") && (type.ret != RDL::Globals.types[:bool])
|
|
321
337
|
# warn "#{RDL::Util.pp_klass_method(klass, meth)}: methods that end in ? should have return type %bool"
|
|
322
338
|
# end
|
|
323
|
-
|
|
324
|
-
unless
|
|
339
|
+
RDL::Globals.info.add(klass, meth, :type, type)
|
|
340
|
+
unless RDL::Globals.info.set(klass, meth, :typecheck, typecheck)
|
|
325
341
|
raise RuntimeError, "Inconsistent typecheck flag on #{RDL::Util.pp_klass_method(klass, meth)}"
|
|
326
342
|
end
|
|
327
|
-
if wrap || typecheck
|
|
343
|
+
if wrap || typecheck
|
|
328
344
|
if RDL::Util.method_defined?(klass, meth) || meth == :initialize
|
|
329
|
-
|
|
330
|
-
|
|
345
|
+
RDL::Globals.info.set(klass, meth, :source_location, RDL::Util.to_class(klass).instance_method(meth).source_location)
|
|
346
|
+
if typecheck == :now
|
|
347
|
+
RDL::Typecheck.typecheck(klass, meth)
|
|
348
|
+
elsif typecheck && (typecheck != :call)
|
|
349
|
+
RDL::Globals.to_typecheck[typecheck] = Set.new unless RDL::Globals.to_typecheck[typecheck]
|
|
350
|
+
RDL::Globals.to_typecheck[typecheck].add([klass, meth])
|
|
351
|
+
end
|
|
331
352
|
RDL::Wrap.wrap(klass, meth) if wrap
|
|
332
353
|
else
|
|
333
354
|
if wrap
|
|
334
|
-
|
|
355
|
+
RDL::Globals.to_wrap << [klass, meth]
|
|
335
356
|
if (typecheck && typecheck != :call)
|
|
336
|
-
|
|
337
|
-
|
|
357
|
+
RDL::Globals.to_typecheck[typecheck] = Set.new unless RDL::Globals.to_typecheck[typecheck]
|
|
358
|
+
RDL::Globals.to_typecheck[typecheck].add([klass, meth])
|
|
338
359
|
end
|
|
339
360
|
end
|
|
340
361
|
end
|
|
341
362
|
end
|
|
342
363
|
else
|
|
343
|
-
|
|
364
|
+
RDL::Globals.deferred << [klass, :type, type, {wrap: wrap,
|
|
344
365
|
typecheck: typecheck}]
|
|
345
366
|
end
|
|
346
367
|
nil
|
|
@@ -354,7 +375,7 @@ class Object
|
|
|
354
375
|
return if var.to_s =~ /^[a-z]/ # local variables handled specially, inside type checker
|
|
355
376
|
raise RuntimeError, "Global variables can't be typed in a class" unless klass = self
|
|
356
377
|
klass = RDL::Util::GLOBAL_NAME if var.to_s =~ /^\$/
|
|
357
|
-
unless
|
|
378
|
+
unless RDL::Globals.info.set(klass, var, :type, RDL::Globals.parser.scan_str("#T #{typ}"))
|
|
358
379
|
raise RuntimeError, "Type already declared for #{var}"
|
|
359
380
|
end
|
|
360
381
|
nil
|
|
@@ -394,42 +415,28 @@ class Object
|
|
|
394
415
|
nil
|
|
395
416
|
end
|
|
396
417
|
|
|
397
|
-
def self.method_added(meth)
|
|
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
|
|
402
|
-
end
|
|
403
|
-
|
|
404
|
-
def self.singleton_method_added(meth)
|
|
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
|
|
410
|
-
end
|
|
411
|
-
|
|
412
418
|
# Aliases contracts for meth_old and meth_new. Currently, this must
|
|
413
419
|
# be called for any aliases or they will not be wrapped with
|
|
414
420
|
# contracts. Only creates aliases in the current class.
|
|
415
|
-
def rdl_alias(new_name, old_name)
|
|
416
|
-
klass =
|
|
421
|
+
def rdl_alias(klass=self, new_name, old_name)
|
|
422
|
+
klass = klass.to_s
|
|
417
423
|
klass = "Object" if (klass.is_a? Object) && (klass.to_s == "main")
|
|
418
|
-
|
|
419
|
-
if
|
|
424
|
+
RDL::Globals.aliases[klass] = {} unless RDL::Globals.aliases[klass]
|
|
425
|
+
if RDL::Globals.aliases[klass][new_name]
|
|
420
426
|
raise RuntimeError,
|
|
421
|
-
"Tried to alias #{new_name}, already aliased to #{
|
|
427
|
+
"Tried to alias #{new_name}, already aliased to #{RDL::Globals.aliases[klass][new_name]}"
|
|
422
428
|
end
|
|
423
|
-
|
|
429
|
+
RDL::Globals.aliases[klass][new_name] = old_name
|
|
424
430
|
|
|
425
|
-
if
|
|
431
|
+
if Module.const_defined?(klass) && RDL::Util.to_class(klass).method_defined?(new_name)
|
|
426
432
|
RDL::Wrap.wrap(klass, new_name)
|
|
427
433
|
else
|
|
428
|
-
|
|
434
|
+
RDL::Globals.to_wrap << [klass, old_name]
|
|
429
435
|
end
|
|
430
436
|
nil
|
|
431
437
|
end
|
|
432
438
|
|
|
439
|
+
# [+ klass +] is the class whose type parameters to set; self if omitted
|
|
433
440
|
# [+params+] is an array of symbols or strings that are the
|
|
434
441
|
# parameters of this (generic) type
|
|
435
442
|
# [+variance+] is an array of the corresponding variances, :+ for
|
|
@@ -442,12 +449,12 @@ class Object
|
|
|
442
449
|
# block will be passed an array typs corresponding to the type
|
|
443
450
|
# parameters of the class, and the block should return true if and
|
|
444
451
|
# only if self is a member of self.class<typs>.
|
|
445
|
-
def type_params(params, all, variance: nil, &blk)
|
|
452
|
+
def type_params(klass=self, params, all, variance: nil, &blk)
|
|
446
453
|
raise RuntimeError, "Empty type parameters not allowed" if params.empty?
|
|
447
|
-
klass = self.to_s
|
|
448
454
|
klass = "Object" if (klass.is_a? Object) && (klass.to_s == "main")
|
|
449
|
-
|
|
450
|
-
|
|
455
|
+
klass = klass.to_s
|
|
456
|
+
if RDL::Globals.type_params[klass]
|
|
457
|
+
raise RuntimeError, "#{klass} already has type parameters #{RDL::Globals.type_params[klass]}"
|
|
451
458
|
end
|
|
452
459
|
params = params.map { |v|
|
|
453
460
|
raise RuntimeError, "Type parameter #{v.inspect} is not symbol or string" unless v.class == String || v.class == Symbol
|
|
@@ -461,123 +468,167 @@ class Object
|
|
|
461
468
|
chk = all || blk
|
|
462
469
|
raise RuntimeError, "At least one of {all, blk} required" unless chk
|
|
463
470
|
variance = params.map { |p| :~ } unless variance # default to invariant
|
|
464
|
-
|
|
465
|
-
nil
|
|
466
|
-
end
|
|
467
|
-
|
|
468
|
-
def rdl_nowrap
|
|
469
|
-
RDL.config { |config| config.add_nowrap(self, self.singleton_class) }
|
|
471
|
+
RDL::Globals.type_params[klass] = [params, variance, chk]
|
|
470
472
|
nil
|
|
471
473
|
end
|
|
472
474
|
|
|
473
|
-
#
|
|
474
|
-
#
|
|
475
|
-
#
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
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
|
|
495
|
-
}
|
|
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
|
|
502
|
-
end
|
|
475
|
+
# The following code attempts to warn about annotation methods already being defined on the class/module.
|
|
476
|
+
# But it doesn't work because `extended` gets called *after* the module's methods are already added...
|
|
477
|
+
# def self.extended(other)
|
|
478
|
+
# [:pre,
|
|
479
|
+
# :post,
|
|
480
|
+
# :type,
|
|
481
|
+
# :var_type,
|
|
482
|
+
# :attr_accessor_type,
|
|
483
|
+
# :attr_reader_type,
|
|
484
|
+
# :attr_type,
|
|
485
|
+
# :attr_writer_type,
|
|
486
|
+
# :rdl_alias,
|
|
487
|
+
# :type_params].each { |a|
|
|
488
|
+
# warn "RDL WARNING: #{other.to_s} extended RDL::Annotate but already has #{a} defined" if other.respond_to? a
|
|
489
|
+
# }
|
|
490
|
+
# end
|
|
491
|
+
end
|
|
503
492
|
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
493
|
+
# all methods in RDL::Annotate but with an `rdl_` prefix
|
|
494
|
+
module RDL::RDLAnnotate
|
|
495
|
+
define_method :rdl_pre, RDL::Annotate.instance_method(:pre)
|
|
496
|
+
define_method :rdl_post, RDL::Annotate.instance_method(:post)
|
|
497
|
+
define_method :rdl_type, RDL::Annotate.instance_method(:type)
|
|
498
|
+
define_method :rdl_var_type, RDL::Annotate.instance_method(:var_type)
|
|
499
|
+
define_method :rdl_attr_accessor_type, RDL::Annotate.instance_method(:attr_accessor_type)
|
|
500
|
+
define_method :rdl_attr_reader_type, RDL::Annotate.instance_method(:attr_reader_type)
|
|
501
|
+
define_method :rdl_attr_type, RDL::Annotate.instance_method(:attr_type)
|
|
502
|
+
define_method :rdl_attr_writer_type, RDL::Annotate.instance_method(:attr_writer_type)
|
|
503
|
+
define_method :rdl_alias, RDL::Annotate.instance_method(:rdl_alias)
|
|
504
|
+
define_method :rdl_type_params, RDL::Annotate.instance_method(:type_params)
|
|
505
|
+
end
|
|
510
506
|
|
|
511
|
-
|
|
512
|
-
|
|
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
|
|
518
|
-
end
|
|
507
|
+
module RDL
|
|
508
|
+
extend RDL::Annotate
|
|
519
509
|
|
|
520
510
|
# Add a new type alias.
|
|
521
511
|
# [+name+] must be a string beginning with %.
|
|
522
512
|
# [+typ+] can be either a string, in which case it will be parsed
|
|
523
513
|
# into a type, or a Type.
|
|
524
|
-
def type_alias(name, typ)
|
|
525
|
-
raise RuntimeError, "Attempt to redefine type #{name}" if
|
|
514
|
+
def self.type_alias(name, typ)
|
|
515
|
+
raise RuntimeError, "Attempt to redefine type #{name}" if RDL::Globals.special_types[name]
|
|
526
516
|
case typ
|
|
527
517
|
when String
|
|
528
|
-
t =
|
|
529
|
-
|
|
518
|
+
t = RDL::Globals.parser.scan_str "#T #{typ}"
|
|
519
|
+
RDL::Globals.special_types[name] = t
|
|
530
520
|
when RDL::Type::Type
|
|
531
|
-
|
|
521
|
+
RDL::Globals.special_types[name] = typ
|
|
532
522
|
else
|
|
533
523
|
raise RuntimeError, "Unexpected typ argument #{typ.inspect}"
|
|
534
524
|
end
|
|
535
525
|
nil
|
|
536
526
|
end
|
|
537
527
|
|
|
528
|
+
def self.nowrap(klass=self)
|
|
529
|
+
RDL.config { |config| config.add_nowrap(klass) }
|
|
530
|
+
nil
|
|
531
|
+
end
|
|
532
|
+
|
|
538
533
|
# Register [+ blk +] to be executed when `rdl_do_typecheck [+ sym +]` is called.
|
|
539
534
|
# The blk will be called with sym as its argument. The order
|
|
540
535
|
# in which multiple blks for the same sym will be executed is unspecified
|
|
541
|
-
def
|
|
542
|
-
|
|
543
|
-
|
|
536
|
+
def self.at(sym, &blk)
|
|
537
|
+
RDL::Globals.to_do_at[sym] = [] unless RDL::Globals.to_do_at[sym]
|
|
538
|
+
RDL::Globals.to_do_at[sym] << blk
|
|
544
539
|
end
|
|
545
540
|
|
|
546
541
|
# Invokes all callbacks from rdl_at(sym), in unspecified order.
|
|
547
542
|
# Afterwards, type checks all methods that had annotation `typecheck: sym' at type call, in unspecified order.
|
|
548
|
-
def
|
|
549
|
-
if
|
|
550
|
-
|
|
543
|
+
def self.do_typecheck(sym)
|
|
544
|
+
if RDL::Globals.to_do_at[sym]
|
|
545
|
+
RDL::Globals.to_do_at[sym].each { |blk| blk.call(sym) }
|
|
551
546
|
end
|
|
552
|
-
|
|
553
|
-
return unless
|
|
554
|
-
|
|
547
|
+
RDL::Globals.to_do_at[sym] = Array.new
|
|
548
|
+
return unless RDL::Globals.to_typecheck[sym]
|
|
549
|
+
RDL::Globals.to_typecheck[sym].each { |klass, meth|
|
|
555
550
|
RDL::Typecheck.typecheck(klass, meth)
|
|
556
551
|
}
|
|
557
|
-
|
|
552
|
+
RDL::Globals.to_typecheck[sym] = Set.new
|
|
558
553
|
nil
|
|
559
554
|
end
|
|
560
555
|
|
|
561
556
|
# Does nothing at run time
|
|
562
|
-
def
|
|
557
|
+
def self.note_type(x)
|
|
563
558
|
return x
|
|
564
559
|
end
|
|
565
560
|
|
|
566
|
-
def
|
|
567
|
-
raise RuntimeError, "No existing type for #{RDL::Util.pp_klass_method(klass, meth)}" unless
|
|
568
|
-
|
|
561
|
+
def self.remove_type(klass, meth)
|
|
562
|
+
raise RuntimeError, "No existing type for #{RDL::Util.pp_klass_method(klass, meth)}" unless RDL::Globals.info.has? klass, meth, :type
|
|
563
|
+
RDL::Globals.info.remove klass, meth, :type
|
|
569
564
|
nil
|
|
570
565
|
end
|
|
571
566
|
|
|
567
|
+
# Returns a new object that wraps self in a type cast. If force is true this cast is *unchecked*, so use with caution
|
|
568
|
+
def self.type_cast(obj, typ, force: false)
|
|
569
|
+
new_typ = if typ.is_a? RDL::Type::Type then typ else RDL::Globals.parser.scan_str "#T #{typ}" end
|
|
570
|
+
raise RuntimeError, "type cast error: self not a member of #{new_typ}" unless force || typ.member?(obj)
|
|
571
|
+
new_obj = SimpleDelegator.new(obj)
|
|
572
|
+
new_obj.instance_variable_set('@__rdl_type', new_typ)
|
|
573
|
+
new_obj
|
|
574
|
+
end
|
|
575
|
+
|
|
576
|
+
# [+typs+] is an array of types, classes, symbols, or strings to instantiate
|
|
577
|
+
# the type parameters. If a class, symbol, or string is given, it is
|
|
578
|
+
# converted to a NominalType.
|
|
579
|
+
def self.instantiate!(obj, *typs, check: false)
|
|
580
|
+
klass = obj.class.to_s
|
|
581
|
+
klass = "Object" if (klass.is_a? Object) && (klass.to_s == "main")
|
|
582
|
+
formals, _, all = RDL::Globals.type_params[klass]
|
|
583
|
+
raise RuntimeError, "Receiver is of class #{klass}, which is not parameterized" unless formals
|
|
584
|
+
raise RuntimeError, "Expecting #{formals.size} type parameters, got #{typs.size}" unless formals.size == typs.size
|
|
585
|
+
raise RuntimeError, "Instance already has type instantiation" if obj.instance_variable_get(:@__rdl_type)
|
|
586
|
+
new_typs = typs.map { |t| if t.is_a? RDL::Type::Type then t else RDL::Globals.parser.scan_str "#T #{t}" end }
|
|
587
|
+
t = RDL::Type::GenericType.new(RDL::Type::NominalType.new(klass), *new_typs)
|
|
588
|
+
if check
|
|
589
|
+
if all.instance_of? Symbol
|
|
590
|
+
obj.send(all) { |*objs|
|
|
591
|
+
new_typs.zip(objs).each { |nt, o|
|
|
592
|
+
if nt.instance_of? RDL::Type::GenericType # require o to be instantiated
|
|
593
|
+
t_o = RDL::Util.rdl_type(o)
|
|
594
|
+
raise RDL::Type::TypeError, "Expecting element of type #{nt.to_s}, but got uninstantiated object #{o.inspect}" unless t_o
|
|
595
|
+
raise RDL::Type::TypeError, "Expecting type #{nt.to_s}, got type #{t_o.to_s}" unless t_o <= nt
|
|
596
|
+
else
|
|
597
|
+
raise RDL::Type::TypeError, "Expecting type #{nt.to_s}, got #{o.inspect}" unless nt.member? o
|
|
598
|
+
end
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
else
|
|
602
|
+
raise RDL::Type::TypeError, "Not an instance of #{t}" unless instance_exec(*new_typs, &all)
|
|
603
|
+
end
|
|
604
|
+
end
|
|
605
|
+
obj.instance_variable_set(:@__rdl_type, t)
|
|
606
|
+
obj
|
|
607
|
+
end
|
|
608
|
+
|
|
609
|
+
def self.deinstantiate!(obj)
|
|
610
|
+
klass = obj.class.to_s
|
|
611
|
+
klass = "Object" if (klass.is_a? Object) && (klass.to_s == "main")
|
|
612
|
+
raise RuntimeError, "Class #{self.to_s} is not parameterized" unless RDL::Globals.type_params[klass]
|
|
613
|
+
raise RuntimeError, "Instance is not instantiated" unless obj.instance_variable_get(:@__rdl_type).instance_of?(RDL::Type::GenericType)
|
|
614
|
+
obj.instance_variable_set(:@__rdl_type, nil)
|
|
615
|
+
obj
|
|
616
|
+
end
|
|
572
617
|
end
|
|
573
618
|
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
619
|
+
class Module
|
|
620
|
+
def method_added(meth)
|
|
621
|
+
klass = self.to_s
|
|
622
|
+
klass = "Object" if (klass.is_a? Object) && (klass.to_s == "main")
|
|
623
|
+
RDL::Wrap.do_method_added(self, false, klass, meth)
|
|
624
|
+
nil
|
|
625
|
+
end
|
|
626
|
+
|
|
627
|
+
def singleton_method_added(meth)
|
|
628
|
+
klass = self.to_s
|
|
629
|
+
klass = "Object" if (klass.is_a? Object) && (klass.to_s == "main")
|
|
630
|
+
sklass = RDL::Util.add_singleton_marker(klass)
|
|
631
|
+
RDL::Wrap.do_method_added(self, true, sklass, meth)
|
|
632
|
+
nil
|
|
633
|
+
end
|
|
634
|
+
end
|