rdl 2.1.0 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +1 -0
  3. data/.travis.yml +7 -6
  4. data/CHANGES.md +29 -0
  5. data/README.md +94 -26
  6. data/lib/rdl/boot.rb +82 -41
  7. data/lib/rdl/boot_rails.rb +5 -0
  8. data/lib/rdl/config.rb +9 -1
  9. data/lib/rdl/query.rb +2 -2
  10. data/lib/rdl/typecheck.rb +972 -225
  11. data/lib/rdl/types/annotated_arg.rb +8 -0
  12. data/lib/rdl/types/ast_node.rb +73 -0
  13. data/lib/rdl/types/bot.rb +8 -0
  14. data/lib/rdl/types/bound_arg.rb +63 -0
  15. data/lib/rdl/types/computed.rb +48 -0
  16. data/lib/rdl/types/dependent_arg.rb +9 -0
  17. data/lib/rdl/types/dynamic.rb +61 -0
  18. data/lib/rdl/types/finite_hash.rb +54 -9
  19. data/lib/rdl/types/generic.rb +33 -0
  20. data/lib/rdl/types/intersection.rb +8 -0
  21. data/lib/rdl/types/lexer.rex +6 -1
  22. data/lib/rdl/types/lexer.rex.rb +13 -1
  23. data/lib/rdl/types/method.rb +14 -0
  24. data/lib/rdl/types/nominal.rb +8 -0
  25. data/lib/rdl/types/non_null.rb +8 -0
  26. data/lib/rdl/types/optional.rb +8 -0
  27. data/lib/rdl/types/parser.racc +31 -5
  28. data/lib/rdl/types/parser.tab.rb +540 -302
  29. data/lib/rdl/types/rdl_types.rb +45 -0
  30. data/lib/rdl/types/singleton.rb +14 -1
  31. data/lib/rdl/types/string.rb +104 -0
  32. data/lib/rdl/types/structural.rb +8 -0
  33. data/lib/rdl/types/top.rb +8 -0
  34. data/lib/rdl/types/tuple.rb +32 -8
  35. data/lib/rdl/types/type.rb +54 -11
  36. data/lib/rdl/types/union.rb +41 -2
  37. data/lib/rdl/types/var.rb +10 -0
  38. data/lib/rdl/types/vararg.rb +8 -0
  39. data/lib/rdl/util.rb +13 -10
  40. data/lib/rdl/wrap.rb +271 -27
  41. data/lib/rdl_disable.rb +16 -2
  42. data/lib/types/active_record.rb +1 -0
  43. data/lib/types/core/array.rb +442 -23
  44. data/lib/types/core/basic_object.rb +3 -3
  45. data/lib/types/core/bigdecimal.rb +5 -0
  46. data/lib/types/core/class.rb +2 -0
  47. data/lib/types/core/dir.rb +3 -3
  48. data/lib/types/core/enumerable.rb +4 -4
  49. data/lib/types/core/enumerator.rb +1 -1
  50. data/lib/types/core/file.rb +4 -4
  51. data/lib/types/core/float.rb +203 -0
  52. data/lib/types/core/hash.rb +390 -15
  53. data/lib/types/core/integer.rb +223 -10
  54. data/lib/types/core/io.rb +2 -2
  55. data/lib/types/core/kernel.rb +8 -5
  56. data/lib/types/core/marshal.rb +3 -0
  57. data/lib/types/core/module.rb +3 -3
  58. data/lib/types/core/numeric.rb +0 -2
  59. data/lib/types/core/object.rb +5 -5
  60. data/lib/types/core/pathname.rb +2 -2
  61. data/lib/types/core/process.rb +1 -3
  62. data/lib/types/core/range.rb +1 -1
  63. data/lib/types/core/regexp.rb +2 -2
  64. data/lib/types/core/set.rb +1 -1
  65. data/lib/types/core/string.rb +408 -16
  66. data/lib/types/core/symbol.rb +3 -3
  67. data/lib/types/core/time.rb +1 -1
  68. data/lib/types/core/uri.rb +13 -13
  69. data/lib/types/rails/_helpers.rb +7 -1
  70. data/lib/types/rails/action_controller/mime_responds.rb +2 -0
  71. data/lib/types/rails/active_record/associations.rb +42 -30
  72. data/lib/types/rails/active_record/comp_types.rb +637 -0
  73. data/lib/types/rails/active_record/finder_methods.rb +1 -1
  74. data/lib/types/rails/active_record/model_schema.rb +28 -16
  75. data/lib/types/rails/active_record/relation.rb +5 -3
  76. data/lib/types/rails/active_record/sql-strings.rb +166 -0
  77. data/lib/types/rails/string.rb +1 -1
  78. data/lib/types/sequel.rb +1 -0
  79. data/lib/types/sequel/comp_types.rb +581 -0
  80. data/rdl.gemspec +5 -4
  81. data/test/test_alias.rb +4 -0
  82. data/test/test_array_types.rb +244 -0
  83. data/test/test_bound_types.rb +80 -0
  84. data/test/test_contract.rb +4 -0
  85. data/test/test_dsl.rb +5 -0
  86. data/test/test_dyn_comptype_checks.rb +206 -0
  87. data/test/test_generic.rb +21 -20
  88. data/test/test_hash_types.rb +322 -0
  89. data/test/test_intersection.rb +1 -0
  90. data/test/test_le.rb +29 -4
  91. data/test/test_member.rb +3 -1
  92. data/test/test_parser.rb +5 -0
  93. data/test/test_query.rb +1 -0
  94. data/test/test_rdl.rb +63 -28
  95. data/test/test_rdl_type.rb +4 -0
  96. data/test/test_string_types.rb +102 -0
  97. data/test/test_type_contract.rb +59 -37
  98. data/test/test_typecheck.rb +480 -75
  99. data/test/test_types.rb +17 -0
  100. data/test/test_wrap.rb +5 -0
  101. metadata +35 -5
  102. data/lib/types/rails/active_record/schema_types.rb +0 -51
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: aabf7af1d05594fdd962964b90285fa322cc9aef
4
- data.tar.gz: 435dff6920f147343d71bf459fa8de1462358195
2
+ SHA256:
3
+ metadata.gz: cd1da46a0ca86e95d211710bd2fbf149d299a4e85d481970afbff765ab9150f1
4
+ data.tar.gz: eb03a0a773a98e85eef56e0fdcc7b266eb35dd960470076d5db6de1259fdb9c4
5
5
  SHA512:
