rdl 2.1.0 → 2.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.gitignore +1 -0
- data/.travis.yml +7 -6
- data/CHANGES.md +29 -0
- data/README.md +94 -26
- data/lib/rdl/boot.rb +82 -41
- data/lib/rdl/boot_rails.rb +5 -0
- data/lib/rdl/config.rb +9 -1
- data/lib/rdl/query.rb +2 -2
- data/lib/rdl/typecheck.rb +972 -225
- data/lib/rdl/types/annotated_arg.rb +8 -0
- data/lib/rdl/types/ast_node.rb +73 -0
- data/lib/rdl/types/bot.rb +8 -0
- data/lib/rdl/types/bound_arg.rb +63 -0
- data/lib/rdl/types/computed.rb +48 -0
- data/lib/rdl/types/dependent_arg.rb +9 -0
- data/lib/rdl/types/dynamic.rb +61 -0
- data/lib/rdl/types/finite_hash.rb +54 -9
- data/lib/rdl/types/generic.rb +33 -0
- data/lib/rdl/types/intersection.rb +8 -0
- data/lib/rdl/types/lexer.rex +6 -1
- data/lib/rdl/types/lexer.rex.rb +13 -1
- data/lib/rdl/types/method.rb +14 -0
- data/lib/rdl/types/nominal.rb +8 -0
- data/lib/rdl/types/non_null.rb +8 -0
- data/lib/rdl/types/optional.rb +8 -0
- data/lib/rdl/types/parser.racc +31 -5
- data/lib/rdl/types/parser.tab.rb +540 -302
- data/lib/rdl/types/rdl_types.rb +45 -0
- data/lib/rdl/types/singleton.rb +14 -1
- data/lib/rdl/types/string.rb +104 -0
- data/lib/rdl/types/structural.rb +8 -0
- data/lib/rdl/types/top.rb +8 -0
- data/lib/rdl/types/tuple.rb +32 -8
- data/lib/rdl/types/type.rb +54 -11
- data/lib/rdl/types/union.rb +41 -2
- data/lib/rdl/types/var.rb +10 -0
- data/lib/rdl/types/vararg.rb +8 -0
- data/lib/rdl/util.rb +13 -10
- data/lib/rdl/wrap.rb +271 -27
- data/lib/rdl_disable.rb +16 -2
- data/lib/types/active_record.rb +1 -0
- data/lib/types/core/array.rb +442 -23
- data/lib/types/core/basic_object.rb +3 -3
- data/lib/types/core/bigdecimal.rb +5 -0
- data/lib/types/core/class.rb +2 -0
- data/lib/types/core/dir.rb +3 -3
- data/lib/types/core/enumerable.rb +4 -4
- data/lib/types/core/enumerator.rb +1 -1
- data/lib/types/core/file.rb +4 -4
- data/lib/types/core/float.rb +203 -0
- data/lib/types/core/hash.rb +390 -15
- data/lib/types/core/integer.rb +223 -10
- data/lib/types/core/io.rb +2 -2
- data/lib/types/core/kernel.rb +8 -5
- data/lib/types/core/marshal.rb +3 -0
- data/lib/types/core/module.rb +3 -3
- data/lib/types/core/numeric.rb +0 -2
- data/lib/types/core/object.rb +5 -5
- data/lib/types/core/pathname.rb +2 -2
- data/lib/types/core/process.rb +1 -3
- data/lib/types/core/range.rb +1 -1
- data/lib/types/core/regexp.rb +2 -2
- data/lib/types/core/set.rb +1 -1
- data/lib/types/core/string.rb +408 -16
- data/lib/types/core/symbol.rb +3 -3
- data/lib/types/core/time.rb +1 -1
- data/lib/types/core/uri.rb +13 -13
- data/lib/types/rails/_helpers.rb +7 -1
- data/lib/types/rails/action_controller/mime_responds.rb +2 -0
- data/lib/types/rails/active_record/associations.rb +42 -30
- data/lib/types/rails/active_record/comp_types.rb +637 -0
- data/lib/types/rails/active_record/finder_methods.rb +1 -1
- data/lib/types/rails/active_record/model_schema.rb +28 -16
- data/lib/types/rails/active_record/relation.rb +5 -3
- data/lib/types/rails/active_record/sql-strings.rb +166 -0
- data/lib/types/rails/string.rb +1 -1
- data/lib/types/sequel.rb +1 -0
- data/lib/types/sequel/comp_types.rb +581 -0
- data/rdl.gemspec +5 -4
- data/test/test_alias.rb +4 -0
- data/test/test_array_types.rb +244 -0
- data/test/test_bound_types.rb +80 -0
- data/test/test_contract.rb +4 -0
- data/test/test_dsl.rb +5 -0
- data/test/test_dyn_comptype_checks.rb +206 -0
- data/test/test_generic.rb +21 -20
- data/test/test_hash_types.rb +322 -0
- data/test/test_intersection.rb +1 -0
- data/test/test_le.rb +29 -4
- data/test/test_member.rb +3 -1
- data/test/test_parser.rb +5 -0
- data/test/test_query.rb +1 -0
- data/test/test_rdl.rb +63 -28
- data/test/test_rdl_type.rb +4 -0
- data/test/test_string_types.rb +102 -0
- data/test/test_type_contract.rb +59 -37
- data/test/test_typecheck.rb +480 -75
- data/test/test_types.rb +17 -0
- data/test/test_wrap.rb +5 -0
- metadata +35 -5
- data/lib/types/rails/active_record/schema_types.rb +0 -51
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: cd1da46a0ca86e95d211710bd2fbf149d299a4e85d481970afbff765ab9150f1
|
4
|
+
data.tar.gz: eb03a0a773a98e85eef56e0fdcc7b266eb35dd960470076d5db6de1259fdb9c4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9ea8b806ceee543905a3c1fbdc9c68ec9eb599b793ef3c24d902c3bca019b7321cc755f5cfca510766c222791426341307228faeea886e1d889eedd6ed1a9150
|
7
|
+
data.tar.gz: 4c1ec45edbff7d790362f97de394eb7d14d5eb7a3d0bb0ff0ae86f637e40f3ae364a0d9fdba53dc931c0bcd3af6ac120374cf8aa3ebef1fff4f374fe02223a56
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
@@ -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
|
-
|
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/
|
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: :
|
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:
|
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: :
|
93
|
-
RDL.type :A, :id, '(Integer) -> Integer', typecheck: :
|
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.
|
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:
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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
|
|
data/lib/rdl/boot.rb
CHANGED
@@ -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'
|