rdl 1.1.1 → 2.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.md +6 -0
  3. data/README.md +211 -32
  4. data/gemfiles/Gemfile.travis +1 -1
  5. data/lib/rdl.rb +85 -18
  6. data/lib/rdl/info.rb +74 -0
  7. data/lib/rdl/query.rb +8 -9
  8. data/lib/rdl/typecheck.rb +1057 -0
  9. data/lib/rdl/types/annotated_arg.rb +5 -5
  10. data/lib/rdl/types/{nil.rb → bot.rb} +9 -13
  11. data/lib/rdl/types/dependent_arg.rb +5 -5
  12. data/lib/rdl/types/dots_query.rb +2 -0
  13. data/lib/rdl/types/finitehash.rb +67 -24
  14. data/lib/rdl/types/generic.rb +13 -21
  15. data/lib/rdl/types/intersection.rb +9 -8
  16. data/lib/rdl/types/method.rb +30 -32
  17. data/lib/rdl/types/nominal.rb +22 -16
  18. data/lib/rdl/types/optional.rb +8 -22
  19. data/lib/rdl/types/parser.racc +8 -3
  20. data/lib/rdl/types/parser.tab.rb +131 -118
  21. data/lib/rdl/types/singleton.rb +15 -10
  22. data/lib/rdl/types/structural.rb +6 -6
  23. data/lib/rdl/types/top.rb +6 -6
  24. data/lib/rdl/types/tuple.rb +56 -24
  25. data/lib/rdl/types/type.rb +9 -0
  26. data/lib/rdl/types/type_inferencer.rb +1 -1
  27. data/lib/rdl/types/union.rb +52 -26
  28. data/lib/rdl/types/var.rb +7 -6
  29. data/lib/rdl/types/vararg.rb +5 -6
  30. data/lib/rdl/types/wild_query.rb +9 -2
  31. data/lib/rdl/util.rb +9 -7
  32. data/lib/rdl/wrap.rb +90 -72
  33. data/lib/rdl_types.rb +2 -2
  34. data/rdl.gemspec +6 -8
  35. data/test/test_alias.rb +4 -3
  36. data/test/test_contract.rb +5 -4
  37. data/test/test_dsl.rb +2 -1
  38. data/test/test_generic.rb +30 -26
  39. data/test/test_intersection.rb +3 -3
  40. data/test/test_le.rb +129 -61
  41. data/test/test_lib_types.rb +3 -2
  42. data/test/test_member.rb +33 -46
  43. data/test/test_parser.rb +113 -116
  44. data/test/test_query.rb +2 -1
  45. data/test/test_rdl.rb +64 -6
  46. data/test/test_rdl_type.rb +3 -2
  47. data/test/test_type_contract.rb +30 -12
  48. data/test/test_typecheck.rb +893 -0
  49. data/test/test_types.rb +50 -54
  50. data/test/test_wrap.rb +2 -1
  51. data/types/ruby-2.x/_aliases.rb +13 -2
  52. data/types/ruby-2.x/bigdecimal.rb +60 -85
  53. data/types/ruby-2.x/bignum.rb +80 -119
  54. data/types/ruby-2.x/complex.rb +33 -40
  55. data/types/ruby-2.x/fixnum.rb +81 -120
  56. data/types/ruby-2.x/float.rb +79 -116
  57. data/types/ruby-2.x/integer.rb +187 -22
  58. data/types/ruby-2.x/nil.rb +12 -0
  59. data/types/ruby-2.x/numeric.rb +38 -38
  60. data/types/ruby-2.x/object.rb +3 -3
  61. data/types/ruby-2.x/random.rb +2 -0
  62. data/types/ruby-2.x/range.rb +20 -19
  63. data/types/ruby-2.x/rational.rb +40 -40
  64. data/types/ruby-2.x/regexp.rb +4 -4
  65. data/types/ruby-2.x/string.rb +15 -17
  66. metadata +17 -16
  67. data/lib/rdl/types/.#lexer.rex +0 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fa0c149f985f9432090d2ef5d504c3b8b7062742
4
- data.tar.gz: 35e30a7d7c07f53490b3ba64fc58fd2aaa5e7fa8
3
+ metadata.gz: 797b63a785ea5ae5e9600d8df722c66cf2a2fe17
4
+ data.tar.gz: dc28d44117114d21fccd6472c77f45b6efa3ae25
5
5
  SHA512:
6
- metadata.gz: d1950b0b67964e18faeda3a063cbdb6e00bd4b89f5037bbd0ad1901589ff1277ecea997a5219957463b80a54f8f679289b41d1a55839698008ef6de5f3f4c003
7
- data.tar.gz: 792070c24a984e54a8755f8399ba5009deba92a6feebdcf56b982b1ff8d2a1b3c4f6c245452d4f9523056cb49a6d7f2dcdd465a7b4d98ac22838a4d8cdb30997
6
+ metadata.gz: 0ea7b41ad216861c4b05a3f7c0204a3c71b8c1ecd9ce2e8c33ab26526dc2f079358bddc0cb18af0cf4e29420fdf2d8e5a574692a1b215c491bbf26f6d6f97804
7
+ data.tar.gz: 18acb05e6f25b0a80716a306c6b340c3d10d6e4f2bed954e42b502b52f240808a1db03652a693f74377ee6a76c1582cc52a0d23000f599710727f5a05184e415
data/CHANGES.md CHANGED
@@ -1,6 +1,12 @@
1
1
  # Change log!
2
2
 
3
3
  ## [Unreleased]
4
+ ### Added
5
+ - Added `wrap: false` optional argument to `type`, `pre`, and `post`
6
+
7
+ ### Changed
8
+ - Modified `self` type to be any instance of the self's class
9
+ - Library types now use new aliases %integer and %numeric instead of the Integer and Numeric classes.
4
10
 
5
11
  ## [1.1.1] - 2016-05-21
6
12
  ### Fixed