6
- metadata.gz: 9ae6a8193a9589662d98b35ab30eec6836dcbd123317e7e820916728a8686b0b3e1d914f433d91a44c904468d763eac316bab00886441675246fc46e8ce410e3
7
- data.tar.gz: b6503f4c82c45555249c85e08c4ae9d10dc287f62462c22cf36dfaa48d63d6a4151b27b747871024fc4b398ea280bb3584355a08dc0b097f11a0c346f08fc9fb
6
+ metadata.gz: 9ea8b806ceee543905a3c1fbdc9c68ec9eb599b793ef3c24d902c3bca019b7321cc755f5cfca510766c222791426341307228faeea886e1d889eedd6ed1a9150
7
+ data.tar.gz: 4c1ec45edbff7d790362f97de394eb7d14d5eb7a3d0bb0ff0ae86f637e40f3ae364a0d9fdba53dc931c0bcd3af6ac120374cf8aa3ebef1fff4f374fe02223a56
data/.gitignore CHANGED
@@ -26,6 +26,7 @@
26
26
  *.rar\
27
27
  *.tar\
28
28
  *.zip\
29
+ *.gem\
29
30
  \'a0\
30
31
  # Logs and databases #\
31
32
  ######################\
@@ -2,12 +2,7 @@ language: ruby
2
2
  gemfile:
3
3
  - gemfiles/Gemfile.travis
4
4
  rvm:
5
- - 2.1.0
6
5
  - 2.1.1
7
- - 2.1.2
8
- - 2.1.3
9
- - 2.1.4
10
- - 2.1.5
11
6
  - 2.1.6
12
7
  - 2.1.7
13
8
  - 2.1.8
@@ -25,7 +20,13 @@ rvm:
25
20
  - 2.3.4
26
21
  - 2.4.0
27
22
  - 2.4.1
28
- sudo: false
23
+ - 2.4.2
24
+ - 2.4.3
25
+ - 2.4.4
26
+ - 2.4.5
27
+ - 2.5.5
28
+ - 2.6.2
29
+ - 2.6.3
29
30
  notifications:
30
31
  email:
31
32
  recipients:
data/CHANGES.md CHANGED
@@ -2,6 +2,35 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [2.2.0] - 2019-06-09
6
+
7
+ ### Fixed
8
+ - Dynamic type checking of initialize method
9
+ - Bug with handling constants of certain types
10
+ - Broken rdl_attr_* methods
11
+ - Bug handling optional annotated/dependent types
12
+ - Add RDLAnnotate to rdl_disable
13
+ - Type checking of Object class singleton methods
14
+ - #52 Kernel.raise annotation
15
+ - #42 use grandparent type information for methods
16
+ - #43 allow fully qualified calls
17
+ - #45 handle local constants
18
+ - #62 allow more method names in structural types
19
+ - #34 clean up discussion of how type checking works in README
20
+ - Reordered list of RDL.* methods in README
21
+ - Remove (most) ordering dependencies among test cases
22
+ - Undefined identified issue for Rails assocation types
23
+ - Type check expressions within casts
24
+ - Fix to `var_type` method when called from outside class
25
+ - Add `RDL.query` support for nested classes, e.g., `RDL.query "ActiveRecord::Base#id`
26
+
27
+ ### Added
28
+ - Support for type-level computations, including new annotations with
29
+ type-level computations for Array, Hash, String, Integer, Float, and
30
+ database query libraries
31
+ - Dynamic type
32
+ - `RDL.reset` method
33
+
5
34
  ## [2.1.0] - 2017-06-14
6
35
 
7
36
  ### Fixed
data/README.md CHANGED
@@ -1,5 +1,4 @@
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
-
1
+ [![Gem Version](https://badge.fury.io/rb/rdl.svg)](https://badge.fury.io/rb/rdl) [![Build Status](https://travis-ci.org/tupl-tufts/rdl.svg?branch=master)](https://travis-ci.org/tupl-tufts/rdl)
3
2
 
4
3
  # Table of Contents
5
4
 
@@ -16,6 +15,7 @@
16
15
  * [Nominal Types](#nominal-types)
17
16
  * [Nil Type](#nil-type)
18
17
  * [Top Type (%any)](#top-type-any)
18
+ * [Dynamic Type (%dyn)](#dynamic-type-dyn)
19
19
  * [Union Types](#union-types)
20
20
  * [Intersection Types](#intersection-types)
21
21
  * [Optional Argument Types](#optional-argument-types)
@@ -40,6 +40,7 @@
40
40
  * [Tuples, Finite Hashes, and Subtyping](#tuples-finite-hashes-and-subtyping)
41
41
  * [Other Features and Limitations](#other-features-and-limitations)
42
42
  * [Assumptions](#assumptions)
43
+ * [Type-Level Computations](#type-level-computations)
43
44
  * [RDL Method Reference](#rdl-method-reference)
44
45
  * [Performance](#performance)
45
46
  * [Queries](#queries)
@@ -70,10 +71,12 @@ file.rb:
70
71
  require 'rdl'
71
72
  extend RDL::Annotate
72
73
 
73
- type '(Integer) -> Integer', typecheck: :now
74
+ type '(Integer) -> Integer', typecheck: :label
74
75
  def id(x)
75
76
  "forty-two"
76
77
  end
78
+
79
+ RDL.do_typecheck :label
77
80
  ```
78
81
 
79
82
  ```
@@ -84,13 +87,15 @@ $ ruby file.rb
84
87
  .../file.rb:6: ^~~~~~~~~~~
85
88
  ```
86
89
 
87
- Passing `typecheck: :now` to `type` checks the method body immediately or as soon as it is defined. Passing `typecheck: :call` to `type` statically type checks the method body whenever it is called. Passing `typecheck: sym` for some other symbol statically type checks the method body when `RDL.do_typecheck sym` is called.
90
+ Passing `typecheck: sym` for some other symbol statically type checks the method body when `RDL.do_typecheck sym` is called. Passing `typecheck: :call` to `type` statically type checks the method body whenever it is called.
91
+
92
+ Note that RDL only type checks the bodies of methods with type signatures. Methods without type signatues, or code that is not in method bodies, is not checked.
88
93
 
89
94
  The `type` method can also be called with the class and method to be annotated, and it can also be invoked as `RDL.type` in case `extend RDL::Annotate` would cause namespace issues:
90
95
 
91
96
  ```
92
- type :A, :id, '(Integer) -> Integer', typecheck: :now # Add a type annotation for A#id.
93
- RDL.type :A, :id, '(Integer) -> Integer', typecheck: :now # Note class and method name required when calling like this
97
+ type :A, :id, '(Integer) -> Integer', typecheck: :label # Add a type annotation for A#id.
98
+ RDL.type :A, :id, '(Integer) -> Integer', typecheck: :label # Note class and method name required when calling like this
94
99
  ```
95
100
 
96
101
  RDL tries to follow the philosophy that you get what you pay for. Methods with type annotations can be checked dynamically or statically; methods without type annotations are unaffected by RDL. See the [performance](#performance) discussion for more detail.
@@ -136,11 +141,13 @@ end
136
141
 
137
142
  Given this program, RDL intercepts the call to `sqrt` and passes its argument to the `pre` block, which checks that the argument 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...) 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`. The `pre` and `post` methods can also be called as `RDL.pre` and `RDL.post`, as long as they are also given class and method arguments, similarly to `type`. Note that pre- and postconditions can't be searched for using `RDL.query`.
138
143
 
144
+ Note: RDL is a research project from the [Tufts University Computer Science Department](https://www.cs.tufts.edu) and the [University of Maryland, College Park Computer Science Department](https://www.cs.umd.edu). If you are looking for an industrial strength Ruby type system, check out Stripe’s [Sorbet](https://sorbet.org) system.
145
+
139
146
  # Using RDL
140
147
 
141
148
  ## Supported versions of Ruby
142
149
 
143
- RDL currently supports Ruby 2.x. It may or may not work with other versions.
150
+ RDL currently supports Ruby 2.x except 2.1.1-2.1.6. RDL may or may not work with other versions.
144
151
 
145
152
  ## Installing RDL
146
153
 
@@ -249,6 +256,14 @@ type Object, :=~, '(%any) -> nil'
249
256
  ```
250
257
  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.
251
258
 
259
+ ## Dynamic Type (%dyn)
260
+
261
+ RDL has the dynamic type `%dyn` that is the subtype and supertype of any type.
262
+ ```ruby
263
+ type Example, :method, '(%dyn) -> %dyn'
264
+ ```
265
+ This is useful for typed portions of a Ruby program that interact with untyped portions. RDL allows setting a [configuration option](#configuration) `assume_dyn_type` to `true` so that any method that is missing a type will be assumed to take and return the dynamic type. By default, this option is set to `false`.
266
+
252
267
  ## Union Types
253
268
 
254
269
  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.
@@ -561,6 +576,12 @@ type MyClass, :foo, '(a: Integer, b: String) { () -> %any } -> %any'
561
576
  ```
562
577
  Here `foo`, takes a hash where key `:a` is mapped to an `Integer` and key `:b` is mapped to a `String`. Similarly, `{'a'=>Integer, 2=>String}` types a hash where keys `'a'` and `2` are mapped to `Integer` and `String`, respectively. Both syntaxes can be used to define hash types.
563
578
 
579
+ Value types can also be declared as optional, indicating that the key/value pair is an optional part of the hash:
580
+ ```ruby
581
+ type MyClass, :foo, '(a: Integer, b: ?String) { () -> %any } -> %any'
582
+ ```
583
+ With this type, `foo` takes a hash where key `:a` is mapped to an `Integer`, and furthermore the hash may or may not include a key `:b` that is mapped to a `String`.
584
+
564
585
  RDL also allows a "rest" type in finite hashes (of course, they're not so finite if they use it!):
565
586
  ```ruby
566
587
  type MyClass, :foo, '(a: Integer, b: String, **Float) -> %any'
@@ -595,11 +616,9 @@ type :initialize, '(String, Fixnum) -> self'
595
616
 
596
617
  # Static Type Checking
597
618
 
598
- As mentioned in the introduction, calling `type` with `typecheck: :now` 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.
599
-
600
- 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, some other symbol can be supplied as `typecheck: sym`. Then when `RDL.do_typecheck sym` is called, all methods typechecked at `sym` will be statically checked.
619
+ As mentioned in the introduction, calling `type` with `typecheck: sym` statically type checks the body of the annotated method body against the given signature when `RDL.do_typecheck sym` is called. Note that at this call, all methods whose `type` call is labeled with `sym` will be statically checked.
601
620
 
602
- Additionally, `type` can be called with `typecheck: :call`, 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.
621
+ Additionally, `type` can be called with `typecheck: :call`, 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. Finally, `type` can be called with `typecheck: :now` to check the method immediately after it is defined. This is probably only useful for demos.
603
622
 
604
623
  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. Typechecking does work in `pry` (this feature has only limited testing) as long as typechecking is delayed until after the method is defined:
605
624
 
@@ -746,6 +765,36 @@ RDL's static type checker makes some assumptions that should hold unless your Ru
746
765
 
747
766
  (More assumptions will be added here as they are added to RDL...)
748
767
 
768
+ # Type-Level Computations
769
+
770
+ RDL includes support for type-level computations: embedding Ruby code *within* a method's type signature. Using these type-level computations, we can write type signatures that can check more expressive properties, such as the correctness of column names and types in a database query. We have found that type-level computations significantly reduce the need for manually inserted type casts. Below we will cover the basics of using type-level computations. For a closer look, check out our [paper](https://www.cs.tufts.edu/~jfoster/papers/pldi19.pdf) on the same topic.
771
+
772
+ We use type-level computations only in type signatures for methods which themselves are *not* type checked. Thus, our primary focus is on applying them to library methods. We add a computation to a type by writing Ruby code, delimited by double backticks \`\`...\`\`, in place of a type within a method's signature. This Ruby code may refer to two special variables: `trec`, which names the RDL type of the receiver for a given method call, and `targs`, which names an array of RDL types of the arguments for a given method call. The code must itself evaluate to a type.
773
+
774
+ As an example, we will write a type-level computation for the `Integer#+` method. Normaly, this method would have the simple RDL type `type '(Integer) -> Integer'` (for the case that it takes another `Integer`). Using type level computations, we can write the following more precise type:
775
+
776
+ ```ruby
777
+ type '(Integer) -> ``if trec.is_a?(RDL::Type::SingletonType) && targs[0].is_a?(RDL::Type::SingletonType) then RDL::Type::SingletonType.new(trec.val+targs[0].val) else RDL::Type::NominalType.new(Integer) end``'
778
+ ```
779
+
780
+ Now, in place of a simple return type, we have written some Ruby code. This code checks if both the receiver (`trec`) and argument (`targs[0]`) have a singleton type. If so, in the first arm of the branch we compute a new singleton type containing the sum of the receiver and argument. Otherwise, we fall back on returning the nominal type `Integer`. With this signature, RDL can determine the expression `1+2` has the type `3`, rather than the less precise type `Integer`.
781
+
782
+ We also provide a way of binding variable names to argument types, to avoid using the variable `targs`. For example, for the type signature above, we can give the argument type the name `t`, which we then refer to in the type-level computation:
783
+
784
+ ```ruby
785
+ type '(Integer) -> ``if trec.is_a?(RDL::Type::SingletonType) && t.is_a?(RDL::Type::SingletonType) then RDL::Type::SingletonType.new(trec.val+t.val) else RDL::Type::NominalType.new(Integer) end``'
786
+ ```
787
+
788
+ We have written type-level computations for methods from the Integer, Float, Array, Hash, and String core libraries, which you can find in the `/lib/types/core/` directory. We have also written them for a number of database query methods from both the [ActiveRecord](https://github.com/rails/rails/tree/master/activerecord) and [Sequel](https://github.com/jeremyevans/sequel) DSLs. They can be found in `comp_types.rb` file in the `/lib/types/rails/active_record` and `/lib/types/sequel` directories, respectively. These type-level computations for the database query methods depend on RDL pre-processing the schema of the database before type checking any user code. RDL provides helper methods to process the database schema:
789
+
790
+ ```ruby
791
+ RDL.load_sequel_schema(DATABASE) # `DATABASE` is the Sequel Database object
792
+
793
+ RDL.load_rails_schema # Automatically triggered if RDL is loaded in a Rails environment. If you want to use ActiveRecord without Rails, you need to call this method
794
+ ```
795
+
796
+ Because type-level computations are used for methods which themselves are not type checked, we include an optional configuration for inserting dynamic checks that ensure that these methods return values of the type expected by their type-level computation. Additionally, because type-level computations can refer to mutable state, we include a second configuration for re-running type-level computations at runtime to ensure that they evaluate to the same value they did at type checking time. Both of these configurations are by default turned off. See the [Configuration](#configuration) section for information on turning them on.
797
+
749
798
  # RDL Method Reference
750
799
 
751
800
  The following methods are available in `RDL::Annotate`.
@@ -768,25 +817,28 @@ The following methods are available in `RDL::Annotate`.
768
817
 
769
818
  The methods above can also be accessed as `rdl_method` after `extend RDL::RDLAnnotate`. They can also be accessed as `RDL.method`, but when doing so, however, the `klass` and `meth` arguments are not optional and must be specified. The `RDL` module also includes several other useful methods:
770
819
 
771
- * `RDL.type_alias '%name', typ` - indicates that if `%name` appears in a type annotations, it should be expanded to `typ`.
820
+ * `RDL.at(sym, &blk)` invokes `blk.call(sym)` when `RDL.do_typecheck(sym)` is called. Useful when type annotations need to be generated at some later time, e.g., because not all classes are loaded.
772
821
 
773
- * `RDL.nowrap [klass]`, 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 `klass`. 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. If `klass` is omitted it's assumed to be `self`.
822
+ * `RDL.deinsantiate!(var)` - remove `var`'s instantiation.
774
823
 
775
824
  * `RDL.do_typecheck(sym)` statically type checks all methods whose type annotations include argument `typecheck: sym`.
776
825
 
777
- * `RDL.at(sym, &blk)` invokes `blk.call(sym)` when `RDL.do_typecheck(sym)` is called. Useful when type annotations need to be generated at some later time, e.g., because not all classes are loaded.
826
+ * `RDL.insantiate!(var, typ1, ...)` - `var` must have a generic type. Instantiates the type of `x` with type parameters `typ1`, ...
778
827
 
779
828
  * `RDL.note_type e` - evaluates `e` and returns it at run time. During static type checking, prints out the type of `e`.
780
829
 
830
+ * `RDL.nowrap [klass]`, 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 `klass`. 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. If `klass` is omitted it's assumed to be `self`.
831
+
832
+ * `RDL.query` prints information about types; see below for details.
833
+
781
834
  * `RDL.remove_type klass, meth` removes the type annotation for meth in klass. Fails if meth does not have a type annotation.
782
835
 
783
- * `RDL.insantiate!(var, typ1, ...)` - `var` must have a generic type. Instantiates the type of `x` with type parameters `typ1`, ...
836
+ * `RDL.reset` resets all global state stored internally inside RDL back to their original settings. Note: this is really only for internal RDL testing, as it doesn't completely undo RDL's effect (e.g., wrapped methods will be broken by a reset).
784
837
 
785
- * `RDL.deinsantiate!(var)` - remove `var`'s instantiation.
838
+ * `RDL.type_alias '%name', typ` - indicates that if `%name` appears in a type annotations, it should be expanded to `typ`.
786
839
 
787
840
  * `RDL.type_cast(e, typ, force: false)` - evaluate `e` and return it at run time. During dynamic contract checking, the returned object will have `typ` associated with it. If `force` is `false`, will also check that `e`'s run-time type is compatible with `typ`. If `force` is true then `typ` will be applied regardless of `e`'s type. During static type checking, the type checker will treat the object returned by `type_cast` has having type `typ`.
788
841
 
789
- * `RDL.query` prints information about types; see below for details.
790
842
 
791
843
 
792
844
  # Performance
@@ -876,51 +928,65 @@ RDL supports the following configuration options:
876
928
  * `config.type_defaults` - Hash containing default options for `type`. Initially `{ wrap: true, typecheck: false }`.
877
929
  * `config.pre_defaults` - Hash containing default options for `pre`. Initially `{ wrap: true }`.
878
930
  * `config.post_defaults` - same as `pre_defaults`, but for `post`.
931
+ * `config.use_comp_types` - when true, RDL makes use of types with type-level computations. When false, RDL ignores such types. By default set to true.
932
+ * `config.check_comp_types` - when true, RDL inserts dynamic checks which ensure that methods with type-level computations will return the expected type. False by default.
933
+ * `config.rerun_comp_types` - when true, RDL inserts dynamic checks which rerun type-level computations at method call sites, ensuring that they evaluate to the same type they did at type checking time. False by default.
934
+
879
935
 
880
936
  # Bibliography
881
937
 
882
938
  Here are some research papers we have written exploring types, contracts, and Ruby.
883
939
 
940
+ * Milod Kazerounian, Sankha Narayan Guria, Niki Vazou, Jeffrey S. Foster, and David Van Horn.
941
+ [Type-Level Computations for Ruby Libraries](https://www.cs.tufts.edu/~jfoster/papers/pldi19.pdf).
942
+ In ACM SIGPLAN Conference on Programming Language Design and Implementation (PLDI), Phoenix, AZ, June 2019.
943
+
884
944
  * Brianna M. Ren and Jeffrey S. Foster.
885
- [Just-in-Time Static Type Checking for Dynamic Languages](http://www.cs.umd.edu/~jfoster/papers/pldi16.pdf).
945
+ [Just-in-Time Static Type Checking for Dynamic Languages](http://www.cs.tufts.edu/~jfoster/papers/pldi16.pdf).
886
946
  In ACM SIGPLAN Conference on Programming Language Design and Implementation (PLDI), Santa Barbara, CA, June 2016.
887
947
 
888
948
  * T. Stephen Strickland, Brianna Ren, and Jeffrey S. Foster.
889
- [Contracts for Domain-Specific Languages in Ruby](http://www.cs.umd.edu/~jfoster/papers/dls12.pdf).
949
+ [Contracts for Domain-Specific Languages in Ruby](http://www.cs.tufts.edu/~jfoster/papers/dls12.pdf).
890
950
  In Dynamic Languages Symposium (DLS), Portland, OR, October 2014.
891
951
 
892
952
  * Brianna M. Ren, John Toman, T. Stephen Strickland, and Jeffrey S. Foster.
893
- [The Ruby Type Checker](http://www.cs.umd.edu/~jfoster/papers/oops13.pdf).
953
+ [The Ruby Type Checker](http://www.cs.tufts.edu/~jfoster/papers/oops13.pdf).
894
954
  In Object-Oriented Program Languages and Systems (OOPS) Track at ACM Symposium on Applied Computing, pages 1565–1572, Coimbra, Portugal, March 2013.
895
955
 
896
956
  * Jong-hoon (David) An, Avik Chaudhuri, Jeffrey S. Foster, and Michael Hicks.
897
- [Dynamic Inference of Static Types for Ruby](http://www.cs.umd.edu/~jfoster/papers/popl11.pdf).
957
+ [Dynamic Inference of Static Types for Ruby](http://www.cs.tufts.edu/~jfoster/papers/popl11.pdf).
898
958
  In ACM SIGPLAN-SIGACT Symposium on Principles of Programming Languages (POPL), pages 459–472, Austin, TX, USA, January 2011.
899
959
 
900
960
  * Jong-hoon (David) An.
901
- [Dynamic Inference of Static Types for Ruby](http://www.cs.umd.edu/~jfoster/papers/thesis-an.pdf).
961
+ [Dynamic Inference of Static Types for Ruby](http://www.cs.tufts.edu/~jfoster/papers/thesis-an.pdf).
902
962
  MS thesis, University of Maryland, College Park, 2010.
903
963
 
904
964
  * Michael Furr.
905
- [Combining Static and Dynamic Typing in Ruby](https://www.cs.umd.edu/~jfoster/papers/thesis-furr.pdf).
965
+ [Combining Static and Dynamic Typing in Ruby](https://www.cs.tufts.edu/~jfoster/papers/thesis-furr.pdf).
906
966
  PhD thesis, University of Maryland, College Park, 2009.
907
967
 
908
968
  * Michael Furr, Jong-hoon (David) An, Jeffrey S. Foster, and Michael Hicks.
909
- [The Ruby Intermediate Langauge](http://www.cs.umd.edu/~jfoster/papers/dls09-ril.pdf).
969
+ [The Ruby Intermediate Langauge](http://www.cs.tufts.edu/~jfoster/papers/dls09-ril.pdf).
910
970
  In Dynamic Languages Symposium (DLS), pages 89–98, Orlando, Florida, October 2009.
911
971
 
912
972
  * Michael Furr, Jong-hoon (David) An, and Jeffrey S. Foster.
913
- [Profile-Guided Static Typing for Dynamic Scripting Languages](http://www.cs.umd.edu/~jfoster/papers/oopsla09.pdf).
973
+ [Profile-Guided Static Typing for Dynamic Scripting Languages](http://www.cs.tufts.edu/~jfoster/papers/oopsla09.pdf).
914
974
  In ACM SIGPLAN International Conference on Object-Oriented Programming, Systems, Languages and Applications (OOPSLA), pages 283–300, Orlando, Floria, October 2009. Best student paper award.
915
975
 
916
976
  * Michael Furr, Jong-hoon (David) An, Jeffrey S. Foster, and Michael Hicks.
917
- [Static Type Inference for Ruby](http://www.cs.umd.edu/~jfoster/papers/oops09.pdf).
977
+ [Static Type Inference for Ruby](http://www.cs.tufts.edu/~jfoster/papers/oops09.pdf).
918
978
  In Object-Oriented Program Languages and Systems (OOPS) Track at ACM Symposium on Applied Computing (SAC), pages 1859–1866, Honolulu, Hawaii, March 2009.
919
979
 
920
980
  # Copyright
921
981
 
922
982
  Copyright (c) 2014-2017, University of Maryland, College Park. All rights reserved.
923
983
 
984
+ # Contributing
985
+
986
+ Contributions are welcome! If you submit any pull requests, please
987
+ submit them against the `dev` branch. We keep the `master` branch so
988
+ it matches the latest gem version.
989
+
924
990
  # Authors
925
991
 
926
992
  * [Jeffrey S. Foster](http://www.cs.umd.edu/~jfoster/)
@@ -928,10 +994,12 @@ Copyright (c) 2014-2017, University of Maryland, College Park. All rights reserv
928
994
  * [T. Stephen Strickland](https://www.cs.umd.edu/~sstrickl/)
929
995
  * Alexander T. Yu
930
996
  * Milod Kazerounian
997
+ * [Sankha Narayan Guria](https://sankhs.com/)
931
998
 
932
999
  # Contributors
933
1000
 
934
1001
  * [Joel Holdbrooks](https://github.com/noprompt)
1002
+ * [Paul Tarjan](https://github.com/ptarjan)
935
1003
 
936
1004
  # Discussion group
937
1005
 
@@ -13,6 +13,8 @@ end
13
13
  require 'rdl/info.rb'
14
14
 
15
15
  module RDL::Globals
16
+ FIXBIG_VERSIONS = ['>= 2.0.0', '< 2.4.0']
17
+
16
18
  # Method/variable info table with kinds:
17
19
  # For methods
18
20
  # :pre to array of precondition contracts
@@ -52,6 +54,18 @@ module RDL::Globals
52
54
 
53
55
  # List of contracts that should be applied to the next method definition
54
56
  @deferred = []
57
+
58
+ # List of method types that have a dependent type. Used to type check type-level code.
59
+ @dep_types = []
60
+
61
+ ## Hash mapping node object IDs (integers) to a list [tmeth, tmeth_old, tmeth_res, self_klass, trecv_old, targs_old], where: tmeth is a MethodType that is fully evaluated (i.e., no ComputedTypes) *and instantiated*, tmeth_old is the unevaluated method type (i.e., with ComputedTypes), tmeth_res is the result of evaluating tmeth_old *but not instantiating it*, self_klass is the class where the MethodType is defined, trecv_old was the receiver type used to evaluate tmeth_old, and targs_old is an Array of the argument types used to evaluate tmeth_old.
62
+ @comp_type_map = {}
63
+
64
+ # Map from ActiveRecord table names (symbols) to their schema types, which should be a Table type
65
+ @ar_db_schema = {}
66
+
67
+ # Map from Sequel table names (symbols) to their schema types, which should be a Table type
68
+ @seq_db_schema = {}
55
69
  end
56
70
 
57
71
  class << RDL::Globals # add accessors and readers for module variables
@@ -63,6 +77,10 @@ class << RDL::Globals # add accessors and readers for module variables
63
77
  attr_accessor :to_typecheck
64
78
  attr_accessor :to_do_at
65
79
  attr_accessor :deferred
80
+ attr_accessor :dep_types
81
+ attr_accessor :comp_type_map
82
+ attr_accessor :ar_db_schema
83
+ attr_accessor :seq_db_schema
66
84
  end
67
85
 
68
86
  # Create switches to control whether wrapping happens and whether
@@ -81,15 +99,19 @@ end
81
99
 
82
100
  require 'rdl/types/type.rb'
83
101
  require 'rdl/types/annotated_arg.rb'
102
+ require 'rdl/types/bound_arg.rb'
84
103
  require 'rdl/types/bot.rb'
104
+ require 'rdl/types/computed.rb'
85
105
  require 'rdl/types/dependent_arg.rb'
86
106
  require 'rdl/types/dots_query.rb'
107
+ require 'rdl/types/dynamic.rb'
87
108
  require 'rdl/types/finite_hash.rb'
88
109
  require 'rdl/types/generic.rb'
89
110
  require 'rdl/types/intersection.rb'
90
111
  require 'rdl/types/lexer.rex.rb'
91
112
  require 'rdl/types/method.rb'
92
113
  require 'rdl/types/singleton.rb'
114
+ require 'rdl/types/ast_node.rb'
93
115
  require 'rdl/types/nominal.rb'
94
116
  require 'rdl/types/non_null.rb'
95
117
  require 'rdl/types/optional.rb'
@@ -102,6 +124,7 @@ require 'rdl/types/union.rb'
102
124
  require 'rdl/types/var.rb'
103
125
  require 'rdl/types/vararg.rb'
104
126
  require 'rdl/types/wild_query.rb'
127
+ require 'rdl/types/string.rb'
105
128
 
106
129
  require 'rdl/contracts/contract.rb'
107
130
  require 'rdl/contracts/and.rb'
@@ -115,50 +138,68 @@ require 'rdl/query.rb'
115
138
  require 'rdl/typecheck.rb'
116
139
  #require_relative 'rdl/stats.rb'
117
140
 
118
- module RDL::Globals
119
- FIXBIG_VERSIONS = ['>= 2.0.0', '< 2.4.0']
120
- # INTEGER_VERSIONS = '>= 2.4.0'
121
-
122
- @parser = RDL::Type::Parser.new
123
-
124
- # Map from file names to [digest, cache] where 2nd elt maps
125
- # :ast to the AST
126
- # :line_defs maps linenumber to AST for def at that line
127
- @parser_cache = Hash.new
128
-
129
- # Some generally useful types; not really a big deal to do this since
130
- # NominalTypes are cached, but these names are shorter to type
131
- @types = Hash.new
132
- @types[:nil] = RDL::Type::NominalType.new NilClass # actually creates singleton type
133
- @types[:top] = RDL::Type::TopType.new
134
- @types[:bot] = RDL::Type::BotType.new
135
- @types[:object] = RDL::Type::NominalType.new Object
136
- @types[:true] = RDL::Type::NominalType.new TrueClass # actually creates singleton type
137
- @types[:false] = RDL::Type::NominalType.new FalseClass # also singleton type
138
- @types[:bool] = RDL::Type::UnionType.new(@types[:true], @types[:false])
139
- @types[:float] = RDL::Type::NominalType.new Float
140
- @types[:complex] = RDL::Type::NominalType.new Complex
141
- @types[:rational] = RDL::Type::NominalType.new Rational
142
- @types[:integer] = RDL::Type::NominalType.new Integer
143
- @types[:numeric] = RDL::Type::NominalType.new Numeric
144
- @types[:string] = RDL::Type::NominalType.new String
145
- @types[:array] = RDL::Type::NominalType.new Array
146
- @types[:hash] = RDL::Type::NominalType.new Hash
147
- @types[:symbol] = RDL::Type::NominalType.new Symbol
148
- @types[:range] = RDL::Type::NominalType.new Range
149
- @types[:regexp] = RDL::Type::NominalType.new Regexp
150
- @types[:standard_error] = RDL::Type::NominalType.new StandardError
151
- @types[:proc] = RDL::Type::NominalType.new Proc
152
-
153
- # Hash from special type names to their values
154
- @special_types = {'%any' => @types[:top],
155
- '%bot' => @types[:bot],
156
- '%bool' => @types[:bool]}
157
- end
158
-
159
141
  class << RDL::Globals
160
142
  attr_reader :parser
161
143
  attr_accessor :parser_cache
162
144
  attr_reader :types
163
145
  attr_reader :special_types
164
146
  end
147
+
148
+ module RDL
149
+ def self.reset
150
+ RDL::Globals.module_eval {
151
+ @info = RDL::Info.new
152
+ @wrapped_calls = Hash.new 0
153
+ @type_params = Hash.new
154
+ @aliases = Hash.new
155
+ @to_wrap = Set.new
156
+ @to_typecheck = Hash.new
157
+ @to_typecheck[:now] = Set.new
158
+ @to_do_at = Hash.new
159
+ @ar_db_schema = Hash.new
160
+ @seq_db_schema = Hash.new
161
+ @deferred = []
162
+
163
+ @parser = RDL::Type::Parser.new
164
+
165
+ # Map from file names to [digest, cache] where 2nd elt maps
166
+ # :ast to the AST
167
+ # :line_defs maps linenumber to AST for def at that line
168
+ @parser_cache = Hash.new
169
+
170
+ # Some generally useful types; not really a big deal to do this since
171
+ # NominalTypes are cached, but these names are shorter to type
172
+ @types = Hash.new
173
+ @types[:nil] = RDL::Type::NominalType.new NilClass # actually creates singleton type
174
+ @types[:top] = RDL::Type::TopType.new
175
+ @types[:bot] = RDL::Type::BotType.new
176
+ @types[:dyn] = RDL::Type::DynamicType.new
177
+ @types[:object] = RDL::Type::NominalType.new Object
178
+ @types[:true] = RDL::Type::NominalType.new TrueClass # actually creates singleton type
179
+ @types[:false] = RDL::Type::NominalType.new FalseClass # also singleton type
180
+ @types[:bool] = RDL::Type::UnionType.new(@types[:true], @types[:false])
181
+ @types[:float] = RDL::Type::NominalType.new Float
182
+ @types[:complex] = RDL::Type::NominalType.new Complex
183
+ @types[:rational] = RDL::Type::NominalType.new Rational
184
+ @types[:integer] = RDL::Type::NominalType.new Integer
185
+ @types[:numeric] = RDL::Type::NominalType.new Numeric
186
+ @types[:string] = RDL::Type::NominalType.new String
187
+ @types[:array] = RDL::Type::NominalType.new Array
188
+ @types[:hash] = RDL::Type::NominalType.new Hash
189
+ @types[:symbol] = RDL::Type::NominalType.new Symbol
190
+ @types[:range] = RDL::Type::NominalType.new Range
191
+ @types[:regexp] = RDL::Type::NominalType.new Regexp
192
+ @types[:standard_error] = RDL::Type::NominalType.new StandardError
193
+ @types[:proc] = RDL::Type::NominalType.new Proc
194
+
195
+ # Hash from special type names to their values
196
+ @special_types = {'%any' => @types[:top],
197
+ '%bot' => @types[:bot],
198
+ '%bool' => @types[:bool],
199
+ '%dyn' => @types[:dyn]}
200
+ }
201
+ end
202
+ end
203
+
204
+ RDL.reset
205
+ require 'rdl/types/rdl_types.rb'