data/README.md CHANGED
@@ -1,6 +1,47 @@
1
1
  [![Gem Version](https://badge.fury.io/rb/rdl.svg)](https://badge.fury.io/rb/rdl) [![Build Status](https://travis-ci.org/plum-umd/rdl.svg?branch=master)](https://travis-ci.org/plum-umd/rdl)
2
2
 
3
3
 
4
+ # Table of Contents
5
+
6
+ * [Introduction](#introduction)
7
+ * [Using RDL](#using-rdl)
8
+ * [Supported versions of Ruby](#supported-versions-of-ruby)
9
+ * [Installing RDL](#installing-rdl)
10
+ * [Loading RDL](#loading-rdl)
11
+ * [Preconditions and Postconditions](#preconditions-and-postconditions)
12
+ * [Type Annotations](#type-annotations)
13
+ * [RDL Types](#rdl-types)
14
+ * [Nominal Types](#nominal-types)
15
+ * [Nil Type](#nil-type)
16
+ * [Top Type (%any)](#top-type-any)
17
+ * [Union Types](#union-types)
18
+ * [Intersection Types](#intersection-types)
19
+ * [Optional Argument Types](#optional-argument-types)
20
+ * [Variable Length Argument Types](#variable-length-argument-types)
21
+ * [Named Argument Types](#named-argument-types)
22
+ * [Dependent Types](#dependent-types)
23
+ * [Higher-order Types](#higher-order-types)
24
+ * [Class/Singleton Method Types](#classsingleton-method-types)
25
+ * [Structural Types](#structural-types)
26
+ * [Singleton Types](#singleton-types)
27
+ * [Self Type](#self-type)
28
+ * [Type Aliases](#type-aliases)
29
+ * [Generic Class Types](#generic-class-types)
30
+ * [Tuple Types](#tuple-types)
31
+ * [Finite Hash Types](#finite-hash-types)
32
+ * [Type Casts](#type-casts)
33
+ * [Bottom Type (%bot)](#bottom-type-bot)
34
+ * [Static Type Checking](#static-type-checking)
35
+ * [Types for Variables](#types-for-variables)
36
+ * [Tuples, Finite Hashes, and Subtyping](#tuples-finite-hashes-and-subtyping)
37
+ * [Other Features and Limitations](#other-features-and-limitations)
38
+ * [Other RDL Methods](#other-rdl-methods)
39
+ * [Queries](#queries)
40
+ * [Bibliography](#bibliography)
41
+ * [Copyright](#copyright)
42
+ * [Contributors](#contributors)
43
+ * [TODO List](#todo-list)
44
+
4
45
  # Introduction
5
46
 
6
47
  RDL is a lightweight system for adding contracts to Ruby. A *contract* decorates a method with assertions describing what the method assumes about its inputs (called a *precondition*) and what the method guarantees about its outputs (called a *postcondition*). For example, using RDL we can write
@@ -17,7 +58,6 @@ end
17
58
 
18
59
  Given this program, RDL intercepts the call to `sqrt` and passes its argument to the `pre` block, which checks that the arugment is positive. Then when `sqrt` returns, RDL passes the return value (as `r`) and the initial argument (as `x`) to the `post` block, which checks that the return is positive. (Let's ignore complex numbers to keep things simple...)
19
60
 
20
-
21
61
  RDL contracts are enforced at method entry and exit. For example, if we call `sqrt(49)`, RDL first checks that `49 > 0`; then it passes `49` to `sqrt`, which (presumably) returns `7`; then RDL checks that `7 > 0`; and finally it returns `7`.
22
62
 
23
63
  In addition to arbitrary pre- and post-conditions, RDL also has extensive support for contracts that are *types*. For example, we can write the following in RDL:
@@ -31,6 +71,28 @@ def m(x,y) ... end
31
71
 
32
72
  This indicates that `m` is that method that returns a `String` if given two `Fixnum` arguments. Again this contract is enforced at run-time: When `m` is called, RDL checks that `m` is given exactly two arguments and both are `Fixnums`, and that `m` returns an instance of `String`. RDL supports many more complex type annotations; see below for a complete discussion and examples. We should emphasize here that RDL types are enforced as contracts at method entry and exit. There is no static checking that the method body conforms to the types.
33
73
 
74
+ The beta version of RDL also has an experimental mode in which method bodies can be *statically type checked* against their signatures. (This feature is only in the beta version of the RDL gem.) For example:
75
+
76
+ ```ruby
77
+ file.rb:
78
+ require 'rdl'
79
+
80
+ type '(Fixnum) -> Fixnum', typecheck_now: true
81
+ def id(x)
82
+ "forty-two"
83
+ end
84
+ ```
85
+
86
+ ```
87
+ $ ruby file.rb
88
+ .../lib/rdl/typecheck.rb:32:in `error': (RDL::Typecheck::StaticTypeError)
89
+ .../file.rb:5:5: error: got type `String' where return type `Fixnum' expected
90
+ .../file.rb:5: "forty-two"
91
+ .../file.rb:5: ^~~~~~~~~~~
92
+ ```
93
+
94
+ Passing `typecheck_now: true` to `type` checks the method body immediately or as soon as it is defined. Passing `typecheck: true` to `type` statically type checks the method body whenever it is called.
95
+
34
96
  RDL contracts and types are stored in memory at run time, so it's also possible for programs to query them. RDL includes lots of contracts and types for the core and standard libraries. Since those methods are generally trustworthy, RDL doesn't actually enforce the contracts (since that would add overhead), but they are available to search and query. RDL includes a small script `rdl_query` to look up type information from the command line. Note you might need to put the argument in quotes depending on your shell.
35
97
 
36
98
  ```shell
@@ -57,7 +119,7 @@ $ irb
57
119
 
58
120
  Currently only type information is returned by `rdl_query` (and not other pre or postconditions).
59
121
 
60
- # RDL Reference
122
+ # Using RDL
61
123
 
62
124
  ## Supported versions of Ruby
63
125
 
@@ -105,7 +167,7 @@ The `post` method can be called in the same ways as `pre`.
105
167
 
106
168
  Methods can have no contracts, `pre` by itself, `post` by itself, both, or multiple instances of either. If there are multiple contracts, RDL checks that *all* contracts are satisfied, in the order that the contracts were bound to the method.
107
169
 
108
- ## Type Signatures
170
+ ## Type Annotations
109
171
 
110
172
  The `type` method adds a type contract to a method. It supports the same calling patterns as `pre` and `post`, except rather than a block, it takes a string argument describing the type. More specifically, `type` can be called as:
111
173
 
@@ -113,9 +175,19 @@ The `type` method adds a type contract to a method. It supports the same calling
113
175
  * `type m, 'typ'`
114
176
  * `type cls, mth, 'typ'`
115
177
 
116
- A type string generally has the form `(typ1, ..., typn) -> typ` indicating a method that takes `n` arguments of types `typ1` through `typn` and returns type `typ`. To illustrate the various types RDL supports, we'll use examples from the core library type annotations.
178
+ A type string generally has the form `(typ1, ..., typn) -> typ` indicating a method that takes `n` arguments of types `typ1` through `typn` and returns type `typ`. Below, to illustrate the various types RDL supports, we'll use examples from the core library type annotations.
179
+
180
+ The `type` method can be called with `wrap: false` so the type information is stored but the type is not enforced. For example, due to the way RDL is implemented, the method `String#=~` can't have a type or contract on it because then it won't set the correct `$1` etc variables:
181
+
182
+ ```ruby
183
+ type :=~, '(Object) -> Fixnum or nil', wrap: false # Wrapping this messes up $1 etc
184
+ ```
185
+
186
+ For consistency, `pre` and `post` can also be called with `wrap: false`, but this is generally not as useful.
187
+
188
+ # RDL Types
117
189
 
118
- ### Nominal Types
190
+ ## Nominal Types
119
191
 
120
192
  A nominal type is simply a class name, and it matches any object of that class or any subclass.
121
193
 
@@ -123,7 +195,7 @@ A nominal type is simply a class name, and it matches any object of that class o
123
195
  type String, :insert, '(Fixnum, String) -> String'
124
196
  ```
125
197
 
126
- ### Nil Type
198
+ ## Nil Type
127
199
 
128
200
  The nominal type `NilClass` can also be written as `nil`. The only object of this type is `nil`:
129
201
 
@@ -138,7 +210,7 @@ x.insert(0, nil) # RDL does not report a type error
138
210
  ```
139
211
  We chose this design based on prior experience with static type systems for Ruby, where not allowing this leads to a lot of false positive errors from the type system. However, we may change this in the future.
140
212
 
141
- ### Top Type (%any)
213
+ ## Top Type (%any)
142
214
 
143
215
  RDL includes a special "top" type `%any` that matches any object:
144
216
  ```ruby
@@ -146,7 +218,7 @@ type Object, :=~, '(%any) -> nil'
146
218
  ```
147
219
  We call this the "top" type because it is the top of the subclassing hierarchy RDL uses. Note that `%any` is more general than `Object`, because not all classes inherit from `Object`, e.g., `BasicObject` does not.
148
220
 
149
- ### Union Types
221
+ ## Union Types
150
222
 
151
223
  Many Ruby methods can take several different types of arguments or return different types of results. The union operator `or` can be used to indicate a position where multiple types are possible.
152
224
 
@@ -157,7 +229,7 @@ type String, :getbyte, '(Fixnum) -> Fixnum or nil'
157
229
 
158
230
  Note that for `getbyte`, we could leave off the `nil`, but we include it to match the current documentation of this method.
159
231
 
160
- ### Intersection Types
232
+ ## Intersection Types
161
233
 
162
234
  Sometimes Ruby methods have several different type signatures. (In Java these would be called *overloaded* methods.) In RDL, such methods are assigned a set of type signatures:
163
235
 
@@ -189,7 +261,7 @@ type String, :[], '(Range) -> String or nil'
189
261
  type String, :[], '(Regexp) -> String or nil'
190
262
  ```
191
263
 
192
- ### Optional Argument Types
264
+ ## Optional Argument Types
193
265
 
194
266
  Optional arguments are denoted in RDL by putting `?` in front of the argument's type. For example:
195
267
 
@@ -208,7 +280,7 @@ but it helps make types more readable.
208
280
 
209
281
  Like Ruby, RDL allows optional arguments to appear anywhere in a method's type signature.
210
282
 
211
- ### Variable Length Argument Types
283
+ ## Variable Length Argument Types
212
284
 
213
285
  In RDL, `*` is used to decorate an argument that may appear zero or more times. Currently in RDL this annotation may only appear on the rightmost argument. For example, `String#delete` takes one or more `String` arguments:
214
286
 
@@ -216,7 +288,7 @@ In RDL, `*` is used to decorate an argument that may appear zero or more times.
216
288
  type String, :delete, '(String, *String) -> String'
217
289
  ```
218
290
 
219
- ### Named Argument Types
291
+ ## Named Argument Types
220
292
 
221
293
  RDL allows arguments to be named, for documentation purposes. Names are given after the argument's type, and they do not affect type contract checking in any way. For example:
222
294
 
@@ -226,7 +298,7 @@ type Fixnum, :to_s, '(?Fixnum base) -> String'
226
298
 
227
299
  Here we've named the first argument of `to_s` as `base` to give some extra hint as to its meaning.
228
300
 
229
- ### Dependent Types
301
+ ## Dependent Types
230
302
 
231
303
  RDL allows for refinement predicates to be attached to named arguments. These predicates are then checked when the method is called and returns. For instance:
232
304
 
@@ -248,9 +320,9 @@ def m(x,y) ... end
248
320
 
249
321
  Any arbitrary code can be placed between the double braces of a type refinement, and RDL will dynmically check that this predicate evaluates to true, or raise a type error if it evaluates to false.
250
322
 
251
- ### Higher-Order Contracts
323
+ ## Higher-order Types
252
324
 
253
- RDL supports contracts for arguments or return values which are themselves `Proc` objects. Simply enclose the corresponding argument's type with braces to denote that it is a `Proc`. For example:
325
+ RDL supports types for arguments or return values which are themselves `Proc` objects. Simply enclose the corresponding argument's type with braces to denote that it is a `Proc`. For example:
254
326
 
255
327
  ```ruby
256
328
  type '(Fixnum, {(Fixnum) -> Fixnum}) -> Fixnum'
@@ -264,7 +336,7 @@ type '(Fixnum) -> {(Float) -> Float}'
264
336
  def m(x) ... end
265
337
  ```
266
338
 
267
- These higher-order contracts are checked by wrapping the corresponding `Proc` argument/return in a new `Proc` which checks that the type contract holds.
339
+ These higher-order types are checked by wrapping the corresponding `Proc` argument/return in a new `Proc` which checks that the type contract holds.
268
340
 
269
341
  A type contract can be provided for a method block as well. The block's type should be included after the method argument types:
270
342
 
@@ -282,7 +354,7 @@ def m(x,y,&blk) ... end
282
354
 
283
355
  The type contract above states that method `m` returns a `Proc` which takes a `Fixnum z` which must be greater than the argument `Float y`. Whenver this `Proc` is called, it will be checked that this contract holds.
284
356
 
285
- ### Class/Singleton Method Types
357
+ ## Class/Singleton Method Types
286
358
 
287
359
  RDL method signatures can be used both for instance methods and for class methods (often called *singleton methods* in Ruby). To indicate a type signature applies to a singleton method, prefix the method name with `self.`:
288
360
 
@@ -298,7 +370,7 @@ Type signatures can be added to `initialize` by giving a type signature for `sel
298
370
  type File, 'self.new', '(String file, ?String mode, ?String perm, ?Fixnum opt) -> File'
299
371
  ```
300
372
 
301
- ### Structural Types
373
+ ## Structural Types
302
374
 
303
375
  Some Ruby methods are intended to take any object that has certain methods. RDL uses *structural types* to denote such cases:
304
376
 
@@ -310,7 +382,7 @@ Here `IO#puts` can take zero or more arguments, all of which must have a `to_s`
310
382
 
311
383
  The actual checking that RDL does here varies depending on what type information is available. Suppose we call `puts(o)`. If `o` is an instance of a class that has a type signature `t` for `to_s`, then RDL will check that `t` is compatible with `() -> String`. On the other hand, if `o` is an instance of a class with no type signature for `to_s`, RDL only checks that `o` has a `to_s` method, but it doesn't check its argument or return types.
312
384
 
313
- ### Singleton Types
385
+ ## Singleton Types
314
386
 
315
387
  Not to be confused with types for singleton methods, RDL includes *singleton types* that denote positions that always have one particular value; this typically happens only in return positions. For example, `Dir#mkdir` always returns the value 0:
316
388
 
@@ -326,8 +398,9 @@ type Float, :angle, '() -> 0 or ${Math::PI}'
326
398
 
327
399
  RDL checks if a value matches a singleton type using `equal?`. As a consequence, singleton string types aren't currently possible.
328
400
 
401
+ Note that the type `nil` is actually implemented as a singleton type with the special behavior that `nil` is a treated as a member of any class. However, while `nil` can in general be used anywhere any type is expected, it *cannot* be used where a different singleton type is expected. For example, `nil` could not be a return value of `Dir#mkdir` or `Float#angle`.
329
402
 
330
- ### Self Type
403
+ ## Self Type
331
404
 
332
405
  Consider a method that returns `self`:
333
406
 
@@ -358,9 +431,9 @@ A.new.id # okay, returns self
358
431
  B.new.id # also okay, returns self
359
432
  ```
360
433
 
361
- Note that type `self` means *exactly* the self object, i.e., it is a singleton type. It does not mean "any object of self's class." Thus, for example, `Object#clone` has type `() -> %any`, since it will return a different object. We might change this behavior in the future.
434
+ Thus, the type `self` means "any object of self's class."
362
435
 
363
- ### Type Aliases
436
+ ## Type Aliases
364
437
 
365
438
  RDL allows types to be aliases to make them faster to write down and more readable. All type aliases begin with `%`. RDL has one built-in alias, `%bool`, which is shorthand for `TrueClass or FalseClass`:
366
439
 
@@ -380,7 +453,7 @@ type_alias '%path', '%string or Pathname'
380
453
 
381
454
  Type aliases have to be created before they are used (so above, `%path` must be defined after `%string`).
382
455
 
383
- ### Generic Class Types
456
+ ## Generic Class Types
384
457
 
385
458
  RDL supports *parametric polymorphism* for classes, a.k.a. *generics*. The `type_params` method names the type parameters of the class, and those parameters can then be used inside type signatures:
386
459
 
@@ -439,7 +512,7 @@ The rules for variances are standard. Let's assume `A` is a subclass of `B`. Als
439
512
  * `C<A>` is a subtype of `C<B>` if `C`'s type parameter is covariant
440
513
  * `C<B>` is a subtype of `C<A>` if `C`'s type parameter is contravariant
441
514
 
442
- ### Tuple Types
515
+ ## Tuple Types
443
516
 
444
517
  A type such as `Array<Fixnum>` is useful for homogeneous arrays, where all elements have the same type. But Ruby programs often use heterogenous arrays, e.g., `[1, "two"]`. The best generic type we can give this is `Array<Fixnum or String>`, but that's imprecise.
445
518
 
@@ -449,7 +522,7 @@ RDL includes special *tuple types* to handle this situation. Tuple types are wri
449
522
  type Process, 'self.getrlimit', '(Symbol or String or Fixnum resource) -> [Fixnum, Fixnum] cur_max_limit'
450
523
  ```
451
524
 
452
- ### Finite Hash Types
525
+ ## Finite Hash Types
453
526
 
454
527
  Similarly to tuple types, RDL also supports *finite hash types* for heterogenous hashes. Finite hash types are written `{k1 => v1, ..., kn => vn}` to indicate a `Hash` with `n` mappings of type `ki` maps to `vi`. The `ki` may be strings, integers, floats, or constants denoted with `${.}`. If a key is a symbol, then the mapping should be written `ki: vi`. In the latter case, the `{}`'s can be left off:
455
528
  ```ruby
@@ -457,19 +530,125 @@ type MyClass, :foo, '(a: Fixnum, b: String) { () -> %any } -> %any'
457
530
  ```
458
531
  Here `foo`, takes a hash where key `:a` is mapped to a `Fixnum` and key `:b` is mapped to a `String`. Similarly, `{'a'=>Fixnum, 2=>String}` types a hash where keys `'a'` and `2` are mapped to a `Fixnum` and `String`, respectively. Both syntaxes can be used to define hash types.
459
532
 
460
- ## Other Methods
533
+ ## Type Casts
461
534
 
462
- RDL also includes a few other useful methods:
535
+ Sometimes RDL does not have precise information about an object's type (this is most useful during static type checking). For these cases, RDL supports type casts of the form `o.type_cast(t)`. This call returns a new object that delegates all methods to `o` but that will be treated by RDL as if it had type `t`. If `force: true` is passed to `type_cast`, RDL will perform the cast without checking whether `o` is actually a member of the given type. For example, `x = "a".type_cast('nil', force: true)` will make RDL treat `x` as if it had type `nil`, even though it's a `String`.
536
+
537
+ ## Bottom Type (%bot)
538
+
539
+ RDL also includes a special *bottom* type `%bot` that is a subtype of any type, including any class and any singleton types. In static type checking, the type `%bot` is given to so-called *void value expressions*, which are `return`, `break`, `next`, `redo`, and `retry` (notice that these expressions perform jumps rather than producing a value, hence they can be treated as having an arbitrary type). No Ruby objects have type `%bot`.
540
+
541
+ # Static Type Checking
542
+
543
+ RDL has experimental support (note: this is in beta release) for static type checking. As mentioned in the introduction, calling `type` with `typecheck_now: true` statically type checks the body of the annotated method body against the given signature. If the method has already been defined, RDL will try to check the method immediately. Otherwise, RDL will statically type check the method as soon as it is loaded.
544
+
545
+ Often method bodies cannot be type checked as soon as they are loaded because they refer to classes, methods, and variables that have not been created yet. To support these cases, `type` can be called with `typecheck: true`, which will delay checking the method's type until the method is called. Currently these checks are not cached, so expect a big performance hit for using this feature.
546
+
547
+ To perform type checking, RDL needs source code, which it gets by parsing the file containing the to-be-typechecked method. Hence, static type checking does not work in `irb` since RDL has no way of getting the source. RDL currently uses the [parser Gem](https://github.com/whitequark/parser) to parse Ruby source code. (And RDL uses the parser gem's amazing diagnostic output facility to print type error messages.)
548
+
549
+ Next we discuss some special features of RDL's type system and some of its limitations.
550
+
551
+ ## Types for Variables
552
+
553
+ In a standard type system, local variables have one type throughout a method or function body. For example, in C and Java, declaring `int x` means `x` can only be used as an integer. However, in Ruby, variables need not be declared before they are used. Thus, by default, RDL treats local variables *flow-sensitively*, meaning at each assignment to a local variable, the variable's type is replaced by the type of the right hand side. For example:
554
+
555
+ ```ruby
556
+ x = 3 # Here `x` is a `Fixnum`
557
+ x = "three" # Now `x` is a `String`
558
+ ```
559
+ (Note this is a slight fib, since after the first line, `x` will actually have the singleton type `3`. But we'll ignore this just to keep the discussion a bit simpler, especially since `3` is a subtype of `Fixnum`.)
560
+
561
+ After conditionals, variables have the union of the types they have along both branches:
562
+
563
+ ```ruby
564
+ if (some condition) then x = 3 else x = "three" end
565
+ # x has type `Fixnum or String`
566
+ ```
567
+
568
+ RDL also provides a method `var_type` that can be used to force a local variable to have a single type through a method body, i.e., to treat it *flow-insensitively* like a standard type system:
463
569
 
464
- * `rdl_alias(new_name, old_name)` tells RDL that method `new_name` is an alias for method `old_name`, and therefore they should have the same contracts and types. This method is only needed when adding contracts and types to method that have already been aliased; it's not needed if the method is aliased after the contract or type has been added.
570
+ ```ruby
571
+ var_type :x, 'Fixnum'
572
+ x = 3 # okay
573
+ x = "three" # type error
574
+ ```
575
+
576
+ The first argument to `var_type` is a symbol with the local variable name, and the second argument is a string containing the variable's type. Note that `var_type` is most useful at the beginning of method or code block. Using it elsewhere may result in surprising error mesages, since RDL requires variables with fixed types to have the same type along all paths. Method parameters are treated as if `var_type` was called on them at the beginning of the method, fixing them to their declared type. This design choice may be revisited in the future.
577
+
578
+ RDL always treats instance, class, and global variables flow-insensitively, hence their types must be defined with `var_type`:
579
+
580
+ ```ruby
581
+ class A
582
+ var_type :@f, 'Fixnum'
583
+ def m
584
+ @f = 3 # type safe
585
+ @f = "three" # type error, incompatible type in assignment
586
+ @g = 42 # type error, no var_type for @g
587
+ end
588
+ end
589
+ ```
590
+
591
+ The `var_type` method may also be called as `var_type klass, :name, typ` to assign a type to an instance or class variable of class `klass`.
592
+
593
+ ## Tuples, Finite Hashes, and Subtyping
594
+
595
+ When RDL encounters a literal array in the program, it assigns it a tuple type, which allows, among other things, precise handling of multiple assignment. For example:
596
+
597
+ ```ruby
598
+ x = [1, 'foo'] # x has type [1, String]
599
+ a, b = x # a has type 1, b has type String
600
+ ```
601
+
602
+ RDL also allows a tuple `[t1, ..., tn]` to be used where `Array<t1 or ... or tn>` is expected. This means both when a tuple is passed to an `Array` position, and when any method is invoked on the tuple (even if RDL could safely apply that method to the tuple; this may change in the future):
603
+
604
+ ```ruby
605
+ var_type @f, 'Array<Fixnum or String>'
606
+ @f = [1, 'foo'] # okay
607
+ @f.length # also okay
608
+ ```
609
+
610
+ To maintain soundness, a tuple that is used as an `Array` is treated as if it were always an array. For example:
611
+
612
+ ```ruby
613
+ x = [1, 'foo'] # at this point, x has type [1, String]
614
+ var_type @f, '[1, String]'
615
+ @f = x # okay so far
616
+ var_type @g, 'Array<Fixnum or String>'
617
+ @g = x # uh oh
618
+ ```
619
+
620
+ When RDL encounters the assignment to `@g`, it retroactively changes `x` to have type `Array<Fixnum or String>`, which is incompatible with type `[1, String]`, so the last assignment signals an error.
621
+
622
+ RDL uses the same approach for hashes: hash literals are treated as finite hashes. A finite hash `{k1=>v1, ..., kn=>vn}` can be used where `Hash<k1 or ... or kn, v1 or ... or vn>` is expected. And if a finite hash is used as a `Hash` (including invoking methods on the finite hash; this may change in the future), then it is retroactively converted to a `Hash`.
623
+
624
+ ## Other Features and Limitations
625
+
626
+ * *Conditional guards and singletons.* If an `if` or `unless` guard has a singleton type, RDL will typecheck both branches but not include types from the unrealizable branch in the expression type. For example, `if true then 1 else 'two' end` has type `1`. RDL behaves similarly for `&&` and `||`. However, RDL does not implement this logic for `case`.
627
+
628
+ * *Multiple Assignment and nil.* In Ruby, extra left-hand sides of multiple assignments are set to `nil`, e.g., `x, y = [1]` sets `x` to `1` and `y` to `nil`. However, RDL reports an error in this case; this may change in the future.
629
+
630
+ * *Block formal arguments.* Similarly, RDL reports an error if a block is called with the wrong number of arguments even though Ruby does not signal an error in this case.
631
+
632
+ * *Caching.* If `typecheck: true` is specified on a method, Ruby will type check the method every time it is called. In the future, RDL will cache these checks.
633
+
634
+ * *Unsupported Features.* There are several features of Ruby that are currently not handled by RDL. Here is a non-exhaustive list:
635
+ * `super` is not supported.
636
+ * `lambda` has special semantics for `return`; this is not supported.
637
+ * Only simple block argument lists and `for` iteration variables are supported.
638
+ * Control flow for exceptions is not analyzed fully soundly; some things are not reported as possibly `nil` that could be.
639
+ * Only simple usage of constants is handled.
640
+
641
+ # Other RDL Methods
642
+
643
+ RDL also includes a few other useful methods:
465
644
 
466
- * `o.type_cast(t)` returns a new object that delegates all methods to `o` but that will be treated by RDL as if it had type `t`. For example, `x = "a".type_cast('nil')` will make RDL treat `x` as if it had type `nil`, even though it's a `String`.
645
+ * `rdl_alias new_name, old_name` tells RDL that method `new_name` is an alias for method `old_name`, and therefore they should have the same contracts and types. This method is only needed when adding contracts and types to method that have already been aliased; it's not needed if the method is aliased after the contract or type has been added.
467
646
 
468
- * `rdl_nowrap`, if called at the top-level of a class, tells RDL to record contracts and types for methods in that class but *not* enforce them. This is mostly used for the core and standard libraries, which have trustworthy behavior hence enforcing their types and contracts is not worth the overhead.
647
+ * `rdl_nowrap`, if called at the top-level of a class, causes RDL to behave as if `wrap: false` were passed to all `type`, `pre`, and `post` calls in the class. This is mostly used for the core and standard libraries, which have trustworthy behavior hence enforcing their types and contracts is not worth the overhead.
469
648
 
470
649
  * `rdl_query` prints information about types; see below for details.
471
650
 
472
- ## Queries
651
+ # Queries
473
652
 
474
653
  As discussed above, RDL includes a small script, `rdl_query`, to look up type information. (Currently it does not support other pre- and postconditions.) The script takes a single argument, which should be a string. Note that when using the shell script, you may need to use quotes depending on your shell. Currently several queries are supported:
475
654
 
@@ -573,7 +752,7 @@ Copyright (c) 2014-2016, University of Maryland, College Park. All rights reserv
573
752
  * Alexander T. Yu
574
753
  * Milod Kazerounian
575
754
 
576
- # TODO list
755
+ # TODO List
577
756
 
578
757
  * How to check whether initialize? is user-defined? method_defined? always
579
758
  returns true, meaning wrapping isn't fully working with initialize.
@@ -2,4 +2,4 @@ source 'https://rubygems.org'
2
2
 
3
3
  gem 'rake'
4
4
  gem 'minitest'
5
- gem 'require_all'
5
+ gem 'parser'