Compare commits

..

320 Commits

Author SHA1 Message Date
Brian Lachance
ec0c8516c2 Add types for combinations, in-combinations 2016-06-15 09:58:36 -04:00
Asumu Takikawa
67614198c3 Avoid doing require lifting repeatedly
The redirection for contracted identifiers used to do a
require lift on each use. Instead, only do the lift once
and reuse the identifier.
2016-06-13 16:05:40 -04:00
Asumu Takikawa
78e0100663 Improve contract inlining
TR sometimes inlines contracts instead of defining them
separately in order to cooperate with the contract system's
optimizations. In some cases, this caused TR to compile
duplicated contract definitions. This commit eliminates
this inefficiency.
2016-06-13 14:55:08 -04:00
Asumu Takikawa
bc6e9e80cc Don't use number literal types as contracts
Using = for the comparison doesn't work for TR

Fixes bug in 295a4b7e39
2016-06-13 13:25:51 -04:00
Asumu Takikawa
285a2b796d Add custom equality for simple static contracts
This improves memoization of contracts

Appears to cut about 6-7% of zo size for the math
library.
2016-06-13 04:08:33 -04:00
Asumu Takikawa
295a4b7e39 Simplify flat contracts for Value types
Potentially speeds up contracts checks for
types like False or Boolean.
2016-06-13 04:08:33 -04:00
Vincent St-Amour
1ba8e5ba33 Fix test that relied on broken mcons printing. 2016-06-11 16:34:23 -05:00
Sam Tobin-Hochstadt
e3863149f5 Increase timeout again. 2016-06-11 15:20:02 -04:00
Chris Jester-Young
6ff74e8c35 Give correct type to in-port when used with custom reader (#367)
* Give correct type to `in-port` when used with custom reader.

Currently, `in-port` returns `(Sequenceof Any)` unconditionally,
which is correct if the given read function is `read` (default
value). However, `(in-port read-line)`, `(in-port read-char)`,
etc. should have more specific types.

* For `in-port`, strip out EOF from the sequence type.
2016-06-07 14:15:04 -04:00
Asumu Takikawa
e39bcc6245 Wrap cast in #%expression to avoid too-early call 2016-06-06 13:20:36 -04:00
AlexKnauth
a846514f28 make cast sound 2016-06-06 13:19:33 -04:00
Asumu Takikawa
f23c07f54a Use the right equality function for parent-of? 2016-06-03 14:55:23 -04:00
Asumu Takikawa
a984281cdc Add first-order checks to simple-result-> contract
Fixes issue #368
2016-06-03 13:49:26 -04:00
Asumu Takikawa
7aea90242a Adjust contract tests to allow first-order checks 2016-06-03 13:46:00 -04:00
Asumu Takikawa
b338fc6b64 Be less conservative about struct overlap
Fixes issue #366
2016-06-01 14:23:13 -04:00
Andrew Kent
c7de819424 fix contract related bugs 2016-05-24 14:07:52 -04:00
Andrew Kent
d66816cf76 use match*/no-order to reduce manual code duplication 2016-05-23 18:13:28 -04:00
Asumu Takikawa
71f17f5cb2 Compute struct intersection more conservatively
Fixes GH issue #205
2016-05-20 16:56:49 -04:00
Asumu Takikawa
43dc59bea2 Restrict struct predicate when parent is mutable
Correctly restrict the struct predicate's filter type when
a parent struct is mutable but the child is not and they both
have polymorphic type variables.

See the discussion in GH issue #205
2016-05-20 16:56:49 -04:00
Asumu Takikawa
e800787773 Don't assume built-in structs are mutable
Doing so adds spurious entries in the type environment for
the setters for these structs, even though the setters are
not defined.
2016-05-20 16:55:23 -04:00
Asumu Takikawa
af8ccae0ff Add comments clarifying struct-desc fields 2016-05-20 16:53:53 -04:00
Andrew Kent
010134d2b1 add intersection to TR docs 2016-05-20 15:38:32 -04:00
Andrew Kent
c7a3fb0cf1 rename restrict to intersect
since 'restrict' will now create intersections when there is
a complex relationship between the two types, calling it
'intersect' makes a lot more sense.
2016-05-20 15:21:41 -04:00
Andrew Kent
b4a4c174e4 initial intersection types addition
Adds intersection types as a better way to handle the the case
when restrict cannot structurally intersect two types (e.g. when
you learn within a polymorphic function a variable x of type A
is also an Integer, but we dont know how A relates to Integer).
This allows for non-lossy refinements of type info while typechecking.
2016-05-20 11:34:04 -04:00
WarGrey Gyoudmon Ju
5552101f5b Add some useful type definitions (#356) 2016-05-19 17:49:20 -04:00
Asumu Takikawa
36610e6239 Add missing kw arg of peeking-input-port
Closes issue #355
2016-05-16 12:00:54 -04:00
Asumu Takikawa
19e8efec0f Avoid internal error on mismatched values
Fixes issue #342
2016-05-09 18:03:17 -04:00
Asumu Takikawa
31bf61e333 Remove redundant values checks
These can all be done via check-below later in the
typechecking process
2016-05-09 18:03:17 -04:00
Sam Tobin-Hochstadt
743be67d67 Revert "Have provide: annotate the original identifier as well."
This reverts commit 3f889bcf8c.

Unfortunately, this change is not backwards compatible, as seen in
both DrDr failures and errors reported by Ben Greenman.
2016-05-03 10:41:50 -04:00
Asumu Takikawa
2763ecd0c5 Restore doc examples that broke in 6.2
Closes issue #264
2016-04-29 18:06:55 -04:00
Asumu Takikawa
7b92405cb7 Fix base-contracted #%contract-defs handling 2016-04-29 18:06:55 -04:00
Sam Tobin-Hochstadt
268543cbd0 Avoid no-declare when documenting extra TR libraries.
Closes #345.
2016-04-29 12:41:02 -04:00
Sam Tobin-Hochstadt
3f889bcf8c Have provide: annotate the original identifier as well.
Fixes PR 15292.
2016-04-29 12:41:02 -04:00
Asumu Takikawa
af385d6932 Fix issue #336 2016-04-28 11:29:12 -04:00
Asumu Takikawa
6621bd5b32 Retain srcloc for match*/early 2016-04-28 11:29:12 -04:00
Andrew Kent
f9c5a534d0 filter -> prop
This pull request is largely a renaming effort to clean up the TR codebase. There are two primary things I wanted to change:

1. Replace all occurrences of "filter" with "prop" or "proposition"
   - The word "filter" is a meaningless opaque term at this point in the Typed Racket implementation. If anything, it just adds confusion to why things are the way the are. We should use "proposition" instead, since that's what they actually are.

2. Avoid using "Top" and "Bottom" in both the type and proposition realm.
   - Currently the top type is called Univ and the bottom type is called bottom, while the top proposition is called Top and the bottom proposition is called Bot. This is just unnecessarily confusing, doesn't really line up w/ the user-space names, and doesn't line up with the names we use in TR formalisms. Worse, all of the top types of primitive types---e.g. the type of all structs, StructTop--- use Top, so it is really easy to get confused about what name to use for these sorts of things.

With these issues in mind, I made the following changes to names:

Top -> TrueProp
Bot -> FalseProp
TypeFilter -> TypeProp
NotTypeFilter -> NotTypeProp
AndFilter -> AndProp
OrFilter -> OrProp
-filter t o -> -is-type o t
-not-filter t o -> -not-type o t
FilterSet -> PropSet
NoFilter -> #f
NoObject -> #f
-FS -> -PS
-top -> -tt
-bot -> -ff
implied-atomic? q p -> implies-atomic? p q
filter-rec-id -> prop-rec-id
-no-filter -> -no-propset
-top-filter -> -tt-propset
-bot-filter -> -ff-propset
-true-filter -> -true-propset
-false-filter -> -false-propset
PredicateFilter: -> PredicateProp:
add-unconditional-filter-all-args add-unconditional-prop-all-args
2016-04-25 18:36:12 -04:00
Sam Tobin-Hochstadt
e855755349 Update HISTORY for 6.5. 2016-04-18 17:29:37 -04:00
Alex Knauth
bacc1b3411 Fix type of syntax->list (#348)
* Fix type of syntax->list

to return `(U False (Listof (Syntaxof Any)))` if it can't prove that the input is a syntax-list.

Fixes https://github.com/racket/typed-racket/issues/347
This makes the type `syntax->list` consistent with the type `stx->list` already has.

* Add tests for syntax->list
2016-04-16 20:18:59 -04:00
ben
f820fac6a0 regression tests
For github issues 111 113 114 115
2016-04-11 17:27:19 -04:00
Vincent St-Amour
b352739131 Always populate type table, regardless of optimizer status.
Closes #343.
2016-04-11 14:43:44 -05:00
Matthew Flatt
a906b1c172 fix documented library name 2016-04-08 19:11:06 -06:00
Asumu Takikawa
cea5091ee6 Fix struct name in Struct type representation
Closes issue #304
2016-04-07 21:49:50 -04:00
WarGrey Gyoudmon Ju
65441301c2 Add typed/images/logos and typed/images/icons without #:material support 2016-04-07 17:03:05 -04:00
Asumu Takikawa
acef58a5d0 Add an extra test for new random case 2016-04-07 17:00:13 -04:00
Asumu Takikawa
7e3178798b Update the type of random for ranges
Also simplify redundant cases
2016-04-07 15:23:06 -04:00
Asumu Takikawa
1a11ac53e2 Add typed/racket/random, bump version 2016-04-07 15:03:57 -04:00
Georges Dupéron
e33c902842 Remove debugging output from test case in typed-racket-test/succeed/define-typed-untyped-identifier-syntax-properties.rkt 2016-04-07 16:47:17 +02:00
Asumu Takikawa
2fc669e136 Adjust error message for test
Needed to change due to recent syntax-parse changes
2016-04-07 00:58:09 -04:00
WarGrey Gyoudmon Ju
0d45168aee Fixup wrong type definition: set-caret-owner, it needs (Instance Snip%) 2016-04-07 09:33:46 +08:00
WarGrey Gyoudmon Ju
bf24ebdd65 Fixup a typo in the definition of (make-font); Fixup the wrong definition of (get-filename); Provide editor-snip% with Editor-Snip% 2016-04-06 16:25:28 -04:00
Vincent St-Amour
6dc5b1a994 Fix optimizer test for changes to syntax-parse. 2016-04-06 12:06:42 -05:00
Georges Dupéron
4ab256abf2 Fixes GH issue #315 Syntax properties are not forwarded by define-typed/untyped-identifier, following @samth's suggestions. 2016-04-06 12:06:42 -05:00
Asumu Takikawa
b869f18f1c Make contracted unit tests pass 2016-03-31 11:57:54 -04:00
Sam Tobin-Hochstadt
900d2b0be0 add missing require 2016-03-29 07:24:51 -04:00
Asumu Takikawa
9ec358b665 Update type-table after tc-funapp processing
This makes the tooltip show the more refined function
type after application typechecking. For example, case->
types will be narrowed to the particular case that fits.

Polymorphic function types will be instantiated at the
appropriate type.

Fixes issue #325
2016-03-28 17:53:34 -04:00
Asumu Takikawa
cffad4df74 Move possible-domains/cleanup-type to new file
This avoids circular dependencies in preparation
for adding calls to type-table functions from
tc-app-helper.
2016-03-28 17:53:34 -04:00
Sam Tobin-Hochstadt
7572adb9c2 Fix use of simple-result-> to check the domains.
This broke pict3d, but also disabled some contract checks that
should have been kept.
2016-03-28 17:11:37 -04:00
Asumu Takikawa
812f1a8c79 Add types for box-cas!, unsafe-box*-cas! 2016-03-23 16:02:22 -04:00
Vincent St-Amour
495da1bd1a Bring `tc-#%expression in line with its contract. 2016-03-21 14:21:57 -05:00
Sam Tobin-Hochstadt
8d5d7bea7a Use simple-result-> on [0,3]-arg functions.
The included microbenchmark shows a 2x speedup on 0-argument
functions. Motivated by the sieve benchmark from [Takikawa et al,
POPL 2016].
2016-03-18 17:13:34 -04:00
Asumu Takikawa
49f20aa7ed Fix incorrect function signature
(the standard fish is happy now)
2016-03-18 11:30:05 -04:00
Daniel Feltey
425ff47700 Raise errors when signatures are not in the signature environment while typechecking
Closes #319
2016-03-12 16:39:39 -06:00
Asumu Takikawa
d23e05f2c3 Escape "~" in tc-error/fields arguments
Fixes issue #314
2016-03-02 04:43:34 -05:00
Georges Dupéron
350a8bb74e Changed how arguments and other pieces of information are printed, to follow the error message conventions, as suggested by samth in PR #250 .
* PR #250: https://github.com/racket/typed-racket/pull/250
* Error message conventions: http://docs.racket-lang.org/reference/exns.html?q=raise-arg#%28part._err-msg-conventions%29
2016-03-01 15:19:09 -06:00
Stephen Chang
5d8949654e add more hash seq ids to base special env
sync with commit 3e29101e48
2016-02-24 15:28:38 -05:00
Asumu Takikawa
72927e2248 Add the rest of Racket's exn hierarchy 2016-02-21 12:20:40 -05:00
Asumu Takikawa
a90f6c46eb Add exn:break:hang-up and exn:break:terminate 2016-02-21 02:48:45 -05:00
Sam Tobin-Hochstadt
79ccd77c6d Create issue_template.md 2016-02-17 14:51:06 -05:00
WarGrey Gyoudmon Ju
e0d067c99a Fixup wrong type definitions. (set/get-keymap) and (get-snipclass) 2016-02-16 16:47:05 -06:00
Stephen Chang
e1b9c06d5c remove old in-hash- private ids 2016-02-06 12:49:50 -05:00
Stephen Chang
c19fac7fd5 add ids from in-hash- expansion to special-env
- fixes test failures due to 048c4b4a73
- add in-hash- tests
2016-02-05 18:26:03 -05:00
ben
89a58bf670 vscoll -> vscroll 2016-02-04 17:24:25 -05:00
Sam Tobin-Hochstadt
0bfaa75bcf Add test for test-case from typed/rackunit. 2016-02-02 08:21:02 -05:00
Sam Tobin-Hochstadt
23bda72953 Add missing function to typed/rackunit implementation.
Repairs 10dc53375. Found by the "semver" package.

Merge to 6.4.
2016-02-01 21:34:10 -05:00
Sam Tobin-Hochstadt
5f35f447b5 TR History 2016-02-01 14:43:16 -05:00
Vincent St-Amour
32d0a97058 Add contract profiling instrumentation to combinators defined by TR. 2016-01-27 16:01:07 -06:00
Stephen Chang
2e7a045012 bump version to support extra hash-iterate ops 2016-01-27 10:32:55 -05:00
Stephen Chang
3a245a27e0 fix type of hash-iterate-key+value 2016-01-26 17:06:33 -05:00
Stephen Chang
c35716d461 add hash-iterate-pair and hash-iterate-key+value
closes #299
2016-01-26 10:21:05 -05:00
Sam Tobin-Hochstadt
9ba130976c Fix hash iteration types for HashTableTop. 2016-01-23 11:56:51 -05:00
Asumu Takikawa
0308a229ed Ensure define-typed-struct/exec has a Procedure
Fixes issue #293
2016-01-22 13:43:03 -05:00
Vincent St-Amour
f53314a21c Fix optimizer tests for nw, optimized hash iteration (in Racket). 2016-01-19 15:40:31 -06:00
Sam Tobin-Hochstadt
8ca2af0f8c Fix simple-result-> when passed a keyword-accepting procedure. 2016-01-17 17:55:46 -05:00
Asumu Takikawa
bad5a35291 Fix type of file-position
Closes issue #297
2016-01-17 16:38:21 -05:00
Sam Tobin-Hochstadt
838431c176 Add the simple-result-> combinator to Typed Racket.
This is used for functions with a single argument imported with
`require/typed`, and avoids unneccessary checks. This produces a
3x speedup on the following benchmark:

  #lang racket/base
  (module m racket/base
    (provide f)
    (define (f x) x))
  (module n typed/racket/base
    (require/typed
     (submod ".." m)
     [f (-> Integer Integer)])
    (time
     (for ([x (in-range 1000000)])
       (f 1) (f 2) (f 3) (f 4))))
  (require 'n)

on top of the previous improvment from using `unsafe-procedure-chaperone`
and `procedure-result-arity`.
2016-01-16 22:27:18 -05:00
Asumu Takikawa
7217e2e531 Fix name argument for dtsi/exec
Looks like this got typoed in commit a3ca5aeefc

Closes issue #291
2016-01-15 11:58:38 -05:00
Asumu Takikawa
1f5c5144f9 Fix type of make-brush
Closes issue #104
Closes PR 14931
2016-01-13 07:56:01 -05:00
Sam Tobin-Hochstadt
1d367003e9 Add some additional clarification on unsafe-provide. 2016-01-09 22:31:17 -05:00
Matthew Flatt
67d989462b adjust doc extfvector example
Make the example typeset in a Racket build that does not support
extflvectors.
2016-01-09 09:11:03 -07:00
Vincent St-Amour
f6bb11c1d5 Fix test for removed missed opt logging. 2016-01-06 12:42:20 -06:00
Vincent St-Amour
319e6fd4e1 Stop tracking struct constructors.
Was only used by OC, for a now-defunct recommendation.
2016-01-05 15:51:19 -06:00
Sam Tobin-Hochstadt
6fb0fa04e7 Merge pull request #285 from florence/update-pict
add new pict function to typed/pict
2016-01-03 13:42:19 -05:00
Sam Tobin-Hochstadt
1e761f2d8a Setof is immutable. 2016-01-02 14:00:32 -05:00
Sam Tobin-Hochstadt
84d26b91ca Avoid generating definitions for contracts that are identifiers.
This reduces the number of pointless definitions, and re-enables
some contract system optimizations.
2016-01-02 11:46:51 -05:00
Sam Tobin-Hochstadt
a0d6ed954d Clarify docs for Setof. 2016-01-02 11:01:09 -05:00
Matthew Flatt
cd7d347051 update types of find-files and pathlist-closure 2016-01-02 07:59:32 -07:00
Spencer Florence
2139c776d8 adding new pict function 2016-01-01 16:22:31 -06:00
Vincent St-Amour
2dafb04587 Fix type of dup-output-port. 2016-01-01 16:05:41 -06:00
Vincent St-Amour
91b78dd9d9 Fix doc example. 2016-01-01 14:46:15 -06:00
Vincent St-Amour
607649c742 Improve link to OC docs. 2016-01-01 11:45:00 -06:00
Vincent St-Amour
40e7c969ab Refine type of min.
Closes PR14896.
2015-12-31 14:32:41 -06:00
Sam Tobin-Hochstadt
32fb50b4ce Merge pull request #283 from bennn/patch-1
typo: remove outdated comment
2015-12-31 14:36:48 -05:00
Benjamin Greenman
8f0f57a187 typo: remove outdated comment 2015-12-31 14:21:21 -05:00
Sam Tobin-Hochstadt
730a72709e Increase memory limit to avoid spurious failures. 2015-12-31 11:34:23 -05:00
Sam Tobin-Hochstadt
3be139b9b5 Merge branch 'opaque' 2015-12-31 10:53:30 -05:00
Robby Findler
d58d7487e8 port typed-racket contracts to late-neg projections 2015-12-31 08:58:32 -06:00
Alexis King
a3ca5aeefc Allow the types created for structs to be specified manually
This allows the types generated by the struct form, as well as #:struct
clauses of require/typed, to be specified explicitly using a #:type-name
option. This allows the name of a struct and the type it is assigned to
be different.

Closes #261
2015-12-30 13:12:05 -08:00
Sam Tobin-Hochstadt
d3fac7c24a Revise handling of #:opaque types, and Any.
Guard opaque predicates with an (-> Any Any) contract. This uses the
contract generation infrastructure to avoid wrapping struct predicates.

Also, relax `any-wrap/c` (the contract used for `Any` in positive
position) to allow opaque structures. This also requires an enumeration
of all the other kinds of values that TR understands, so that they are
not confused with opaque structures.

Joint work with @bennn.

Closes #202.
Closes #203.
Closes #241.
2015-12-30 12:33:15 -05:00
Sam Tobin-Hochstadt
6c4e584946 Revise Travis tests for 43a8cce3f. 2015-12-29 17:58:00 -05:00
Sam Tobin-Hochstadt
43694bf595 Merge pull request #250 from jsmaniac/improve-error-messages
Improving error messages, by providing more context.
2015-12-29 17:36:58 -05:00
Georges Dupéron
ce4a2b3d36 Improved error messages given by typed/racket, by always including the type name and arguments in the error message.
When a typing error is located inside macro-expanded code, a message such as “wrong number of arguments to polymorphic type: expected 1 and got 2” does not help much in locating which instantiation is wrong.
2015-12-29 17:35:32 -05:00
Sam Tobin-Hochstadt
3a7e616f97 Merge pull request #278 from AlexKnauth/flatten
add type for flatten
2015-12-29 17:24:43 -05:00
Sam Tobin-Hochstadt
43a8cce3fc Reorganize the TR testing files, and add progress output.
`typed-racket-test/main` is now the file to run for almost everything.
`typed-racket-test/run` continues to exist so that DrDr preserves the
timing history.

Integration tests now print a progress meter, which should fix the
Travis timeouts.
2015-12-29 16:43:25 -05:00
AlexKnauth
96fd22a7a3 add type for flatten
fixes https://github.com/racket/typed-racket/issues/277

Can’t make it polymorphic, settle for `Any -> (Listof Any)`
2015-12-24 10:09:23 -05:00
Sam Tobin-Hochstadt
10dc533751 Succcessfully typecheck new rackunit test-begin expansion. 2015-12-21 19:46:21 -05:00
Sam Tobin-Hochstadt
b00f74dad2 Avoid local-expand in typed/rackunit.
Fixes build problem.
2015-12-21 15:36:56 -05:00
Asumu Takikawa
b4489012a7 Temporarily disable examples until #264 is fixed 2015-12-20 17:04:55 -05:00
Asumu Takikawa
8791bdcdfc Remove evaluation imports that aren't needed 2015-12-20 17:04:55 -05:00
Asumu Takikawa
f08f3d07d4 Convert TR docs to use scribble/example 2015-12-20 17:04:55 -05:00
Asumu Takikawa
b18d940f1a Fix GH issue #271
Propagate syntax properties when opening up begins at the top-level
so that ignore properties will get transferred.
2015-12-18 18:10:48 -05:00
Vincent St-Amour
beb517c9c8 Ugh, typo. 2015-12-18 17:09:58 -06:00
Vincent St-Amour
439e0ba650 Required in wrong module. 2015-12-18 16:49:59 -06:00
Vincent St-Amour
f9e3418d8a Only show OC button in DrR when the tool is actually loaded.
Closes PR13344.
2015-12-18 15:58:40 -06:00
Sam Tobin-Hochstadt
519dfb6fdc Handle Sequenceof in the type parser to support multiple values. 2015-12-17 19:42:29 -05:00
Sam Tobin-Hochstadt
ad88f45bbe Merge pull request #269 from jsmaniac/fix-promise-provide
Fixes GH issue #268 “Can't provide promise for polymorphic struct”.
2015-12-17 09:50:21 -05:00
Georges Dupéron
f992786243 Fixes GH issue #268 “Can't provide promise for polymorphic struct”.
See also commit 5cd5f77 “Don't allow promises created with `delay/name` as `(Promise T)`.”.

The contracts in `typed-racket-lib/typed-racket/static-contracts/combinators/structural.rkt` should be just a single identifier, not a lambda expression, because `typed-racket-lib/typed-racket/private/type-contract.rkt` relies on that, and passes the contract name to free-identifier=?, which won't work on a lambda.
2015-12-17 14:52:40 +01:00
Georges Dupéron
c9e0197d51 Merge branch 'master' of github.com:racket/typed-racket into improve-error-messages 2015-12-17 13:51:25 +01:00
Sam Tobin-Hochstadt
077ff4ab2f Remove use of this-expression-source-directory.
Fixes presence of build system path in .zo files.
2015-12-11 10:12:33 -05:00
Alex Knauth
fdbf052bdb Merge pull request #259 from AlexKnauth/define-new-subtype-docs
Clarify define-new-subtype docs
2015-12-10 16:00:04 -05:00
Matthew Flatt
38e091c1c4 add type for an internal function in a match expansion 2015-12-09 17:21:56 -07:00
Asumu Takikawa
7f05dc6731 Update docs & tests for struct constructor options 2015-12-08 00:33:45 -05:00
Asumu Takikawa
796af399bf Support #:constructor-name in TR's struct 2015-12-08 00:33:43 -05:00
Asumu Takikawa
fc809e370e Add support for #:extra-constructor-name to struct
Simplify TR's define-struct to expand to struct
2015-12-08 00:33:06 -05:00
Alex Knauth
d3b56d8a5c Clarify define-new-subtype docs 2015-12-03 17:15:38 -05:00
Vincent St-Amour
70afdf6f70 Relax type of expt.
Made possible by the fix to its dynamic behavior.

Should be fully backwards compatible.
2015-12-02 15:44:23 -06:00
Sam Tobin-Hochstadt
600935aae1 Don't report duplicate error messages. 2015-12-02 11:00:47 -05:00
Sam Tobin-Hochstadt
9c1569646e Construct syntax to be checked with source locations.
Closes #258.
2015-12-01 14:22:16 -05:00
Sam Tobin-Hochstadt
ab4514bb56 Use symbols instead of identifiers for poorly-printing struct types.
This case only comes up when something else unfortunate has happened
with type printing, but the current implementation can lead to paths
in the type printing.
2015-11-30 17:55:26 -05:00
Vincent St-Amour
1d69569382 Fix tests. 2015-11-30 07:56:08 -06:00
Vincent St-Amour
781e0504bb Type of expt was overly optimistic. 2015-11-29 15:47:33 -06:00
Andrew Kent
581469e749 removed scope structs from types 2015-11-28 15:04:03 -05:00
Matthew Flatt
c4f39433e1 refine the type of collect-garbage
The `collect-garbage` function now accepts a mode argument.
2015-11-27 06:57:37 -07:00
Vincent St-Amour
6a8c366210 Fix type of expt to reflect fix to dynamic behavior. 2015-11-25 17:33:00 -06:00
Vincent St-Amour
fe4808f96a Fix magnitude on numbers with negative components.
Closes PR 15183.
2015-11-23 14:30:20 -06:00
Georges Dupéron
0201de0466 Improved error messages given by typed/racket, by always including the type name and arguments in the error message.
When a typing error is located inside macro-expanded code, a message such as “wrong number of arguments to polymorphic type: expected 1 and got 2” does not help much in locating which instantiation is wrong.
2015-11-20 14:08:33 +01:00
Sam Tobin-Hochstadt
f9825cb250 Add test for top-level mutated var fix. 2015-11-18 14:31:48 -05:00
Sam Tobin-Hochstadt
9fc2c5b3c3 Fix mutated variable handling at the top level. 2015-11-18 13:16:41 -05:00
Sam Tobin-Hochstadt
f7123b8e57 Fix build dep. 2015-11-16 12:58:44 -05:00
Sam Tobin-Hochstadt
577ab41da6 Keep ignored provide forms in the output.
Closes #231.
2015-11-16 09:45:08 -05:00
Jordan Johnson
46836184f2 Document types exported by wrapper libs.
This includes types for all wrapper libs that define types, except for
typed/pict, typed/racket/*, and typed/syntax/stx.
2015-11-15 23:38:38 -08:00
Jordan Johnson
ea9467c826 net/url: fix PortT/Bytes, HTTP-Connection
* PortT/Bytes now correctly lists the Bytes parameter as _not_ optional. (For that matter, so does PortT/String.)
 * HTTP-Connection was defined as an opaque type in net/url, but really is the same type defined in net/http-client, so now it is imported from there.
2015-11-15 20:12:11 -08:00
Sam Tobin-Hochstadt
0e23bb48cb Add missing file. 2015-11-14 20:49:30 -05:00
Sam Tobin-Hochstadt
71b6fc456c Progress towards deterministic TR compilation.
Traverse many dictionaries in sorted order when generating
residual code.

Closes racket/racket#1138.
2015-11-14 20:32:05 -05:00
Leif Andersen
4af7c9d10e Fix typo in the docs.
p -> pt
(Thanks to Ben Chung for finding it.)
2015-11-13 14:06:14 -05:00
Vincent St-Amour
1e32397658 Refine type for expt.
Fixes rsound.
2015-11-13 10:14:39 -06:00
Sam Tobin-Hochstadt
5f39bb3647 Increase with-tr-contracts timeout. 2015-11-12 18:41:52 -05:00
Matthew Flatt
265453def2 remove redundant doc lists for typed/openssl 2015-11-12 15:57:12 -07:00
Vincent St-Amour
1187281bf7 Fix sign propagation in some real-complex corner cases.
May have been found using random testing a while back.
2015-11-12 16:08:20 -06:00
Vincent St-Amour
fb3dee24db Avoid double coercion. 2015-11-12 16:01:21 -06:00
Vincent St-Amour
b88b4a8829 Fix magnitude opt to not overflow.
May have been found with random testing, a while ago.
2015-11-12 15:36:44 -06:00
Vincent St-Amour
53e501bb8b Avoid generating contraints for optimized-away contract definitions.
Closes #214.
2015-11-12 13:46:37 -06:00
Earl Dean
be29c556cd added missing optional checked aurgument to Checkable-Menu-Item% in gui-types.rkt 2015-11-12 12:14:30 -05:00
Vincent St-Amour
bcd5fe531d Document for/and and co as not working. 2015-11-11 13:30:24 -06:00
Vincent St-Amour
3c1c5b1d03 Be more robust against one of {TR,R} erroring. 2015-11-10 16:39:18 -06:00
Eric Dobson
c9db5dded7 Make infer/dotted instantiate the dotted variable to improve inference.
With this we don't need to infer the length of the dotted variable,
in parts of the inference.

Closes #120.
2015-11-10 16:56:14 -05:00
Sam Tobin-Hochstadt
4b9689e88a Revise docs for minor improvements in support.
Closes #137.
2015-11-10 16:40:22 -05:00
Matthew Butterick
e27a1e24df Clarification of for/ forms
Make it explicit in the docs that  `for/vector`, `for/flvector`, `for/first`, `for/last`, and `for/and` aren't yet supported by the typechecker.
2015-11-10 16:34:50 -05:00
WarGrey Gyoudmon Ju
dc73660242 Add typed/web-server/http.rkt, typed/web-server/configuration/responders.rkt
Closes #153.
2015-11-10 16:32:20 -05:00
Spencer Florence
e0cbc15625 fix a bug where cover+tests could call syntax-local-introduce when not transforming
Closes #204.
2015-11-10 16:32:09 -05:00
Sam Tobin-Hochstadt
6cbd6d872f Remove obsolete comment. 2015-11-10 13:24:52 -05:00
Sam Tobin-Hochstadt
93507eb519 Avoid doing too much duplicate typechecking work. 2015-11-10 13:23:39 -05:00
Sam Tobin-Hochstadt
aa969302f8 Avoid typechecking multiple times when handling eta expansions. 2015-11-10 12:34:14 -05:00
Vincent St-Amour
caf62c5fc6 Travis CI has yet another FPU. 2015-11-10 10:33:50 -06:00
Vincent St-Amour
65f375f065 Refactoring. 2015-11-10 10:33:49 -06:00
Vincent St-Amour
7e7bef773f Have resource limits for individual test cases.
So that a term that takes too long doesn't doom the whole run.
2015-11-10 10:33:49 -06:00
Sam Tobin-Hochstadt
d9e3c2ac6a Repair da574a4 again. 2015-11-10 08:59:18 -05:00
Sam Tobin-Hochstadt
983b509f2a Repair da574a4. 2015-11-09 23:28:00 -05:00
Vincent St-Amour
249ae295e8 DrDr has a different FPU than I do. 2015-11-09 21:36:28 -06:00
Sam Tobin-Hochstadt
da574a47d0 Handle in-vector with range arguments.
* Add `normalise-inputs` to special function env.
* Treat eta-expansion specially. Now
    `(lambda (x ...) (f x ...))`
  will typecheck like `f` but with a type restricted to
  the size of `x ...`.

  Currently, this special case only works for non-polymorphic
  functions.
2015-11-09 19:04:14 -05:00
ben
5d4477d08d safe & efficient (-> Any Boolean) contract
New strategy for compiling the (-> Any Boolean) type to a contract.
When possible, uses `struct-predicate-procedure?` instead of
 wrapping in `(-> any-wrap/c boolean?)`.
Makes exceptions for untyped chaperones/impersonators over struct predicates;
 those are always wrapped with `(-> any-wrap/c boolean?)`.

This change also affects (require/typed ... [#:struct ...]), but not #:opaque
2015-11-09 19:04:02 -05:00
Georges Dupéron
67bd07a84a Fixes type of member and assoc, plus some tests for them. See github bug #223: “(member) has wrong type, exploiting the hole causes segfault”. 2015-11-09 19:04:02 -05:00
Vincent St-Amour
177fdb9684 More drdr-found counterexamples.
All fixed already, but were found before I pushed the fixes.
2015-11-09 17:24:12 -06:00
Vincent St-Amour
65b6d3e019 Abstract over operations properly.
Found using random testing.
2015-11-09 17:16:10 -06:00
Vincent St-Amour
7346abf91c Preserve sign better in real-complex ops.
Found using random testing.
2015-11-09 17:16:10 -06:00
Vincent St-Amour
d91d89ffc1 Improve code gen for real-complex division. 2015-11-09 17:16:10 -06:00
Vincent St-Amour
db79beaf12 Actual fix for not suppressing divide-by-0 errors. 2015-11-09 17:16:10 -06:00
Vincent St-Amour
623a29eff4 Avoid dependency between non-float and is-real properties. 2015-11-09 17:16:10 -06:00
Vincent St-Amour
36a39f7e5d Simplify interface. 2015-11-09 17:16:10 -06:00
Vincent St-Amour
d50ccec0b9 Revert div-by-0 fix. Breaks other things. 2015-11-09 17:16:10 -06:00
Vincent St-Amour
63e26cf17a Avoid suppressing divide-by-0 errors. 2015-11-09 17:16:09 -06:00
Vincent St-Amour
6a2c8ca9f7 Fix the fix to flexpt. 2015-11-09 17:16:09 -06:00
Vincent St-Amour
5fe4e6b03f Add missing coercion. 2015-11-09 17:16:09 -06:00
Vincent St-Amour
0b2ae25c92 Propagate fix to division.
Found using random testing, at least twice.
2015-11-09 17:16:09 -06:00
Vincent St-Amour
7ef06f74c9 Extend this handling to multiplication.
Found using random testing.
2015-11-09 17:16:09 -06:00
Vincent St-Amour
b101d396a3 Avoid premature float conversions.
Found using random testing.
2015-11-09 17:16:09 -06:00
Vincent St-Amour
7ba1ab6e51 Use correct imaginary binding.
Fixes 13 bugs found using random testing.
2015-11-09 17:16:09 -06:00
Vincent St-Amour
b6e6a6fa98 Add test for real-complex interaction. 2015-11-09 17:16:09 -06:00
Vincent St-Amour
f2bb83b012 That bug has been fixed. 2015-11-09 17:16:09 -06:00
Vincent St-Amour
f523fb1721 Recognize as real values that are not immediately consumed.
Fixes 18 bugs found via random testing.
2015-11-09 17:16:09 -06:00
Vincent St-Amour
46f2ed95d3 Fix tests for more conservative types. 2015-11-09 17:16:08 -06:00
Vincent St-Amour
9385f6e350 Yet another fix for expt and complexes.
Found using random testing. Found once.
2015-11-09 17:16:08 -06:00
Vincent St-Amour
5ce00a90d2 Fix sign property of n-ary division.
Found using random testing. Found 4 times.
2015-11-09 17:16:08 -06:00
Vincent St-Amour
23de6a654e Fix another NaN case in expt.
Found using random testing. Found 3 times.
2015-11-09 17:16:08 -06:00
Vincent St-Amour
3ef8fe1739 Fix type of expt for bignums that get converted to infinity.
Found using random testing. Found 10 times.
2015-11-09 17:16:08 -06:00
Vincent St-Amour
a0ef6b1d8c Fix type of expt.
Found using random testing. Found 10 times.
2015-11-09 17:16:08 -06:00
Vincent St-Amour
ef80d61ae9 Fix port interleaving. 2015-11-09 17:16:08 -06:00
Vincent St-Amour
16a18d7648 Fix type of flexpt.
Found using random testing. Found 9 times.
2015-11-09 17:16:08 -06:00
Vincent St-Amour
bd12a1b928 Add a regression test suite with historical counterexamples found by DrDr. 2015-11-09 17:16:08 -06:00
Sam Tobin-Hochstadt
37bfd24a0b Add test for or/c problem. 2015-11-06 14:35:46 -05:00
Alex Knauth
6c11b58f69 Merge pull request #236 from AlexKnauth/racket/unit-base
Don't provide Unit from typed/racket/base
2015-11-05 17:49:49 -05:00
Alex Knauth
43dc7632d4 don't provide Unit from typed/racket/base 2015-11-05 17:14:35 -05:00
Alex Knauth
390dc3a2b1 provide Unit from typed/racket/unit 2015-11-05 17:14:35 -05:00
Vincent St-Amour
2e100bcb33 Remove unsafety altogether.
The bytecode optimizer can do the same transformation.
2015-11-03 20:11:46 -06:00
Vincent St-Amour
2881cffdc2 Simplify unsafe op usage. 2015-11-03 18:27:20 -06:00
Vincent St-Amour
e4edf7a9ee Remove tests made obsolete by safety improvements. 2015-11-03 18:04:44 -06:00
Vincent St-Amour
89a06cfae6 Fix bitwise-and on negative numbers.
Found using random testing.
2015-11-03 16:02:05 -06:00
Vincent St-Amour
58e97f83ea Fix sign propagation for division.
Found using random testing.
2015-11-03 15:47:32 -06:00
Vincent St-Amour
207a12fa23 Fix the fix to compound pair optimimzations.
Previous version could drop code on the ground in some cases.
2015-11-03 14:35:32 -06:00
Vincent St-Amour
8f32aad3ee Remove not-actually-unsafe unsafe operations. 2015-11-03 14:34:39 -06:00
Vincent St-Amour
5b57736af6 Guard some unsafe ops. 2015-11-03 14:32:23 -06:00
Vincent St-Amour
59b5cb7346 Remove unused dependency. 2015-11-03 14:25:49 -06:00
Vincent St-Amour
f14793c462 Remove potentially incorrect unsafe operation. 2015-11-03 14:11:49 -06:00
Vincent St-Amour
da97da5ff8 Fix type of expt when mixing floats and float complexes.
Found using random testing.
2015-11-02 19:31:00 -06:00
Vincent St-Amour
ca9306bb1d Use more precise notion of "real argument" for multiplication too.
Found using random testing.
2015-11-02 19:31:00 -06:00
Vincent St-Amour
e47ffeb0e8 Fix interaction of sign and underflow in fl/.
Found using random testing.
2015-11-02 19:31:00 -06:00
Vincent St-Amour
a3d29d9e03 Align float-complex/float division with Racket more.
Found using random testing.
2015-11-02 19:30:59 -06:00
Sam Tobin-Hochstadt
ad0c69ea29 Make these definitions safe again.
As of this moment, the performance win on new-metrics.rkt for
using the unsafe version is about 1% (avg over 10 runs), which
isn't enough to make it worth the segfaults. I believe that
changes to the JIT since 2012 (when the unsafe ops were added)
have sped up struct access.
2015-11-02 14:49:25 -05:00
Sam Tobin-Hochstadt
cb35383143 Add test case for issue #215. 2015-11-02 14:49:25 -05:00
Vincent St-Amour
ea6968f1d9 Don't attempt to unfold pair opts when we have no type info.
Fixes compilation of the `midi-readwrite` package.
2015-11-02 13:28:22 -06:00
Asumu Takikawa
ae0741aaa7 Use cond-contract forms instead of lazy-require 2015-10-31 04:15:06 -04:00
Asumu Takikawa
47ba1391f5 Add begin-for-cond-contract 2015-10-31 04:15:06 -04:00
Asumu Takikawa
0be2156521 Remove unused lazy-requires 2015-10-31 04:15:06 -04:00
Asumu Takikawa
15aa3d875f Delete top-level hack that's no longer necessary
The trampolining implementation of the top-level solves this
without the dependency on rep/type-rep.rkt
2015-10-31 02:32:42 -04:00
Asumu Takikawa
a24852548a Adjust expansion of contract submod redirection
The `local-expand` based trick defeats optimizations for the
contract system because of the extra `let-values` that's
introduced, so use `syntax-local-lift-require` instead.

This commit combined with the previous commit improves
the performance of the test at

  typed-racket-test/performance/function-contract.

by a significant amount back to v6.1.1 performance.

Thanks to Robby for discovering the regression.
2015-10-29 17:50:18 -04:00
Asumu Takikawa
555571c268 Inline ->* contracts in type->contract generation
The contract generation process was aggressively optimizing
some contracts, leading to a pessimization when interacting
with the contract system's own optimizations.

This inlining addition undoes a small portion of the contract
generation in some cases to better cooperate with the contract
system's optimizations.

This commit alone doesn't solve the optimization problem.
But it does when combined with the next commit.
2015-10-29 17:50:18 -04:00
Asumu Takikawa
0d4b2fb3f7 Refactor define-values for contract definition
This worked by accident and added an extraneous function
definition and call. Refactor to avoid that.
2015-10-29 17:50:18 -04:00
Daniel Feltey
d7ae7dbdd8 Fix incorrect serialization of signature environment
Closes #229
2015-10-29 01:35:33 -05:00
Andrew Kent
60c37ab2bf conservatively label define-new-subtype as experimental for now 2015-10-27 19:32:29 -04:00
Asumu Takikawa
ac880411d4 Memoize the Un constructor for unions
This seems to speed up typechecking by 5-10% (depending
on the machine) on compiling the math library and on
the "new-metrics.rkt" test.
2015-10-24 21:39:43 -04:00
Asumu Takikawa
9b8b525d42 Make unit test less noisy 2015-10-24 04:13:47 -04:00
Sam Tobin-Hochstadt
c8ea37c64e Add error message for incorrect use of Parameterof.
Closes PR 14417.
2015-10-23 15:40:31 -04:00
Asumu Takikawa
2479dffde0 Fix #:opaque require clauses at the top-level 2015-10-21 17:23:24 -04:00
Asumu Takikawa
f1cb23062a Add examples for typed/racket/unsafe docs 2015-10-21 14:26:53 -04:00
Asumu Takikawa
5fa40de546 Avoid ignoring class/unit forms at the top-level
Possibly a better long-term solution (for after the release)
is to not use the ignore property here and instead just use the
ignore table.
2015-10-21 13:33:30 -04:00
Asumu Takikawa
c3a59ee1c4 Use protect-out for unsafe operations
Disallows usage in sandboxes and similar contexts
2015-10-20 18:00:35 -04:00
Sam Tobin-Hochstadt
6aa635d740 Fix equality handling on literals.
Repairs eb93a2b571 and closes #215.
2015-10-20 15:56:07 -04:00
Asumu Takikawa
b5dc5585be Fix part of GH issue #208
For private `define-values` in classes with multiple variables, don't
eagerly throw type errors in the synthesis step. Instead, wait
until the later checking step when the environment will be correctly
set up.

When the initial synthesis typecheck fails, yield type Any for
the environment. If the typecheck should really fail, this is ok. If
not, then the user can add a type annotation.

A better long-term strategy is to change the handling of environments
so that the type environment gets refined as definitions are checked.
This way all annotations that the user writes are factored into the
initial environment and unannotated variables will have their types
synthesized.
2015-10-20 14:06:32 -04:00
Vincent St-Amour
bbe3521530 Update history.
Please merge to 6.3.
2015-10-20 11:20:46 -05:00
Vincent St-Amour
d2a7fb31bc Improve internal error checking. 2015-10-19 16:23:26 -05:00
Vincent St-Amour
59a61cc732 Fix tests. 2015-10-19 16:23:26 -05:00
Vincent St-Amour
22bfce117b Add missing bits to type and ignore tables for make-object-related code. 2015-10-19 16:23:26 -05:00
Vincent St-Amour
36a40b8334 Ignore code that results from the expansion of keyword function call sites
...that also involve contracts.

That code was previously not marked as lifted by the contract system, and
thus was not ignored by TR. But TR was not giving it a type, which made the
optimizer unhappy, now that it looks at the types of everything.
2015-10-19 16:23:26 -05:00
Vincent St-Amour
4aed44370d Ignore more code that has no types. 2015-10-19 16:23:26 -05:00
Vincent St-Amour
6245807b7c Log uses of float vectors.
For OC to recommend using flvectors instead.
2015-10-19 16:23:26 -05:00
Vincent St-Amour
6ccb0939f8 Have optimizer skip more code that is not typechecked. 2015-10-19 16:23:26 -05:00
Asumu Takikawa
3149b0a305 Fix send on a receiver with recursive type
Thanks to Matthias for finding the bug
2015-10-19 16:27:11 -04:00
Asumu Takikawa
638618ae40 Fix unsafe-require/typed for kw functions 2015-10-16 15:52:21 -04:00
Sam Tobin-Hochstadt
3d91ebeb4c Increase timeout instead of removing it.
Having no timeout definition reverts to the default, which is 90 seconds.
2015-10-16 11:48:28 -04:00
Vincent St-Amour
b40cde6b67 Remove stray timeout.
The random tester has its own timeout, which doesn't cause drdr to error.
2015-10-16 10:20:32 -05:00
Asumu Takikawa
30ecfef309 Fix type of system-type
Closes GH issue #210
2015-10-10 14:59:49 -04:00
Vincent St-Amour
98f90cce2c Ignore random testing running out of resources.
Evaluating random bignum expressions sometimes goes off the rails.
2015-10-06 11:44:27 -05:00
Matthew Flatt
ca46d80189 fix use of code:contract
Unlike `code:comment`, which wants a string or other content,
`code:contract` wants datums.
2015-10-02 15:32:50 -06:00
Asumu Takikawa
bf3f86a2b0 Add a performance test for function contracts 2015-10-01 18:04:20 -04:00
Asumu Takikawa
ef6d82e81f Add a test for top-level unsafe-require/typed 2015-10-01 17:41:03 -04:00
Vincent St-Amour
913ef6a2ef Ensure a minimum of actually random testing.
Previously, most of it was actually deterministic enumeration, which meant
repeating the same tests over and over again. We still want to run those,
if only to catch regressions, so now we run both enumeration and truly
random tests, separately.

This does mean that the set of tests being run for a given seed is not the
same as it used to, so old seeds won't give the same results as before.
2015-10-01 13:57:35 -05:00
Asumu Takikawa
435e733d66 Forgot to add fixes into last commit 2015-09-30 18:16:51 -04:00
Asumu Takikawa
e8820503e7 Fix source location tracking for top-level forms
This had broken due to trampoline-based refactoring
2015-09-30 17:03:22 -04:00
Asumu Takikawa
2cbadeaccc Bump version for typed/racket/unsafe addition
Also add history annotations to docs
2015-09-30 15:37:02 -04:00
Asumu Takikawa
eb90cd4e8c Add a typed/racket/unsafe library.
Comes with `unsafe-require/typed` and `unsafe-provide`.
These operations do not generate contracts but are not
exported by default by Typed Racket.
2015-09-29 18:48:08 -04:00
Georges Dupéron
7f8e91c571 Used new #:context variant (list symbol stx) for the error messages for , as per samth's suggestion. 2015-09-29 18:32:01 -04:00
Georges Dupéron
09d60e003b Simplified bad syntax error reporting for let (changes c8f02eb by samth) 2015-09-29 18:32:01 -04:00
Asumu Takikawa
eb93a2b571 Improve typechecking for equality
Closes GH issue #164
2015-09-22 22:57:18 -04:00
Sam Tobin-Hochstadt
a90a1bd689 Merge pull request #200 from EmmanuelOga/patch-1
typo
2015-09-16 15:02:13 -04:00
Emmanuel Oga
0191afbe98 typo 2015-09-16 04:21:03 -07:00
Daniel Feltey
c6743b4423 Don't expose require-typed-signature 2015-09-13 04:09:48 -05:00
Daniel Feltey
cbb76b987c Fix tc/letrec-values contract and document require-typed-signature 2015-09-12 13:45:33 -05:00
Daniel Feltey
2ad3dc5f75 Add docs for typed units 2015-09-11 17:30:20 -05:00
Daniel Feltey
93b9390e3b Fix Unit integration tests to match AnyValues in error messages 2015-09-11 15:43:51 -05:00
Vincent St-Amour
f89d91d864 Add timing for contract generation. 2015-09-11 13:39:03 -05:00
Vincent St-Amour
5ed30d7fcf Fix contract build. 2015-09-11 13:12:49 -05:00
Daniel Feltey
2e0cc095c7 Initial support for typed units in typed racket.
Most unit forms are supported, including most of the "infer" forms that
infer imports/exports/linkages from the current context.

Notably, none of the structural linking forms for units are supported, and
`define-unit-binding` is also currently unsupported.
2015-09-10 16:32:11 -05:00
Asumu Takikawa
26c4a199fb Fix accidentally added redundant requires
See last commit
2015-09-09 17:54:09 -04:00
Asumu Takikawa
3d6418b8be Refactor provide handling to reduce code 2015-09-09 12:50:32 -04:00
Asumu Takikawa
2b2e87010a Update version dependency on base
Depends on the rename transformer change
2015-09-09 11:59:43 -04:00
Alexis King
4bf3479776 Add types for writeln and println 2015-09-08 15:58:07 -04:00
Asumu Takikawa
241f04bcdb Fix typed provide uses in some modules
When identifiers provided by typed modules were used in
certain submodules of the form (module* n #f ...) or were
used by modules implemented in a language defined by TR,
the wrong redirection was used in the expansion.

The reason was because TR's identifier redirection decided
whether it was in a typed or untyped context at module visit
time, but that's too early in the cases above.

(because TR's #%module-begin may not have begun expanding yet)

The fix uses a rename-transformer that delays the decision
to use the typed or untyped identifier until expansion time.

Closes GH issue #163 and #181

Closes PR 15118
2015-09-08 15:53:34 -04:00
Asumu Takikawa
552f509102 Refactor the renamer module
Moves `get-alternate` since its only user is the require-contract
module. In addition, it appears that one of the cases in the
conditional in its body is unnecessary. This likely means that
the extra machinery for typed-renamers are not needed at all.

Also adds a test for `require/typed` of a typed module
2015-09-08 13:31:02 -04:00
Vincent St-Amour
fd3941c062 Remove dependency on unstable/contract. 2015-09-07 21:38:22 -05:00
Vincent St-Amour
bdbd18b839 Document AnyValues. 2015-09-07 20:41:05 -05:00
Vincent St-Amour
fbf200c034 Use 2d instead of unstable/2d. 2015-09-07 19:13:50 -05:00
Sam Tobin-Hochstadt
c8f02eb93f Improve error message for (let).
Closes #193.
2015-09-07 17:31:46 -04:00
Vincent St-Amour
a25a07987e Update type of pretty-format. 2015-09-07 15:35:14 -05:00
Brian Lachance
77334808a8 Allow AnyValues as a return type in user code
Closes PR 14217
2015-09-07 13:26:34 -05:00
Vincent St-Amour
9ab862c668 Remove now unused bound-names parameter. 2015-09-07 13:26:07 -05:00
Vincent St-Amour
602223e74a Prevent open SC terms.
Closes PR 15144.
2015-09-07 13:24:16 -05:00
Alexis King
c48abf6dff Fix typo in generated contract for Nonpositive-Integer 2015-08-29 16:41:10 -07:00
Vincent St-Amour
584d01314e Use math/flonum instead of unstable/flonum. 2015-08-27 14:14:51 -05:00
Asumu Takikawa
f5f84c7625 Fix for*/fold and for*/lists
Only parse and use the type annotations if they are present on
all fold variables. This matches the default for other forms in TR.
Also, this will usually result in a "insufficient type information"
message which is more helpful than if TR chose some default type.

Closes PR 15138
Closes PR 14893
2015-08-25 11:44:24 -04:00
Spencer Florence
9fb79b4e18 more missing srclocs 2015-08-22 08:42:14 -04:00
Spencer Florence
1d2da49dfb fixed src loc propigation for n-ary*->binary 2015-08-22 08:42:14 -04:00
Asumu Takikawa
20f3badc98 Enable prefab support correctly for define-struct
I had forgotten to adjust the define-struct macro to work
like the struct macro for the #:prefab keyword, which made
TR think prefabs were ordinary structs.

Closes GH issue #188
2015-08-21 16:38:04 -04:00
Asumu Takikawa
51cd8db3d6 Add types for a few missing syntax functions
Also fix some existing types
2015-08-20 17:08:47 -04:00
Spencer Florence
c8ebec62e8 fixed unbox-let optimization srcloc propigation 2015-08-20 15:20:51 -05:00
AlexKnauth
30cdfd16cc print more Error types as Error types 2015-08-17 17:48:00 -04:00
Asumu Takikawa
d4a9052f52 Fix outdated comment for redirect-contract 2015-08-17 13:12:25 -04:00
AlexKnauth
10c85f911a don't export Distinction 2015-08-17 01:08:07 -04:00
AlexKnauth
09203307cb test docs-complete on Travis CI 2015-08-17 01:08:07 -04:00
AlexKnauth
0ea39a1177 clean up unused definition 2015-08-16 00:00:58 -05:00
AlexKnauth
0037a0277f allow values with define-new-subtype types as functions 2015-08-15 19:00:35 -05:00
AlexKnauth
efb877dbfb fix inference with define-new-subtype 2015-08-15 14:36:45 -05:00
AlexKnauth
af2c22f542 Add error message specifications 2015-08-14 21:42:21 -05:00
AlexKnauth
f8cc9e8dcd fix issue #169 2015-08-14 21:42:20 -05:00
Asumu Takikawa
dfdf86e527 Add identifier-binding-symbol to TR base-env 2015-08-14 21:25:54 -04:00
Asumu Takikawa
2e97280335 Eliminate the eval hack for top-level structs
This eliminates a hack used to make the `struct` form
work at the TR top-level. The trampolining top-level
typechecker makes this unnecessary.
2015-08-14 21:25:54 -04:00
Asumu Takikawa
d85a267c42 Declare require/typed internal identifier first
Uses the `define-syntaxes` trick to declare the identifier
so that its binding symbol doesn't change later.
2015-08-14 21:25:54 -04:00
Asumu Takikawa
e031d6c47e Typecheck the top-level using trampolining macros
Instead of local-expanding the entire top-level forms at
once, wrap expressions in a top-level begin in trampolining
macro forms. This allows the typechecker to trampoline back
to the evaluator, which is necessary to declare/register
declarations made in a top-level begin.

The point of this change is to eliminate top-level hacks
and faciliate various macros that need to communicate using
multiple top-level forms.
2015-08-14 21:25:54 -04:00
Asumu Takikawa
84bd502d46 Refactor TR top-interaction
Use a macro to abstract out the implementation of most
of the top-level TR commands.
2015-08-14 21:25:54 -04:00
Asumu Takikawa
94ce4b203e Use tc-expr for top-interaction functions instead
This restricts operations like `:print-type` to only
work on expressions. This seems like a reasonable
restriction and simplifies the implementation.
2015-08-14 21:25:54 -04:00
Vincent St-Amour
e997f02095 Rewrite compound pair operation optimization.
Previous version replaced calls to, e.g., `cadr` with calls to `cdr`
then `car`, called the typechecker to populate the type table, then
optimized the exploded operations. The call to the typechecker failed
on open terms, limiting the applicability of the optimization, and was
just generally brittle.

The new version instead explodes operations, then optimizes them inside
out for as long as the argument's type guarantees it's safe. This works
on open terms, and should be more robust.
2015-08-14 17:28:47 -05:00
Sam Tobin-Hochstadt
c5a75df00c Merge pull request #176 from racket/sudo-false
Try running travis on docker.
2015-08-14 10:11:58 -04:00
349 changed files with 13911 additions and 4174 deletions

View File

@ -17,15 +17,16 @@ install:
- raco pkg config catalogs >> catalog-config.txt
- raco pkg config --set catalogs `cat catalog-config.txt`
- raco pkg update -i --no-setup source-syntax/ typed-racket-lib/ typed-racket-more/ typed-racket-compatibility/ typed-racket-doc/ typed-racket/ typed-racket-test/
- raco setup typed typed-racket typed-racket-test
- raco setup typed typed-racket typed-racket-test typed-scheme
script:
- racket -l typed-racket-test/run -- --unit
- racket -l typed-racket-test/run -- --int
- racket -l typed-racket-test/run -- --opt
- racket -l typed-racket-test/run -- --missed-opt
- racket -l typed-racket-test -- --unit
- racket -l typed-racket-test -- --int
- racket -l typed-racket-test -- --opt
- racket -l typed-racket-test -- --missed-opt
- raco setup -j 1 math
- racket -l typed-racket-test/run -- --math
- racket -l typed-racket-test -- --math
- racket -l typed-racket-test/test-docs-complete
- echo "done"
after_script:

7
issue_template.md Normal file
View File

@ -0,0 +1,7 @@
### What version of Racket are you using?
### What program did you run?
### What should have happened?
### If you got an error message, please include it here.

View File

@ -11,4 +11,4 @@
(define pkg-authors '(samth stamourv))
(define version "1.1")
(define version "1.5")

View File

@ -6,14 +6,11 @@ typed-scheme
#:read-syntax r:read-syntax
#:info make-info
(require (prefix-in r: typed-racket/typed-reader))
(require (prefix-in r: typed-racket/typed-reader)
typed-racket/private/oc-button)
(define (make-info key default use-default)
(case key
[(drscheme:toolbar-buttons)
;; If Optimization Coach is installed, load it.
(with-handlers ([exn:fail:filesystem? (lambda _ '())]) ; not found
(collection-path "optimization-coach")
(list (dynamic-require 'optimization-coach/tool
'optimization-coach-drracket-button)))]
(maybe-show-OC)]
[else (use-default key default)]))

View File

@ -17,12 +17,13 @@
(rename-in
(except-in typed-racket/base-env/prims
require-typed-struct
require/typed)
require/typed
require-typed-signature)
(require-typed-struct-legacy require-typed-struct)
(require/typed-legacy require/typed))
typed-racket/base-env/base-types
typed-racket/base-env/base-types-extra
(for-syntax typed-racket/base-env/base-types-extra))
(except-in typed-racket/base-env/base-types-extra Distinction)
(for-syntax (except-in typed-racket/base-env/base-types-extra Distinction)))
(provide (rename-out [define-type-alias define-type])
(all-from-out typed-racket/base-env/prims)
(all-from-out typed-racket/base-env/base-types)

View File

@ -7,14 +7,12 @@ typed/scheme/base
#:info make-info
#:language-info make-language-info
(require typed-racket/private/oc-button)
(define (make-info key default use-default)
(case key
[(drscheme:toolbar-buttons)
;; If Optimization Coach is installed, load it.
(with-handlers ([exn:fail:filesystem? (lambda _ '())]) ; not found
(collection-path "optimization-coach")
(list (dynamic-require 'optimization-coach/tool
'optimization-coach-drracket-button)))]
(maybe-show-OC)]
[else (use-default key default)]))
(define make-language-info

View File

@ -7,14 +7,12 @@ typed/scheme
#:info make-info
#:language-info make-language-info
(require typed-racket/private/oc-button)
(define (make-info key default use-default)
(case key
[(drscheme:toolbar-buttons)
;; If Optimization Coach is installed, load it.
(with-handlers ([exn:fail:filesystem? (lambda _ '())]) ; not found
(collection-path "optimization-coach")
(list (dynamic-require 'optimization-coach/tool
'optimization-coach-drracket-button)))]
(maybe-show-OC)]
[else (use-default key default)]))
(define make-language-info

View File

@ -10,12 +10,13 @@
"r6rs-lib"
"sandbox-lib"
"at-exp-lib"
"scribble-lib"
("scribble-lib" #:version "1.16")
"pict-lib"
"typed-racket-lib"
("typed-racket-lib" #:version "1.5")
"typed-racket-compatibility"
"typed-racket-more"
"racket-doc"))
"racket-doc"
"draw-lib"))
(define deps '("base"))
(define update-implies '("typed-racket-lib"))
@ -23,4 +24,4 @@
(define pkg-authors '(samth stamourv))
(define version "1.1")
(define version "1.5")

View File

@ -1,6 +1,7 @@
#lang scribble/manual
@begin[(require (for-label (only-meta-in 0 typed/racket)) scribble/eval
@begin[(require (for-label (only-meta-in 0 typed/racket))
scribble/example
"../utils.rkt" (only-in "quick.scrbl" typed-mod))]
@(define the-eval (make-base-eval))
@ -23,7 +24,7 @@ are provided as well; for example, the
@racketmodname[typed/racket/base] language corresponds to
@racketmodname[racket/base].
@racketblock+eval[#:eval the-eval (struct pt ([x : Real] [y : Real]))]
@examples[#:no-result #:eval the-eval (struct pt ([x : Real] [y : Real]))]
@margin-note{Typed Racket provides modified versions of core Racket forms,
which permit type annotations. Previous versions of Typed Racket provided
@ -34,11 +35,11 @@ This defines a new structure, named @racket[pt], with two fields,
@racket[Real], which corresponds to the @rtech{real numbers}.
The
@racket[struct] form corresponds to its untyped counterpart from
from @racketmodname[racket]---when porting a program from
@racketmodname[racket]---when porting a program from
@racketmodname[racket] to @racketmodname[typed/racket], simply add
type annotations to existing field declarations.
@racketblock+eval[#:eval the-eval (: distance (-> pt pt Real))]
@examples[#:no-result #:eval the-eval (: distance (-> pt pt Real))]
This declares that @racket[distance] has the type @racket[(-> pt pt Real)].
@;{@racket[distance] must be defined at the top-level of the module containing
@ -54,7 +55,7 @@ function type, in this case @racket[Real].
If you are familiar with @rtech{contracts}, the notation for function
types is similar to function contract combinators.
@racketblock+eval[#:eval the-eval
@examples[#:no-result #:eval the-eval
(define (distance p1 p2)
(sqrt (+ (sqr (- (pt-x p2) (pt-x p1)))
(sqr (- (pt-y p2) (pt-y p1))))))
@ -71,14 +72,14 @@ the program is accepted.
In the Typed Racket @gtech{REPL}, calling @racket[distance] will
show the result as usual and will also print the result's type:
@interaction[#:eval the-eval (distance (pt 0 0) (pt 3.1415 2.7172))]
@examples[#:label #f #:eval the-eval (distance (pt 0 0) (pt 3.1415 2.7172))]
Just evaluating the function name will print the function value and its type,
which can be useful for discovering the types that Typed Racket ascribes to
Racket functions. Alternatively, the @racket[:print-type] command will just
print the type:
@interaction[#:eval the-eval distance string-length (:print-type string-ref)]
@examples[#:label #f #:eval the-eval distance string-length (:print-type string-ref)]
@section{Datatypes and Unions}
@ -141,14 +142,14 @@ When Typed Racket detects a type error in the module, it raises an
error before running the program.
@examples[#:eval the-eval
(add1 "not a number")
(eval:error (add1 "not a number"))
]
@;{
Typed Racket also attempts to detect more than one error in the module.
@examples[#:eval the-eval
(string-append "a string" (add1 "not a number"))
(eval:error (string-append "a string" (add1 "not a number")))
]
}

View File

@ -1,7 +1,7 @@
#lang scribble/manual
@(require "../utils.rkt"
scribble/eval
scribble/example
(for-label (only-meta-in 0 typed/racket)))
@(define the-eval (make-base-eval))
@ -38,19 +38,19 @@ on higher-order arguments that are themselves polymorphic.
For example, the following program results in a type error
that demonstrates this limitation:
@interaction[#:eval the-eval
(map cons '(a b c d) '(1 2 3 4))
@examples[#:label #f #:eval the-eval
(eval:error (map cons '(a b c d) '(1 2 3 4)))
]
The issue is that the type of @racket[cons] is also polymorphic:
@interaction[#:eval the-eval cons]
@examples[#:label #f #:eval the-eval cons]
To make this expression type-check, the @racket[inst] form can
be used to instantiate the polymorphic argument (e.g., @racket[cons])
at a specific type:
@interaction[#:eval the-eval
@examples[#:label #f #:eval the-eval
(map (inst cons Symbol Integer) '(a b c d) '(1 2 3 4))
]
@ -69,10 +69,11 @@ fixed in a future release.
The following illustrates an example type that cannot be
converted to a contract:
@interaction[#:eval the-eval
(require/typed racket/base
[object-name (case-> (-> Struct-Type-Property Symbol)
(-> Regexp (U String Bytes)))])
@examples[#:label #f #:eval the-eval
(eval:error
(require/typed racket/base
[object-name (case-> (-> Struct-Type-Property Symbol)
(-> Regexp (U String Bytes)))]))
]
This function type by cases is a valid type, but a corresponding
@ -83,7 +84,7 @@ supported with dependent contracts.
A more approximate type will work for this case, but with a loss
of type precision at use sites:
@interaction[#:eval the-eval
@examples[#:label #f #:eval the-eval
(require/typed racket/base
[object-name (-> (U Struct-Type-Property Regexp)
(U String Bytes Symbol))])
@ -94,14 +95,11 @@ Use of @racket[define-predicate] also involves contract generation, and
so some types cannot have predicates generated for them. The following
illustrates a type for which a predicate can't be generated:
@interaction[#:eval the-eval
(define-predicate p? (All (A) (Listof A)))]
@examples[#:label #f #:eval the-eval
(eval:error (define-predicate p? (All (A) (Listof A))))]
@section{Unsupported features}
Units are not currently supported at all in Typed Racket, but they
will potentially be supported in a future version.
Most structure type properties do not work in Typed Racket, including
support for generic interfaces.
@ -112,7 +110,7 @@ To make programming with invariant type constructors (such as @racket[Boxof])
easier, Typed Racket generalizes types that are used as arguments to invariant
type constructors. For example:
@interaction[#:eval the-eval
@examples[#:label #f #:eval the-eval
0
(define b (box 0))
b
@ -126,7 +124,7 @@ initialize it with @racket[0]. Type generalization does exactly that.
In some cases, however, type generalization can lead to unexpected results:
@interaction[#:eval the-eval
@examples[#:label #f #:eval the-eval
(box (ann 1 Fixnum))
]
@ -134,7 +132,7 @@ The intent of this code may be to create of box of @racket[Fixnum], but Typed
Racket will generalize it anyway. To create a box of @racket[Fixnum], the box
itself should have a type annotation:
@interaction[#:eval the-eval
@examples[#:label #f #:eval the-eval
(ann (box 1) (Boxof Fixnum))
((inst box Fixnum) 1)
]
@ -149,22 +147,24 @@ occur inside macros---are not checked.
Concretely, this means that expressions inside, for example, a
@racket[begin-for-syntax] block are not checked:
@interaction[#:eval the-eval
(begin-for-syntax (+ 1 "foo"))
@examples[#:label #f #:eval the-eval
(eval:error (begin-for-syntax (+ 1 "foo")))
]
Similarly, expressions inside of macros defined in Typed Racket are
not type-checked. On the other hand, the macro's expansion is always
type-checked:
@defs+int[#:eval the-eval
((define-syntax (example-1 stx)
@examples[#:label #f #:eval the-eval
(eval:no-prompt
(define-syntax (example-1 stx)
(+ 1 "foo")
#'1)
#'1))
(eval:no-prompt
(define-syntax (example-2 stx)
#'(+ 1 "foo")))
(example-1)
(example-2)
(eval:error (example-1))
(eval:error (example-2))
]
Note that functions defined in Typed Racket that are used at

View File

@ -1,7 +1,7 @@
#lang scribble/manual
@begin[(require "../utils.rkt"
scribble/core scribble/eval
scribble/core scribble/example
(for-label (only-meta-in 0 typed/racket)
(prefix-in base: racket)))]
@ -127,8 +127,8 @@ This ensures that the expression, here @racket[(+ 7 1)], has the
desired type, here @racket[Number]. Otherwise, the type checker
signals an error. For example:
@interaction[#:eval the-eval
(ann "not a number" Number)]
@examples[#:label #f #:eval the-eval
(eval:error (ann "not a number" Number))]
@section{Type Inference}

View File

@ -1,7 +1,7 @@
#lang scribble/manual
@begin[(require "../utils.rkt"
scribble/core scribble/eval
scribble/core scribble/example
(for-label (only-meta-in 0 typed/racket)))]
@(define the-eval (make-base-eval))
@ -18,7 +18,7 @@ fails.
To illustrate, consider the following code:
@racketblock+eval[#:eval the-eval
@examples[#:no-result #:eval the-eval
(: flexible-length (-> (U String (Listof Any)) Integer))
(define (flexible-length str-or-lst)
(if (string? str-or-lst)
@ -51,7 +51,7 @@ have based on a predicate check in a conditional expression, it can
narrow the type of the variable within the appropriate branch of the
conditional.
@section[#:tag "filters-and-predicates"]{Filters and Predicates}
@section[#:tag "propositions-and-predicates"]{Propositions and Predicates}
In the previous section, we demonstrated that a Typed Racket programmer
can take advantage of occurrence typing to type-check functions
@ -59,26 +59,30 @@ with union types and conditionals. This may raise the question: how
does Typed Racket know how to narrow the type based on the predicate?
The answer is that predicate types in Typed Racket are annotated
with @deftech{filters} that tell the typechecker what additional
with logical @deftech{propositions} that tell the typechecker what additional
information is gained when a predicate check succeeds or fails.
For example, consider the REPL's type printout for @racket[string?]:
@interaction[#:eval the-eval string?]
@examples[#:label #f #:eval the-eval string?]
The type @racket[(-> Any Boolean : String)] has three parts. The first
two are the same as any other function type and indicate that the
predicate takes any value and returns a boolean. The third part, after
the @racket[_:], is a @tech{filter} that tells the typechecker two
things:
the @racket[_:], represents the logical @tech{propositions}
the typechecker learns from the result of applying the function:
@itemlist[#:style 'ordered
@item{If the predicate check succeeds, the argument variable has type @racket[String]}
@item{If the predicate check fails, the argument variable @emph{does not} have type @racket[String]}
]
Predicates for all built-in types are annotated with similar filters
that allow the type system to reason about predicate checks.
@item{If the predicate check succeeds (i.e. produces a
non-@racket[#f] value), the argument variable has type
@racket[String]}
@item{If the predicate check fails (i.e. produces @racket[#f]), the
argument variable @emph{does not} have type @racket[String]} ]
Predicates for all built-in types are annotated with similar propositions
that allow the type system to reason logically about predicate checks.
@section{Other conditionals and assertions}
@ -93,7 +97,7 @@ control flow constructs that are present in Racket such as
For example, the @racket[_flexible-length] function from earlier can
be re-written to use @racket[cond] with no additional effort:
@racketblock+eval[#:eval the-eval
@examples[#:no-result #:eval the-eval
(: flexible-length/cond (-> (U String (Listof Any)) Integer))
(define (flexible-length/cond str-or-lst)
(cond [(string? str-or-lst) (string-length str-or-lst)]
@ -104,13 +108,13 @@ In some cases, the type system does not have enough information or is
too conservative to typecheck an expression. For example, consider
the following interaction:
@interaction[#:eval the-eval
@examples[#:label #f #:eval the-eval
(: a Positive-Integer)
(define a 15)
(: b Positive-Integer)
(define b 20)
(: c Positive-Integer)
(define c (- b a))
(eval:error (define c (- b a)))
]
In this case, the type system only knows that @racket[_a] and
@ -119,13 +123,13 @@ difference will always be positive in defining @racket[_c]. In cases
like this, occurrence typing can be used to make the code type-check
using an @emph{assertion}. For example,
@racketblock+eval[#:eval the-eval
@examples[#:no-result #:eval the-eval
(: d Positive-Integer)
(define d (assert (- b a) positive?))
]
Using the filter on @racket[positive?], Typed Racket can assign the
type @racket[Positive-Integer] to the whole @racket[assert]
Using the logical propositions on @racket[positive?], Typed Racket can
assign the type @racket[Positive-Integer] to the whole @racket[assert]
expression. This type-checks, but note that the assertion may raise
an exception at run-time if the predicate returns @racket[#f].
@ -133,7 +137,7 @@ Note that @racket[assert] is a derived concept in Typed Racket and is
a natural consequence of occurrence typing. The assertion above is
essentially equivalent to the following:
@racketblock+eval[#:eval the-eval
@examples[#:no-result #:eval the-eval
(: e Positive-Integer)
(define e (let ([diff (- b a)])
(if (positive? diff)
@ -165,7 +169,7 @@ by let-expressions alias other values (e.g. when they alias non-mutated identifi
This allows programs which explicitly rely on occurrence typing and aliasing to
typecheck:
@racketblock+eval[#:eval the-eval
@examples[#:no-result #:eval the-eval
(: f (Any -> Number))
(define (f x)
(let ([y x])
@ -180,7 +184,7 @@ typecheck:
It also allows the typechecker to check programs which use macros
that heavily rely on let-bindings internally (such as @racket[match]):
@racketblock+eval[#:eval the-eval
@examples[#:no-result #:eval the-eval
(: g (Any -> Number))
(define (g x)
(match x

View File

@ -1,7 +1,6 @@
#lang scribble/manual
@begin[(require (for-label (only-meta-in 0 typed/racket))
scribble/eval racket/sandbox
"../utils.rkt" (only-in "quick.scrbl" typed-mod))]
@title[#:tag "optimization"]{Optimization in Typed Racket}
@ -30,9 +29,9 @@ Racket idioms. However, it does a better job on some idioms than on
others. By writing your programs using the right idioms, you can help
the optimizer help you.
To best take advantage of the Typed Racket optimizer, keep the following in
mind. The @emph{Optimization Coach} package provides optimization coaching
support to help you in this task.
To best take advantage of the Typed Racket optimizer, consult
@other-doc['(lib "optimization-coach/scribblings/optimization-coach.scrbl")
#:indirect "Optimization Coach"]{}.
@subsection{Numeric types}

View File

@ -1,7 +1,7 @@
#lang scribble/manual
@(require "../utils.rkt"
scribble/eval
scribble/example
(for-label (only-meta-in 0 typed/racket)))
@(define the-eval (make-base-eval))
@ -49,7 +49,7 @@ typed/racket
[#:struct pt ([x : Real] [y : Real])]
[distance (-> pt pt Real)])
(distance (pt 3 5) (p 7 0))
(distance (pt 3 5) (pt 7 0))
]
The @racket[require/typed] form has several kinds of clauses. The
@ -100,17 +100,17 @@ function:
@margin-note{For general information on Racket's contract system
, see @secref[#:doc '(lib "scribblings/guide/guide.scrbl")]{contracts}.}
@interaction[#:eval the-eval
@examples[#:label #f #:eval the-eval
(module increment racket
(provide increment)
(code:contract "increment : exact-integer? -> exact-integer?")
(code:contract increment : exact-integer? -> exact-integer?)
(define (increment x) "this is broken"))
]
and a typed module that uses it:
@interaction[#:eval the-eval
@examples[#:label #f #:eval the-eval
(module client typed/racket
(require/typed 'increment [increment (-> Integer Integer)])
@ -127,7 +127,7 @@ strings.
On the other hand, when the program is run:
@interaction[#:eval the-eval (require 'client)]
@examples[#:label #f #:eval the-eval (eval:error (require 'client))]
we find that the contract system checks the assumption made by the typed
module and correctly finds that the assumption failed because of the

View File

@ -1,7 +1,7 @@
#lang scribble/manual
@begin[(require "../utils.rkt"
scribble/core scribble/eval
scribble/core scribble/example
(for-label (only-meta-in 0 typed/racket)))]
@(define the-eval (make-base-eval))
@ -18,7 +18,7 @@ The most basic types in Typed Racket are those for primitive data,
such as @racket[True] and @racket[False] for booleans, @racket[String]
for strings, and @racket[Char] for characters.
@interaction[#:eval the-eval
@examples[#:label #f #:eval the-eval
'"hello, world"
#\f
#t
@ -27,14 +27,14 @@ for strings, and @racket[Char] for characters.
Each symbol is given a unique type containing only that symbol. The
@racket[Symbol] type includes all symbols.
@interaction[#:eval the-eval
@examples[#:label #f #:eval the-eval
'foo
'bar]
Typed Racket also provides a rich hierarchy for describing particular
kinds of numbers.
@interaction[#:eval the-eval
@examples[#:label #f #:eval the-eval
0
-7
14
@ -43,7 +43,7 @@ kinds of numbers.
Finally, any value is itself a type:
@interaction[#:eval the-eval
@examples[#:label #f #:eval the-eval
(ann 23 23)]
@section{Function Types}
@ -65,7 +65,7 @@ one argument, and produces @rtech{multiple values}, of types
@racket[String] and @racket[Natural]. Here are example functions for
each of these types.
@interaction[#:eval the-eval
@examples[#:label #f #:eval the-eval
(lambda ([x : Number]) x)
(lambda ([a : String] [b : String]) (equal? a b))
(lambda ([c : Char]) (values (string c) (char->integer c)))]
@ -106,7 +106,7 @@ The result is two values of type @racket[Number].
Sometimes a value can be one of several types. To specify this, we
can use a union type, written with the type constructor @racket[U].
@interaction[#:eval the-eval
@examples[#:label #f #:eval the-eval
(let ([a-number 37])
(if (even? a-number)
'yes
@ -141,9 +141,9 @@ type defintion could also be written like this.
Of course, types which directly refer to themselves are not
permitted. For example, both of these definitions are illegal.
@interaction[#:eval the-eval
(define-type BinaryTree BinaryTree)
(define-type BinaryTree (U Number BinaryTree))]
@examples[#:label #f #:eval the-eval
(eval:error (define-type BinaryTree BinaryTree))
(eval:error (define-type BinaryTree (U Number BinaryTree)))]
@section{Structure Types}

View File

@ -1,17 +1,17 @@
Data Structures used in Typechecking.
The main data structure used in typechecking is a tc-results/c.
This currently has two variants
(struct/c tc-results ((listof (struct/c tc-result (Type/c FilterSet? Object?)))
(struct/c tc-results ((listof (struct/c tc-result (Type/c PropSet? Object?)))
(or/c #f (cons/c Type/c symbol?))))
(struct/c tc-any-results (or/c Filter/c NoFilter?))
(struct/c tc-any-results (or/c Prop? #f))
The first represents a fixed number of values with optional dotted return values.
The second represents an unknown number of values.
A value has three main parts: a Type, a FilterSet, and an Object. For dotted values we do no store a
FilterSet or an Object because they would almost never be useful. They are thus implicitly -top-filter
A value has three main parts: a Type, a PropSet, and an Object. For dotted values we do no store a
PropSet or an Object because they would almost never be useful. They are thus implicitly -tt-propset
and -empty-obj. In the tc-any-results case since we don't know the number of values, we do not store
the Type or the Object, but we do store a filter. This is useful in cases like
the Type or the Object, but we do store a proposition. This is useful in cases like
(let ((x (read)))
(unless (number? x) (error 'bad-input))
(do-stuff x))

View File

@ -1,8 +1,13 @@
#lang scribble/manual
@begin[(require "../utils.rkt")
@begin[(require "../utils.rkt" scribble/example)
(require (for-label (only-meta-in 0 [except-in typed/racket for])))]
@(define the-top-eval (make-base-eval #:lang 'typed/racket))
@(define-syntax-rule (ex . args)
(examples #:eval the-top-eval . args))
@title{Experimental Features}
These features are currently experimental and subject to change.
@ -15,3 +20,27 @@ predicate @racket[id], which must have been specified with
@racket[declare-refinement].}
@defform[(define-typed-struct/exec forms ...)]{Defines an executable structure.}
@defform[(define-new-subtype name (constructor t))]{
Defines a new type @racket[name] that is a subtype of @racket[t].
The @racket[constructor] is defined as a function that takes a value of type
@racket[t] and produces a value of the new type @racket[name].
A @racket[define-new-subtype] definition is only allowed at the top level of a
file or module.
This is purely a type-level distinction, with no way to distinguish the new type
from the base type at runtime. Predicates made by @racket[make-predicate]
won't be able distinguish them properly, so they will return true for all values
that the base type's predicate would return true for. This is usually not what
you want, so you shouldn't use @racket[make-predicate] with these types.
@ex[(module m typed/racket
(provide Radians radians f)
(define-new-subtype Radians (radians Real))
(: f : [Radians -> Real])
(define (f a)
(sin a)))
(require 'm)
(radians 0)
(f (radians 0))]
}

View File

@ -1,7 +1,7 @@
#lang scribble/manual
@begin[(require "../utils.rkt")
(require scribble/eval)
(require scribble/example)
(require (for-label (only-meta-in 0 [except-in typed/racket for])))]
@(define the-top-eval (make-base-eval))
@ -33,7 +33,8 @@ The following bindings are only available at the Typed Racket REPL.
]
}
@defform[(:print-type e)]{Prints the type of @racket[_e]. This prints the whole
@defform[(:print-type e)]{Prints the type of @racket[_e], which must be
an expression. This prints the whole
type, which can sometimes be quite large.
@examples[#:eval the-top-eval

View File

@ -1,7 +1,7 @@
#lang scribble/manual
@begin[(require "../utils.rkt")
(require scribble/eval
(require scribble/example
(for-label (only-meta-in 0 [except-in typed/racket for])))]
@(define the-eval (make-base-eval))

View File

@ -4,14 +4,21 @@
(require (for-label (only-meta-in 0 [except-in typed/racket for])
(only-in racket/base for)
racket/list srfi/14 net/url
version/check))]
version/check
;; Specific libraries wrapped for TR:
file/gif
net/http-client
net/url-structs
net/url
openssl
json))]
@title{Libraries Provided With Typed Racket}
The @racketmodname[typed/racket] language corresponds to the
@racketmodname[racket] language---that is, any identifier provided
by @racketmodname[racket], such as @racket[modulo] is available by default in
by @racketmodname[racket], such as @racket[modulo], is available by default in
@racketmodname[typed/racket].
@racketmod[typed/racket
@ -44,25 +51,66 @@ Other libraries can be used with Typed Racket via
The following libraries are included with Typed Racket in the
@racketfont{typed} collection:
@(define-syntax-rule @defmodule/incl[name]
@defmodule[name #:no-declare])
@(define-syntax-rule @defmodule/incl[name rest ...]
(list
(section #:style '(hidden toc-hidden unnumbered)
(string-append "Typed for " (symbol->string 'name)))
@defmodule[name rest ...]))
@(define-syntax-rule (deftype name . parts)
(defidform #:kind "type" name . parts))
@;; framework and mred left out until support for classes
@;; is more complete
@defmodule/incl[typed/file/gif]
@deftype[GIF-Stream]{
Describe a GIF stream, as produced by @racket[gif-start]
and accepted by the other functions from @racketmodname[file/gif].
}
@deftype[GIF-Colormap]{
Type alias for a list of three-element (R,G,B) vectors representing an image.
}
@defmodule/incl[typed/file/md5]
@defmodule/incl[typed/file/tar]
@defmodule/incl[typed/framework]
@defmodule/incl[typed/json]
@deftype[JSExpr]{
Describes a @tech["jsexpr" #:doc '(lib "json/json.scrbl")].
}
@defmodule/incl[typed/mred/mred]
@defmodule/incl[typed/net/base64]
@defmodule/incl[typed/net/cgi]
@defmodule/incl[typed/net/cookie]
@deftype[Cookie]{
Describes an HTTP cookie as implemented by @racketmodname[net/cookie].
}
@defmodule/incl[typed/net/dns]
@defmodule/incl[typed/net/ftp]
@deftype[FTP-Connection]{
Describes an open FTP connection.
}
@defmodule/incl[typed/net/gifwrite]
@defmodule/incl[typed/net/git-checkout]
@defmodule/incl[typed/net/head]
@defmodule/incl[typed/net/http-client]
@deftype[HTTP-Connection]{
Describes an HTTP connection, corresponding to @racket[http-conn?].
}
@defmodule/incl[typed/net/imap]
@deftype[IMAP-Connection]{
Describes an IMAP connection.
}
@defmodule/incl[typed/net/mime]
@defmodule/incl[typed/net/nntp]
@defmodule/incl[typed/net/pop3]
@ -73,15 +121,61 @@ The following libraries are included with Typed Racket in the
@defmodule/incl[typed/net/uri-codec]
@defmodule/incl[typed/net/url-connect]
@defmodule/incl[typed/net/url-structs]
@deftype[Path/Param]{
Describes the @racket[path/param] struct from @racketmodname[net/url-structs].
}
@deftype[URL]{
Describes an @racket[url] struct from @racketmodname[net/url-structs].
}
@defmodule/incl[typed/net/url]
In addition to defining the following types, this module also provides the
@racket[HTTP-Connection] type defined by @racketmodname[typed/net/http-client],
and the @racket[URL] and @racket[Path/Param] types from
@racketmodname[typed/net/url-structs].
@deftype[URL-Exception]{
Describes exceptions raised by URL-related functions; corresponds
to @racket[url-exception?].
}
@deftype[PortT]{
Describes the functions @racket[head-pure-port], @racket[delete-pure-port],
@racket[get-impure-port], @racket[head-impure-port], and
@racket[delete-impure-port].
}
@deftype[PortT/Bytes]{
Like @racket[PortT], but describes the functions that make POST and PUT
requests, which require an additional byte-string argument for POST or PUT
data.
}
@defmodule/incl[typed/openssl]
@deftype[SSL-Protocol]{
Describes an SSL protocol, defined as
@racket[(U 'auto 'sslv2-or-v3 'sslv2 'sslv3 'tls 'tls11 'tls12)].
}
@deftogether[(@deftype[SSL-Server-Context]
@deftype[SSL-Client-Context])]{
Describes an OpenSSL server or client context.
}
@deftype[SSL-Context]{Supertype of OpenSSL server and client contexts.}
@deftype[SSL-Listener]{
Describes an SSL listener, as produced by @racket[ssl-listen].
}
@deftype[SSL-Verify-Source]{
Describes a verification source usable by @racket[ssl-load-verify-source!]
and the @racket[ssl-default-verify-sources] parameter.
}
@defmodule/incl[typed/openssl/md5]
@defmodule/incl[typed/openssl/sha1]
@defmodule/incl[typed/openssl]
@defmodule/incl[typed/pict]
@defmodule[typed/racket/async-channel #:no-declare @history[#:added "1.1"]]
@defmodule/incl[typed/racket/async-channel @history[#:added "1.1"]]
@defmodule/incl[typed/racket/date]
@defmodule/incl[typed/racket/draw]
@defmodule/incl[typed/racket/gui]
@defmodule/incl[typed/racket/random @history[#:added "1.5"]]
@defmodule/incl[typed/racket/sandbox]
@defmodule/incl[typed/racket/snip]
@defmodule/incl[typed/racket/system]
@ -90,8 +184,24 @@ The following libraries are included with Typed Racket in the
@defmodule/incl[typed/rackunit/text-ui]
@defmodule/incl[typed/rackunit]
@defmodule/incl[typed/srfi/14]
@deftype[Char-Set]{
Describes a character set usable by the @racketmodname[srfi/14] functions.
}
@deftype[Cursor]{
Describes a cursor for iterating over character sets.
}
@defmodule/incl[typed/srfi/19]
@deftogether[(@defidform[#:kind "type" Time]
@defidform[#:kind "type" Date])]{
Describes an SRFI 19 time or date structure.
}
@defmodule/incl[typed/syntax/stx]
@defmodule/incl[typed/web-server/configuration/responders]
@defmodule/incl[typed/web-server/http]
In some cases, these typed adapters may not contain all of exports of the
original module, or their types may be more limited.
@ -103,7 +213,11 @@ written in Typed Racket or have adapter modules that are typed:
@defmodule[name #:no-declare #:link-target? #f #:indirect])
@defmodule/also[math]
@defmodule/also[plot/typed]
@defmodule/also[plot]
@defmodule/incl[typed/pict]
@defmodule/also[images/flomap]
@defmodule/incl[typed/images/logos]
@defmodule/incl[typed/images/icons]
@section{Porting Untyped Modules to Typed Racket}

View File

@ -1,6 +1,6 @@
#lang scribble/manual
@begin[(require "../utils.rkt" scribble/eval racket/sandbox)
@begin[(require "../utils.rkt" scribble/example racket/sandbox)
(require (for-label (only-meta-in 0 [except-in typed/racket])
(only-in racket/base)))]
@ -229,12 +229,7 @@ variants.
@defform[(for/hasheq type-ann-maybe (for-clause ...) expr ...+)]
@defform[(for/hasheqv type-ann-maybe (for-clause ...) expr ...+)]
@defform[(for/vector type-ann-maybe (for-clause ...) expr ...+)]
@defform[(for/flvector type-ann-maybe (for-clause ...) expr ...+)]
@defform[(for/extflvector type-ann-maybe (for-clause ...) expr ...+)]
@defform[(for/and type-ann-maybe (for-clause ...) expr ...+)]
@defform[(for/or type-ann-maybe (for-clause ...) expr ...+)]
@defform[(for/first type-ann-maybe (for-clause ...) expr ...+)]
@defform[(for/last type-ann-maybe (for-clause ...) expr ...+)]
@defform[(for/sum type-ann-maybe (for-clause ...) expr ...+)]
@defform[(for/product type-ann-maybe (for-clause ...) expr ...+)]
@defform[(for/set type-ann-maybe (for-clause ...) expr ...+)]
@ -260,6 +255,16 @@ type @racket[u]. For example, a @racket[for/list] form would be
annotated with a @racket[Listof] type. All annotations are optional.
}
@deftogether[[
@defform[(for/flvector type-ann-maybe (for-clause ...) expr ...+)]
@defform[(for/extflvector type-ann-maybe (for-clause ...) expr ...+)]
@defform[(for/and type-ann-maybe (for-clause ...) expr ...+)]
@defform[(for/first type-ann-maybe (for-clause ...) expr ...+)]
@defform[(for/last type-ann-maybe (for-clause ...) expr ...+)]
]]{
Like the above, except they are not yet supported by the typechecker.
}
@deftogether[[
@defform[(for/lists type-ann-maybe ([id : t] ...)
(for-clause ...)
@ -373,14 +378,19 @@ those functions.
@section{Structure Definitions}
@defform/subs[
@defform/subs[#:literals (:)
(struct maybe-type-vars name-spec ([f : t] ...) options ...)
([maybe-type-vars code:blank (v ...)]
[name-spec name (code:line name parent)]
[options #:transparent #:mutable #:prefab])]{
Defines a @rtech{structure} with the name @racket[name], where the
[name-spec name-id (code:line name-id parent)]
[options #:transparent #:mutable #:prefab
(code:line #:constructor-name constructor-id)
(code:line #:extra-constructor-name constructor-id)
(code:line #:type-name type-id)])]{
Defines a @rtech{structure} with the name @racket[name-id], where the
fields @racket[f] have types @racket[t], similar to the behavior of @|struct-id|
from @racketmodname[racket/base].
from @racketmodname[racket/base]. If @racket[type-id] is specified, then it will
be used for the name of the type associated with instances of the declared
structure, otherwise @racket[name-id] will be used for both.
When @racket[parent] is present, the
structure is a substructure of @racket[parent].
@ -397,36 +407,47 @@ amount it needs.
@ex[
(struct (X Y) 2-tuple ([first : X] [second : Y]))
(struct (X Y Z) 3-tuple 2-tuple ([first : X] [second : Y] [third : Z]))
(struct (X Y Z) 3-tuple 2-tuple ([third : Z]))
]
Options provided have the same meaning as for the @|struct-id| form
from @racketmodname[racket/base].
from @racketmodname[racket/base] (with the exception of @racket[#:type-name], as
described above).
A prefab structure type declaration will bind the given @racket[name] to a
@racket[Prefab] type. Unlike in @racketmodname[racket/base], a non-prefab
structure type cannot extend a prefab structure type.
A prefab structure type declaration will bind the given @racket[name-id]
or @racket[type-id] to a @racket[Prefab] type. Unlike the @|struct-id| form from
@racketmodname[racket/base], a non-prefab structure type cannot extend
a prefab structure type.
@ex[
(struct a-prefab ([x : String]) #:prefab)
(:type a-prefab)
(struct not-allowed a-prefab ())
(eval:error (struct not-allowed a-prefab ()))
]
@history[#:changed "1.4" @elem{Added the @racket[#:type-name] option.}]
}
@defform/subs[
@defform/subs[#:literals (:)
(define-struct maybe-type-vars name-spec ([f : t] ...) options ...)
([maybe-type-vars code:blank (v ...)]
[name-spec name (name parent)]
[options #:transparent #:mutable])]{Legacy version of @racket[struct],
corresponding to @|define-struct-id| from @racketmodname[racket/base].}
[name-spec name-id (code:line name-id parent)]
[options #:transparent #:mutable
(code:line #:type-name type-id)])]{
Legacy version of @racket[struct], corresponding to @|define-struct-id|
from @racketmodname[racket/base].
@history[#:changed "1.4" @elem{Added the @racket[#:type-name] option.}]}
@defform/subs[
(define-struct/exec name-spec ([f : t] ...) [e : proc-t])
([name-spec name (name parent)])]{
@defform/subs[#:literals (:)
(define-struct/exec name-spec ([f : t] ...) [e : proc-t] maybe-type-name)
([name-spec name-id (code:line name-id parent)]
[maybe-type-name (code:line)
(code:line #:type-name type-id)])]{
Like @racket[define-struct], but defines a procedural structure.
The procdure @racket[e] is used as the value for @racket[prop:procedure], and must have type @racket[proc-t].}
The procedure @racket[e] is used as the value for @racket[prop:procedure],
and must have type @racket[proc-t].
@history[#:changed "1.4" @elem{Added the @racket[#:type-name] option.}]}
@section{Names for Types}
@defform*[[(define-type name t maybe-omit-def)
@ -461,27 +482,8 @@ back to itself.
However, the recursive reference may not occur immediately inside
the type:
@ex[(define-type Foo Foo)
(define-type Bar (U Bar False))]
}
@section{Defining New Subtypes}
@defform[(define-new-subtype name (constructor t))]{
Defines a new type @racket[name] that is a subtype of @racket[t].
The @racket[constructor] is defined as a function that takes a value of type
@racket[t] and produces a value of the new type @racket[name].
A @racket[define-new-subtype] definition is only allowed at the top level of a
file or module.
@ex[(module m typed/racket
(provide Radians radians f)
(define-new-subtype Radians (radians Real))
(: f : [Radians -> Real])
(define (f a)
(sin a)))
(require 'm)
(radians 0)
(f (radians 0))]
@ex[(eval:error (define-type Foo Foo))
(eval:error (define-type Bar (U Bar False)))]
}
@section{Generating Predicates Automatically}
@ -538,10 +540,19 @@ returned by @racket[e], protected by a contract ensuring that it has type
@racket[t]. This is legal only in expression contexts.
@ex[(cast 3 Integer)
(cast 3 String)
(cast (lambda: ([x : Any]) x) (String -> String))
(eval:error (cast 3 String))
(cast (lambda ([x : Any]) x) (String -> String))
((cast (lambda ([x : Any]) x) (String -> String)) "hello")
]
}
The value is actually protected with two contracts. The second contract checks
the new type, but the first contract is put there to enforce the old type, to
protect higher-order uses of the value.
@ex[
((cast (lambda ([s : String]) s) (Any -> Any)) "hello")
(eval:error ((cast (lambda ([s : String]) s) (Any -> Any)) 5))
]}
@defform*[[(inst e t ...)
(inst e t ... t ooo bound)]]{
@ -572,33 +583,34 @@ Here, @racket[_m] is a module spec, @racket[_pred] is an identifier
naming a predicate, and @racket[_maybe-renamed] is an
optionally-renamed identifier.
@defform/subs[#:literals (struct)
@defform/subs[#:literals (struct :)
(require/typed m rt-clause ...)
([rt-clause [maybe-renamed t]
[#:struct name ([f : t] ...)
[#:struct name-id ([f : t] ...)
struct-option ...]
[#:struct (name parent) ([f : t] ...)
[#:struct (name-id parent) ([f : t] ...)
struct-option ...]
[#:opaque t pred]]
[#:opaque t pred]
[#:signature name ([id : t] ...)]]
[maybe-renamed id
(orig-id new-id)]
[struct-option
(code:line #:constructor-name constructor-id)
(code:line #:extra-constructor-name constructor-id)])]
(code:line #:extra-constructor-name constructor-id)
(code:line #:type-name type-id)])]
This form requires identifiers from the module @racket[m], giving
them the specified types.
The first case requires @racket[_maybe-renamed], giving it type
@racket[t].
The first case requires @racket[_maybe-renamed], giving it type @racket[t].
@index["struct"]{The second and third cases} require the struct with name @racket[name]
with fields @racket[f ...], where each field has type @racket[t]. The
third case allows a @racket[parent] structure type to be specified.
The parent type must already be a structure type known to Typed
Racket, either built-in or via @racket[require/typed]. The
structure predicate has the appropriate Typed Racket filter type so
that it may be used as a predicate in @racket[if] expressions in Typed
Racket.
@index["struct"]{The second and third cases} require the struct with name
@racket[name-id] and creates a new type with the name @racket[type-id], or
@racket[name-id] if no @racket[type-id] is provided, with fields @racket[f ...],
where each field has type @racket[t]. The third case allows a @racket[parent]
structure type to be specified. The parent type must already be a structure type
known to Typed Racket, either built-in or via @racket[require/typed]. The
structure predicate has the appropriate Typed Racket filter type so that it may
be used as a predicate in @racket[if] expressions in Typed Racket.
@ex[(module UNTYPED racket/base
@ -630,6 +642,11 @@ Opaque types must be required lexically before they are used.
evt?
(sync (alarm-evt (+ 100 (current-inexact-milliseconds))))]
@index["signature"]{The @racket[#:signature] keyword} registers the required
signature in the signature environment. For more information on the use of
signatures in Typed Racket see the documentation for
@racketmodname[typed/racket/unit].
In all cases, the identifiers are protected with @rtech{contracts} which
enforce the specified types. If this contract fails, the module
@racket[m] is blamed.
@ -652,7 +669,9 @@ a @racket[require/typed] form. Here is an example of using
Any])]))
@racket[file-or-directory-modify-seconds] has some arguments which are optional,
so we need to use @racket[case->].}
so we need to use @racket[case->].
@history[#:changed "1.4" @elem{Added the @racket[#:type-name] option.}]}
@defform[(require/typed/provide m rt-clause ...)]{
Similar to @racket[require/typed], but also provides the imported identifiers.
@ -697,7 +716,7 @@ but provides additional annotations to assist the typechecker.
(default-continuation-prompt-tag)
(code:comment "the function cannot be passed an argument")
(λ (f) (f 3))))
(require 'untyped)
(eval:error (require 'untyped))
]
}

View File

@ -1,6 +1,6 @@
#lang scribble/manual
@begin[(require "../utils.rkt" scribble/eval racket/sandbox)
@begin[(require "../utils.rkt" scribble/example racket/sandbox)
(require (for-label (only-meta-in 0 [except-in typed/racket for])))]
@(define the-eval (make-base-eval))
@ -127,9 +127,10 @@ additional provides all other bindings from @racketmodname[racket/class].
class form's clauses) are restricted.
@ex[
(class object%
(code:comment "Note the missing `super-new`")
(init-field [x : Real 0] [y : Real 0]))
(eval:error
(class object%
(code:comment "Note the missing `super-new`")
(init-field [x : Real 0] [y : Real 0])))
]
If any identifier with an optional type annotation is left without an

View File

@ -1,6 +1,6 @@
#lang scribble/manual
@begin[(require "../utils.rkt" scribble/eval racket/sandbox)
@begin[(require "../utils.rkt" scribble/example racket/sandbox)
(require (for-label (only-meta-in 0 [except-in typed/racket for])))]
@(define the-eval (make-base-eval))
@ -35,19 +35,21 @@ have the types ascribed to them; these types are converted to contracts and chec
@examples[#:eval the-eval
(with-type #:result Number 3)
((with-type #:result (Number -> Number)
(lambda: ([x : Number]) (add1 x)))
#f)
(eval:error
((with-type #:result (Number -> Number)
(lambda: ([x : Number]) (add1 x)))
#f))
(let ([x "hello"])
(with-type #:result String
#:freevars ([x String])
(string-append x ", world")))
(let ([x 'hello])
(with-type #:result String
#:freevars ([x String])
(string-append x ", world")))
(eval:error
(let ([x 'hello])
(with-type #:result String
#:freevars ([x String])
(string-append x ", world"))))
(with-type ([fun (Number -> Number)]
[val Number])

View File

@ -0,0 +1,405 @@
#lang scribble/manual
@begin[(require "../utils.rkt" scribble/example racket/sandbox)
(require (for-label (only-meta-in 0 [except-in typed/racket for]))
(for-label (only-in racket/unit tag unit/c)))]
@(define the-eval (make-base-eval))
@(the-eval '(require (except-in typed/racket #%top-interaction #%module-begin)))
@(define the-top-eval (make-base-eval))
@(the-top-eval '(require (except-in typed/racket #%module-begin)))
@(define-syntax-rule (ex . args)
(examples #:eval the-top-eval . args))
@title{Typed Units}
@bold{Warning}: the features described in this section are experimental
and may not work correctly. Some of the features may change by
the next release.
Typed Racket provides support for modular programming with the units
and signatures provided by the @racketmodname[racket/unit] library.
@section[#:tag "unit-forms"]{Special forms}
@defmodule[typed/racket/unit]
The special forms below are provided by the @racketmodname[typed/racket/unit]
and @racketmodname[typed/racket] modules, but not by
@racketmodname[typed/racket/base]. The @racketmodname[typed/racket/unit] module
additionally provides all other bindings from @racketmodname[racket/unit].
@;; This trick is borrowed from the Typed Class reference to link to
@;; the identifiers in racket/unit
@(module id-holder racket/base
(require scribble/manual (for-label racket/unit))
(provide (all-defined-out))
(define ut:define-signature (racket define-signature))
(define ut:unit (racket unit))
(define ut:invoke-unit (racket invoke-unit))
(define ut:define-values/invoke-unit (racket define-values/invoke-unit))
(define ut:compound-unit (racket compound-unit))
(define ut:define-unit (racket define-unit))
(define ut:compound-unit/infer (racket compound-unit/infer))
(define ut:define-compound-unit (racket define-compound-unit))
(define ut:define-compound-unit/infer (racket define-compound-unit/infer))
(define ut:invoke-unit/infer (racket invoke-unit/infer))
(define ut:define-values/invoke-unit/infer (racket define-values/invoke-unit/infer))
(define ut:unit-from-context (racket unit-from-context))
(define ut:define-unit-from-context (racket define-unit-from-context))
(define ut:define-values-for-export (racket define-values-for-export))
(define ut:define-values (racket define-values))
(define ut:open (racket open))
(define ut:define-syntaxes (racket define-syntaxes))
(define ut:define-unit-binding (racket define-unit-binding))
(define ut:unit/s (racket unit/s))
(define ut:unit/new-import-export (racket unit/new-import-export)))
@(require 'id-holder)
@defform[
#:literals (extends :)
(define-signature id extension-decl
(sig-elem ...))
#:grammar
([extension-decl
code:blank
(code:line extends sig-id)]
[sig-elem [id : type]])]{
Binds an identifier to a signature and registers the identifier in the signature
environment with the specified type bindings. Sigantures in Typed Racket allow
only specifications of variables and their types. Variable and syntax definitions
are not allowed in the @racket[define-signature] form. This is only a limitation
of the @racket[define-signature] form in Typed Racket.
As in untyped Racket, the @racket[extends] clause includes all elements of
extended signature and any implementation of the new signature can be used
as an implementation of the extended signature.}
@defform[
#:literals (import export prefix rename only except init-depend)
(unit
(import sig-spec ...)
(export sig-spec ...)
init-depends-decl
unit-body-expr-or-defn
...)
#:grammar ([sig-spec
sig-id
(prefix id sig-spec)
(rename sig-spec (id id) ...)
(only sig-spec id ...)
(except sig-spec id ...)]
[init-depends-decl
code:blank
(init-depend sig-id ...)])]{
The typed version of the Racket @ut:unit form. Unit expressions in Typed Racket
do not support tagged signatures with the @racket[tag] keyword.}
@defform*[
#:literals (import)
[(invoke-unit unit-expr)
(invoke-unit unit-expr (import sig-spec ...))]]{
The typed version of the Racket @ut:invoke-unit form.}
@defform[
#:literals (import export)
(define-values/invoke-unit unit-expr
(import def-sig-spec ...)
(export def-sig-spec ...))
#:grammar ([def-sig-spec
sig-id
(prefix id def-sig-spec)
(rename def-sig-spec (id id) ...)])]{
The typed version of the Racket @ut:define-values/invoke-unit form. In Typed
Racket @racket[define-values/invoke-unit] is only allowed at the top-level
of a module.}
@defform[
#:literals (: import export link tag)
(compound-unit
(import link-binding ...)
(export link-id ...)
(link linkage-decl ...))
#:grammar ([link-binding
(link-id : sig-id)]
[linkage-decl
((link-binding ...) unit-expr link-id ...)])]{
The typed version of the Racket @ut:compound-unit form.}
@defform[
#:literals (import export)
(define-unit unit-id
(import sig-spec ...)
(export sig-spec ...)
init-depends-decl
unit-body-expr-or-defn
...)]{
The typed version of the Racket @ut:define-unit form.}
@defform[
#:literals (import export link :)
(compound-unit/infer
(import infer-link-import ...)
(export infer-link-export ...)
(link infer-linkage-decl ...))
#:grammar ([infer-link-import
sig-id
(link-id : sig-id)]
[infer-link-export
link-id
sig-id]
[infer-linkage-decl
((link-binding ...) unit-id
tagged-link-id ...)
unit-id])]{
The typed version of the Racket @ut:compound-unit/infer form.}
@defform[
#:literals (import export link)
(define-compound-unit id
(import link-binding ...)
(export link-id ...)
(link linkage-decl ...))]{
The typed version of the Racket @ut:define-compound-unit form.}
@defform[
#:literals (import export link)
(define-compound-unit/infer id
(import link-binding ...)
(export infer-link-export ...)
(link infer-linkage-decl ...))]{
The typed version of the Racket @ut:define-compound-unit/infer form.}
@defform[
#:literals (link)
(invoke-unit/infer unit-spec)
#:grammar ([unit-spec
unit-id
(link link-unit-id ...)])]{
The typed version of the Racket @ut:invoke-unit/infer form.}
@defform[
#:literals (export link)
(define-values/invoke-unit/infer maybe-exports unit-spec)
#:grammar ([maybe-exports
code:blank
(export sig-sepc ...)]
[unit-spec
unit-id
(link link-unit-id ...)])]{
The typed version of the Racket @ut:define-values/invoke-unit/infer form. Like
the @racket[define-values/invoke-unit] form above, this form is only allowed at
the toplevel of a module.}
@defform[
(unit-from-context sig-spec)]{
The typed version of the Racket @ut:unit-from-context form.}
@defform[
(define-unit-from-context id sig-spec)]{
The typed version of the Racket @ut:define-unit-from-context form.}
@section[#:tag "unit-types"]{Types}
@defform[
#:literals (import export init-depend Values)
(Unit
(import sig-id ...)
(export sig-id ...)
optional-init-depend-clause
optional-body-type-clause)
#:grammar ([optional-init-depend-clause
code:blank
(init-depend sig-id ...)]
[optional-body-type-clause
code:blank
type
(Values type ...)])]{
The type of a unit with the given imports, exports, initialization dependencies,
and body type. Omitting the init-depend clause is equivalent to an
@racket[init-depend] clause that contains no signatures. The body type is the
type of the last expression in the unit's body. If a unit contains only
definitions and no expressions its body type is @racket[Void]. Omitting the body
type is equivalent to specifying a body type of @racket[Void].
@ex[(module Unit-Types typed/racket
(define-signature fact^ ([fact : (-> Natural Natural)]))
(: use-fact@ (Unit (import fact^)
(export)
Natural))
(define use-fact@ (unit (import fact^) (export) (fact 5))))]
}
@defidform[UnitTop]{
The supertype of all unit types. Values of this type cannot be linked or invoked.
The primary use of is for the reflective operation @racket[unit?]}
@section[#:tag "unit-typed/untyped-interactions"]{Interacting with Untyped Code}
@defform/subs[#:link-target? #f
#:literals (struct)
(require/typed m rt-clause ...)
([rt-clause [maybe-renamed t]
[#:struct name ([f : t] ...)
struct-option ...]
[#:struct (name parent) ([f : t] ...)
struct-option ...]
[#:opaque t pred]
[#:signature name ([id : t] ...)]]
[maybe-renamed id
(orig-id new-id)]
[struct-option
(code:line #:constructor-name constructor-id)
(code:line #:extra-constructor-name constructor-id)])]
The @racket[#:signature] clause of @racket[require/typed] requires the given
signature and registers it in the signature environment with the specified
bindings. Unlike other identifiers required with @racket[require/typed], signatures
are not protected by contracts.
@margin-note{Signatures are not runtime values and therefore do not need to be protected by contracts.}
@ex[
(module UNTYPED-1 racket
(provide a^)
(define-signature a^ (a)))
(module TYPED-1 typed/racket
(require/typed 'UNTYPED-1
[#:signature a^ ([a : Integer])])
(unit (import a^) (export) (add1 a)))]
Typed Racket will infer whether the named signature @racket[extends]
another signature. It is an error to require a signature that extends a signature
not present in the signature environment.
@ex[
(module UNTYPED-2 racket
(provide a-sub^)
(define-signature a^ (a1))
(define-signature a-sub^ extends a^ (a2)))
(eval:error
(module TYPED-2 typed/racket
(require/typed 'UNTYPED-2
[#:signature a-sub^
([a1 : Integer]
[a2 : String])])))]
Requiring a signature from an untyped module that contains variable definitions is an error
in Typed Racket.
@ex[
(module UNTYPED racket
(provide bad^)
(define-signature bad^ (bad (define-values (bad-ref) (car bad)))))
(eval:error
(module TYPED typed/racket
(require/typed 'UNTYPED
[#:signature bad^
([bad : (Pairof Integer Integer)]
[bad-ref : Integer])])))]
@section{Limitations}
@subsection{Signature Forms}
Unlike Racket's @ut:define-signature form, in Typed Racket
@racket[define-signature] only supports one kind of signature element that
specifies the types of variables in the signature. In particular Typed Racket's
@racket[define-signature] form does not support uses of @ut:define-syntaxes,
@ut:define-values, or @ut:define-values-for-export . Requiring an untyped
signature that contains definitions in a typed module will result in an error.
@ex[(module UNTYPED racket
(provide bad^)
(define-signature bad^ ((define-values (bad) 13))))
(eval:error
(module TYPED typed/racket
(require/typed 'UNTYPED
[#:signature bad^ ([bad : Integer])])))]
@subsection{Contracts and Unit Static Information}
Unit values that flow between typed and untyped contexts are wrapped in
@racket[unit/c] contracts to guard the unit's imports, exports, and result upon
invocation. When identifers that are additionally bound to static information
about a unit, such as those defined by @racket[define-unit], flow between typed
and untyped contexts contract application can result the static information
becoming inaccessible.
@ex[
(module UNTYPED racket
(provide u@)
(define-unit u@ (import) (export) "Hello!"))
(eval:error
(module TYPED typed/racket
(require/typed 'UNTYPED
[u@ (Unit (import) (export) String)])
(invoke-unit/infer u@)))]
When an identifier bound to static unit information flows from a typed module to
an untyped module, however, the situation is worse. Because unit static
information is bound to an identifier as a macro definition, any use of the
typed unit is disallowed in untyped contexts.
@ex[
(module TYPED typed/racket
(provide u@)
(define-unit u@ (import) (export) "Hello!"))
(eval:error
(module UNTYPED racket
(require 'TYPED)
u@))]
@subsection{Signatures and Internal Definition Contexts}
Typed Racket's @racket[define-signature] form is allowed in both top-level and
internal definition contexts. As the following example shows, defining
signatures in internal definiition contexts can be problematic.
@ex[
(eval:error
(module TYPED typed/racket
(define-signature a^ ())
(define u@
(let ()
(define-signature a^ ())
(unit (import a^) (export) (init-depend a^) 5)))
(invoke-unit u@ (import a^))))]
Even though the unit imports a signature named @racket[a^], the @racket[a^]
provided for the import refers to the top-level @racket[a^] signature and the
type system prevents invoking the unit. This issue can be avoided by defining
signatures only at the top-level of a module.
@subsection{Tagged Signatures}
Various unit forms in Racket allow for signatures to be tagged to support the
definition of units that import or export the same signature multiple times.
Typed Racket does not support the use of tagged signatures, using the
@racket[tag] keyword, anywhere in the various unit forms described above.
@subsection{Structural Matching and Other Unit Forms}
Typed Racket supports only those unit forms described above. All other bindings
exported by @racketmodname[racket/unit] are not supported in the type system. In
particular, the structural matching forms including @ut:unit/new-import-export
and @ut:unit/s are unsupported.

View File

@ -2,7 +2,7 @@
@begin[(require "../utils.rkt"
"numeric-tower-pict.rkt"
scribble/eval
scribble/example
racket/sandbox)
(require (for-label (only-meta-in 0 [except-in typed/racket for])
racket/async-channel))]
@ -21,6 +21,8 @@
@defidform[Any]{Any Racket value. All other types are subtypes of @racket[Any].}
@defidform[AnyValues]{Any number of Racket values of any type.}
@defidform[Nothing]{The empty type. No values inhabit this type, and
any expression of this type will not evaluate to a value.}
@ -370,7 +372,10 @@ corresponding to @racket[trest], where @racket[bound]
@defidform[FlVector]{An @rtech{flvector}.
@ex[(flvector 1.0 2.0 3.0)]}
@defidform[ExtFlVector]{An @rtech{extflvector}.
@ex[(extflvector 1.0t0 2.0t0 3.0t0)]}
@ex[(eval:alts (extflvector 1.0t0 2.0t0 3.0t0)
(eval:result @racketresultfont{#<extflvector>}
"- : ExtFlVector"
""))]}
@defidform[FxVector]{An @rtech{fxvector}.
@ex[(fxvector 1 2 3)]}
@ -397,8 +402,11 @@ corresponding to @racket[trest], where @racket[bound]
@ex[(lambda: ([x : Any]) (if (hash? x) x (error "not a hash table!")))]
}
@defform[(Setof t)]{is the type of a @rtech{set} of @racket[t].
@defform[(Setof t)]{is the type of a @rtech{hash set} of
@racket[t]. This includes custom hash sets, but not mutable hash set
or sets that are implemented using @racket[gen:set].
@ex[(set 0 1 2 3)]
@ex[(seteq 0 1 2 3)]
}
@defform[(Channelof t)]{A @rtech{channel} on which only @racket[t]s can be sent.
@ -560,29 +568,33 @@ functions and continuation mark functions.
@section{Other Type Constructors}
@defform*/subs[#:id -> #:literals (|@| * ... ! and or implies car cdr)
[(-> dom ... rng optional-filter)
[(-> dom ... rng opt-proposition)
(-> dom ... rest * rng)
(-> dom ... rest ooo bound rng)
(dom ... -> rng optional-filter)
(dom ... -> rng opt-proposition)
(dom ... rest * -> rng)
(dom ... rest ooo bound -> rng)]
([ooo #,(racket ...)]
[dom type
mandatory-kw
optional-kw]
opt-kw]
[mandatory-kw (code:line keyword type)]
[optional-kw [keyword type]]
[optional-filter (code:line)
[opt-kw [keyword type]]
[opt-proposition (code:line)
(code:line : type)
(code:line : pos-filter neg-filter object)]
[pos-filter (code:line)
(code:line #:+ proposition ...)]
[neg-filter (code:line)
(code:line #:- proposition ...)]
(code:line : pos-proposition
neg-proposition
object)]
[pos-proposition (code:line)
(code:line #:+ proposition ...)]
[neg-proposition (code:line)
(code:line #:- proposition ...)]
[object (code:line)
(code:line #:object index)]
[proposition type
[proposition Top
Bot
type
(! type)
(type |@| path-elem ... index)
(! type |@| path-elem ... index)
@ -596,15 +608,15 @@ functions and continuation mark functions.
The type of functions from the (possibly-empty)
sequence @racket[dom ....] to the @racket[rng] type.
@ex[(λ: ([x : Number]) x)
: () 'hello)]
@ex[(λ ([x : Number]) x)
(λ () 'hello)]
The second form specifies a uniform rest argument of type @racket[rest], and the
third form specifies a non-uniform rest argument of type
@racket[rest] with bound @racket[bound]. The bound refers to the type variable
that is in scope within the rest argument type.
@ex[(λ: ([x : Number] . [y : String *]) (length y))
@ex[(λ ([x : Number] . [y : String *]) (length y))
ormap]
In the third form, the @racket[...] introduced by @racket[ooo] is literal,
@ -621,20 +633,24 @@ functions and continuation mark functions.
(is-zero? 2 #:equality =)
(is-zero? 2 #:equality eq? #:zero 2.0)]
When @racket[optional-filter] is provided, it specifies the @emph{filter} for the
function type (for an introduction to filters, see @tr-guide-secref["filters-and-predicates"]).
For almost all use cases, only the simplest form of filters, with a single type after a
When @racket[opt-proposition] is provided, it specifies the
@emph{proposition} for the function type (for an introduction to
propositions in Typed Racket, see
@tr-guide-secref["propositions-and-predicates"]). For almost all use
cases, only the simplest form of propositions, with a single type after a
@racket[:], are necessary:
@ex[string?]
The filter specifies that when @racket[(string? x)] evaluates to a true value for
a conditional branch, the variable @racket[x] in that branch can be assumed to have
type @racket[String]. Likewise, if the expression evaluates to @racket[#f] in a branch,
the variable @emph{does not} have type @racket[String].
The proposition specifies that when @racket[(string? x)] evaluates to a
true value for a conditional branch, the variable @racket[x] in that
branch can be assumed to have type @racket[String]. Likewise, if the
expression evaluates to @racket[#f] in a branch, the variable
@emph{does not} have type @racket[String].
In some cases, asymmetric type information is useful in filters. For example, the
@racket[filter] function's first argument is specified with only a positive filter:
In some cases, asymmetric type information is useful in the
propositions. For example, the @racket[filter] function's first
argument is specified with only a positive proposition:
@ex[filter]
@ -645,7 +661,7 @@ functions and continuation mark functions.
Conversely, @racket[#:-] specifies that a function provides information for the
false branch of a conditional.
The other filter proposition cases are rarely needed, but the grammar documents them
The other proposition cases are rarely needed, but the grammar documents them
for completeness. They correspond to logical operations on the propositions.
The type of functions can also be specified with an @emph{infix} @racket[->]
@ -687,7 +703,7 @@ functions and continuation mark functions.
@ex[(: +all (->* (Integer) #:rest Integer (Listof Integer)))
(define (+all inc . rst)
(map (λ: ([x : Integer]) (+ x inc)) rst))
(map (λ ([x : Integer]) (+ x inc)) rst))
(+all 20 1 2 3)]
Both the mandatory and optional argument lists may contain keywords paired
@ -702,9 +718,9 @@ functions and continuation mark functions.
@deftogether[(
@defidform[Top]
@defidform[Bot])]{ These are filters that can be used with @racket[->].
@racket[Top] is the filter with no information.
@racket[Bot] is the filter which means the result cannot happen.
@defidform[Bot])]{ These are propositions that can be used with @racket[->].
@racket[Top] is the propositions with no information.
@racket[Bot] is the propositions which means the result cannot happen.
}
@ -720,13 +736,17 @@ functions and continuation mark functions.
@ex[
(: my-list Procedure)
(define my-list list)
(my-list "zwiebelkuchen" "socca")
(eval:error (my-list "zwiebelkuchen" "socca"))
]
}
@defform[(U t ...)]{is the union of the types @racket[t ...].
@ex[(λ: ([x : Real])(if (> 0 x) "yes" 'no))]}
@ex[(λ ([x : Real]) (if (> 0 x) "yes" 'no))]}
@defform[(∩ t ...)]{is the intersection of the types @racket[t ...].
@ex[((λ #:forall (A) ([x : (∩ Symbol A)]) x) 'foo)]}
@defform[(case-> fun-ty ...)]{is a function that behaves like all of
the @racket[fun-ty]s, considered in order from first to last. The @racket[fun-ty]s must all be function
types constructed with @racket[->].

View File

@ -0,0 +1,75 @@
#lang scribble/manual
@(require scribble/example
(for-label (only-meta-in 0 [except-in typed/racket for])))
@(define eval (make-base-eval))
@(eval '(require typed/racket/base))
@title{Unsafe Typed Racket operations}
@defmodule[typed/racket/unsafe]
@bold{Warning}: the operations documented in this section are @emph{unsafe},
meaning that they can circumvent the invariants of the type system. Unless the
@racket[#:no-optimize] language option is used, this may result in unpredictable
behavior and may even crash Typed Racket.
@defform[(unsafe-require/typed m rt-clause ...)]{
This form requires identifiers from the module @racket[m] with the same
import specifications as @racket[require/typed].
Unlike @racket[require/typed], this form is unsafe and will not generate
contracts that correspond to the specified types to check that the values
actually match their types.
@examples[#:eval eval
(require typed/racket/unsafe)
(code:comment "import with a bad type")
(unsafe-require/typed racket/base [values (-> String Integer)])
(code:comment "unchecked call, the result type is wrong")
(values "foo")
]
@history[#:added "1.3"]
}
@defform[(unsafe-provide provide-spec ...)]{
This form declares exports from a module with the same syntax as
the @racket[provide] form.
Unlike @racket[provide], this form is unsafe and Typed Racket will not generate
any contracts that correspond to the specified types. This means that uses of the
exports in other modules may circumvent the type system's invariants.
Additionally, importing an identififer that is exported with
@racket[unsafe-provide] into another typed module, and then
re-exporting it with @racket[provide] will not cause contracts to be
generated.
Uses of the provided identifiers in other typed modules are not
affected by @racket[unsafe-provide]---in these situations it behaves
identically to @racket[provide]. Furthermore, other typed modules
that @emph{use} a binding that is in an @racket[unsafe-provide] will
still have contracts generated as usual.
@examples[#:eval eval
(module t typed/racket/base
(require typed/racket/unsafe)
(: f (-> Integer Integer))
(define (f x) (add1 x))
(code:comment "unsafe export, does not install checks")
(unsafe-provide f))
(module u racket/base
(require 't)
(code:comment "bad call that's unchecked")
(f "foo"))
(eval:error (require 'u))
]
@history[#:added "1.3"]
}
@close-eval[eval]

View File

@ -1,6 +1,6 @@
#lang scribble/manual
@begin[(require "../utils.rkt" scribble/eval racket/sandbox)
@begin[(require "../utils.rkt" scribble/example racket/sandbox)
(require (for-label (only-meta-in 0 [except-in typed/racket for])
typed/untyped-utils))]
@ -30,7 +30,7 @@ x
(define: y : (U String Symbol) "hello")
y
(assert y string?)
(assert y boolean?)]
(eval:error (assert y boolean?))]
@defform*/subs[[(with-asserts ([id maybe-pred] ...) body ...+)]
([maybe-pred code:blank
@ -64,10 +64,11 @@ the error message.
#`(cond clause ... [else (typecheck-fail #,stx "incomplete coverage"
#:covered-id x)])]))
(define: (f [x : (U String Integer)]) : Boolean
(cond* x
[(string? x) #t]
[(exact-nonnegative-integer? x) #f]))
(eval:error
(define: (f [x : (U String Integer)]) : Boolean
(cond* x
[(string? x) #t]
[(exact-nonnegative-integer? x) #f])))
]
}

View File

@ -29,11 +29,13 @@ For a friendly introduction, see the companion manual
@include-section["reference/special-forms.scrbl"]
@include-section["reference/libraries.scrbl"]
@include-section["reference/typed-classes.scrbl"]
@include-section["reference/typed-units.scrbl"]
@include-section["reference/utilities.scrbl"]
@include-section["reference/exploring-types.scrbl"]
@include-section["reference/no-check.scrbl"]
@include-section["reference/typed-regions.scrbl"]
@include-section["reference/optimization.scrbl"]
@include-section["reference/unsafe.scrbl"]
@include-section["reference/legacy.scrbl"]
@include-section["reference/compatibility-languages.scrbl"]
@include-section["reference/experimental.scrbl"]

View File

@ -2,20 +2,14 @@
(define collection 'multi)
(define deps '(("base" #:version "6.2.900.6")
(define deps '(("base" #:version "6.4.0.5")
"pconvert-lib"
"unstable-contract-lib"
"source-syntax"
"compatibility-lib" ;; to assign types
"string-constants-lib"))
;; This is needed since the expansion of TR
;; can insert `(require unstable/contract)` into
;; the expanded code.
(define implies '("unstable-contract-lib"))
(define pkg-desc "implementation (no documentation) part of \"typed-racket\"")
(define pkg-authors '(samth stamourv))
(define version "1.1")
(define version "1.5")

View File

@ -1,5 +1,24 @@
(add1 6.2)
6.5
- Added `simple-result->` to improve generated contract performance.
- Improve error message printing.
- Add `typed/racket/random`.
- Internal: populate type table unconditionally, for use in tooltips.
6.4
- Contract performance improvements, including generating code that
the contract system can optimize
- Make `any-wrap/c` more permissive on opaque structs.
- Soundly check opaque predicates.
- Add `#:type-name` option to `struct`.
6.3
- Startup time reduction
- Tightening and cleanup of numeric types
- Sealing contracts for row polymorphic types
- `define-new-subtype`
- More robust compound pair operations optimizations
- Redesign of top-level support, using trampolining macros
- Static contract caching more conservative, causes contract generation slowdowns
- Experimental unit support
- `typed/racket/unsafe`, with `unsafe-require/typed` and `unsafe-provide`
6.2
- Use submodules to avoid allocating contract wrappers when not needed.
- Class types and contract generation are significantly improved, but still experimental.

File diff suppressed because it is too large Load Diff

View File

@ -17,7 +17,7 @@
racket/logging
racket/private/stx
(only-in mzscheme make-namespace)
(only-in racket/match/runtime match:error matchable? match-equality-test))
(only-in racket/match/runtime match:error matchable? match-equality-test syntax-srclocs))
"base-structs.rkt"
racket/file
(only-in racket/private/pre-base new-apply-proc)
@ -25,6 +25,7 @@
(only-in (types numeric-tower) [-Number N])
(only-in (rep type-rep)
make-ClassTop
make-UnitTop
make-Name
make-ValuesDots
make-MPairTop
@ -65,8 +66,8 @@
;; Section 4.2.2.7 (Random Numbers)
[random
(cl->* (->opt -PosFixnum [-Pseudo-Random-Generator] -NonNegFixnum)
(->opt -Int [-Pseudo-Random-Generator] -Nat)
(cl->* (->opt -Int -Int [-Pseudo-Random-Generator] -NonNegFixnum)
(->opt -Int [-Pseudo-Random-Generator] -NonNegFixnum)
(->opt [-Pseudo-Random-Generator] -Flonum))]
[random-seed (-> -PosInt -Void)]
@ -176,6 +177,11 @@
#:repeat? Univ #f
-String)]
[non-empty-string? (make-pred-ty -String)]
[string-contains? (-> -String -String -Boolean)]
[string-prefix? (-> -String -String -Boolean)]
[string-suffix? (-> -String -String -Boolean)]
;; Section 4.3.6 (racket/format)
[~a (->optkey []
#:rest Univ
@ -631,15 +637,18 @@
[memq (-poly (a) (-> Univ (-lst a) (-opt (-ne-lst a))))]
[memv (-poly (a) (-> Univ (-lst a) (-opt (-ne-lst a))))]
[memf (-poly (a) ((a . -> . Univ) (-lst a) . -> . (-opt (-ne-lst a))))]
[member (-poly (a)
[member (-poly (a b)
(cl->* (Univ (-lst a) . -> . (-opt (-ne-lst a)))
(Univ (-lst a) (-> a a Univ)
(b (-lst a) (-> b a Univ)
. -> . (-opt (-ne-lst a)))))]
[findf (-poly (a) ((a . -> . B) (-lst a) . -> . (-opt a)))]
[assq (-poly (a b) (Univ (-lst (-pair a b)) . -> . (-opt (-pair a b))))]
[assv (-poly (a b) (Univ (-lst (-pair a b)) . -> . (-opt (-pair a b))))]
[assoc (-poly (a b) (Univ (-lst (-pair a b)) . -> . (-opt (-pair a b))))]
[assoc (-poly (a b c)
(cl->* (Univ (-lst (-pair a b)) . -> . (-opt (-pair a b)))
(c (-lst (-pair a b)) (-> c a Univ)
. -> . (-opt (-pair a b)))))]
[assf (-poly (a b) ((a . -> . Univ) (-lst (-pair a b))
. -> . (-opt (-pair a b))))]
@ -668,7 +677,7 @@
[((a b c . -> . c) c (-lst a) (-lst b)) c]
[((a b c d . -> . d) d (-lst a) (-lst b) (-lst c)) d]))]
[filter (-poly (a b) (cl->*
((asym-pred a Univ (-FS (-filter b 0) -top))
((asym-pred a Univ (-PS (-is-type 0 b) -tt))
(-lst a)
. -> .
(-lst b))
@ -708,7 +717,7 @@
-Index))]
[partition
(-poly (a b) (cl->*
(-> (asym-pred b Univ (-FS (-filter a 0) -top)) (-lst b) (-values (list (-lst a) (-lst b))))
(-> (asym-pred b Univ (-PS (-is-type 0 a) -tt)) (-lst b) (-values (list (-lst a) (-lst b))))
(-> (-> a Univ) (-lst a) (-values (list (-lst a) (-lst a))))))]
[last (-poly (a) ((-lst a) . -> . a))]
@ -726,7 +735,7 @@
(-poly (a b)
(cl->*
(-> (-lst a)
(asym-pred a Univ (-FS (-filter b 0) -top))
(asym-pred a Univ (-PS (-is-type 0 b) -tt))
(-lst b))
(-> (-lst a) (-> a Univ) (-lst a))))]
[dropf (-poly (a) (-> (-lst a) (-> a Univ) (-lst a)))]
@ -734,14 +743,14 @@
(-poly (a b)
(cl->*
(-> (-lst a)
(asym-pred a Univ (-FS (-filter b 0) -top))
(asym-pred a Univ (-PS (-is-type 0 b) -tt))
(-values (list (-lst b) (-lst a))))
(-> (-lst a) (-> a Univ) (-values (list (-lst a) (-lst a))))))]
[takef-right
(-poly (a b)
(cl->*
(-> (-lst a)
(asym-pred a Univ (-FS (-filter b 0) -top))
(asym-pred a Univ (-PS (-is-type 0 b) -tt))
(-lst b))
(-> (-lst a) (-> a Univ) (-lst a))))]
[dropf-right (-poly (a) (-> (-lst a) (-> a Univ) (-lst a)))]
@ -749,7 +758,7 @@
(-poly (a b)
(cl->*
(-> (-lst a)
(asym-pred a Univ (-FS (-filter b 0) -top))
(asym-pred a Univ (-PS (-is-type 0 b) -tt))
(-values (list (-lst a) (-lst b))))
(-> (-lst a) (-> a Univ) (-values (list (-lst a) (-lst a))))))]
@ -767,6 +776,14 @@
((-lst b) b) . ->... .(-lst c)))]
[append*
(-poly (a) ((-lst (-lst a)) . -> . (-lst a)))]
[flatten
(Univ . -> . (-lst Univ))]
[combinations (-poly (a) (cl->*
(-> (-lst a) (-lst (-lst a)))
(-> (-lst a) -Nat (-lst (-lst a)))))]
[in-combinations (-poly (a) (cl->*
(-> (-lst a) (-seq (-lst a)))
(-> (-lst a) -Nat (-seq (-lst a)))))]
[permutations (-poly (a) (-> (-lst a) (-lst (-lst a))))]
[in-permutations (-poly (a) (-> (-lst a) (-seq (-lst a))))]
[argmin (-poly (a) ((a . -> . -Real) (-lst a) . -> . a))]
@ -836,7 +853,7 @@
. ->... .
-Index))]
[vector-filter (-poly (a b) (cl->*
((asym-pred a Univ (-FS (-filter b 0) -top))
((asym-pred a Univ (-PS (-is-type 0 b) -tt))
(-vec a)
. -> .
(-vec b))
@ -870,6 +887,7 @@
((-box a) . -> . a)
((make-BoxTop) . -> . Univ)))]
[set-box! (-poly (a) ((-box a) a . -> . -Void))]
[box-cas! (-poly (a) ((-box a) a a . -> . -Boolean))]
[unsafe-unbox (-poly (a) (cl->*
((-box a) . -> . a)
((make-BoxTop) . -> . Univ)))]
@ -878,6 +896,7 @@
((-box a) . -> . a)
((make-BoxTop) . -> . Univ)))]
[unsafe-set-box*! (-poly (a) ((-box a) a . -> . -Void))]
[unsafe-box*-cas! (-poly (a) ((-box a) a a . -> . -Boolean))]
[box? (make-pred-ty (make-BoxTop))]
;; Section 4.13 (Hash Tables)
@ -951,13 +970,25 @@
[equal-hash-code (-> Univ -Fixnum)]
[equal-secondary-hash-code (-> Univ -Fixnum)]
[hash-iterate-first (-poly (a b)
((-HT a b) . -> . (Un (-val #f) -Integer)))]
(cl->*
((-HT a b) . -> . (Un (-val #f) -Integer))
(-> -HashTop (Un (-val #f) -Integer))))]
[hash-iterate-next (-poly (a b)
((-HT a b) -Integer . -> . (Un (-val #f) -Integer)))]
(cl->*
((-HT a b) -Integer . -> . (Un (-val #f) -Integer))
(-> -HashTop -Integer (Un (-val #f) -Integer))))]
[hash-iterate-key (-poly (a b)
((-HT a b) -Integer . -> . a))]
(cl->* ((-HT a b) -Integer . -> . a)
(-> -HashTop -Integer Univ)))]
[hash-iterate-value (-poly (a b)
((-HT a b) -Integer . -> . b))]
(cl->* ((-HT a b) -Integer . -> . b)
(-> -HashTop -Integer Univ)))]
[hash-iterate-pair (-poly (a b)
(cl->* ((-HT a b) -Integer . -> . (-pair a b))
(-> -HashTop -Integer Univ)))]
[hash-iterate-key+value (-poly (a b)
(cl->* ((-HT a b) -Integer . -> . (-values (list a b)))
(-> -HashTop -Integer (-values (list Univ Univ)))))]
[make-custom-hash (->opt (-> Univ Univ Univ) (-> Univ -Nat) [(-> Univ -Nat)] Univ)]
[make-immutable-custom-hash (->opt (-> Univ Univ Univ) (-> Univ -Nat) [(-> Univ -Nat)] Univ)]
@ -1010,7 +1041,7 @@
[sequence-fold (-poly (a b) ((b a . -> . b) b (-seq a) . -> . b))]
[sequence-count (-poly (a) ((a . -> . Univ) (-seq a) . -> . -Nat))]
[sequence-filter (-poly (a b) (cl->*
((asym-pred a Univ (-FS (-filter b 0) -top))
((asym-pred a Univ (-PS (-is-type 0 b) -tt))
(-seq a)
. -> .
(-seq b))
@ -1040,7 +1071,7 @@
[proper-subset? (-poly (e) (-> (-set e) (-set e) B))]
[set-map (-poly (e b) (-> (-set e) (-> e b) (-lst b)))]
[set-for-each (-poly (e b) (-> (-set e) (-> e b) -Void))]
[generic-set? (asym-pred Univ B (-FS -top (-not-filter (-set Univ) 0)))]
[generic-set? (asym-pred Univ B (-PS -tt (-not-type 0 (-set Univ))))]
[set? (make-pred-ty (-set Univ))]
[set-equal? (-poly (e) (-> (-set e) B))]
[set-eqv? (-poly (e) (-> (-set e) B))]
@ -1079,14 +1110,14 @@
[identity (-poly (a) (->acc (list a) a null))]
[const (-poly (a) (-> a (->* '() Univ a)))]
[negate (-polydots (a b c d)
(cl->* (-> (-> c Univ : (-FS (-filter a 0) (-not-filter b 0)))
(-> c -Boolean : (-FS (-not-filter b 0) (-filter a 0))))
(-> (-> c Univ : (-FS (-filter a 0) (-filter b 0)))
(-> c -Boolean : (-FS (-filter b 0) (-filter a 0))))
(-> (-> c Univ : (-FS (-not-filter a 0) (-filter b 0)))
(-> c -Boolean : (-FS (-filter b 0) (-not-filter a 0))))
(-> (-> c Univ : (-FS (-not-filter a 0) (-not-filter b 0)))
(-> c -Boolean : (-FS (-not-filter b 0) (-not-filter a 0))))
(cl->* (-> (-> c Univ : (-PS (-is-type 0 a) (-not-type 0 b)))
(-> c -Boolean : (-PS (-not-type 0 b) (-is-type 0 a))))
(-> (-> c Univ : (-PS (-is-type 0 a) (-is-type 0 b)))
(-> c -Boolean : (-PS (-is-type 0 b) (-is-type 0 a))))
(-> (-> c Univ : (-PS (-not-type 0 a) (-is-type 0 b)))
(-> c -Boolean : (-PS (-is-type 0 b) (-not-type 0 a))))
(-> (-> c Univ : (-PS (-not-type 0 a) (-not-type 0 b)))
(-> c -Boolean : (-PS (-not-type 0 b) (-not-type 0 a))))
(-> ((list) [d d] . ->... . Univ)
((list) [d d] . ->... . -Boolean))))]
[conjoin (-polydots (a) (->* '() (->... '() (a a) Univ) (->... '() (a a) Univ)))]
@ -1159,6 +1190,9 @@
[object-info (-> (-object) (-values (list (Un (make-ClassTop) (-val #f)) -Boolean)))]
;; TODO: class-info (is this sound to allow?)
;; Section 7.8 (Unit Utilities)
[unit? (make-pred-ty (make-UnitTop))]
;; Section 9.1
[exn:misc:match? (-> Univ B)]
;; this is a hack
@ -1166,6 +1200,7 @@
;[match:error (Univ . -> . (Un))]
[match-equality-test (-Param (Univ Univ . -> . Univ) (Univ Univ . -> . Univ))]
[matchable? (make-pred-ty (Un -String -Bytes))]
[syntax-srclocs (Univ . -> . Univ)]
;; Section 10.1
[values (-polydots (a b) (cl->*
@ -1268,7 +1303,7 @@
[call-with-continuation-barrier (-poly (a) (-> (-> a) a))]
[continuation-prompt-available? (-> (make-Prompt-TagTop) B)]
[continuation?
(asym-pred Univ B (-FS (-filter top-func 0) -top))]
(asym-pred Univ B (-PS (-is-type 0 top-func) -tt))]
[continuation-prompt-tag? (make-pred-ty (make-Prompt-TagTop))]
[dynamic-wind (-poly (a) (-> (-> ManyUniv) (-> a) (-> ManyUniv) a))]
@ -1389,7 +1424,7 @@
[never-evt (-evt (Un))]
[system-idle-evt (-> (-evt -Void))]
[alarm-evt (-> -Real (-mu x (-evt x)))]
[handle-evt? (asym-pred Univ B (-FS (-filter (-evt Univ) 0) -top))]
[handle-evt? (asym-pred Univ B (-PS (-is-type 0 (-evt Univ)) -tt))]
[current-evt-pseudo-random-generator
(-Param -Pseudo-Random-Generator -Pseudo-Random-Generator)]
@ -1400,7 +1435,7 @@
[channel-try-get (-poly (a) ((-channel a) . -> . (Un a (-val #f))))]
[channel-put (-poly (a) ((-channel a) a . -> . -Void))]
[channel-put-evt (-poly (a) (-> (-channel a) a (-mu x (-evt x))))]
[channel-put-evt? (asym-pred Univ B (-FS (-filter (-mu x (-evt x)) 0) -top))]
[channel-put-evt? (asym-pred Univ B (-PS (-is-type 0 (-mu x (-evt x))) -tt))]
;; Section 11.2.3 (Semaphores)
[semaphore? (make-pred-ty -Semaphore)]
@ -1410,7 +1445,7 @@
[semaphore-try-wait? (-> -Semaphore B)]
[semaphore-wait/enable-break (-> -Semaphore -Void)]
[semaphore-peek-evt (-> -Semaphore (-mu x (-evt x)))]
[semaphore-peek-evt? (asym-pred Univ B (-FS (-filter (-mu x (-evt x)) 0) -top))]
[semaphore-peek-evt? (asym-pred Univ B (-PS (-is-type 0 (-mu x (-evt x))) -tt))]
[call-with-semaphore
(-polydots (b a)
(cl->* (->... (list -Semaphore (->... '() [a a] b))
@ -1506,7 +1541,10 @@
[syntax-original? (-poly (a) (-> (-Syntax a) B))]
[syntax-source-module (->opt (-Syntax Univ) [Univ] (Un (-val #f) -Path Sym -Module-Path-Index))]
[syntax-e (-poly (a) (->acc (list (-Syntax a)) a (list -syntax-e)))]
[syntax->list (-poly (a) (-> (-Syntax (-lst a)) (-lst a)))]
[syntax->list (-poly (a)
(cl->* (-> (-Syntax (-lst a)) (-lst a))
(-> (-Syntax Univ)
(Un (-val #f) (-lst (-Syntax Univ))))))]
[syntax->datum (cl->* (-> Any-Syntax -Sexp)
(-> (-Syntax Univ) Univ))]
@ -1592,6 +1630,8 @@
-Nat
(-opt -Integer)
(-opt -Integer))))]
[identifier-binding-symbol
(Ident . ->opt . [(Un -Int (-val #f))] -Symbol)]
;; Section 12.4
[set!-transformer? (-> Univ B)]
@ -1668,6 +1708,7 @@
[syntax-local-lift-expression (-> (-Syntax Univ) (-Syntax Sym))]
[syntax-local-lift-values-expression (-> -Nat (-Syntax Univ) (-lst (-Syntax Sym)))]
[syntax-local-lift-context (-> Univ)]
[syntax-local-lift-module (-> (-Syntax Univ) -Void)]
[syntax-local-lift-module-end-declaration (-> (-Syntax Univ) -Void)]
[syntax-local-lift-require (-poly (a) (-> Univ (-Syntax a) (-Syntax a)))]
[syntax-local-lift-provide (-> Univ -Void)]
@ -1675,14 +1716,20 @@
[syntax-local-context (-> (Un (-val 'expression) (-val 'top-level) (-val 'module) (-val 'module-begin) (-lst Univ)))]
[syntax-local-phase-level (-> -Int)]
[syntax-local-module-exports (-> -Module-Path (-values (list (-lst Sym) (-lst Sym) (-lst Sym))))]
[syntax-local-get-shadower (-> (-Syntax Sym) (-Syntax Sym))]
[syntax-local-submodules (-> (-lst -Symbol))]
[syntax-local-get-shadower (->opt (-Syntax Sym) [Univ] (-Syntax Sym))]
[syntax-local-certifier (->opt [B] (-poly (a) (->opt (-Syntax a) [Univ (-opt (-poly (b) (-> (-Syntax b) (-Syntax b))))] (-Syntax a))))]
[syntax-transforming? (-> B)]
[syntax-transforming-module-expression? (-> B)]
[syntax-local-identifier-as-binding (-> (-Syntax -Symbol) (-Syntax -Symbol))]
[syntax-local-introduce (-poly (a) (-> (-Syntax a) (-Syntax a)))]
[make-syntax-introducer (-> (-poly (a) (-> (-Syntax a) (-Syntax a))))]
[make-syntax-delta-introducer (->opt (-Syntax Univ) [(-opt (-Syntax Univ)) (-opt -Int)] (-poly (a) (-> (-Syntax a) (-Syntax a))))]
[make-syntax-introducer
(-> (-poly (a) (->opt (-Syntax a) [(one-of/c 'flip 'add 'remove)] (-Syntax a))))]
[make-syntax-delta-introducer
(->opt (-Syntax Univ) (-opt (-Syntax Univ))
[(-opt -Int)]
(-poly (a) (->opt (-Syntax a) [(one-of/c 'flip 'add 'remove)] (-Syntax a))))]
[syntax-local-transforming-module-provides? (-> B)]
[syntax-local-module-defined-identifiers (-> (-HT (Un (-val #f) -Int) (-lst (-Syntax Sym))))]
@ -1743,7 +1790,7 @@
[file-stream-buffer-mode (cl-> [(-Port) (one-of/c 'none 'line 'block #f)]
[(-Port (one-of/c 'none 'line 'block)) -Void])]
[file-position (cl-> [(-Port) -Nat]
[(-Port -Integer) -Void])]
[(-Port (Un -Integer (-val eof))) -Void])]
[file-position* (-> -Port (Un -Nat (-val #f)))]
;; Section 13.1.4
@ -1802,8 +1849,8 @@
[port-file-identity (-> (Un -Input-Port -Output-Port) -PosInt)]
;; Section 13.1.6
[open-input-string (-> -String -Input-Port)]
[open-input-bytes (-> -Bytes -Input-Port)]
[open-input-string (->opt -String [Univ] -Input-Port)]
[open-input-bytes (->opt -Bytes [Univ] -Input-Port)]
[open-output-string
([Univ] . ->opt . -Output-Port)]
[open-output-bytes
@ -1816,7 +1863,7 @@
;; Section 13.1.7
[make-pipe
(cl->* [->opt [N] (-values (list -Input-Port -Output-Port))])]
(cl->* [->opt [N Univ Univ] (-values (list -Input-Port -Output-Port))])]
[pipe-content-length (-> (Un -Input-Port -Output-Port) -Nat)]
;; Section 13.1.8
@ -1918,8 +1965,10 @@
[make-pipe-with-specials (->opt [-Nat Univ Univ] (-values (list -Input-Port -Output-Port)))]
[merge-input (->opt -Input-Port -Input-Port [(-opt -Nat)] -Input-Port)]
[open-output-nowhere (-> -Output-Port)]
[peeking-input-port (->opt -Input-Port [Univ -Nat] -Input-Port)]
[open-output-nowhere (->opt [Univ Univ] -Output-Port)]
[peeking-input-port (->optkey -Input-Port [Univ -Nat]
#:init-position -Nat #f
-Input-Port)]
[reencode-input-port
(->opt -Input-Port -String (-opt -Bytes) [Univ Univ Univ (-> -String -Input-Port ManyUniv)] -Input-Port)]
@ -1927,7 +1976,7 @@
(->opt -Output-Port -String (-opt -Bytes) [Univ Univ (-opt -Bytes) (-> -String -Output-Port ManyUniv)] -Output-Port)]
[dup-input-port (-Input-Port (B) . ->opt . -Input-Port)]
[dup-output-port (-Output-Port (B) . ->opt . -Input-Port)]
[dup-output-port (-Output-Port (B) . ->opt . -Output-Port)]
[relocate-input-port (->opt -Input-Port (-opt -PosInt) (-opt -Nat) -PosInt [Univ] -Input-Port)]
[relocate-output-port (->opt -Output-Port (-opt -PosInt) (-opt -Nat) -PosInt [Univ] -Output-Port)]
@ -2061,7 +2110,9 @@
[write (Univ [-Output-Port] . ->opt . -Void)]
[display (Univ [-Output-Port] . ->opt . -Void)]
[print (Univ [-Output-Port (one-of/c 0 1)] . ->opt . -Void)]
[writeln (Univ [-Output-Port] . ->opt . -Void)]
[displayln (Univ [-Output-Port] . ->opt . -Void)]
[println (Univ [-Output-Port (one-of/c 0 1)] . ->opt . -Void)]
[fprintf (->* (list -Output-Port -String) Univ -Void)]
[printf (->* (list -String) Univ -Void)]
[eprintf (->* (list -String) Univ -Void)]
@ -2099,7 +2150,7 @@
[pretty-print (Univ [-Output-Port (one-of/c 0 1)] . ->opt . -Void)]
[pretty-write (Univ [-Output-Port] . ->opt . -Void)]
[pretty-display (Univ [-Output-Port] . ->opt . -Void)]
[pretty-format (Univ [-Nat] . ->opt . -String)]
[pretty-format (Univ [-Nat] #:mode -Symbol #f . ->optkey . -String)]
[pretty-print-handler (-> Univ -Void)]
[pretty-print-columns (-Param (Un -Nat (-val 'infinity)) (Un -Nat (-val 'infinity)))]
@ -2265,7 +2316,7 @@
[resolved-module-path? (make-pred-ty -Resolved-Module-Path)]
[make-resolved-module-path (-> (Un -Symbol -Path) -Resolved-Module-Path)]
[resolved-module-path-name (-> -Resolved-Module-Path (Un -Path -Symbol))]
[module-path? (asym-pred Univ B (-FS (-filter -Module-Path 0) -top))]
[module-path? (asym-pred Univ B (-PS (-is-type 0 -Module-Path) -tt))]
[current-module-name-resolver (-Param (cl->* (-Resolved-Module-Path Univ . -> . Univ)
((Un -Module-Path -Path)
@ -2449,8 +2500,8 @@
;; Section 15.1 (Path Manipulation)
[path? (make-pred-ty -Path)]
[path-string? (asym-pred Univ B
(-FS (-filter (Un -Path -String) 0)
(-not-filter -Path 0)))]
(-PS (-is-type 0 (Un -Path -String))
(-not-type 0 -Path)))]
[path-for-some-system? (make-pred-ty -SomeSystemPath)]
[string->path (-> -String -Path)]
@ -2515,6 +2566,16 @@
(Un -SomeSystemPath (one-of/c 'up 'same))
B))))]
[path-replace-extension
(cl->*
(-> -Pathlike (Un -String -Bytes) -Path)
(-> -SomeSystemPathlike (Un -String -Bytes) -SomeSystemPath))]
[path-add-extension
(cl->*
(-> -Pathlike (Un -String -Bytes) -Path)
(-> -SomeSystemPathlike (Un -String -Bytes) -SomeSystemPath))]
[path-replace-suffix
(cl->*
(-> -Pathlike (Un -String -Bytes) -Path)
@ -2589,8 +2650,14 @@
-Void)]
[delete-directory/files (->key -Pathlike #:must-exist? Univ #f -Void)]
[find-files (->optkey (-> -Path Univ) [(-opt -Pathlike)] #:follow-links? Univ #f (-lst -Path))]
[pathlist-closure (->key (-lst -Pathlike) #:follow-links? Univ #f (-lst -Path))]
[find-files (->optkey (-> -Path Univ) [(-opt -Pathlike)]
#:skip-filtered-directories? Univ #f
#:follow-links? Univ #f
(-lst -Path))]
[pathlist-closure (->key (-lst -Pathlike)
#:path-filter (Un (-val #f) (-Path . -> . Univ)) #f
#:follow-links? Univ #f
(-lst -Path))]
[fold-files
(-poly
@ -2634,10 +2701,10 @@
[tcp-abandon-port (-Port . -> . -Void)]
[tcp-addresses (cl->*
(-Port [(-val #f)] . ->opt . (-values (list -String -String)))
(-Port (-val #t) . -> . (-values (list -String -Index -String -Index))))]
((Un -TCP-Listener -Port) [(-val #f)] . ->opt . (-values (list -String -String)))
((Un -TCP-Listener -Port) (-val #t) . -> . (-values (list -String -Index -String -Index))))]
[tcp-port? (asym-pred Univ B (-FS (-filter (Un -Input-Port -Output-Port) 0) -top))]
[tcp-port? (asym-pred Univ B (-PS (-is-type 0 (Un -Input-Port -Output-Port)) -tt))]
;; Section 15.3.2 (racket/udp)
[udp-open-socket (->opt [(-opt -String) (-opt -String)] -UDP-Socket)]
@ -2903,6 +2970,7 @@
;; Section 15.8
[system-type
(cl->*
(-> (Un (-val 'unix) (-val 'windows) (-val 'macosx)))
(-> (-val 'os) (Un (-val 'unix) (-val 'windows) (-val 'macosx)))
(-> (-val 'gc) (Un (-val 'cgc) (-val '3m)))
(-> (-val 'link) (Un (-val 'static) (-val 'shared) (-val 'dll) (-val 'framework)))
@ -2952,7 +3020,9 @@
[will-try-execute (-> -Will-Executor ManyUniv)]
;; Section 16.4
[collect-garbage (-> -Void)]
[collect-garbage (cl->*
(-> -Void)
(-> (Un (-val 'minor) (-val 'major) (-val 'incremental)) -Void))]
[current-memory-use (-> -Nat)]
[dump-memory-stats (-> Univ)]
@ -2993,7 +3063,7 @@
[assert (-poly (a b) (cl->*
(Univ (make-pred-ty (list a) Univ b) . -> . b)
(-> (Un a (-val #f)) a)))]
[defined? (->* (list Univ) -Boolean : (-FS (-not-filter -Undefined 0) (-filter -Undefined 0)))]
[defined? (->* (list Univ) -Boolean : (-PS (-not-type 0 -Undefined) (-is-type 0 -Undefined)))]
;; Syntax Manual
;; Section 2.1 (syntax/stx)

View File

@ -13,6 +13,7 @@
(rename-in (types abbrev numeric-tower union) [make-arr* make-arr])
(for-syntax racket/base syntax/parse
(only-in racket/syntax syntax-local-eval)))
(provide make-template-identifier)
(define (make-template-identifier what where)
(let ([name (module-path-index-resolve (module-path-index-join where #f))])
@ -45,6 +46,10 @@
(cl->*
(-> (-lst a) -Null (-lst a))
(-> (-lst a) (-lst b) (-lst (Un a b)))))]
;; normalise-inputs
[(make-template-identifier 'normalise-inputs 'racket/private/for)
(-poly (a)
(-> -Symbol -String (-> a -Boolean) (-> a -Nat) a -Nat (Un (-val #f) -Nat) -Nat (-values (list a -Index -Index -Index))))]
;; make-sequence
[(make-template-identifier 'make-sequence 'racket/private/for)
(-poly (a b)
@ -111,25 +116,75 @@
[(make-template-identifier 'in-bytes 'racket/private/for)
(->opt -Bytes [-Int (-opt -Int) -Int] (-seq -Byte))]
;; in-hash and friends
[(make-template-identifier 'in-hash 'racket/private/for)
[(make-template-identifier 'default-in-hash 'racket/private/for)
(-poly (a b)
(cl-> [((-HT a b)) (-seq a b)]
[(-HashTop) (-seq Univ Univ)]))]
[(make-template-identifier 'in-hash-keys 'racket/private/for)
[(make-template-identifier 'default-in-hash-keys 'racket/private/for)
(-poly (a b)
(cl-> [((-HT a b)) (-seq a)]
[(-HashTop) (-seq Univ)]))]
[(make-template-identifier 'in-hash-values 'racket/private/for)
[(make-template-identifier 'default-in-hash-values 'racket/private/for)
(-poly (a b)
(cl-> [((-HT a b)) (-seq b)]
[(-HashTop) (-seq Univ)]))]
[(make-template-identifier 'in-hash-pairs 'racket/private/for)
[(make-template-identifier 'default-in-hash-pairs 'racket/private/for)
(-poly (a b)
(cl-> [((-HT a b)) (-seq (-pair a b))]
[(-HashTop) (-seq (-pair Univ Univ))]))]
[(make-template-identifier 'default-in-immutable-hash 'racket/private/for)
(-poly (a b)
(cl-> [((-HT a b)) (-seq a b)]
[(-HashTop) (-seq Univ Univ)]))]
[(make-template-identifier 'default-in-immutable-hash-keys 'racket/private/for)
(-poly (a b)
(cl-> [((-HT a b)) (-seq a)]
[(-HashTop) (-seq Univ)]))]
[(make-template-identifier 'default-in-immutable-hash-values 'racket/private/for)
(-poly (a b)
(cl-> [((-HT a b)) (-seq b)]
[(-HashTop) (-seq Univ)]))]
[(make-template-identifier 'default-in-immutable-hash-pairs 'racket/private/for)
(-poly (a b)
(cl-> [((-HT a b)) (-seq (-pair a b))]
[(-HashTop) (-seq (-pair Univ Univ))]))]
[(make-template-identifier 'default-in-mutable-hash 'racket/private/for)
(-poly (a b)
(cl-> [((-HT a b)) (-seq a b)]
[(-HashTop) (-seq Univ Univ)]))]
[(make-template-identifier 'default-in-mutable-hash-keys 'racket/private/for)
(-poly (a b)
(cl-> [((-HT a b)) (-seq a)]
[(-HashTop) (-seq Univ)]))]
[(make-template-identifier 'default-in-mutable-hash-values 'racket/private/for)
(-poly (a b)
(cl-> [((-HT a b)) (-seq b)]
[(-HashTop) (-seq Univ)]))]
[(make-template-identifier 'default-in-mutable-hash-pairs 'racket/private/for)
(-poly (a b)
(cl-> [((-HT a b)) (-seq (-pair a b))]
[(-HashTop) (-seq (-pair Univ Univ))]))]
[(make-template-identifier 'default-in-weak-hash 'racket/private/for)
(-poly (a b)
(cl-> [((-HT a b)) (-seq a b)]
[(-HashTop) (-seq Univ Univ)]))]
[(make-template-identifier 'default-in-weak-hash-keys 'racket/private/for)
(-poly (a b)
(cl-> [((-HT a b)) (-seq a)]
[(-HashTop) (-seq Univ)]))]
[(make-template-identifier 'default-in-weak-hash-values 'racket/private/for)
(-poly (a b)
(cl-> [((-HT a b)) (-seq b)]
[(-HashTop) (-seq Univ)]))]
[(make-template-identifier 'default-in-weak-hash-pairs 'racket/private/for)
(-poly (a b)
(cl-> [((-HT a b)) (-seq (-pair a b))]
[(-HashTop) (-seq (-pair Univ Univ))]))]
;; in-port
[(make-template-identifier 'in-port 'racket/private/for)
(->opt [(-> -Input-Port Univ) -Input-Port] (-seq Univ))]
(-poly (a)
(cl->* (-> (-seq Univ))
(->opt (-> -Input-Port (Un a (-val eof))) [-Input-Port] (-seq a))))]
;; in-input-port-bytes
[(make-template-identifier 'in-input-port-bytes 'racket/private/for)
(-> -Input-Port (-seq -Byte))]

View File

@ -70,7 +70,10 @@
([message : -String] [continuation-marks : -Cont-Mark-Set])
(define-hierarchy exn:break (#:kernel-maker k:exn:break)
([continuation : top-func]))
([continuation : top-func])
(define-hierarchy exn:break:hang-up (#:kernel-maker k:exn:break:hang-up) ())
(define-hierarchy exn:break:terminate (#:kernel-maker k:exn:break:terminate) ()))
(define-hierarchy exn:fail (#:kernel-maker k:exn:fail) ()
@ -81,7 +84,10 @@
(define-hierarchy exn:fail:contract:continuation (#:kernel-maker k:exn:fail:contract:continuation) ())
(define-hierarchy exn:fail:contract:variable (#:kernel-maker k:exn:fail:contract:variable) ()))
(define-hierarchy exn:fail:syntax (#:kernel-maker k:exn:fail:syntax) ([exprs : (-lst Any-Syntax)]))
(define-hierarchy exn:fail:syntax (#:kernel-maker k:exn:fail:syntax) ([exprs : (-lst Any-Syntax)])
(define-hierarchy exn:fail:syntax:unbound (#:kernel-maker k:exn:fail:syntax:unbound) ())
(define-hierarchy exn:fail:syntax:missing-module (#:kernel-maker k:exn:fail:syntax:missing-module)
([path : -Module-Path])))
(define-hierarchy exn:fail:read (#:kernel-maker k:exn:fail:read)
([srclocs : (-lst Univ)]) ;; cce: Univ here should be srcloc
@ -90,9 +96,15 @@
(define-hierarchy exn:fail:filesystem (#:kernel-maker k:exn:fail:filesystem) ()
(define-hierarchy exn:fail:filesystem:exists (#:kernel-maker k:exn:fail:filesystem:exists) ())
(define-hierarchy exn:fail:filesystem:version (#:kernel-maker k:exn:fail:filesystem:version) ()))
(define-hierarchy exn:fail:filesystem:version (#:kernel-maker k:exn:fail:filesystem:version) ())
(define-hierarchy exn:fail:filesystem:errno (#:kernel-maker k:exn:fail:filesystem:errno)
([errno : (-pair -Integer (one-of/c 'posix 'windows 'gai))]))
(define-hierarchy exn:fail:filesystem:missing-module (#:kernel-maker k:exn:fail:filesystem:missing-module)
([path : -Module-Path])))
(define-hierarchy exn:fail:network (#:kernel-maker k:exn:fail:network) ())
(define-hierarchy exn:fail:network (#:kernel-maker k:exn:fail:network) ()
(define-hierarchy exn:fail:network:errno (#:kernel-maker k:exn:fail:network:errno)
([errno : (-pair -Integer (one-of/c 'posix 'windows 'gai))])))
(define-hierarchy exn:fail:out-of-memory (#:kernel-maker k:exn:fail:out-of-memory) ())

View File

@ -7,7 +7,12 @@
[(_ nm ...)
#'(begin (define-syntax nm
(lambda (stx)
(raise-syntax-error 'type-check "type name used out of context"
(raise-syntax-error 'type-check
(format "type name used out of context\n type: ~a\n in: ~a"
(syntax->datum (if (stx-pair? stx)
(stx-car stx)
stx))
(syntax->datum stx))
stx
(and (stx-pair? stx) (stx-car stx)))))
...
@ -16,8 +21,9 @@
;; special type names that are not bound to particular types
(define-other-types
-> ->* case-> U Rec All Opaque Vector
Parameterof List List* Class Object Values Instance Refinement
pred Struct Struct-Type Prefab Top Bot)
Parameterof List List* Class Object Unit Values AnyValues Instance Refinement
pred Struct Struct-Type Prefab Top Bot Distinction Sequenceof
)
(provide (rename-out [All ]
[U Un]
@ -26,4 +32,3 @@
[List Tuple]
[Rec mu]
[Parameterof Parameter]))

View File

@ -126,6 +126,7 @@
[Continuation-Mark-KeyTop -Continuation-Mark-KeyTop]
[Struct-TypeTop (make-StructTypeTop)]
[ClassTop (make-ClassTop)]
[UnitTop (make-UnitTop)]
[Keyword -Keyword]
[Thread -Thread]
[Resolved-Module-Path -Resolved-Module-Path]
@ -186,7 +187,6 @@
[Pairof (-poly (a b) (-pair a b))]
[MPairof (-poly (a b) (-mpair a b))]
[MListof (-poly (a) (-mlst a))]
[Sequenceof (-poly (a) (-seq a))]
[Thread-Cellof (-poly (a) (-thread-cell a))]
[Custodian-Boxof (-poly (a) (make-CustodianBox a))]

View File

@ -282,7 +282,7 @@
[_ #f]))
;; clauses->names : (-> Clause Boolean) Listof<Clause> -> Listof<Id>
;; filter clauses by some property and spit out the names in those clauses
;; prop clauses by some property and spit out the names in those clauses
(define (clauses->names prop clauses [keep-pair? #f])
(apply append
(for/list ([clause (in-list clauses)]

View File

@ -5,7 +5,7 @@
(require (for-syntax racket/base syntax/parse)
(utils tc-utils)
(env init-envs)
(types abbrev numeric-tower union filter-ops))
(types abbrev numeric-tower union prop-ops))
(define-syntax (-#%module-begin stx)
(define-syntax-class clause
@ -29,4 +29,4 @@
require
(except-out (all-from-out racket/base) #%module-begin)
types rep private utils
(types-out abbrev numeric-tower union filter-ops))
(types-out abbrev numeric-tower union prop-ops))

View File

@ -11,7 +11,7 @@
(for-syntax racket/base
syntax/parse
syntax/stx)
(for-syntax (types abbrev numeric-tower union filter-ops)))
(for-syntax (types abbrev numeric-tower union prop-ops)))
(provide type-environment
(rename-out [-#%module-begin #%module-begin])
@ -20,7 +20,7 @@
(except-out (all-from-out racket/base) #%module-begin)
(for-syntax (except-out (all-from-out racket/base) #%module-begin))
types rep private utils
(for-syntax (types-out abbrev numeric-tower union filter-ops)))
(for-syntax (types-out abbrev numeric-tower union prop-ops)))
;; syntax classes for type clauses in the type-environment macro
(begin-for-syntax
@ -52,7 +52,11 @@
;; lift out to utility module maybe
(define-syntax (type stx)
(raise-syntax-error 'type-check
"type name used out of context"
(format "type name used out of context\n type: ~a\n in: ~a"
(syntax->datum (if (stx-pair? stx)
(stx-car stx)
stx))
(syntax->datum stx))
stx
(and (stx-pair? stx) (stx-car stx))))
(provide type pred))))

View File

@ -19,28 +19,49 @@
(provide require/opaque-type require-typed-struct-legacy require-typed-struct
require/typed-legacy require/typed require/typed/provide
require-typed-struct/provide cast make-predicate define-predicate)
require-typed-struct/provide core-cast make-predicate define-predicate
require-typed-signature)
(module forms racket/base
(require (for-syntax racket/lazy-require racket/base))
(begin-for-syntax
(lazy-require [(submod "..")
(require/opaque-type
(require/opaque-type
require-typed-signature
require-typed-struct-legacy
require-typed-struct
require/typed-legacy require/typed require/typed/provide
require-typed-struct/provide cast make-predicate define-predicate)]))
require-typed-struct/provide core-cast make-predicate define-predicate)]))
(define-syntax (def stx)
(syntax-case stx ()
[(_ id ...)
(with-syntax ([(names ...) (generate-temporaries #'(id ...))])
#'(begin (provide (rename-out [names id] ...))
(define-syntax (names stx) (id stx)) ...))]))
(def require/opaque-type
(def require/opaque-type
require-typed-signature
require-typed-struct-legacy
require-typed-struct
require/typed-legacy require/typed require/typed/provide
require-typed-struct/provide cast make-predicate define-predicate))
require-typed-struct/provide make-predicate define-predicate)
;; Expand `cast` to a `core-cast` with an extra `#%expression` in order
;; to prevent the contract generation pass from executing too early
;; (i.e., before the `cast` typechecks)
(define-syntax (-core-cast stx) (core-cast stx))
(define-syntax (cast stx)
(syntax-case stx ()
[(_ e ty) (quasisyntax/loc stx (#%expression #,(syntax/loc stx (-core-cast e ty))))]))
(provide cast))
;; unsafe operations go in this submodule
(module* unsafe #f
;; turned into a macro on the requiring side
(provide -unsafe-require/typed))
;; used for private unsafe functionality in require macros
;; *do not export*
(define-syntax unsafe-kw (syntax-rules ()))
(require (for-template (submod "." forms) "../utils/require-contract.rkt"
(submod "../typecheck/internal-forms.rkt" forms)
@ -60,8 +81,10 @@
racket/struct-info
syntax/struct
syntax/location
(for-template "../utils/any-wrap.rkt")
"../utils/tc-utils.rkt"
"../private/syntax-properties.rkt"
"../private/cast-table.rkt"
"../typecheck/internal-forms.rkt"
;; struct-extraction is actually used at both of these phases
"../utils/struct-extraction.rkt"
@ -71,11 +94,9 @@
;; Lazily loaded b/c they're only used sometimes, so we save a lot
;; of loading by not having them when they are unneeded
(lazy-require ["../rep/type-rep.rkt" (make-Opaque Error?)]
(lazy-require ["../rep/type-rep.rkt" (Error?)]
["../types/utils.rkt" (fv)]
[syntax/define (normalize-definition)]
[typed-racket/private/parse-type (parse-type)]
[typed-racket/env/type-alias-env (register-resolved-type-alias)])
[typed-racket/private/parse-type (parse-type)])
(define (with-type* expr ty)
(with-type #`(ann #,expr #,ty)))
@ -89,11 +110,12 @@
(pattern (nm:id parent:id)))
(define-values (require/typed-legacy require/typed)
(define-values (require/typed-legacy require/typed -unsafe-require/typed)
(let ()
(define-syntax-class opt-rename
#:attributes (nm spec)
#:attributes (nm orig-nm spec)
(pattern nm:id
#:with orig-nm #'nm
#:with spec #'nm)
(pattern (orig-nm:id internal-nm:id)
#:with spec #'(orig-nm internal-nm)
@ -103,23 +125,29 @@
#:attributes (nm ty)
(pattern [nm:opt-rename ty]))
(define-splicing-syntax-class (opt-constructor legacy struct-name)
#:attributes (value)
(pattern (~seq)
#:attr value (if legacy
#`(#:extra-constructor-name
#,(format-id struct-name "make-~a" struct-name))
#'()))
(pattern (~seq (~and key (~or #:extra-constructor-name #:constructor-name)) name:id)
#:attr value #'(key name)))
(define-splicing-syntax-class (struct-opts legacy struct-name)
#:attributes (ctor-value type)
(pattern (~seq (~optional (~seq (~and key (~or #:extra-constructor-name #:constructor-name))
name:id))
(~optional (~seq #:type-name type:id) #:defaults ([type struct-name])))
#:attr ctor-value (if (attribute key) #'(key name)
(if legacy
#`(#:extra-constructor-name
#,(format-id struct-name "make-~a" struct-name))
#'()))))
(define-syntax-class (struct-clause legacy)
;#:literals (struct)
#:attributes (nm (body 1) (constructor-parts 1))
#:attributes (nm type (body 1) (constructor-parts 1))
(pattern [(~or (~datum struct) #:struct)
nm:opt-parent (body ...)
(~var constructor (opt-constructor legacy #'nm.nm))]
#:with (constructor-parts ...) #'constructor.value))
(~var opts (struct-opts legacy #'nm.nm))]
#:with (constructor-parts ...) #'opts.ctor-value
#:attr type #'opts.type))
(define-syntax-class signature-clause
#:literals (:)
#:attributes (sig-name [var 1] [type 1])
(pattern [#:signature sig-name:id ([var:id : type] ...)]))
(define-syntax-class opaque-clause
;#:literals (opaque)
@ -129,38 +157,63 @@
(pattern [(~or (~datum opaque) #:opaque) opaque ty:id pred:id #:name-exists]
#:with opt #'(#:name-exists)))
(define-syntax-class (clause legacy lib)
(define-syntax-class (clause legacy unsafe? lib)
#:attributes (spec)
(pattern oc:opaque-clause #:attr spec
#`(require/opaque-type oc.ty oc.pred #,lib . oc.opt))
(pattern (~var strc (struct-clause legacy)) #:attr spec
#`(require-typed-struct strc.nm (strc.body ...) strc.constructor-parts ... #,lib))
#`(require-typed-struct strc.nm (strc.body ...) strc.constructor-parts ...
#:type-name strc.type
#,@(if unsafe? #'(unsafe-kw) #'())
#,lib))
(pattern sig:signature-clause #:attr spec
#`(require-typed-signature sig.sig-name (sig.var ...) (sig.type ...) #,lib))
(pattern sc:simple-clause #:attr spec
#`(require/typed #:internal sc.nm sc.ty #,lib)))
#`(require/typed #:internal sc.nm sc.ty #,lib
#,@(if unsafe? #'(unsafe-kw) #'()))))
(define ((r/t-maker legacy) stx)
(define ((r/t-maker legacy unsafe?) stx)
(unless (or (unbox typed-context?) (eq? (syntax-local-context) 'module-begin))
(raise-syntax-error #f "only allowed in a typed module" stx))
(syntax-parse stx
[(_ lib:expr (~var c (clause legacy #'lib)) ...)
[(_ lib:expr (~var c (clause legacy unsafe? #'lib)) ...)
(when (zero? (syntax-length #'(c ...)))
(raise-syntax-error #f "at least one specification is required" stx))
#`(begin c.spec ...)]
[(_ #:internal nm:opt-rename ty lib (~optional [~seq #:struct-maker parent]) ...)
[(_ #:internal nm:opt-rename ty lib
(~optional [~seq #:struct-maker parent])
(~optional (~and (~seq (~literal unsafe-kw))
(~bind [unsafe? #t]))
#:defaults ([unsafe? #f])))
(define/with-syntax hidden (generate-temporary #'nm.nm))
(define/with-syntax sm (if (attribute parent)
#'(#:struct-maker parent)
#'()))
;; define `cnt*` to be fixed up later by the module type-checking
(define cnt*
(syntax-local-lift-expression
(make-contract-def-rhs #'ty #f (attribute parent))))
(quasisyntax/loc stx
(begin
#,(internal #'(require/typed-internal hidden ty . sm))
#,(ignore #`(require/contract nm.spec hidden #,cnt* lib))))]))
(values (r/t-maker #t) (r/t-maker #f))))
(cond [(not (attribute unsafe?))
;; define `cnt*` to be fixed up later by the module type-checking
(define cnt*
(syntax-local-lift-expression
(make-contract-def-rhs #'ty #f (attribute parent))))
(quasisyntax/loc stx
(begin
;; register the identifier so that it has a binding (for top-level)
#,@(if (eq? (syntax-local-context) 'top-level)
(list #'(define-syntaxes (hidden) (values)))
null)
#,(internal #'(require/typed-internal hidden ty . sm))
#,(ignore #`(require/contract nm.spec hidden #,cnt* lib))))]
[else
(define/with-syntax hidden2 (generate-temporary #'nm.nm))
(quasisyntax/loc stx
(begin
(require (only-in lib [nm.orig-nm hidden]))
;; need this indirection since `hidden` may expand
;; to a different identifier that TR doesn't know about
#,(ignore #'(define hidden2 hidden))
(rename-without-provide nm.nm hidden2)
#,(internal #'(require/typed-internal hidden2 ty . sm))))])]))
(values (r/t-maker #t #f) (r/t-maker #f #f) (r/t-maker #f #t))))
(define (require/typed/provide stx)
@ -207,9 +260,28 @@
;; make-predicate
;; cast
;; Helper to construct syntax for contract definitions
;; Helpers to construct syntax for contract definitions
;; make-contract-def-rhs : Type-Stx Boolean Boolean -> Syntax
(define (make-contract-def-rhs type flat? maker?)
(contract-def-property #'#f `#s(contract-def ,type ,flat? ,maker? untyped)))
(define contract-def `#s(contract-def ,type ,flat? ,maker? untyped))
(contract-def-property #'#f (λ () contract-def)))
;; make-contract-def-rhs/from-typed : Id Boolean Boolean -> Syntax
(define (make-contract-def-rhs/from-typed id flat? maker?)
(contract-def-property
#'#f
;; This function should only be called after the type-checking pass has finished.
;; By then `tc/#%expression` will have recognized the `casted-expr` property and
;; will have added the casted expression's original type to the cast-table, so
;; that `(cast-table-ref id)` can get that type here.
(λ ()
(define type-stx
(or (cast-table-ref id)
(int-err (string-append
"contract-def-property: thunk called too early\n"
" This should only be called after the type-checking pass has finished."))))
`#s(contract-def ,type-stx ,flat? ,maker? typed))))
(define (define-predicate stx)
(syntax-parse stx
@ -241,21 +313,21 @@
#`(#,(external-check-property #'#%expression check-valid-type)
#,(ignore-some/expr #`(flat-contract-predicate #,name) #'(Any -> Boolean : ty)))]))
(define (cast stx)
;; wrapped above in the `forms` submodule
(define (core-cast stx)
(syntax-parse stx
[(_ v:expr ty:expr)
(define (apply-contract ctc-expr)
(define (apply-contract v ctc-expr pos neg)
#`(#%expression
#,(ignore-some/expr
#`(let-values (((val) #,(with-type* #'v #'Any)))
#`(let-values (((val) #,(with-type* v #'Any)))
#,(syntax-property
(quasisyntax/loc stx
(contract
#,ctc-expr
val
'cast
'typed-world
'#,pos
'#,neg
val
(quote-srcloc #,stx)))
'feature-profile:TR-dynamic-check #t))
@ -264,8 +336,13 @@
(cond [(not (unbox typed-context?)) ; no-check, don't check
#'v]
[else
(define ctc (syntax-local-lift-expression
(make-contract-def-rhs #'ty #f #f)))
(define new-ty-ctc (syntax-local-lift-expression
(make-contract-def-rhs #'ty #f #f)))
(define existing-ty-id new-ty-ctc)
(define existing-ty-ctc (syntax-local-lift-expression
(make-contract-def-rhs/from-typed existing-ty-id #f #f)))
(define (store-existing-type existing-type)
(cast-table-set! existing-ty-id existing-type))
(define (check-valid-type _)
(define type (parse-type #'ty))
(define vars (fv type))
@ -275,7 +352,12 @@
"Type ~a could not be converted to a contract because it contains free variables."
type)))
#`(#,(external-check-property #'#%expression check-valid-type)
#,(apply-contract ctc))])]))
#,(apply-contract
(apply-contract
#`(#,(casted-expr-property #'#%expression store-existing-type)
v)
existing-ty-ctc 'typed-world 'cast)
new-ty-ctc 'cast 'typed-world))])]))
@ -283,21 +365,25 @@
(define-syntax-class name-exists-kw
(pattern #:name-exists))
(syntax-parse stx
[_ #:when (eq? 'module-begin (syntax-local-context))
;; it would be inconvenient to find the correct #%module-begin here, so we rely on splicing
#`(begin #,stx (begin))]
[(_ ty:id pred:id lib (~optional ne:name-exists-kw) ...)
;; This line appears redundant with the use of `define-type-alias` below, but
;; it's actually necessary for top-level uses because this opaque type may appear
;; in subsequent `require/typed` clauses, which needs to parse the types at
;; expansion-time, not at typechecking time when aliases are installed.
(register-resolved-type-alias #'ty (make-Opaque #'pred))
(with-syntax ([hidden (generate-temporary #'pred)])
(define pred-cnt
(syntax-local-lift-expression
(make-contract-def-rhs #'(-> Any Boolean) #f #f)))
(quasisyntax/loc stx
(begin
#,(ignore #'(define pred-cnt (any/c . c-> . boolean?)))
;; register the identifier for the top-level (see require/typed)
#,@(if (eq? (syntax-local-context) 'top-level)
(list #'(define-syntaxes (hidden) (values)))
null)
#,(internal #'(require/typed-internal hidden (Any -> Boolean : (Opaque pred))))
#,(if (attribute ne)
(internal (syntax/loc stx (define-type-alias-internal ty (Opaque pred))))
(syntax/loc stx (define-type-alias ty (Opaque pred))))
#,(ignore #'(require/contract pred hidden pred-cnt lib)))))]))
#,(ignore #`(require/contract pred hidden #,pred-cnt lib)))))]))
@ -342,10 +428,18 @@
(pattern (~seq #:constructor-name name:id) #:attr extra #f)
(pattern (~seq #:extra-constructor-name name:id) #:attr extra #t))
(define-splicing-syntax-class unsafe-clause
(pattern (~seq) #:attr unsafe? #f)
(pattern (~seq (~literal unsafe-kw)) #:attr unsafe? #t))
(define ((rts legacy) stx)
(syntax-parse stx #:literals (:)
[(_ name:opt-parent ([fld : ty] ...) (~var input-maker (constructor-term legacy #'name.nm)) lib)
[(_ name:opt-parent
([fld : ty] ...)
(~var input-maker (constructor-term legacy #'name.nm))
(~optional (~seq #:type-name type:id) #:defaults ([type #'name.nm]))
unsafe:unsafe-clause
lib)
(with-syntax* ([nm #'name.nm]
[parent #'name.parent]
[hidden (generate-temporary #'name.nm)]
@ -421,20 +515,52 @@
(make-struct-info-self-ctor #'internal-maker si)
si))
(dtsi* () spec ([fld : ty] ...) #:maker maker-name #:type-only)
#,(ignore #'(require/contract pred hidden (any/c . c-> . boolean?) lib))
#,(internal #'(require/typed-internal hidden (Any -> Boolean : nm)))
(require/typed #:internal (maker-name real-maker) nm lib
#:struct-maker parent)
(dtsi* () spec type ([fld : ty] ...) #:maker maker-name #:type-only)
#,(ignore #'(require/contract pred hidden (or/c struct-predicate-procedure?/c (c-> any-wrap/c boolean?)) lib))
#,(internal #'(require/typed-internal hidden (Any -> Boolean : type)))
(require/typed #:internal (maker-name real-maker) type lib
#:struct-maker parent
#,@(if (attribute unsafe.unsafe?) #'(unsafe-kw) #'()))
;This needs to be a different identifier to meet the specifications
;of struct (the id constructor shouldn't expand to it)
#,(if (syntax-e #'extra-maker)
#'(require/typed #:internal (maker-name extra-maker) nm lib
#:struct-maker parent)
#`(require/typed #:internal (maker-name extra-maker) type lib
#:struct-maker parent
#,@(if (attribute unsafe.unsafe?) #'(unsafe-kw) #'()))
#'(begin))
(require/typed lib
[sel (nm -> ty)]) ...)))]))
#,(if (not (free-identifier=? #'nm #'type))
#'(define-syntax type
(lambda (stx)
(raise-syntax-error
'type-check
(format "type name ~a used out of context in ~a"
(syntax->datum (if (stx-pair? stx)
(stx-car stx)
stx))
(syntax->datum stx))
stx
(and (stx-pair? stx) (stx-car stx)))))
#'(begin))
#,@(if (attribute unsafe.unsafe?)
#'((require/typed #:internal sel (type -> ty) lib unsafe-kw) ...)
#'((require/typed lib [sel (type -> ty)]) ...)))))]))
(values (rts #t) (rts #f))))
(define (require-typed-signature stx)
(syntax-parse stx
#:literals (:)
[(_ sig-name:id (var ...) (type ...) lib)
(quasisyntax/loc stx
(begin
(require (only-in lib sig-name))
#,(internal (quasisyntax/loc stx
(define-signature-internal sig-name
#:parent-signature #f
([var type] ...)
;; infer parent relationships using the static information
;; bound to this signature
#:check? #t)))))]))

View File

@ -46,7 +46,7 @@
(format "field `~a' requires a type annotation"
(syntax-e #'fld))
#:with form 'dummy))
(define-syntax-class struct-name
#:description "struct name (with optional super-struct name)"
#:attributes (name super)
@ -72,16 +72,31 @@
(define-splicing-syntax-class struct-options
#:description "typed structure type options"
#:attributes (guard mutable? transparent? prefab? [prop 1] [prop-val 1])
#:attributes (guard mutable? transparent? prefab? cname ecname type untyped
[prop 1] [prop-val 1])
(pattern (~seq (~or (~optional (~seq (~and #:mutable mutable?)))
(~optional (~seq (~and #:transparent transparent?)))
(~optional (~seq (~and #:prefab prefab?)))
(~optional (~or (~and (~seq #:constructor-name cname)
(~bind [ecname #f]))
(~and (~seq #:extra-constructor-name ecname)
(~bind [cname #f]))))
(~optional (~seq #:type-name type:id))
;; FIXME: unsound, but relied on in core libraries
;; #:guard ought to be supportable with some work
;; #:property is harder
(~optional (~seq #:guard guard:expr))
(~seq #:property prop:expr prop-val:expr))
...)))
...)
#:attr untyped #`(#,@(if (attribute mutable?) #'(#:mutable) #'())
#,@(if (attribute transparent?) #'(#:transparent) #'())
#,@(if (attribute prefab?) #'(#:prefab) #'())
#,@(if (attribute cname) #'(#:constructor-name cname) #'())
#,@(if (attribute ecname) #'(#:extra-constructor-name ecname) #'())
#,@(if (attribute guard) #'(#:guard guard) #'())
#,@(append* (for/list ([prop (in-list (attribute prop))]
[prop-val (in-list (attribute prop-val))])
(list #'#:property prop prop-val))))))
(define-syntax-class dtsi-struct-name
#:description "struct name (with optional super-struct name)"
@ -94,13 +109,27 @@
(define-syntax (define-typed-struct/exec stx)
(syntax-parse stx #:literals (:)
[(_ nm ((~describe "field specification" [fld:optionally-annotated-name]) ...) [proc : proc-ty])
[(_ nm:struct-name ((~describe "field specification" [fld:optionally-annotated-name]) ...)
[proc : proc-ty] (~optional (~seq #:type-name type:id)))
(with-syntax*
([proc* (with-type* #'proc #'proc-ty)]
([type (or (attribute type) #'nm.name)]
[proc* (with-type* #'proc #'proc-ty)]
[d-s (ignore-some (syntax/loc stx (define-struct nm (fld.name ...)
#:property prop:procedure proc*)))]
[dtsi (quasisyntax/loc stx (dtsi/exec* () nm (fld ...) proc-ty))])
#'(begin d-s dtsi))]))
[stx-err-fun (if (not (free-identifier=? #'nm.name #'type))
(syntax/loc stx
(define-syntax type
(lambda (stx)
(raise-syntax-error
'type-check
(format "type name ~a used out of context in ~a"
(syntax->datum (if (stx-pair? stx) (stx-car stx) stx))
(syntax->datum stx))
stx
(and (stx-pair? stx) (stx-car stx))))))
#'(begin))]
[dtsi (quasisyntax/loc stx (dtsi/exec* () nm type (fld ...) proc-ty))])
#'(begin d-s stx-err-fun dtsi))]))
(define-syntaxes (dtsi* dtsi/exec*)
(let ()
@ -121,57 +150,63 @@
;; User-facing macros for defining typed structure types
(define-syntaxes (define-typed-struct -struct)
(values
(lambda (stx)
(syntax-parse stx
[(_ vars:maybe-type-vars nm:struct-name (fs:fld-spec ...)
opts:struct-options)
(let ([mutable? (if (attribute opts.mutable?) #'(#:mutable) #'())]
[cname (second (build-struct-names #'nm.name null #t #t))])
(with-syntax ([d-s (ignore-some
(syntax/loc stx (define-struct nm (fs.fld ...) . opts)))]
[dtsi (quasisyntax/loc stx
(dtsi* (vars.vars ...) nm (fs.form ...)
#:maker #,cname
#,@mutable?))])
(if (eq? (syntax-local-context) 'top-level)
;; Use `eval` at top-level to avoid an unbound id error
;; from dtsi trying to look at the d-s bindings.
#'(begin (eval (quote-syntax d-s))
;; It is important here that the object under the
;; eval is a quasiquoted literal in order
;; for #%top-interaction to get the lexical
;; information for TR's actual #%top-interaction.
;; This effectively lets us invoke the type-checker
;; dynamically.
;;
;; The quote-syntax is also important because we want
;; the `dtsi` to have the lexical information from
;; this module. This ensures that the `dtsi` macro
;; is actually bound to its definition above.
(eval `(#%top-interaction . ,(quote-syntax dtsi))))
#'(begin d-s dtsi))))]))
(lambda (stx)
(syntax-parse stx
[(_ vars:maybe-type-vars nm:struct-name/new (fs:fld-spec ...)
opts:struct-options)
(let ([mutable? (if (attribute opts.mutable?) #'(#:mutable) #'())]
[prefab? (if (attribute opts.prefab?) #'(#:prefab) #'())])
(with-syntax ([d-s (ignore (quasisyntax/loc stx
(struct #,@(attribute nm.new-spec) (fs.fld ...)
. opts)))]
[dtsi (quasisyntax/loc stx
(dtsi* (vars.vars ...)
nm.old-spec (fs.form ...)
#,@mutable?
#,@prefab?))])
;; see comment above
(if (eq? (syntax-local-context) 'top-level)
#'(begin (eval (quote-syntax d-s))
(eval `(#%top-interaction . ,(quote-syntax dtsi))))
#'(begin d-s dtsi))))]))))
(define-syntax (define-typed-struct stx)
(syntax-parse stx
[(_ vars:maybe-type-vars nm:struct-name (fs:fld-spec ...) opts:struct-options)
(quasisyntax/loc stx
(-struct #,@#'vars
#,@(if (stx-pair? #'nm)
#'nm
(list #'nm))
(fs ...)
;; If there's already a (extra) constructor name supplied,
;; then Racket's `define-struct` doesn't define a `make-`
;; constructor either so don't pass anything extra.
#,@(if (or (attribute opts.cname)
(attribute opts.ecname))
null
(list #'#:extra-constructor-name
(second (build-struct-names #'nm.name null #t #t))))
. opts))]))
(define-syntax (-struct stx)
(syntax-parse stx
[(_ vars:maybe-type-vars nm:struct-name/new (fs:fld-spec ...)
opts:struct-options)
(let ([mutable? (if (attribute opts.mutable?) #'(#:mutable) #'())]
[prefab? (if (attribute opts.prefab?) #'(#:prefab) #'())]
[maker (if (attribute opts.cname)
#`(#:maker #,(attribute opts.cname))
#'())]
[extra-maker (if (attribute opts.ecname)
#`(#:extra-maker #,(attribute opts.ecname))
#'())])
(with-syntax* ([type (or (attribute opts.type) #'nm.name)]
[d-s (ignore (quasisyntax/loc stx
(struct #,@(attribute nm.new-spec) (fs.fld ...)
. opts.untyped)))]
[stx-err-fun (if (not (free-identifier=? #'nm.name #'type))
(syntax/loc stx
(define-syntax type
(lambda (stx)
(raise-syntax-error
'type-check
(format "type name ~a used out of context in ~a"
(syntax->datum (if (stx-pair? stx)
(stx-car stx)
stx))
(syntax->datum stx))
stx
(and (stx-pair? stx) (stx-car stx))))))
#'(begin))]
[dtsi (quasisyntax/loc stx
(dtsi* (vars.vars ...)
nm.old-spec type (fs.form ...)
#,@mutable?
#,@prefab?
#,@maker
#,@extra-maker))])
#'(begin d-s stx-err-fun dtsi)))]))
;; this has to live here because it's used below
(define-syntax (define-type-alias stx)
@ -200,7 +235,9 @@
#'(lambda (stx)
(raise-syntax-error
'type-check
"type name used out of context"
(format "type name used out of context\n type: ~a\n in: ~a"
(syntax->datum (if (stx-pair? stx) (stx-car stx) stx))
(syntax->datum stx))
stx
(and (stx-pair? stx) (stx-car stx)))))
#`(begin
@ -218,18 +255,10 @@
(syntax-parse stx
[(define-new-subtype ty:id (constructor:id rep-ty:expr))
#:with gen-id (generate-temporary #'ty)
#:with stx-err-fun
#'(lambda (stx)
(raise-syntax-error
'type-check
"type name used out of context"
stx
(and (stx-pair? stx) (stx-car stx))))
#`(begin
(define-type-alias ty (Distinction ty gen-id rep-ty))
#,(ignore
#'(begin
(define-syntax ty stx-err-fun)
(define constructor (lambda (x) x))))
#'(define constructor (lambda (x) x)))
#,(internal (syntax/loc stx
(define-new-subtype-internal ty (constructor rep-ty) #:gen-id gen-id))))])))

View File

@ -145,9 +145,12 @@ the typed racket language.
(provide (all-from-out "base-contracted.rkt")))
(begin-for-syntax
(require racket/runtime-path
(for-syntax racket/base))
(define-runtime-module-path-index contract-defs-submod
'(submod "." #%contract-defs))
(require racket/base "../utils/redirect-contract.rkt")
(define varref (#%variable-reference))
(define mk (make-make-redirect-to-contract varref)))
(define mk (make-make-redirect-to-contract contract-defs-submod)))
(define-syntax-rule (def-redirect id ...)
(begin (define-syntax id (mk (quote-syntax id))) ... (provide id ...)))
@ -157,11 +160,7 @@ the typed racket language.
;; Lazily loaded b/c they're only used sometimes, so we save a lot
;; of loading by not having them when they are unneeded
(begin-for-syntax
(lazy-require ["../rep/type-rep.rkt" (make-Opaque Error?)]
["../types/utils.rkt" (fv)]
[syntax/define (normalize-definition)]
[typed-racket/private/parse-type (parse-type)]
[typed-racket/env/type-alias-env (register-resolved-type-alias)]))
(lazy-require [syntax/define (normalize-definition)]))
(define-for-syntax (with-type* expr ty)
(with-type #`(ann #,expr #,ty)))
@ -203,7 +202,7 @@ the typed racket language.
(let ([mk (lambda (form)
(lambda (stx)
(syntax-parse stx
#:context form
#:context (list (syntax-e form) stx)
[(_ (bs:optionally-annotated-binding ...) . body)
(quasisyntax/loc stx (#,form (bs.binding ...) . body))])))])
(values (mk #'let) (mk #'let*) (mk #'letrec))))
@ -481,14 +480,17 @@ the typed racket language.
clause:for-clauses
a2:optional-standalone-annotation*
c ...)
(define all-typed? (andmap values (attribute var.ty)))
(define for-stx
(quasisyntax/loc stx
(for/lists (var.ann-name ...)
(clause.expand* ... ...)
c ...)))
((attribute a1.annotate)
((attribute a2.annotate)
(add-ann
(quasisyntax/loc stx
(for/lists (var.ann-name ...)
(clause.expand* ... ...)
c ...))
#'(values var.ty ...))))]))
(if all-typed?
(add-ann for-stx #'(values var.ty ...))
for-stx)))]))
(define-syntax (for*/fold: stx)
(syntax-parse stx #:literals (:)
[(_ a1:optional-standalone-annotation*
@ -496,14 +498,17 @@ the typed racket language.
clause:for-clauses
a2:optional-standalone-annotation*
c ...)
(define all-typed? (andmap values (attribute var.ty)))
(define for-stx
(quasisyntax/loc stx
(for/fold ((var.ann-name init) ...)
(clause.expand* ... ...)
c ...)))
((attribute a1.annotate)
((attribute a2.annotate)
(add-ann
(quasisyntax/loc stx
(for/fold ((var.ann-name init) ...)
(clause.expand* ... ...)
c ...))
#'(values var.ty ...))))]))
(if all-typed?
(add-ann for-stx #'(values var.ty ...))
for-stx)))]))
(define-for-syntax (define-for/acc:-variant for*? for/folder: for/folder op initial final)
(lambda (stx)
@ -806,7 +811,13 @@ the typed racket language.
(define i 0)
(for (clauses ...)
(define v body-expr)
(cond [(unsafe-fx= i 0) (define new-vs (ann (make-vector n v) T))
;; can't use `unsafe-fx=` here
;; if `n` is larger than a fixnum, this is unsafe, and we
;; don't know whether that's the case until we try creating
;; the vector
;; other unsafe ops are after vector allocation, and so are
;; fine
(cond [(= i 0) (define new-vs (ann (make-vector n v) T))
(set! vs new-vs)]
[else (unsafe-vector-set! vs i v)])
(set! i (unsafe-fx+ i 1))

View File

@ -0,0 +1,108 @@
#lang racket/base
;; This file implements unit signatures for typed racket
(provide define-signature)
(require "../utils/utils.rkt"
"colon.rkt"
(for-syntax syntax/parse
racket/base
racket/list
racket/syntax
syntax/kerncase
"../private/syntax-properties.rkt"
(typecheck internal-forms)
syntax/id-table
racket/dict
racket/unit-exptime
(utils tc-utils))
(only-in racket/unit
[define-signature untyped-define-signature]
extends)
(for-label "colon.rkt")
(submod "../typecheck/internal-forms.rkt" forms)
(only-in "../../typed/racket/base.rkt" define-type))
(begin-for-syntax
(define-literal-set colon #:for-label (:))
;; TODO: there should be a more extensible way of handling signatures
(define-syntax-class signature-forms
(pattern (form:def-sig-form ...)))
(define-syntax-class def-sig-form
#:attributes (internal-form erased)
(pattern :sig-var-form
#:attr kind 'var)
;; The define-type form is explicitly disallowed until I can figure out how
;; to sensibly support them in signature definitions - dfeltey
(pattern :sig-type-form
#:fail-when #t "type definitions are not allowed within signature definitions"
#:attr kind 'type))
(define-syntax-class sig-var-form
#:literal-sets (colon)
(pattern [name:id : type]
#:with internal-form #'(name type)
#:with erased #'name))
;; Preliminary support for type definitions in signatures
;; The form is allowed in signature definitions, but currently
;; fails on parsing.
;; In the future supporting type definitions inside of signatures
;; would be a worthwhile feature, but their implemention is not obvious
(define-syntax-class sig-type-form
#:literals (define-type)
(pattern (define-type t ty)
;; These attributes are dummy values
#:attr internal-form #f
#:attr erased #f))
(define-splicing-syntax-class extends-form
#:literals (extends)
(pattern (~seq extends super:id)
#:with internal-form #'super
#:with extends-id #'super
#:attr form #'(extends super))
(pattern (~seq)
#:with internal-form #'#f
#:with extends-id '()
#:attr form '())))
;; process-signature-forms : (listof syntax?) -> (listof (pairof id id))
;; Processes the raw syntax of signature forms and returns a list
;; of pairs representing names and types bound by the signature
(define-for-syntax (process-signature-forms forms)
(for/list ([form (in-list forms)])
(syntax-parse form
[member:sig-var-form
(syntax-e #'member.internal-form)])))
;; typed define-signature macro
;; This expands into the untyped define-signature syntax as well as an
;; internal form used by TR to register signatures in the signature environment
;; The `define-signature-internal` form specifies
;; - the `name` of the signature being defined
;; - it's parent-signature, or #f if this signature does not extend another signature
;; - the list of member variables contained in this signature along with their types
;; - and a boolean flag indicating whether the signature came from an instance of
;; require/typed in which case additional checking must occur when the internal
;; form is parsed
(define-syntax (define-signature stx)
(syntax-parse stx
[(_ sig-name:id super-form:extends-form forms:signature-forms)
(define members (process-signature-forms (syntax->list #'forms)))
(define erased-members (map car members))
#`(begin
#,(ignore (quasisyntax/loc stx
(untyped-define-signature sig-name #,@(attribute super-form.form)
(#,@erased-members))))
#,(internal (quasisyntax/loc stx
(define-signature-internal sig-name
#:parent-signature super-form.internal-form
(#,@members)
;; no need to further check parent information
#:check? #f))))]))

View File

@ -43,13 +43,16 @@
"../tc-setup.rkt"
(private parse-type syntax-properties)
(types utils abbrev printer)
(typecheck tc-toplevel tc-app-helper)
(typecheck possible-domains typechecker)
(rep type-rep)
(utils tc-utils)
(for-syntax racket/base syntax/parse)
(for-template racket/base))
(provide
:type-impl :print-type-impl :query-type/args-impl :query-type/result-impl)
;; this one doesn't quite fit the pattern of the next three REPL operations, so
;; this one isn't defined with a macro as below
(define (:type-impl stx)
(syntax-parse stx
[(_ (~optional (~and #:verbose verbose-kw)) ty:expr)
@ -72,59 +75,55 @@
[form
(raise-syntax-error #f "must be applied to exactly one argument" #'form)]))
(define-syntax (define-repl-op stx)
(syntax-parse stx
[(_ op args to-expand handler err)
#'(define (op stx)
(syntax-parse stx
[args
(define result
(tc-expr (local-expand to-expand 'expression (list #'module*))))
(handler result)]
[form
(raise-syntax-error #f err #'form)]))]))
;; TODO what should be done with stx
;; Prints the _entire_ type. May be quite large.
(define (:print-type-impl stx)
(syntax-parse stx
[(_ e)
(tc-toplevel/full stx #'e
(λ (expanded type)
#`(displayln
#,(if (eq? type 'no-type)
"This form has no type (it does not produce a value)."
(pretty-format-type
(match type
[(tc-result1: t f o) t]
[(tc-results: t) (-values t)]
[(tc-any-results: f) (-AnyValues f)]))))))]
[form
(raise-syntax-error #f "must be applied to exactly one argument" #'form)]))
(define-repl-op :print-type-impl (_ e) #'e
(λ (type)
#`(displayln
#,(pretty-format-type
(match type
[(tc-result1: t f o) t]
[(tc-results: t) (-values t)]
[(tc-any-results: f) (-AnyValues f)]))))
"must be applied to exactly one argument")
;; given a function and input types, display the result type
(define (:query-type/args-impl stx)
(syntax-parse stx
[(_ op arg-type ...)
(with-syntax ([(dummy-arg ...) (generate-temporaries #'(arg-type ...))])
(tc-toplevel/full
stx
;; create a dummy function with the right argument types
#`(lambda #,(stx-map type-label-property
#'(dummy-arg ...) #'(arg-type ...))
(op dummy-arg ...))
(λ (expanded type)
#`(display
#,(pretty-format-type
(match type
[(tc-result1: (and t (Function: _)) f o) t]))))))]
[form
(raise-syntax-error #f "must be applied to at least one argument" #'form)]))
(define-repl-op :query-type/args-impl (_ op arg-type ...)
(with-syntax ([(dummy-arg ...) (generate-temporaries #'(arg-type ...))])
;; create a dummy function with the right argument types
#`(lambda #,(stx-map type-label-property
#'(dummy-arg ...) #'(arg-type ...))
(op dummy-arg ...)))
(λ (type)
#`(display
#,(pretty-format-type
(match type
[(tc-result1: (and t (Function: _)) f o) t]))))
"must be applied to at least one argument" )
;; given a function and a desired return type, fill in the blanks
(define (:query-type/result-impl stx)
(syntax-parse stx
[(_ op desired-type)
(let ([expected (parse-type #'desired-type)])
(tc-toplevel/full stx #'op
(λ (expanded type)
(match type
[(tc-result1: (and t (Function: _)) f o)
(let ([cleaned (cleanup-type t expected #f)])
#`(display
#,(match cleaned
[(Function: '())
"Desired return type not in the given function's range.\n"]
[(Function: arrs)
(pretty-format-type cleaned)])))]
[_ (error (format "~a: not a function" (syntax->datum #'op)))]))))]
[form
(raise-syntax-error #f "must be applied to exactly two arguments" #'form)])))
(define-repl-op :query-type/result-impl (_ op desired-type) #'op
(λ (type)
(match type
[(tc-result1: (and t (Function: _)) f o)
(let ([cleaned (cleanup-type t (parse-type #'desired-type) #f)])
#`(display
#,(match cleaned
[(Function: '())
"Desired return type not in the given function's range.\n"]
[(Function: arrs)
(pretty-format-type cleaned)])))]
[_ (error (format "~a: not a function" (syntax->datum #'op)))]))
"must be applied to exactly two arguments"))

View File

@ -9,7 +9,10 @@
(begin
(define-syntax (nm stx)
(raise-syntax-error
'type-check "type name used out of context"
'type-check
(format "type name used out of context\n type: ~a\n in: ~a"
(syntax->datum (if (stx-pair? stx) (stx-car stx) stx))
(syntax->datum stx))
stx
(and (stx-pair? stx) (stx-car stx))))
...

View File

@ -0,0 +1,492 @@
#lang racket/base
;; Primitive forms for units/signatures
(provide unit
define-unit
compound-unit
define-compound-unit
compound-unit/infer
define-compound-unit/infer
invoke-unit
invoke-unit/infer
define-values/invoke-unit
define-values/invoke-unit/infer
unit-from-context
define-unit-from-context)
(require "../utils/utils.rkt"
"colon.rkt"
(for-syntax syntax/parse
racket/base
racket/list
racket/match
racket/syntax
racket/sequence
syntax/context
syntax/flatten-begin
syntax/kerncase
"../private/syntax-properties.rkt"
(typecheck internal-forms)
syntax/id-table
racket/dict
racket/unit-exptime
syntax/strip-context
(utils tc-utils)
syntax/id-table
syntax/id-set)
(prefix-in untyped- (only-in racket/unit
define-signature
unit
invoke-unit
invoke-unit/infer
compound-unit
define-unit
define-compound-unit
define-values/invoke-unit
define-values/invoke-unit/infer
compound-unit/infer
define-compound-unit/infer
unit-from-context
define-unit-from-context))
(only-in racket/unit
extends
import
export
init-depend
link
prefix
rename)
"base-types.rkt"
"base-types-extra.rkt"
(for-label "colon.rkt")
(for-template (rep type-rep))
(submod "../typecheck/internal-forms.rkt" forms))
(begin-for-syntax
(define-literal-set colon #:for-label (:))
;; process-definition-form handles all of the `define-` style unit macros
;; such as define-unit, define-compound-unit, define-unit-from-context. but
;; not the corresponding unit, compound-unit, etc forms
;; Performs local expansion in order to apply a syntax property to the
;; subexpression of the `define-*` form correpsonding to the body of the
;; definition being created
;; - eg for a define-unit form, a syntax property will be attached to the
;; subexpression that creates the unit
(define (process-definition-form apply-property stx)
(define exp-stx (local-expand stx (syntax-local-context) (kernel-form-identifier-list)))
(syntax-parse exp-stx
#:literal-sets (kernel-literals)
[(begin e ...)
(quasisyntax/loc stx
(begin #,@(map (λ (e) (process-definition-form apply-property e))
(syntax->list #'(e ...)))))]
[(define-values (name ...) rhs)
(quasisyntax/loc stx (define-values (name ...) #,(ignore (apply-property #'rhs))))]
;; define-syntaxes that actually create the binding given in the
;; `define-*` macro will fall through to this case, and should be left as-is
[_ exp-stx]))
(define-splicing-syntax-class init-depend-form
#:literals (init-depend)
(pattern (~and this-syntax (init-depend sig:id ...))
#:attr form (list #'this-syntax)
#:with names #'(sig ...))
(pattern (~seq)
#:attr form '()
#:with names #'()))
;; The `rename` attribute in the sig-spec syntax class is used to correctly
;; map names of signature bound variables in unit bodies to their names in
;; the fully expanded syntax. It applies prefixes and renamings from
;; signature specifications to identifiers.
(define-syntax-class sig-spec
#:literals (prefix rename)
(pattern sig-id:id
#:attr rename (lambda (id) id)
#:with sig-name #'sig-id)
(pattern (prefix p:id sig:sig-spec)
#:attr rename (lambda (id) (format-id #'sig.sig-name
"~a~a"
#'p
((attribute sig.rename) id)))
#:with sig-name #'sig.sig-name)
(pattern (rename sig:sig-spec (new:id old:id) ...)
#:attr rename
(lambda (id)
(define (lookup id)
(for/first ([old-id (in-syntax #'(old ...))]
[new-id (in-syntax #'(new ...))]
#:when (free-identifier=? id old-id))
new-id))
(define rn ((attribute sig.rename) id))
(or (lookup rn) rn))
#:with sig-name #'sig.sig-name)))
;; imports/members : identifier? -> syntax?
;; given an identifier bound to a signature
;; returns syntax containing the signature name and the names of each variable contained
;; in the signature, this is needed to typecheck define-values/invoke-unit forms
(define-for-syntax (imports/members sig-id)
(define-values (_1 imp-mem _2 _3) (signature-members sig-id sig-id))
#`(#,sig-id #,@(map (lambda (id)
(local-expand
id
(syntax-local-context)
(kernel-form-identifier-list)))
imp-mem)))
;; Given a list of signature specs
;; Processes each signature spec to determine the variables exported
;; and produces syntax containing the signature id and the exported variables
(define-for-syntax (process-dv-exports es)
(for/list ([e (in-list es)])
(syntax-parse e
[s:sig-spec
(define sig-id #'s.sig-name)
(define renamer (attribute s.rename))
(define-values (_1 ex-mem _2 _3) (signature-members sig-id sig-id))
#`(#,sig-id #,@(map renamer ex-mem))])))
;; Typed macro for define-values/invoke-unit
;; This has to be handled specially because the types of
;; the defined values must be registered in the environment
(define-syntax (define-values/invoke-unit stx)
(syntax-parse stx
#:literals (import export)
[(_ unit-expr
(import isig:sig-spec ...)
(export esig:sig-spec ...))
(define imports-stx (syntax->list #'(isig.sig-name ...)))
(define exports-stx (syntax->list #'(esig ...)))
(define/with-syntax temp (syntax-local-introduce (generate-temporary)))
#`(begin
#,(internal (quasisyntax/loc stx
(define-values/invoke-unit-internal
(#,@(map imports/members imports-stx))
(#,@(process-dv-exports exports-stx)))))
(: temp (Unit (import isig.sig-name ...)
(export esig.sig-name ...)
(init-depend isig.sig-name ...)
AnyValues))
(define temp unit-expr)
#,(ignore (quasisyntax/loc stx
(untyped-define-values/invoke-unit unit-expr
(import isig ...)
(export esig ...)))))]))
(begin-for-syntax
;; flat signatures allow easy comparisons of whether one
;; such flat-signature implements another
;; - name is the identifier corresponding to the signatures name
;; - implements is a free-id-set of this signature and all its ancestors
;; flat-signatures enable a comparison of whether one signature implements
;; another as a subset comparison on their contained sets of parent signatures
(struct flat-signature (name implements) #:transparent)
;; implements? : flat-signature? flat-signature? -> Boolean
;; true iff signature sig1 implements signature sig2
(define (implements? sig1 sig2)
(match* (sig1 sig2)
[((flat-signature name1 impls1) (flat-signature name2 impls2))
(free-id-subset? impls2 impls1)]))
;; Given: a list of identifiers bound to static unit information
;; Returns: two lists
;; 1. A list of flat-signatures representing the signatures imported by
;; the given units
;; 2. A list of flat-signatures representing the signatures exported by
;; the given units
(define (get-imports/exports unit-ids)
(define-values (imports exports)
(for/fold ([imports null]
[exports null])
([unit-id (in-list unit-ids)])
(match-define-values ((list (cons _ new-imports) ...)
(list (cons _ new-exports) ...))
(unit-static-signatures unit-id unit-id))
(values (append imports new-imports) (append exports new-exports))))
(values (map make-flat-signature imports)
(map make-flat-signature exports)))
;; Given the id of a signature, return a corresponding flat-signature
(define (make-flat-signature sig-name)
(flat-signature sig-name (get-signature-ancestors sig-name)))
;; Walk the chain of parent signatures to build a list, and convert it to
;; a free-id-set
(define (get-signature-ancestors sig)
(immutable-free-id-set
(with-handlers ([exn:fail:syntax? (λ (e) null)])
(let loop ([sig sig] [ancestors null])
(define-values (parent _1 _2 _3) (signature-members sig sig))
(if parent
(loop parent (cons sig ancestors))
(cons sig ancestors))))))
;; Calculate the set of inferred imports for a list of units
;; The inferred imports are those which are not provided as
;; exports from any of the units taking signature subtyping into account
(define (infer-imports unit-ids)
(define-values (imports exports) (get-imports/exports unit-ids))
(define remaining-imports (remove* exports imports implements?))
(map flat-signature-name remaining-imports))
;; infer-exports returns all the exports from linked
;; units rather than just those that are not also
;; imported
(define (infer-exports unit-ids)
(define-values (imports exports) (get-imports/exports unit-ids))
(map flat-signature-name exports))
(define-splicing-syntax-class maybe-exports
#:literals (export)
(pattern (~seq)
#:attr exports #f)
(pattern (export sig:id ...)
#:attr exports (syntax->list #'(sig ...))))
(define-syntax-class dviu/infer-unit-spec
#:literals (link)
(pattern unit-id:id
#:attr unit-ids (list #'unit-id))
(pattern (link uid-inits:id ...)
#:attr unit-ids (syntax->list #'(uid-inits ...)))))
;; Note: This may not correctly handle all use cases of
;; define-values/invoke-unit/infer
;; inferred imports and exports are handled in the following way
;; - the exports of ALL units being linked are added to the export list
;; to be registered in tc-toplevel, this appears to be how exports are treated
;; by the unit inference process
;; - inferred imports are those imports which are not provided by
;; any of the exports
;; This seems to correctly handle both recursive and non-recursive
;; linking patterns
(define-syntax (define-values/invoke-unit/infer stx)
(syntax-parse stx
[(_ exports:maybe-exports us:dviu/infer-unit-spec)
(define inferred-imports (infer-imports (attribute us.unit-ids)))
(define inferred-exports (or (attribute exports.exports)
(infer-exports (attribute us.unit-ids))))
#`(begin
#,(internal (quasisyntax/loc stx
(define-values/invoke-unit-internal
(#,@(map imports/members inferred-imports))
(#,@(process-dv-exports inferred-exports)))))
#,(ignore
(quasisyntax/loc stx (untyped-define-values/invoke-unit/infer #,@#'exports us))))]))
(define-syntax (invoke-unit/infer stx)
(syntax-parse stx
[(_ . rest)
(ignore
(tr:unit:invoke-property
(quasisyntax/loc stx (untyped-invoke-unit/infer . rest)) 'infer))]))
;; The typed invoke-unit macro must attach a syntax property to the expression
;; being invoked in order to reliably find it during typechecking.
;; Otherwise the expanded syntax may be confused for that of invoke-unit/infer
;; and be typechecked incorrectly
(define-syntax (invoke-unit stx)
(syntax-parse stx
[(invoke-unit expr . rest)
(ignore
(tr:unit:invoke-property
(quasisyntax/loc stx
(untyped-invoke-unit
#,(tr:unit:invoke:expr-property #'expr #t)
. rest)) #t))]))
;; Trampolining macro that cooperates with the unit macro in order
;; to add information needed for typechecking units
;; Essentially head expands each expression in the body of a unit
;; - leaves define-syntaxes forms alone, to allow for macro definitions in unit bodies
;; - Inserts syntax into define-values forms that allow mapping the names of definitions
;; to their bodies during type checking
;; - Also specially handles type annotations in order to correctly associate variables
;; with their types
;; - All other expressions are marked as 'expr for typechecking
(define-syntax (add-tags stx)
(syntax-parse stx
[(_ e)
(define exp-e (local-expand #'e (syntax-local-context) (kernel-form-identifier-list)))
(syntax-parse exp-e
#:literals (begin define-values define-syntaxes :)
[(begin b ...)
#'(add-tags b ...)]
[(define-syntaxes (name:id ...) rhs:expr)
exp-e]
;; Annotations must be handled specially
;; Exported variables are renamed internally in units, which leads
;; to them not being correctly associated with their type annotations
;; This extra bit of inserted syntax allows the typechecker to
;; properly associate all annotated variables with their types.
;; The inserted lambda expression will be expanded to the internal
;; name of the variable being annotated, this internal name
;; can then be associated with the type annotation during typechecking
[(define-values () (colon-helper (: name:id type) rest ...))
(quasisyntax/loc stx
(define-values ()
#,(tr:unit:body-exp-def-type-property
#`(#%expression
(begin (void (lambda () name))
(colon-helper (: name type) rest ...)))
'def/type)))]
[(define-values (name:id ...) rhs)
(quasisyntax/loc stx
(define-values (name ...)
#,(tr:unit:body-exp-def-type-property
#'(#%expression
(begin
(void (lambda () name ... (void)))
rhs))
'def/type)))]
[_
(tr:unit:body-exp-def-type-property exp-e 'expr)])]
[(_ e ...)
#'(begin (add-tags e) ...)]))
(define-syntax (unit stx)
(syntax-parse stx
#:literals (import export)
[(unit imports exports init-depends:init-depend-form e ...)
(ignore
(tr:unit
(quasisyntax/loc stx
(untyped-unit
imports
exports
#,@(attribute init-depends.form)
(add-tags e ...)))))]))
(define-syntax (define-unit stx)
(syntax-parse stx
#:literals (import export)
[(define-unit uid:id
imports
exports
init-depends:init-depend-form
e ...)
(process-definition-form
(λ (stx) (tr:unit stx))
(quasisyntax/loc stx
(untyped-define-unit uid
imports
exports
#,@(attribute init-depends.form)
(add-tags e ...))))]))
(begin-for-syntax
(define-syntax-class compound-imports
#:literals (import)
(pattern (import lb:link-binding ...)
#:attr import-tags (syntax->list #'(lb.link-id ...))))
(define-syntax-class compound-links
#:literals (link)
(pattern (link ld:linkage-decl ...)
#:attr all-export-links (map syntax->list (syntax->list #'(ld.exported-keys ...)))
#:attr all-import-links (map syntax->list (syntax->list #'(ld.imported-keys ...)))))
(define-syntax-class linkage-decl
(pattern ((lb:link-binding ...)
unit-expr:expr
link-id:id ...)
#:attr exported-keys #'(lb.link-id ...)
#:with imported-keys #'(link-id ...)))
(define-syntax-class link-binding
(pattern (link-id:id : sig-id:id)))
;; build-compound-unit-prop : (listof id) (listof (listof id?)) (listof id?)
;; -> (list (listof symbol?)
;; (listof (listof symbol?))
;; (listof (listof symbol?)))
;; Process the link bindings of a compound-unit form
;; to return a syntax property used for typechecking compound-unit forms
;; The return value is a list to be attached as a syntax property to compound-unit
;; forms.
;; The list contains 3 elements
;; - The first element is a list of symbols corresponding to the link-ids of
;; the compound-unit's imports
;; - The second element is a list of lists of symbols, corresponding to the
;; link-ids exported by units in the compound-unit's linking clause
;; - The last element is also a list of lists of symbols, corresponding to the
;; link-ids being imported by units in the compound-unit's linking clause
(define (build-compound-unit-prop import-tags all-import-links all-export-links)
(define table
(make-immutable-free-id-table
(for/list ([link (in-list (append import-tags (flatten all-export-links)))])
(cons link (gensym (syntax-e link))))))
(define imports-tags
(map (λ (id) (free-id-table-ref table id #f)) import-tags))
(define units-exports
(map
(λ (lst) (map (λ (id) (free-id-table-ref table id #f)) lst))
all-export-links))
(define units-imports
(for/list ([unit-links (in-list all-import-links)])
(for/list ([unit-link (in-list unit-links)])
(free-id-table-ref table unit-link #f))))
(list imports-tags units-exports units-imports)))
(define-syntax (compound-unit stx)
(syntax-parse stx
[(_ imports:compound-imports
exports
links:compound-links)
(define import-tags (attribute imports.import-tags))
(define all-import-links (attribute links.all-import-links))
(define all-export-links (attribute links.all-export-links))
(define prop (build-compound-unit-prop import-tags all-import-links all-export-links))
(ignore (tr:unit:compound-property
(quasisyntax/loc stx (untyped-compound-unit imports exports links))
prop))]))
(define-syntax (define-compound-unit stx)
(syntax-parse stx
[(_ uid
imports:compound-imports
exports
links:compound-links)
(define import-tags (attribute imports.import-tags))
(define all-import-links (attribute links.all-import-links))
(define all-export-links (attribute links.all-export-links))
(define prop (build-compound-unit-prop import-tags all-import-links all-export-links))
(process-definition-form
(λ (stx) (tr:unit:compound-property stx prop))
(quasisyntax/loc stx
(untyped-define-compound-unit uid imports exports links)))]))
(define-syntax (compound-unit/infer stx)
(syntax-parse stx
#:literals (import export link)
[(_ . rest)
(ignore
(tr:unit:compound-property
(quasisyntax/loc stx
(untyped-compound-unit/infer . rest))
'infer))]))
(define-syntax (define-compound-unit/infer stx)
(syntax-parse stx
[(_ . rest)
(process-definition-form
(λ (stx) (tr:unit:compound-property stx'infer))
(quasisyntax/loc stx (untyped-define-compound-unit/infer . rest)))]))
(define-syntax (unit-from-context stx)
(syntax-parse stx
[(_ . rest)
(ignore
(tr:unit:from-context
(quasisyntax/loc stx
(untyped-unit-from-context . rest))))]))
(define-syntax (define-unit-from-context stx)
(syntax-parse stx
[(_ . rest)
(process-definition-form
(λ (stx) (tr:unit:from-context stx))
(quasisyntax/loc stx (untyped-define-unit-from-context . rest)))]))

View File

@ -39,7 +39,8 @@
(do-time "Fixed contract ids"))]
;; add the real definitions of contracts on the before- and after-code
[(before-code ...) (change-provide-fixups (flatten-all-begins pre-before-code))]
[(after-code ...) (change-provide-fixups (flatten-all-begins pre-after-code))]
[(after-code ...) (begin0 (change-provide-fixups (flatten-all-begins pre-after-code))
(do-time "Generated contracts"))]
;; potentially optimize the code based on the type information
[(optimized-body ...) (maybe-optimize #'transformed-body)] ;; has own call to do-time
;; add in syntax property on useless expression to draw check-syntax arrows
@ -54,8 +55,6 @@
#,(if (unbox include-extra-requires?) extra-requires #'(begin))
before-code ... optimized-body ... after-code ... check-syntax-help)))))))]))
(define did-I-suggest-:print-type-already? #f)
(define :print-type-message " ... [Use (:print-type <expr>) to see more.]")
(define (ti-core stx )
(current-type-names (init-current-type-names))
(syntax-parse stx
@ -69,67 +68,4 @@
;; TODO(endobson): Remove the call to do-standard-inits when it is no longer necessary
;; Cast at the top-level still needs this for some reason
(do-standard-inits)
(tc-toplevel/full stx #'form
(λ (body2 type)
(with-syntax*
([(optimized-body ...) (maybe-optimize #`(#,body2))]
;; Transform after optimization for top-level because the flattening will
;; change syntax object identity (via syntax-track-origin) which doesn't work
;; for looking up types in the optimizer.
[(transformed-body ...)
(change-contract-fixups (flatten-all-begins #'(begin optimized-body ...)))])
(define ty-str
(match type
;; 'no-type means the form is not an expression and
;; has no meaningful type to print
['no-type #f]
;; don't print results of type void
[(tc-result1: (== -Void type-equal?)) #f]
;; don't print results of unknown type
[(tc-any-results: f) #f]
[(tc-result1: t f o)
;; Don't display the whole types at the REPL. Some case-lambda types
;; are just too large to print.
;; Also, to avoid showing too precise types, we generalize types
;; before printing them.
(define tc (cleanup-type t))
(define tg (generalize tc))
(format "- : ~a~a~a\n"
(pretty-format-type tg #:indent 4)
(cond [(equal? tc tg) ""]
[else (format " [more precisely: ~a]" tc)])
(cond [(equal? tc t) ""]
[did-I-suggest-:print-type-already? " ..."]
[else (set! did-I-suggest-:print-type-already? #t)
:print-type-message]))]
[(tc-results: t)
(define tcs (map cleanup-type t))
(define tgs (map generalize tcs))
(define tgs-val (make-Values (map -result tgs)))
(define formatted (pretty-format-type tgs-val #:indent 4))
(define indented? (regexp-match? #rx"\n" formatted))
(format "- : ~a~a~a\n"
formatted
(cond [(andmap equal? tgs tcs) ""]
[indented?
(format "\n[more precisely: ~a]"
(pretty-format-type (make-Values (map -result tcs))
#:indent 17))]
[else (format " [more precisely: ~a]" (cons 'Values tcs))])
;; did any get pruned?
(cond [(andmap equal? t tcs) ""]
[did-I-suggest-:print-type-already? " ..."]
[else (set! did-I-suggest-:print-type-already? #t)
:print-type-message]))]
[x (int-err "bad type result: ~a" x)]))
(if (and ty-str
(not (null? (syntax-e #'(transformed-body ...)))))
(with-syntax ([(transformed-body ... transformed-last)
#'(transformed-body ...)])
#`(begin #,(if (unbox include-extra-requires?)
extra-requires
#'(begin))
#,(arm #'(begin transformed-body ...))
(begin0 #,(arm #'transformed-last)
(display '#,ty-str))))
(arm #'(begin transformed-body ...))))))]))
(tc-toplevel/full stx #'form)]))

View File

@ -0,0 +1,14 @@
#lang racket/base
(require racket/dict racket/sequence)
(provide id< sorted-dict-map in-sorted-dict)
(define (id< a b) (symbol<? (syntax-e a) (syntax-e b)))
(define (sorted-dict-map dict f <)
(define sorted (sort #:key car (dict-map dict cons) <))
(map (lambda (a) (f (car a) (cdr a))) sorted))
(define (in-sorted-dict dict <)
(define sorted (sort #:key car (dict-map dict cons) <))
(in-dict sorted))

View File

@ -5,6 +5,7 @@
(require "../types/tc-error.rkt"
"../utils/tc-utils.rkt"
"env-utils.rkt"
syntax/parse
syntax/id-table
racket/lazy-require)
@ -102,4 +103,4 @@
;; map over the-mapping, producing a list
;; (id type -> T) -> listof[T]
(define (type-env-map f)
(free-id-table-map the-mapping f))
(sorted-dict-map the-mapping f id<))

View File

@ -8,11 +8,12 @@
"type-name-env.rkt"
"type-alias-env.rkt"
"mvar-env.rkt"
"signature-env.rkt"
(rename-in racket/private/sort [sort raw-sort])
(rep type-rep object-rep filter-rep rep-utils free-variance)
(rep type-rep object-rep prop-rep rep-utils free-variance)
(for-syntax syntax/parse racket/base)
(types abbrev union)
racket/dict racket/list
racket/dict racket/list racket/set racket/promise
mzlib/pconvert racket/match)
(provide ;; convenience form for defining an initial environment
@ -26,7 +27,8 @@
tvariance-env-init-code
talias-env-init-code
env-init-code
mvar-env-init-code)
mvar-env-init-code
signature-env-init-code)
(define-syntax (define-initial-env stx)
(syntax-parse stx
@ -62,22 +64,28 @@
[(? Rep? (app (lambda (v) (hash-ref predefined-type-table (Rep-seq v) #f)) (? values id))) id]
[(Listof: elem-ty)
`(-lst ,(sub elem-ty))]
[(Function: (list (arr: dom (Values: (list (Result: t (FilterSet: (Top:) (Top:)) (Empty:)))) #f #f '())))
[(Function: (list (arr: dom (Values: (list (Result: t
(PropSet: (TrueProp:)
(TrueProp:))
(Empty:))))
#f #f '())))
`(simple-> (list ,@(map sub dom)) ,(sub t))]
[(Function: (list (arr: dom (Values: (list (Result: t (FilterSet: (TypeFilter: ft pth)
(NotTypeFilter: ft pth))
[(Function: (list (arr: dom (Values: (list (Result: t (PropSet: (TypeProp: pth ft)
(NotTypeProp: pth ft))
(Empty:))))
#f #f '())))
`(make-pred-ty (list ,@(map sub dom)) ,(sub t) ,(sub ft) ,(sub pth))]
[(Function: (list (arr: dom (Values: (list (Result: t (FilterSet: (NotTypeFilter: (== -False)
(Path: pth (list 0 0)))
(TypeFilter: (== -False)
(Path: pth (list 0 0))))
[(Function: (list (arr: dom (Values: (list (Result: t (PropSet: (NotTypeProp: (Path: pth (list 0 0))
(== -False))
(TypeProp: (Path: pth (list 0 0))
(== -False)))
(Path: pth (list 0 0)))))
#f #f '())))
`(->acc (list ,@(map sub dom)) ,(sub t) ,(sub pth))]
[(Result: t (FilterSet: (Top:) (Top:)) (Empty:)) `(-result ,(sub t))]
[(Result: t (PropSet: (TrueProp:) (TrueProp:)) (Empty:)) `(-result ,(sub t))]
[(Union: elems) (split-union elems)]
[(Intersection: elems) `(make-Intersection (set ,@(for/list ([elem (in-immutable-set elems)])
(sub elem))))]
[(Base: n cnt pred _) (int-err "Base type ~a not in predefined-type-table" n)]
[(Name: stx args struct?)
`(make-Name (quote-syntax ,stx) ,args ,struct?)]
@ -118,12 +126,23 @@
(set-box! cache-box
(dict-set (unbox cache-box) v (list name class-type))))
(if cache-box name class-type)])]
[(Signature: name extends mapping)
(define (serialize-mapping m)
(map (lambda (id/ty)
(define id (car id/ty))
(define ty (force (cdr id/ty)))
`(cons (quote-syntax ,id) ,(sub ty)))
m))
(define serialized-extends (and extends `(quote-syntax ,extends)))
`(make-Signature (quote-syntax ,name)
,serialized-extends
(list ,@(serialize-mapping mapping)))]
[(arr: dom rng rest drest kws)
`(make-arr ,(sub dom) ,(sub rng) ,(sub rest) ,(sub drest) ,(sub kws))]
[(TypeFilter: t p)
`(make-TypeFilter ,(sub t) ,(sub p))]
[(NotTypeFilter: t p)
`(make-NotTypeFilter ,(sub t) ,(sub p))]
[(TypeProp: o t)
`(make-TypeProp ,(sub o) ,(sub t))]
[(NotTypeProp: o t)
`(make-NotTypeProp ,(sub o) ,(sub t))]
[(Path: p i)
`(make-Path ,(sub p) ,(if (identifier? i)
`(quote-syntax ,i)
@ -184,3 +203,8 @@
(make-init-code
(λ (f) (dict-map mvar-env f))
(lambda (id v) (and v #`(register-mutated-var #'#,id)))))
(define (signature-env-init-code)
(make-init-code
signature-env-map
(lambda (id sig) #`(register-signature! #'#,id #,(quote-type sig)))))

View File

@ -0,0 +1,81 @@
#lang racket/base
;; Environment for signature definitions
;; to track bindings and type definitions inside of signatures
(provide register-signature!
finalize-signatures!
lookup-signature
lookup-signature/check
signature-env-map
with-signature-env/extend)
(require syntax/id-table
racket/match
racket/promise
(for-syntax syntax/parse racket/base)
"env-utils.rkt"
"../utils/utils.rkt"
(utils tc-utils)
(rep type-rep))
;; initial signature environment
(define signature-env (make-parameter (make-immutable-free-id-table)))
;; register-signature! : identifier? Signature? -> Void
;; adds a mapping from the given identifier to the given signature
;; in the signature environment
(define (register-signature! id sig)
(when (lookup-signature id)
(tc-error/fields "duplicate signature definition"
"identifier" (syntax-e id)))
(signature-env (free-id-table-set (signature-env) id sig)))
(define-syntax-rule (with-signature-env/extend ids sigs . b)
(let ([ids* ids]
[sigs* sigs])
(define new-env
(for/fold ([env (signature-env)])
([id (in-list ids*)]
[sig (in-list sigs*)])
(free-id-table-set env id sig)))
(parameterize ([signature-env new-env]) . b)))
;; Iterate over the signature environment forcing the types of bindings
;; in each signature
(define (finalize-signatures!)
(signature-env
(make-immutable-free-id-table
(signature-env-map
(lambda (id sig)
(cons
id
(match sig
[(Signature: name extends mapping)
(make-Signature
name
extends
(map
(match-lambda [(cons id ty) (cons id (force ty))])
mapping))]
[_ #f])))))))
;; lookup-signature : identifier? -> (or/c #f Signature?)
;; look up the signature corresponding to the given identifier
;; in the signature environment
(define (lookup-signature id)
(free-id-table-ref (signature-env) id #f))
;; lookup-signature/check : identifier? -> Signature?
;; lookup the identifier in the signature environment
;; errors if there is no such typed signature
(define (lookup-signature/check id)
(or (lookup-signature id)
(tc-error/fields "use of untyped signature in typed code"
#:more "consider using `require/typed' to import it"
"signature" (syntax-e id)
#:stx id)))
(define (signature-env-map f)
(sorted-dict-map (signature-env) f id<))

View File

@ -0,0 +1,157 @@
#lang racket/base
;; This module provides helper functions for typed signatures
(require "../utils/utils.rkt"
syntax/id-set
(utils tc-utils)
(env signature-env)
(rep type-rep)
(private parse-type)
syntax/parse
racket/list
racket/match
racket/promise
racket/unit-exptime
(for-template racket/base
(submod "../typecheck/internal-forms.rkt" forms)))
(provide parse-and-register-signature! signature->bindings signatures->bindings)
;; parse-signature : Syntax -> Signature
;; parses the internal syntax of a signature form to build the internal representation
;; of a typed signature and registers the internal representation with the
;; Signature environment
;; The parsed syntax is created by uses of `define-signature-internal` which are
;; inserted by the typed version of the `define-signature` macro, this function
;; uses that syntax to created a Typed Representation of the signature to
;; place in the Signature environment
;; The parsed syntax contains the following fields:
;; - name is the name of the signature being defined
;; - the parent-signature is the name of the signature being extended or #f
;; if the signature defintion does not extend any signature
;; - a list of bindings, each of the form [name : Type], for each variable
;; in the signature
;; - The check field indicates that this syntax came from a use of require/typed
;; using the #:signature clause, and listed bindings muct be checked to ensure
;; they are consistent with the statically known signature variables
(define (parse-and-register-signature! form)
(syntax-parse form
#:literal-sets (kernel-literals)
#:literals (values define-signature-internal)
[(define-values ()
(begin
(quote-syntax
(define-signature-internal name
#:parent-signature super
(binding ...)
#:check? check) #:local)
(#%plain-app values)))
(define raw-map (syntax->list #'(binding ...)))
(define check? (syntax->datum #'check))
(define extends (get-extended-signature #'name #'super check? form))
(define super-bindings (get-signature-mapping extends))
(define new-bindings (map parse-signature-binding raw-map))
(define pre-mapping (append super-bindings new-bindings))
;; Make sure a require/typed signature has bindings listed
;; that are consistent with its statically determined bindings
(when check?
(check-signature-bindings #'name (map car pre-mapping) form))
;; require/typed signature bindings may not be in the correct order
;; this fixes the ordering based on the static order determined
;; by signature-members
(define mapping (if check?
(fix-order #'name pre-mapping)
pre-mapping))
(define signature (make-Signature #'name extends mapping))
(register-signature! #'name signature)]))
;; check-signature-bindings : Identifier (Listof Identifier) -> Void
;; checks that the bindings of a signature identifier are consistent with
;; those listed in a require/typed clause
(define (check-signature-bindings name vars stx)
(match-define-values (_ inferred-vars inferred-defs _) (signature-members name name))
(define (make-id-set set) (immutable-free-id-set set #:phase (add1 (syntax-local-phase-level))))
(define inferred-vars-set (make-id-set inferred-vars))
(define vars-set (make-id-set vars))
(unless (empty? inferred-defs)
(tc-error/stx name "untyped signatures containing definitions are prohibited"))
(unless (free-id-set=? inferred-vars-set vars-set)
(tc-error/fields "required signature declares inconsistent members"
"expected members" (map syntax-e inferred-vars)
"received members" (map syntax-e vars)
#:stx stx)))
;; get-extended-signature : Identifier Syntax Boolean -> (Option Signature)
;; Checks if the extended signature information must be inferred and looks
;; up the super signature in the environment
;; Raises an error if a super signature is inferred that is not in the
;; signature environment indicative of a signature that should be require/typed
;; but was not, a typed binding for the parent signature is necessary to correctly
;; check subtyping for units
(define (get-extended-signature name super check? stx)
(cond
[check?
(match-define-values (inferred-super _ _ _) (signature-members name name))
(and inferred-super
(or (and (lookup-signature inferred-super) inferred-super)
(tc-error/fields "required signature extends an untyped signature"
"required signature" (syntax-e name)
"extended signature" (syntax-e inferred-super)
#:stx stx)))]
[(not (syntax->datum super)) #f]
;; This case should probably be an error, because if the signature was not false
;; the lookup may still silently fail which should not be allowed here
[else (or (and (lookup-signature super) super)
(tc-error/fields "signature definition extends untyped signature"
"in the definition of signature" (syntax-e name)
"which extends signature" (syntax-e super)
#:stx stx))]))
;; parse-signature-binding : Syntax -> (list/c identifier? syntax?)
;; parses the binding forms inside of a define signature into the
;; form used by the Signature type representation
;; The call to `parse-type` is delayed to allow signatures and type aliases
;; to be mutually recursive, after aliases are registered in the environment
;; the promise will be forced to perform the actual type parsing
(define (parse-signature-binding binding-stx)
(syntax-parse binding-stx
[[name:id type]
(cons #'name (delay (parse-type #'type)))]))
;; signature->bindings : identifier? -> (listof (cons/c identifier? type?))
;; GIVEN: a signature name
;; RETURNS: the list of variables bound by that signature
;; inherited bindings come first
(define (signature->bindings sig-id)
(define sig (lookup-signature sig-id))
(let loop ([sig (Signature-extends sig)]
[mapping (Signature-mapping sig)]
[bindings null])
(if sig
(loop (Signature-extends (lookup-signature sig))
(Signature-mapping (lookup-signature sig))
(append mapping bindings))
(append mapping bindings))))
;; (listof identifier?) -> (listof (cons/c identifier? type?))
;; GIVEN: a list of signature names
;; RETURNS: the list of all bindings from those signatures
;; TODO: handle required renamings/prefix/only/except
(define (signatures->bindings ids)
(append-map signature->bindings ids))
;; get-signature-mapping : (Option Signature) -> (Listof (Cons Id Type))
(define (get-signature-mapping sig)
(if sig (Signature-mapping (lookup-signature sig)) null))
;; fix-order : id (listof (cons/c id type?)) -> (listof (cons/c id type?)
;; Returns a reordered list of signature bindings to match the order given
;; by signature-members
(define (fix-order sig-id sig-bindings)
(match-define-values (_ members _ _) (signature-members sig-id sig-id))
(map
(lambda (id) (assoc id sig-bindings free-transformer-identifier=?))
members))

View File

@ -1,6 +1,7 @@
#lang racket/base
(require "../utils/utils.rkt"
"env-utils.rkt"
syntax/id-table racket/dict
(utils tc-utils)
(typecheck renamer)
@ -61,6 +62,6 @@
;; map over the-mapping, producing a list
;; (id type -> T) -> listof[T]
(define (type-alias-env-map f)
(for/list ([(id t) (in-dict the-mapping)]
(for/list ([(id t) (in-sorted-dict the-mapping id<)]
#:when (resolved? t))
(f id (resolved-ty t))))

View File

@ -6,12 +6,12 @@
(contract-req)
(rep object-rep))
(require-for-cond-contract (rep type-rep filter-rep))
(require-for-cond-contract (rep type-rep prop-rep))
;; types is a free-id-table of identifiers to types
;; props is a list of known propositions
(define-struct/cond-contract env ([types immutable-free-id-table?]
[props (listof Filter/c)]
[props (listof Prop?)]
[aliases immutable-free-id-table?])
#:transparent
#:property prop:custom-write
@ -23,8 +23,8 @@
[extend (env? identifier? Type/c . -> . env?)]
[extend/values (env? (listof identifier?) (listof Type/c) . -> . env?)]
[lookup (env? identifier? (identifier? . -> . any) . -> . any)]
[env-props (env? . -> . (listof Filter/c))]
[replace-props (env? (listof Filter/c) . -> . env?)]
[env-props (env? . -> . (listof Prop?))]
[replace-props (env? (listof Prop?) . -> . env?)]
[empty-prop-env env?]
[extend+alias/values (env? (listof identifier?) (listof Type/c) (listof Object?) . -> . env?)]
[lookup-alias (env? identifier? (identifier? . -> . (or/c #f Object?)) . -> . (or/c #f Object?))])

View File

@ -2,7 +2,8 @@
;; Environment for type names
(require "../utils/utils.rkt")
(require "../utils/utils.rkt"
"env-utils.rkt")
(require syntax/id-table
(contract-req)
@ -58,7 +59,7 @@
;; map over the-mapping, producing a list
;; (id type -> T) -> listof[T]
(define (type-name-env-map f)
(free-id-table-map the-mapping f))
(sorted-dict-map the-mapping f id<))
(define (add-alias from to)
(when (lookup-type-name to (lambda () #f))
@ -83,7 +84,7 @@
;; map over the-mapping, producing a list
;; (id variance -> T) -> listof[T]
(define (type-variance-env-map f)
(free-id-table-map variance-mapping f))
(sorted-dict-map variance-mapping f id<))
;; Refines the variance of a type in the name environment
(define (refine-variance! names types tvarss)

View File

@ -7,7 +7,7 @@
racket/match
racket/list)
(import restrict^ dmap^)
(import intersect^ dmap^)
(export constraints^)
;; Widest constraint possible
@ -34,7 +34,7 @@
;; intersect the given types. produces a lower bound on both, but
;; perhaps not the GLB
(define (meet S T)
(let ([s* (restrict S T)])
(let ([s* (intersect S T)])
(if (and (subtype s* S)
(subtype s* T))
s*

View File

@ -14,7 +14,7 @@
#'((early-return rhs ...))))
(syntax-parse stx
[(_ e [c . r:rhs] ...)
#'(match* e [c . r.r] ...)]))
(syntax/loc stx (match* e [c . r.r] ...))]))
(begin-for-syntax
(define-splicing-syntax-class arg

View File

@ -11,7 +11,7 @@
(except-in
(combine-in
(utils tc-utils)
(rep free-variance type-rep filter-rep object-rep rep-utils)
(rep free-variance type-rep prop-rep object-rep rep-utils)
(types utils abbrev numeric-tower union subtype resolve
substitute generalize prefab)
(env index-env tvar-env))
@ -19,7 +19,7 @@
"constraint-structs.rkt"
"signatures.rkt" "fail.rkt"
"promote-demote.rkt"
racket/match
racket/match racket/set
mzlib/etc
(contract-req)
(for-syntax
@ -212,7 +212,7 @@
[(_ seq) #'(app List->seq (? values seq))])))
;; generate-dbound-prefix: Symbol Type/c Natural Symbol -> (Values (Listof Symbol) (Listof Type/c))
;; generate-dbound-prefix: Symbol Type/c Natural (U Symbol #f) -> (Values (Listof Symbol) (Listof Type/c))
;; Substitutes n fresh new variables, replaces dotted occurences of v in t with the variables (and
;; maybe new-end), and then for each variable substitutes it in for regular occurences of v.
(define (generate-dbound-prefix v ty n new-end)
@ -224,23 +224,23 @@
(substitute (make-F var) v ty*))))
(define/cond-contract (cgen/filter context s t)
(context? Filter? Filter? . -> . (or/c #f cset?))
(define/cond-contract (cgen/prop context s t)
(context? Prop? Prop? . -> . (or/c #f cset?))
(match* (s t)
[(e e) (empty-cset/context context)]
[(e (Top:)) (empty-cset/context context)]
[(e (TrueProp:)) (empty-cset/context context)]
;; FIXME - is there something to be said about the logical ones?
[((TypeFilter: s p) (TypeFilter: t p)) (cgen/inv context s t)]
[((NotTypeFilter: s p) (NotTypeFilter: t p)) (cgen/inv context s t)]
[((TypeProp: o s) (TypeProp: o t)) (cgen/inv context s t)]
[((NotTypeProp: o s) (NotTypeProp: o t)) (cgen/inv context s t)]
[(_ _) #f]))
;; s and t must be *latent* filter sets
(define/cond-contract (cgen/filter-set context s t)
(context? FilterSet? FilterSet? . -> . (or/c #f cset?))
;; s and t must be *latent* prop sets
(define/cond-contract (cgen/prop-set context s t)
(context? PropSet? PropSet? . -> . (or/c #f cset?))
(match* (s t)
[(e e) (empty-cset/context context)]
[((FilterSet: s+ s-) (FilterSet: t+ t-))
(% cset-meet (cgen/filter context s+ t+) (cgen/filter context s- t-))]
[((PropSet: p+ p-) (PropSet: q+ q-))
(% cset-meet (cgen/prop context p+ q+) (cgen/prop context p- q-))]
[(_ _) #f]))
(define/cond-contract (cgen/object context s t)
@ -320,7 +320,7 @@
(% move-dotted-rest-to-dmap (cgen (context-add-var context dbound) s-dty t-dty) dbound dbound*)))]
[((seq ss (dotted-end s-dty dbound))
(seq ts (dotted-end t-dty dbound*)))
#:when (inferable-index? context dbound*)
#:return-unless (inferable-index? context dbound*) #f
#:return-unless (= (length ss) (length ts)) #f
(% cset-meet
(cgen/list context ss ts)
@ -439,26 +439,26 @@
;; CG-Top
[(_ (Univ:)) empty]
;; AnyValues
[((AnyValues: s-f) (AnyValues: t-f))
(cgen/filter context s-f t-f)]
[((AnyValues: p) (AnyValues: q))
(cgen/prop context p q)]
[((or (Values: (list (Result: _ fs _) ...))
(ValuesDots: (list (Result: _ fs _) ...) _ _))
(AnyValues: t-f))
[((or (Values: (list (Result: _ psets _) ...))
(ValuesDots: (list (Result: _ psets _) ...) _ _))
(AnyValues: q))
(cset-join
(filter identity
(for/list ([f (in-list fs)])
(match f
[(FilterSet: f+ f-)
(% cset-meet (cgen/filter context f+ t-f) (cgen/filter context f- t-f))]))))]
(for/list ([pset (in-list psets)])
(match pset
[(PropSet: p+ p-)
(% cset-meet (cgen/prop context p+ q) (cgen/prop context p- q))]))))]
;; check all non Type/c first so that calling subtype is safe
;; check each element
[((Result: s f-s o-s)
(Result: t f-t o-t))
[((Result: s pset-s o-s)
(Result: t pset-t o-t))
(% cset-meet (cg s t)
(cgen/filter-set context f-s f-t)
(cgen/prop-set context pset-s pset-t)
(cgen/object context o-s o-t))]
;; Values just delegate to cgen/seq, except special handling for -Bottom.
@ -525,6 +525,19 @@
[((? Mu? s) t) (cg (unfold s) t)]
[(s (? Mu? t)) (cg s (unfold t))]
;; find *an* element of elems which can be made a subtype of T
[((Intersection: ts) T)
(cset-join
(for*/list ([t (in-immutable-set ts)]
[v (in-value (cg t T))]
#:when v)
v))]
;; constrain S to be below *each* element of elems, and then combine the constraints
[(S (Intersection: ts))
(define cs (for/list/fail ([ts (in-immutable-set ts)]) (cg S ts)))
(and cs (cset-meet* (cons empty cs)))]
;; constrain *each* element of es to be below T, and then combine the constraints
[((Union: es) T)
(define cs (for/list/fail ([e (in-list es)]) (cg e T)))
@ -540,6 +553,13 @@
#:when v)
v))]
;; from define-new-subtype
[((Distinction: nm1 id1 S) (app resolve (Distinction: nm2 id2 T)))
#:when (and (equal? nm1 nm2) (equal? id1 id2))
(cg S T)]
[((Distinction: _ _ S) T)
(cg S T)]
;; two structs with the same name
;; just check pairwise on the fields
[((Struct: nm _ flds proc _ _) (Struct: nm* _ flds* proc* _ _))
@ -861,21 +881,22 @@
(early-return
(define short-S (take S (length T)))
(define rest-S (drop S (length T)))
(define ctx (context null X (list dotted-var)))
(define expected-cset (if expected
(cgen ctx R expected)
(empty-cset '() '())))
#:return-unless expected-cset #f
(define cs-short (cgen/list ctx short-S T #:expected-cset expected-cset))
#:return-unless cs-short #f
;; Generate a new type corresponding to T-dotted for every extra arg.
(define-values (new-vars new-Ts)
(generate-dbound-prefix dotted-var T-dotted (length rest-S) #f))
(define cs-dotted (cgen/list (context-add-vars ctx new-vars) rest-S new-Ts
#:expected-cset expected-cset))
#:return-unless cs-dotted #f
(define cs-dotted* (move-vars-to-dmap cs-dotted dotted-var new-vars))
#:return-unless cs-dotted* #f
(define cs (cset-meet cs-short cs-dotted*))
(define (subst t)
(substitute-dots (map make-F new-vars) #f dotted-var t))
(define ctx (context null (append new-vars X) (list dotted-var)))
(define expected-cset (if expected
(cgen ctx (subst R) expected)
(empty-cset '() '())))
#:return-unless expected-cset #f
(define cs (% move-vars-to-dmap
(% cset-meet
(cgen/list ctx short-S (map subst T) #:expected-cset expected-cset)
(cgen/list ctx rest-S new-Ts #:expected-cset expected-cset))
dotted-var new-vars))
#:return-unless cs #f
(define m (cset-meet cs expected-cset))
#:return-unless m #f

View File

@ -1,11 +1,11 @@
#lang racket/base
(require "infer-unit.rkt" "constraints.rkt" "dmap.rkt" "signatures.rkt"
"restrict.rkt"
"intersect.rkt"
(only-in racket/unit provide-signature-elements
define-values/invoke-unit/infer link))
(provide-signature-elements restrict^ infer^)
(provide-signature-elements intersect^ infer^)
(define-values/invoke-unit/infer
(link infer@ constraints@ dmap@ restrict@))
(link infer@ constraints@ dmap@ intersect@))

View File

@ -0,0 +1,71 @@
#lang racket/unit
(require "../utils/utils.rkt")
(require (rep type-rep)
(types abbrev base-abbrev union subtype resolve)
"signatures.rkt"
racket/match
racket/set)
(import infer^)
(export intersect^)
;; compute the intersection of two types
;; (note: previously called restrict)
(define (intersect t1 t2)
;; build-type: build a type while propogating bottom
(define (build-type constructor . args)
(if (memf Bottom? args) -Bottom (apply constructor args)))
;; resolved is a set tracking previously seen intersect cases
;; (i.e. pairs of t1 t2) to prevent infinite unfolding.
;; subtyping performs a similar check for the same
;; reason
(let intersect
([t1 t1] [t2 t2] [resolved (set)])
(match*/no-order
(t1 t2)
;; already a subtype
[(t1 t2) #:no-order #:when (subtype t1 t2) t1]
;; polymorphic intersect
[(t1 (Poly: vars t))
#:no-order
#:when (infer vars null (list t1) (list t) #f)
t1]
;; structural recursion on types
[((Pair: a1 d1) (Pair: a2 d2))
(build-type -pair
(intersect a1 a2 resolved)
(intersect d1 d2 resolved))]
;; FIXME: support structural updating for structs when structs are updated to
;; contain not only *if* they are polymorphic, but *which* fields are too
;;[((Struct: _ _ _ _ _ _)
;; (Struct: _ _ _ _ _ _))]
[((Syntax: t1*) (Syntax: t2*))
(build-type -Syntax (intersect t1* t2* resolved))]
[((Promise: t1*) (Promise: t2*))
(build-type -Promise (intersect t1* t2* resolved))]
;; unions
[((Union: t1s) t2)
#:no-order
(apply Un (map (λ (t1) (intersect t1 t2 resolved)) t1s))]
;; intersections
[((Intersection: t1s) t2)
#:no-order
(apply -unsafe-intersect (for/list ([t1 (in-immutable-set t1s)])
(intersect t1 t2 resolved)))]
;; resolve resolvable types if we haven't already done so
[((? needs-resolving? t1) t2)
#:no-order
#:when (not (or (set-member? resolved (cons t1 t2))
(set-member? resolved (cons t2 t1))))
(intersect (resolve t1) t2 (set-add resolved (cons t1 t2)))]
;; t2 and t1 have a complex relationship, so we build an intersection
;; (note: intersection checks for overlap)
[(t1 t2) (-unsafe-intersect t1 t2)])))

View File

@ -15,13 +15,13 @@
(for/or ([e (in-list (append* (map fv ts)))])
(memq e V)))
;; get-filters : SomeValues -> FilterSet
;; extract filters out of the range of a function type
(define (get-filters rng)
;; get-propset : SomeValues -> PropSet
;; extract prop sets out of the range of a function type
(define (get-propsets rng)
(match rng
[(AnyValues: f) (list (-FS f f))]
[(Values: (list (Result: _ lf _) ...)) lf]
[(ValuesDots: (list (Result: _ lf _) ...) _ _) lf]))
[(AnyValues: p) (list (-PS p p))]
[(Values: (list (Result: _ propsets _) ...)) propsets]
[(ValuesDots: (list (Result: _ propsets _) ...) _ _) propsets]))
(begin-encourage-inline
@ -43,7 +43,7 @@
(match arr
[(arr: dom rng rest drest kws)
(cond
[(apply V-in? V (get-filters rng))
[(apply V-in? V (get-propsets rng))
#f]
[(and drest (memq (cdr drest) V))
(make-arr (map contra dom)
@ -63,7 +63,7 @@
[(Function: arrs)
(make-Function (filter-map arr-change arrs))]
[(? structural?) (structural-map T structural-recur)]
[(? Filter?) ((sub-f co) T)]
[(? Prop?) ((sub-f co) T)]
[(? Object?) ((sub-o co) T)]
[(? Type?) ((sub-t co) T)]))
(define (var-promote T V)

View File

@ -1,68 +0,0 @@
#lang racket/unit
(require "../utils/utils.rkt")
(require (rep type-rep)
(types abbrev base-abbrev union subtype remove-intersect resolve)
"signatures.rkt"
racket/match
racket/set)
(import infer^)
(export restrict^)
;; restrict t1 to be a subtype of t2
;; if `pref' is 'new, use t2 when giving up, otherwise use t1
(define (restrict t1 t2 [pref 'new])
;; build-type: build a type while propogating bottom
(define (build-type constructor . args)
(if (memf Bottom? args) -Bottom (apply constructor args)))
;; resolved is a set tracking previously seen restrict cases
;; (i.e. pairs of t1 t2) to prevent infinite unfolding.
;; subtyping performs a similar check for the same
;; reason
(define (restrict* t1 t2 pref resolved)
(match* (t1 t2)
;; already a subtype
[(_ _) #:when (subtype t1 t2)
t1]
;; polymorphic restrict
[(_ (Poly: vars t)) #:when (infer vars null (list t1) (list t) #f)
t1]
;; structural recursion on types
[((Pair: a1 d1) (Pair: a2 d2))
(build-type -pair
(restrict* a1 a2 pref resolved)
(restrict* d1 d2 pref resolved))]
;; FIXME: support structural updating for structs when structs are updated to
;; contain not only *if* they are polymorphic, but *which* fields are too
;;[((Struct: _ _ _ _ _ _)
;; (Struct: _ _ _ _ _ _))]
[((Syntax: t1*) (Syntax: t2*))
(build-type -Syntax (restrict* t1* t2* pref resolved))]
[((Promise: t1*) (Promise: t2*))
(build-type -Promise (restrict* t1* t2* pref resolved))]
;; unions
[((Union: t1s) _) (apply Un (map (λ (t1*) (restrict* t1* t2 pref resolved)) t1s))]
[(_ (Union: t2s)) (apply Un (map (λ (t2*) (restrict* t1 t2* pref resolved)) t2s))]
;; resolve resolvable types if we haven't already done so
[((? needs-resolving?) _) #:when (not (set-member? resolved (cons t1 t2)))
(restrict* (resolve t1) t2 pref (set-add resolved (cons t1 t2)))]
[(_ (? needs-resolving?)) #:when (not (set-member? resolved (cons t1 t2)))
(restrict* t1 (resolve t2) pref (set-add resolved (cons t1 t2)))]
;; we don't actually want this - want something that's a part of t1
[(_ _) #:when (subtype t2 t1)
t2]
;; there's no overlap, so the restriction is empty
[(_ _) #:when (not (overlap t1 t2))
(Un)]
;; t2 and t1 have a complex relationship, so we punt
[(_ _) (if (eq? pref 'new) t2 t1)]))
(restrict* t1 t2 pref (set)))

View File

@ -20,8 +20,8 @@
[cond-contracted cset-join ((listof cset?) . -> . cset?)]
[cond-contracted c-meet ((c? c?) (symbol?) . ->* . (or/c #f c?))]))
(define-signature restrict^
([cond-contracted restrict ((Type/c Type/c) ((or/c 'new 'orig)) . ->* . Type/c)]))
(define-signature intersect^
([cond-contracted intersect (Type/c Type/c . -> . Type/c)]))
(define-signature infer^
([cond-contracted infer ((;; variables from the forall

View File

@ -42,12 +42,12 @@
#:literal-sets (kernel-literals)
(pattern (#%plain-app op:unary-extflonum-op t:opt-expr)
#:do [(log-extfl-opt "unary extflonum")]
#:with opt #'(op.unsafe t.opt))
#:with opt (syntax/loc this-syntax (op.unsafe t.opt)))
(pattern (#%plain-app op:binary-extflonum-op t1:opt-expr t2:opt-expr)
#:do [(log-extfl-opt "binary extflonum")]
#:with opt #'(op.unsafe t1.opt t2.opt))
#:with opt (syntax/loc this-syntax (op.unsafe t1.opt t2.opt)))
(pattern (#%plain-app :fx->extfl-op f:fixnum-expr)
#:do [(log-extfl-opt "fixnum to extflonum conversion")]
#:with opt #'(unsafe-fx->extfl f.opt))
#:with opt (syntax/loc this-syntax (unsafe-fx->extfl f.opt)))
)

View File

@ -142,13 +142,13 @@
#:with opt #'(op.unsafe n.opt))
(pattern (op:fixnum-binary-op (~between ns:fixnum-expr 2 +inf.0) ...)
#:do [(log-fx-opt "binary fixnum")]
#:with opt (n-ary->binary #'op.unsafe #'(ns.opt ...)))
#:with opt (n-ary->binary this-syntax #'op.unsafe #'(ns.opt ...)))
(pattern (op:fixnum-binary-comp n1:fixnum-expr n2:fixnum-expr)
#:do [(log-fx-opt "binary fixnum comp")]
#:with opt #'(op.unsafe n1.opt n2.opt))
(pattern (op:fixnum-binary-comp n1:fixnum-expr n2:fixnum-expr ns:fixnum-expr ...)
#:do [(log-fx-opt "multi fixnum comp")]
#:with opt (n-ary-comp->binary #'op.unsafe #'n1.opt #'n2.opt #'(ns.opt ...)))
#:with opt (n-ary-comp->binary this-syntax #'op.unsafe #'n1.opt #'n2.opt #'(ns.opt ...)))
(pattern (op:nonzero-fixnum-binary-op n1:fixnum-expr n2:nonzero-fixnum-expr)
#:do [(log-fx-opt "binary nonzero fixnum")]
@ -202,7 +202,7 @@
(pattern (op:potentially-bounded-fixnum-op (~between ns:fixnum-expr 2 +inf.0) ...)
#:when (check-if-safe stx)
#:do [(log-fx-opt "fixnum bounded expr")]
#:with opt (n-ary->binary #'op.unsafe #'(ns.opt ...)))
#:with opt (n-ary->binary this-syntax #'op.unsafe #'(ns.opt ...)))
(pattern (op:potentially-bounded-nonzero-fixnum-op n1:fixnum-expr n2:nonzero-fixnum-expr)
#:when (check-if-safe stx)
#:do [(log-fx-opt "nonzero fixnum bounded expr")]

View File

@ -1,11 +1,12 @@
#lang racket/base
(require syntax/parse syntax/stx racket/promise
(require syntax/parse syntax/stx syntax/id-table racket/promise
racket/syntax racket/match syntax/parse/experimental/specialize
"../utils/utils.rkt" racket/unsafe/ops racket/sequence
(for-template racket/base racket/math racket/flonum racket/unsafe/ops)
(types numeric-tower subtype type-table utils)
(optimizer utils numeric-utils logging float unboxed-tables))
(optimizer utils numeric-utils logging float unboxed-tables)
(utils tc-utils))
(provide float-complex-opt-expr
float-complex-expr
@ -50,14 +51,56 @@
"The optimizer could optimize it better if it had type Float-Complex.")
this-syntax))
;; If a part is 0.0?
(define (0.0? stx)
(equal? (syntax->datum stx) 0.0))
;; keep track of operands that were reals (and thus had exact 0 as imaginary part)
(define real-id-table (make-free-id-table))
(define (was-real? stx)
(free-id-table-ref real-id-table stx #f))
(define (mark-as-real stx)
(free-id-table-set! real-id-table stx #t)
stx)
;; keep track of operands that were not floats (i.e. rationals and single floats)
;; to avoid prematurely converting to floats, which may change results
(define non-float-table (make-hash))
(define (as-non-float stx)
(hash-ref non-float-table stx #f))
(define (mark-as-non-float stx [orig stx])
(hash-set! non-float-table stx orig)
stx)
(define (n-ary->binary/non-floats op unsafe this-syntax cs)
(let loop ([o (stx-car cs)] [cs (stx-cdr cs)])
;; we're guaranteed to hit non-"non-float" operands before
;; we hit the end of the list. otherwise we wouldn't be doing
;; float-complex optimizations
(define c1 (stx-car cs))
(define o-nf (as-non-float o))
(define c1-nf (as-non-float c1))
(if (or o-nf c1-nf)
;; can't convert those to floats just yet, or may change
;; the result
(let ([new-o (mark-as-non-float
(quasisyntax/loc this-syntax
(#,op #,(or o-nf o) #,(or c1-nf c1))))])
(if (stx-null? (stx-cdr cs))
new-o
(loop new-o
(stx-cdr cs))))
;; we've hit floats, can start coercing
(n-ary->binary this-syntax unsafe (cons #`(real->double-flonum #,(or o-nf o)) cs)))))
;; a+bi / c+di, names for real and imag parts of result -> one let-values binding clause
(define (unbox-one-complex-/ a b c d res-real res-imag)
(define both-real? (and (0.0? b) (0.0? d)))
(define first-arg-real? (was-real? b))
(define second-arg-real? (was-real? d))
;; if both are real, we can short-circuit a lot
(define both-real? (and first-arg-real? second-arg-real?))
(define first-non-float (as-non-float a))
(define second-non-float (as-non-float c))
(when (and first-non-float (not second-non-float))
;; we're going from non-float to float operands, so need to coerce the first
(set! a #`(real->double-flonum #,a)))
;; we have the same cases as the Racket `/' primitive (except for the non-float ones)
(define d=0-case
#`(values (unsafe-fl+ (unsafe-fl/ #,a #,c)
@ -85,10 +128,42 @@
(unsafe-fl/ (unsafe-fl- (unsafe-fl* b r) a) den))])
(values (unsafe-fl/ (unsafe-fl+ b (unsafe-fl* a r)) den)
i)))
(cond [both-real?
#`[(#,res-real #,res-imag)
(cond [(and first-non-float second-non-float both-real?)
;; we haven't hit float operands, so we shouldn't coerce to float yet
#`[(#,(mark-as-non-float res-real)
#,(mark-as-real res-imag)) ; this case implies real
(values (/ #,first-non-float #,second-non-float)
0.0)]]
[second-non-float
;; may be dividing by exact 0, be conservative to preserve error
;; (res-real can't be non-float, since we've hit a float, so we either
;; error or coerce)
#`[(#,res-real #,(if both-real?
(mark-as-real res-imag)
res-imag))
(let-values ([(res-div)
(/ #,(if first-arg-real?
a
#`(make-rectangular #,a #,b))
#,(if second-arg-real?
second-non-float
#`(make-rectangular #,second-non-float
#,d)))])
#,(if both-real?
#'(values res-div 0.0)
#'(values (real-part res-div)
(imag-part res-div))))]]
[both-real?
#`[(#,res-real #,(mark-as-real res-imag))
(values (unsafe-fl/ #,a #,c)
0.0)]] ; currently not propagated
[second-arg-real?
#`[(#,res-real #,res-imag)
(values (unsafe-fl/ #,a #,c)
(unsafe-fl/ #,b #,c))]]
[first-arg-real?
(unbox-one-float-complex-/ a c d res-real res-imag)]
[else
#`[(#,res-real #,res-imag)
(cond [(unsafe-fl= #,d 0.0) #,d=0-case]
@ -112,7 +187,7 @@
#`(let* ([cm (unsafe-flabs #,c)]
[dm (unsafe-flabs #,d)]
[swap? (unsafe-fl< cm dm)]
[a #,a]
[a #,a] ; don't swap with `b` (`0`) here, but handle below
[c (if swap? #,d #,c)]
[d (if swap? #,c #,d)]
[r (unsafe-fl/ c d)]
@ -145,15 +220,25 @@
#:with (bindings ...)
#`(cs.bindings ... ...
#,@(let ()
(define (fl-sum cs) (n-ary->binary #'unsafe-fl+ cs))
(define (fl-sum cs)
(n-ary->binary/non-floats #'+ #'unsafe-fl+ this-syntax cs))
(define non-0-imags
;; to preserve result sign, ignore exact 0s
;; o/w, can have (+ -0.0 (->fl 0)) => 0.0, but would be -0.0
;; without the coercion
(for/list ([i (syntax->list #'(cs.imag-binding ...))]
#:unless (was-real? i))
i))
(list
#`((real-binding) #,(fl-sum #'(cs.real-binding ...)))
#`((imag-binding) #,(fl-sum #'(cs.imag-binding ...)))))))
#`((imag-binding)
#,(if (null? (cdr non-0-imags)) ; only one actual imag part
(car non-0-imags)
(fl-sum non-0-imags)))))))
(pattern (#%plain-app op:+^ :unboxed-float-complex-opt-expr)
#:when (subtypeof? this-syntax -FloatComplex)
#:do [(log-unboxing-opt "unboxed unary float complex")])
(pattern (#%plain-app op:-^ (~between cs:unboxed-float-complex-opt-expr 2 +inf.0) ...)
#:when (subtypeof? this-syntax -FloatComplex)
#:with (real-binding imag-binding) (binding-names)
@ -161,10 +246,21 @@
#:with (bindings ...)
#`(cs.bindings ... ...
#,@(let ()
(define (fl-subtract cs) (n-ary->binary #'unsafe-fl- cs))
(define (fl-subtract cs)
(n-ary->binary/non-floats #'- #'unsafe-fl- this-syntax cs))
(list
#`((real-binding) #,(fl-subtract #'(cs.real-binding ...)))
#`((imag-binding) #,(fl-subtract #'(cs.imag-binding ...)))))))
#`((imag-binding)
;; can't ignore exact 0 imag parts from real numbers, as with
;; addition, because the first value is special
;; so just conservatively use generic subtraction
#,(if (ormap was-real? (syntax->list #'(cs.imag-binding ...)))
(n-ary->binary
this-syntax
#'-
(for/list ([i (syntax->list #'(cs.imag-binding ...))])
(if (was-real? i) #'0 i)))
(fl-subtract #'(cs.imag-binding ...))))))))
(pattern (#%plain-app op:-^ c1:unboxed-float-complex-opt-expr) ; unary -
#:when (subtypeof? this-syntax -FloatComplex)
#:with (real-binding imag-binding) (binding-names)
@ -198,27 +294,45 @@
#'(cs.imag-binding ...))
(list #'imag-binding))]
[res '()])
(if (null? e1)
(reverse res)
(loop (car rs) (car is) (cdr e1) (cdr e2) (cdr rs) (cdr is)
;; complex multiplication, imag part, then real part (reverse)
;; we eliminate operations on the imaginary parts of reals
(let ((o-real? (0.0? o2))
(e-real? (0.0? (car e2))))
(list* #`((#,(car is))
#,(cond ((and o-real? e-real?) #'0.0)
(o-real? #`(unsafe-fl* #,o1 #,(car e2)))
(e-real? #`(unsafe-fl* #,o2 #,(car e1)))
(else
#`(unsafe-fl+ (unsafe-fl* #,o2 #,(car e1))
(unsafe-fl* #,o1 #,(car e2))))))
#`((#,(car rs))
#,(cond ((or o-real? e-real?)
#`(unsafe-fl* #,o1 #,(car e1)))
(else
#`(unsafe-fl- (unsafe-fl* #,o1 #,(car e1))
(unsafe-fl* #,o2 #,(car e2))))))
res))))))))
(cond
[(null? e1)
(reverse res)]
[else
(define o-real? (was-real? o2))
(define e-real? (was-real? (car e2)))
(define both-real? (and o-real? e-real?))
(define o-nf (as-non-float o1))
(define e-nf (as-non-float (car e1)))
(define new-imag-id (if both-real?
(mark-as-real (car is))
(car is)))
(loop (car rs) new-imag-id (cdr e1) (cdr e2) (cdr rs) (cdr is)
;; complex multiplication, imag part, then real part (reverse)
;; we eliminate operations on the imaginary parts of reals
(list* #`((#,new-imag-id)
#,(cond ((and o-real? e-real?) #'0.0)
(o-real? #`(unsafe-fl* #,o1 #,(car e2)))
(e-real? #`(unsafe-fl* #,o2 #,(car e1)))
(else
#`(unsafe-fl+ (unsafe-fl* #,o2 #,(car e1))
(unsafe-fl* #,o1 #,(car e2))))))
#`((#,(car rs))
#,(cond [(and o-nf e-nf both-real?)
;; we haven't seen float operands yet, so
;; shouldn't prematurely convert to floats
(mark-as-non-float (car rs))
#`(* #,o-nf #,e-nf)]
[(or o-real? e-real?)
#`(unsafe-fl*
#,(if (as-non-float o1)
;; we hit floats, need to coerce
#`(real->double-flonum #,o1)
o1)
#,(car e1))]
[else
#`(unsafe-fl- (unsafe-fl* #,o1 #,(car e1))
(unsafe-fl* #,o2 #,(car e2)))]))
res))])))))
(pattern (#%plain-app op:*^ :unboxed-float-complex-opt-expr)
#:when (subtypeof? this-syntax -FloatComplex)
#:do [(log-unboxing-opt "unboxed unary float complex")])
@ -332,10 +446,21 @@
((real-binding) (unsafe-flreal-part e*))
((imag-binding) (unsafe-flimag-part e*))))
;; The following optimization is incorrect and causes bugs because it turns exact numbers into inexact
(pattern e:number-expr
#:with e* (generate-temporary)
#:with (real-binding imag-binding) (binding-names)
#:with (real-binding* imag-binding*) (binding-names)
#:with real-binding (if (and (subtypeof? #'e -Real)
(not (subtypeof? #'e -Flonum)))
;; values that were originally non-floats (e.g.
;; rationals or single floats) may need to be
;; handled specially
(mark-as-non-float #'real-binding* #'e*)
#'real-binding*)
#:with imag-binding (if (subtypeof? #'e -Real)
;; values that were originally reals may need to be
;; handled specially
(mark-as-real #'imag-binding*)
#'imag-binding*)
#:do [(log-unboxing-opt
(if (subtypeof? #'e -Flonum)
"float in complex ops"
@ -443,9 +568,13 @@
[(#%plain-app op:magnitude^ c:unboxed-float-complex-opt-expr)
(log-unboxing-opt "unboxed unary float complex")
#`(let*-values (c.bindings ...)
(unsafe-flsqrt
(unsafe-fl+ (unsafe-fl* c.real-binding c.real-binding)
(unsafe-fl* c.imag-binding c.imag-binding))))])))
;; reuses the algorithm used by the Racket runtime
(let*-values ([(r) (unsafe-flabs c.real-binding)]
[(i) (unsafe-flabs c.imag-binding)]
[(q) (unsafe-fl/ r i)])
(unsafe-fl* i
(unsafe-flsqrt (unsafe-fl+ 1.0
(unsafe-fl* q q))))))])))
(pattern (#%plain-app op:float-complex-op e:expr ...)

View File

@ -192,16 +192,16 @@
this-syntax extra-precision-subexprs)))
safe-to-opt?)
#:do [(log-fl-opt "binary float")]
#:with opt (n-ary->binary #'op.unsafe #'(fs.opt ...)))
#:with opt (n-ary->binary this-syntax #'op.unsafe #'(fs.opt ...)))
(pattern (#%plain-app op:binary-float-comp f1:float-expr f2:float-expr)
#:do [(log-fl-opt "binary float comp")]
#:with opt #'(op.unsafe f1.opt f2.opt))
#:with opt (syntax/loc this-syntax (op.unsafe f1.opt f2.opt)))
(pattern (#%plain-app op:binary-float-comp
f1:float-expr
f2:float-expr
fs:float-expr ...)
#:do [(log-fl-opt "multi float comp")]
#:with opt (n-ary-comp->binary #'op.unsafe #'f1.opt #'f2.opt #'(fs.opt ...)))
#:with opt (n-ary-comp->binary this-syntax #'op.unsafe #'f1.opt #'f2.opt #'(fs.opt ...)))
(pattern (#%plain-app op:binary-float-comp args:opt-expr ...)
;; some args, but not all (otherwise above would have matched) are floats
;; mixed-type comparisons are slow and block futures
@ -227,13 +227,13 @@
(pattern (#%plain-app op:-^ f:float-expr)
#:do [(log-fl-opt "unary float")]
#:with opt #'(unsafe-fl* -1.0 f.opt))
#:with opt (syntax/loc this-syntax (unsafe-fl* -1.0 f.opt)))
(pattern (#%plain-app op:/^ f:float-expr)
#:do [(log-fl-opt "unary float")]
#:with opt #'(unsafe-fl/ 1.0 f.opt))
#:with opt (syntax/loc this-syntax (unsafe-fl/ 1.0 f.opt)))
(pattern (#%plain-app op:sqr^ f:float-expr)
#:do [(log-fl-opt "unary float")]
#:with opt #'(let ([tmp f.opt]) (unsafe-fl* tmp tmp)))
#:with opt (syntax/loc this-syntax (let ([tmp f.opt]) (unsafe-fl* tmp tmp))))
;; we can optimize exact->inexact if we know we're giving it an Integer
(pattern (#%plain-app op:->float^ n:int-expr)
@ -250,19 +250,19 @@
(pattern (#%plain-app op:zero?^ f:float-expr)
#:do [(log-fl-opt "float zero?")]
#:with opt #'(unsafe-fl= f.opt 0.0))
#:with opt (syntax/loc this-syntax (unsafe-fl= f.opt 0.0)))
(pattern (#%plain-app op:add1^ n:float-expr)
#:do [(log-fl-opt "float add1")]
#:with opt #'(unsafe-fl+ n.opt 1.0))
#:with opt (syntax/loc this-syntax (unsafe-fl+ n.opt 1.0)))
(pattern (#%plain-app op:sub1^ n:float-expr)
#:do [(log-fl-opt "float sub1")]
#:with opt #'(unsafe-fl- n.opt 1.0))
#:with opt (syntax/loc this-syntax (unsafe-fl- n.opt 1.0)))
(pattern (#%plain-app op:random-op prng:opt-expr)
#:when (subtypeof? #'prng -Pseudo-Random-Generator)
#:do [(log-fl-opt "float random")]
#:with opt #'(unsafe-flrandom prng.opt))
#:with opt (syntax/loc this-syntax (unsafe-flrandom prng.opt)))
(pattern (#%plain-app op:random^) ; random with no args
#:do [(log-fl-opt "float 0-arg random")
;; We introduce a reference to `current-pseudo-random-generator',
@ -270,7 +270,7 @@
;; from triggering down the line (see hidden-cost.rkt), so we need
;; to do the logging ourselves.
(log-optimization-info "hidden parameter (random)" #'op)]
#:with opt #'(unsafe-flrandom (current-pseudo-random-generator)))
#:with opt (syntax/loc this-syntax (unsafe-flrandom (current-pseudo-random-generator))))
;; warn about (potentially) exact real arithmetic, in general
;; Note: These patterns don't perform optimization. They only produce logging
@ -278,15 +278,15 @@
(pattern (#%plain-app op:binary-float-op n:opt-expr ...)
#:when (maybe-exact-rational? this-syntax)
#:do [(log-opt-info "possible exact real arith")]
#:with opt #'(op n.opt ...))
#:with opt (syntax/loc this-syntax (op n.opt ...)))
(pattern (#%plain-app op:binary-float-comp n:opt-expr ...)
;; can't look at return type, since it's always bool
#:when (andmap maybe-exact-rational? (syntax->list #'(n ...)))
#:do [(log-opt-info "possible exact real arith")]
#:with opt #'(op n.opt ...))
#:with opt (syntax/loc this-syntax (op n.opt ...)))
(pattern (#%plain-app op:unary-float-op n:opt-expr ...)
#:when (maybe-exact-rational? this-syntax)
#:do [(log-opt-info "possible exact real arith")]
#:with opt #'(op n.opt ...))
#:with opt (syntax/loc this-syntax (op n.opt ...)))
)

View File

@ -4,7 +4,7 @@
(for-template racket/base)
"../utils/utils.rkt"
(optimizer utils logging)
(types abbrev struct-table))
(types abbrev numeric-tower struct-table))
(provide hidden-cost-log-expr)
@ -45,18 +45,6 @@
#:do [(log-optimization-info "hidden parameter (random)" #'op)]
#:with opt (syntax/loc this-syntax (op args.opt ...)))
;; Log calls to struct constructors, so that OC can report those used in
;; hot loops.
;; Note: Sometimes constructors are wrapped in `#%expression', need to watch
;; for that too.
(pattern (#%plain-app (~and op-part (~or op:id (#%expression op:id)))
args:opt-expr ...)
#:when (let ([constructor-for (syntax-property #'op 'constructor-for)])
(or (and constructor-for (struct-constructor? constructor-for))
(struct-constructor? #'op)))
#:do [(log-optimization-info "struct constructor" #'op)]
#:with opt (syntax/loc this-syntax (op-part args.opt ...)))
;; regexp-match (or other regexp operation) with non-regexp pattern argument
;; (i.e. string or bytes)
(pattern (#%plain-app op:regexp-function pattern-arg:opt-expr
@ -64,4 +52,12 @@
#:when (not (or (subtypeof? #'pattern-arg -Regexp)
(subtypeof? #'pattern-arg -Byte-Regexp)))
#:do [(log-optimization-info "non-regexp pattern" #'pattern-arg)]
#:with opt (syntax/loc this-syntax (op pattern-arg.opt args.opt ...))))
#:with opt (syntax/loc this-syntax (op pattern-arg.opt args.opt ...)))
;; vectors of floats can be replaced with flvectors in most cases
;; need to deconstruct to not infinite loop
(pattern (#%plain-app es ...)
#:when (subtypeof? this-syntax (-vec -Flonum))
#:with (es*:opt-expr ...) #'(es ...)
#:do [(log-optimization-info "vector of floats" this-syntax)]
#:with opt (syntax/loc this-syntax (es*.opt ...))))

View File

@ -24,9 +24,9 @@
(pattern opt:ignore-table^)
;; Can't optimize the body of this code because it isn't typechecked
(pattern (~and _:kw-lambda^
((~and op let-values)
([(i:id) e-rhs:opt-expr]) e-body:expr ...))
(pattern (~and (~or _:kw-lambda^ _:opt-lambda^)
((~and op let-values)
([(i:id) e-rhs:opt-expr]) e-body:expr ...))
#:with opt (quasisyntax/loc/origin this-syntax #'op
(op ([(i) e-rhs.opt]) e-body ...)))

View File

@ -6,7 +6,7 @@
(for-syntax racket/base syntax/parse racket/syntax)
"../utils/utils.rkt"
(rep type-rep)
(types type-table utils base-abbrev)
(types type-table utils base-abbrev resolve subtype)
(typecheck typechecker)
(optimizer utils logging))
@ -25,10 +25,7 @@
(define (has-pair-type? e)
(and (subtypeof? e (-pair Univ Univ))
;; sometimes composite operations end up with Nothing as result type,
;; not sure why. TODO investigate
(not (isoftype? e -Bottom))))
(subtypeof? e (-pair Univ Univ)))
;; can't do the above for mpairs, as they are invariant
(define (has-mpair-type? e)
(match (type-of e) ; type of the operand
@ -67,25 +64,12 @@
;; change the source location of a given syntax object
(define (relocate stx loc-stx)
(define ((relocate loc-stx) stx)
(datum->syntax stx (syntax->datum stx) loc-stx stx stx))
;; if the equivalent sequence of cars and cdrs is guaranteed not to fail,
;; we can optimize
;; accessors is a list of syntax objects, all #'car or #'cdr
(define (gen-alt accessors op arg stx)
(define (gen-alt-helper accessors)
(for/fold [(accum arg)] [(acc (reverse accessors))]
(quasisyntax/loc stx (#%plain-app #,(relocate acc op) #,accum))))
(let ((ty (type-of stx))
(obj (gen-alt-helper accessors)))
;; we're calling the typechecker, but this is just a shortcut, we're
;; still conceptually single pass (we're not iterating). we could get
;; the same result by statically destructing the types.
(tc-expr/check obj ty)
obj))
(define-syntax gen-pair-derived-expr
(syntax-parser
[(_ name:id (orig:id seq ...) ...)
@ -96,8 +80,9 @@
(define-literal-syntax-class lit-class-name (orig))
(define-syntax-class syntax-class-name
#:commit
#:attributes (arg alt)
(pattern (#%plain-app (~var op lit-class-name) arg)
#:with alt (gen-alt (list seq ...) #'op #'arg this-syntax)))) ...
#:with alt (map (relocate #'op) (list seq ...))))) ...
(define-merged-syntax-class name (syntax-class-name ...)))]))
(gen-pair-derived-expr pair-derived-expr
@ -144,5 +129,30 @@
(define-syntax-class pair-derived-opt-expr
#:commit
(pattern e:pair-derived-expr
#:with e*:pair-opt-expr #'e.alt
#:with opt #'e*.opt))
#:with opt
;; optimize alt inside-out, as long as it's safe to
(let-values
([(t res)
(for/fold ([t (match (type-of #'e.arg)
[(tc-result1: t) t])]
[res #'e.arg])
([accessor (in-list (reverse (syntax->list #'e.alt)))])
(cond
[(and t (subtype t (-pair Univ Univ))) ; safe to optimize this one layer
(syntax-parse accessor
[op:pair-op
(log-pair-opt)
(values
(match (resolve t)
[(Pair: a d) ; peel off one layer of the type
(syntax-parse #'op
[:car^ a]
[:cdr^ d])]
[_ ; not a pair type, give up on optimizing more
#f])
#`(op.unsafe #,res))])]
[else ; unsafe, just rebuild the rest of the accessors
(log-pair-missed-opt accessor #'e.arg)
(values t ; stays unsafe from now on
#`(#,accessor #,res))]))])
res)))

View File

@ -99,14 +99,15 @@
(define-syntax-class unboxed-clauses
#:attributes (bindings)
(pattern (clauses:unboxed-clause ...)
#:attr bindings (delay (template ((?@ . clauses.bindings) ...)))))]
#:attr bindings (delay (template ((?@ . clauses.bindings) ...)))))
(define top-stx this-syntax)]
#:attr opt
(syntax-parse #'(clause ...)
[clauses:unboxed-clauses
(delay
(quasisyntax/loc/origin
this-syntax #'letk.kw
top-stx #'letk.kw
(letk.key ... clauses.bindings body.opt ...)))])))

View File

@ -51,11 +51,12 @@
;; unlike their safe counterparts, unsafe binary operators can only take 2 arguments
;; this works on operations that are (A A -> A)
(define (n-ary->binary op stx)
(define (n-ary->binary src-stx op stx)
(for/fold ([o (stx-car stx)]) ([e (in-syntax (stx-cdr stx))])
#`(#,op #,o #,e)))
(quasisyntax/loc src-stx
(#,op #,o #,e))))
;; this works on operations that are (A A -> B)
(define (n-ary-comp->binary op arg1 arg2 rest)
(define (n-ary-comp->binary src-stx op arg1 arg2 rest)
;; First, generate temps to bind the result of each arg2 args ...
;; to avoid computing them multiple times.
(define lifted (stx-map (lambda (x) (generate-temporary)) #`(#,arg2 #,@rest)))
@ -69,10 +70,11 @@
(car l)
(cdr l))])))
;; Finally, build the whole thing.
#`(let #,(for/list ([lhs (in-list lifted)]
(quasisyntax/loc src-stx
(let #,(for/list ([lhs (in-list lifted)]
[rhs (in-syntax #`(#,arg2 #,@rest))])
#`(#,lhs #,rhs))
(and #,@tests)))
(and #,@tests))))
;; to avoid mutually recursive syntax classes
;; will be set to the actual optimization function at the entry point

View File

@ -0,0 +1,25 @@
#lang racket/base
(provide cast-table-ref
cast-table-set!)
(require syntax/id-table)
;; A module that helps store information about the original types of casted
;; expressions.
;;
;; Casts in Typed Racket must generate two contracts. One from typed to untyped,
;; and another from untyped to typed. The contract from typed to untyped is
;; generated based on the existing type of the expression, which must be stored
;; in this table so that it can be looked up later in the contract-generation
;; pass.
(define cast-table (make-free-id-table))
;; cast-table-set! : Id Type-Stx -> Void
(define (cast-table-set! id type-stx)
(free-id-table-set! cast-table id type-stx))
;; cast-table-ref : Id -> (U False Type-Stx)
(define (cast-table-ref id)
(free-id-table-ref cast-table id #f))

View File

@ -0,0 +1,16 @@
#lang racket/base
;; Control whether the OC button show up for TR files in DrR.
(provide maybe-show-OC)
(define (maybe-show-OC)
;; If Optimization Coach is installed, load it.
(with-handlers ([exn:fail:filesystem? (lambda _ '())]) ; not found
(collection-path "optimization-coach")
(if (dynamic-require 'optimization-coach/tool
'optimization-coach-loaded?)
;; OC is loaded, show button
(list (dynamic-require 'optimization-coach/tool
'optimization-coach-drracket-button))
'())))

View File

@ -2,16 +2,18 @@
;; This module provides functions for parsing types written by the user
(require "../utils/utils.rkt"
(require (rename-in "../utils/utils.rkt" [infer infer-in])
(except-in (rep type-rep object-rep) make-arr)
(rename-in (types abbrev union utils filter-ops resolve
classes prefab)
(rename-in (types abbrev union utils prop-ops resolve
classes prefab signatures)
[make-arr* make-arr])
(only-in (infer-in infer) intersect)
(utils tc-utils stxclass-util literal-syntax-class)
syntax/stx (prefix-in c: (contract-req))
syntax/parse racket/sequence
(env tvar-env type-alias-env mvar-env
lexical-env index-env row-constraint-env)
lexical-env index-env row-constraint-env
signature-env)
racket/dict
racket/list
racket/promise
@ -20,6 +22,7 @@
"parse-classes.rkt"
(for-label
(except-in racket/base case-lambda)
racket/unit
"../base-env/colon.rkt"
"../base-env/base-types-extra.rkt"
;; match on the `case-lambda` binding in the TR primitives
@ -79,6 +82,10 @@
(define-literal-syntax-class #:for-label cons)
(define-literal-syntax-class #:for-label Class)
(define-literal-syntax-class #:for-label Object)
(define-literal-syntax-class #:for-label Unit)
(define-literal-syntax-class #:for-label import)
(define-literal-syntax-class #:for-label export)
(define-literal-syntax-class #:for-label init-depend)
(define-literal-syntax-class #:for-label Refinement)
(define-literal-syntax-class #:for-label Instance)
(define-literal-syntax-class #:for-label List)
@ -98,8 +105,12 @@
(define-literal-syntax-class #:for-label Prefab)
(define-literal-syntax-class #:for-label Values)
(define-literal-syntax-class #:for-label values)
(define-literal-syntax-class #:for-label AnyValues)
(define-literal-syntax-class #:for-label Top)
(define-literal-syntax-class #:for-label Bot)
(define-literal-syntax-class #:for-label Distinction)
(define-literal-syntax-class #:for-label Sequenceof)
(define-literal-syntax-class #:for-label )
;; (Syntax -> Type) -> Syntax Any -> Syntax
;; See `parse-type/id`. This is a curried generalization.
@ -218,7 +229,7 @@
#:attributes (type)
(pattern (~optional (~seq #:rest type:non-keyword-ty))))
;; syntax classes for filters, objects, and related things
;; syntax classes for props, objects, and related things
(define-syntax-class path-elem
#:description "path element"
(pattern :car^
@ -235,8 +246,8 @@
#:description "!"
(pattern (~datum !)))
(define-splicing-syntax-class simple-latent-filter
#:description "latent filter"
(define-splicing-syntax-class simple-latent-prop
#:description "latent prop"
(pattern (~seq t:expr :@ pe:path-elem ...)
#:attr type (parse-type #'t)
#:attr path (attribute pe.pe))
@ -245,54 +256,54 @@
#:attr path '()))
(define-syntax-class (prop doms)
#:description "filter proposition"
#:description "proposition"
#:attributes (prop)
(pattern :Top^ #:attr prop -top)
(pattern :Bot^ #:attr prop -bot)
(pattern :Top^ #:attr prop -tt)
(pattern :Bot^ #:attr prop -ff)
;; Here is wrong check
(pattern (t:expr :@ ~! pe:path-elem ... (~var o (filter-object doms)))
#:attr prop (-filter (parse-type #'t) (-acc-path (attribute pe.pe) (attribute o.obj))))
(pattern (t:expr :@ ~! pe:path-elem ... (~var o (prop-object doms)))
#:attr prop (-is-type (-acc-path (attribute pe.pe) (attribute o.obj)) (parse-type #'t)))
;; Here is wrong check
(pattern (:! t:expr :@ ~! pe:path-elem ... (~var o (filter-object doms)))
#:attr prop (-not-filter (parse-type #'t) (-acc-path (attribute pe.pe) (attribute o.obj))))
(pattern (:! t:expr :@ ~! pe:path-elem ... (~var o (prop-object doms)))
#:attr prop (-not-type (-acc-path (attribute pe.pe) (attribute o.obj)) (parse-type #'t)))
(pattern (:! t:expr)
#:attr prop (-not-filter (parse-type #'t) 0))
#:attr prop (-not-type 0 (parse-type #'t)))
(pattern ((~datum and) (~var p (prop doms)) ...)
#:attr prop (apply -and (attribute p.prop)))
(pattern ((~datum or) (~var p (prop doms)) ...)
#:attr prop (apply -or (attribute p.prop)))
(pattern ((~literal implies) (~var p1 (prop doms)) (~var p2 (prop doms)))
#:attr prop (-imp (attribute p1.prop) (attribute p2.prop)))
#:attr prop (-or (negate-prop (attribute p1.prop)) (attribute p2.prop)))
(pattern t:expr
#:attr prop (-filter (parse-type #'t) 0)))
#:attr prop (-is-type 0 (parse-type #'t))))
(define-splicing-syntax-class (filter-object doms)
#:description "filter object"
(define-splicing-syntax-class (prop-object doms)
#:description "prop object"
#:attributes (obj)
(pattern i:id
#:fail-unless (identifier-binding #'i)
"Filters for predicates may not reference identifiers that are unbound"
"Propositions for predicates may not reference identifiers that are unbound"
#:fail-when (is-var-mutated? #'i)
"Filters for predicates may not reference identifiers that are mutated"
"Propositions for predicates may not reference identifiers that are mutated"
#:attr obj (-id-path #'i))
(pattern idx:nat
#:do [(define arg (syntax-e #'idx))]
#:fail-unless (< arg (length doms))
(format "Filter proposition's object index ~a is larger than argument length ~a"
(format "Proposition's object index ~a is larger than argument length ~a"
arg (length doms))
#:attr obj (-arg-path arg 0))
(pattern (~seq depth-idx:nat idx:nat)
#:do [(define arg (syntax-e #'idx))
(define depth (syntax-e #'depth-idx))]
#:fail-unless (<= depth (length (current-arities)))
(format "Index ~a used in a filter, but the use is only within ~a enclosing functions"
(format "Index ~a used in a proposition, but the use is only within ~a enclosing functions"
depth (length (current-arities)))
#:do [(define actual-arg
(if (zero? depth)
(length doms)
(list-ref (current-arities) (sub1 depth))))]
#:fail-unless (< arg actual-arg)
(format "Filter proposition's object index ~a is larger than argument length ~a"
(format "Proposition's object index ~a is larger than argument length ~a"
depth actual-arg)
#:attr obj (-arg-path arg (syntax-e #'depth-idx))))
@ -374,6 +385,42 @@
"given" v)
(make-Instance (Un)))
(make-Instance v)))]
[(:Unit^ (:import^ import:id ...)
(:export^ export:id ...)
(~optional (:init-depend^ init-depend:id ...)
#:defaults ([(init-depend 1) null]))
(~optional result
#:defaults ([result #f])))
;; Lookup an identifier in the signature environment
;; Fail with a parse error, if the lookup returns #f
(define (id->sig id)
(or (lookup-signature id)
(parse-error #:stx id
#:delayed? #f
"Unknown signature used in Unit type"
"signature" (syntax-e id))))
(define (import/export-error)
(parse-error #:stx stx
#:delayed? #f
"Unit types must import and export distinct signatures"))
(define (init-depend-error)
(parse-error
#:stx stx
#:delayed? #f
"Unit type initialization dependencies must be a subset of imports"))
(define imports
(check-imports/exports (stx-map id->sig #'(import ...)) import/export-error))
(define exports
(check-imports/exports (stx-map id->sig #'(export ...)) import/export-error))
(define init-depends
(check-init-depends/imports (stx-map id->sig #'(init-depend ...))
imports
init-depend-error))
(define res (attribute result))
(make-Unit imports
exports
init-depends
(if res (parse-values-type res) (-values (list -Void))))]
[(:List^ ts ...)
(parse-list-type stx)]
[(:List*^ ts ... t)
@ -421,17 +468,30 @@
t*))))]
[(:U^ ts ...)
(apply Un (parse-types #'(ts ...)))]
[(:∩^ ts ...)
(for/fold ([ty Univ])
([t (in-list (parse-types #'(ts ...)))])
(intersect ty t))]
[(:quote^ t)
(parse-quoted-type #'t)]
[(:All^ . rest)
(parse-all-type stx)]
[(:Opaque^ p?:id)
(make-Opaque #'p?)]
[(:Distinction^ name:id unique-id:id rep-ty:expr)
(-Distinction (syntax-e #'name) (syntax-e #'unique-id) (parse-type #'rep-ty))]
[(:Parameter^ t)
(let ([ty (parse-type #'t)])
(-Param ty ty))]
[(:Parameter^ t1 t2)
(-Param (parse-type #'t1) (parse-type #'t2))]
[((~and p :Parameter^) args ...)
(parse-error
#:stx stx
(~a (syntax-e #'p) " expects one or two type arguments, given "
(sub1 (length (syntax->list #'(args ...))))))]
[(:Sequenceof^ t ...)
(apply -seq (parse-types #'(t ...)))]
;; curried function notation
[((~and dom:non-keyword-ty (~not :->^)) ...
:->^
@ -444,9 +504,9 @@
(list (make-arr
doms
(parse-type (syntax/loc stx (rest-dom ...))))))))]
[(~or (:->^ dom rng :colon^ latent:simple-latent-filter)
(dom :->^ rng :colon^ latent:simple-latent-filter))
;; use parse-type instead of parse-values-type because we need to add the filters from the pred-ty
[(~or (:->^ dom rng :colon^ latent:simple-latent-prop)
(dom :->^ rng :colon^ latent:simple-latent-prop))
;; use parse-type instead of parse-values-type because we need to add the props from the pred-ty
(with-arity 1
(make-pred-ty (list (parse-type #'dom)) (parse-type #'rng) (attribute latent.type)
(-acc-path (attribute latent.path) (-arg-path 0))))]
@ -506,11 +566,11 @@
:colon^ (~var latent (full-latent (syntax->list #'(dom ...)))))
(dom:non-keyword-ty ... :->^ rng:expr
~! :colon^ (~var latent (full-latent (syntax->list #'(dom ...))))))
;; use parse-type instead of parse-values-type because we need to add the filters from the pred-ty
;; use parse-type instead of parse-values-type because we need to add the props from the pred-ty
(with-arity (length (syntax->list #'(dom ...)))
(->* (parse-types #'(dom ...))
(parse-type #'rng)
: (-FS (attribute latent.positive) (attribute latent.negative))
: (-PS (attribute latent.positive) (attribute latent.negative))
: (attribute latent.object)))]
[(:->*^ (~var mand (->*-args #t))
(~optional (~var opt (->*-args #f))
@ -546,7 +606,8 @@
(when (current-referenced-aliases)
(define alias-box (current-referenced-aliases))
(set-box! alias-box (cons #'id (unbox alias-box))))
(add-disappeared-use (syntax-local-introduce #'id))
(and (syntax-transforming?)
(add-disappeared-use (syntax-local-introduce #'id)))
t)]
[else
(parse-error #:delayed? #t (~a "type name `" (syntax-e #'id) "' is unbound"))
@ -619,7 +680,7 @@
(-Tuple (parse-types #'(tys ...)))])))
;; Syntax -> Type
;; Parse a (Values ...) type
;; Parse a (Values ...) or AnyValues type
(define (parse-values-type stx)
(parameterize ([current-orig-stx stx])
(syntax-parse stx
@ -641,6 +702,7 @@
var))]
[((~or :Values^ :values^) tys ...)
(-values (parse-types #'(tys ...)))]
[:AnyValues^ ManyUniv]
[t
(-values (list (parse-type #'t)))])))
@ -868,10 +930,12 @@
(define (parse-tc-results stx)
(syntax-parse stx
[((~or :Values^ :values^) t ...)
(define empties (stx-map (λ (x) #f) #'(t ...)))
(ret (parse-types #'(t ...))
(stx-map (lambda (x) -no-filter) #'(t ...))
(stx-map (lambda (x) -no-obj) #'(t ...)))]
[t (ret (parse-type #'t) -no-filter -no-obj)]))
empties
empties)]
[:AnyValues^ (tc-any-results #f)]
[t (ret (parse-type #'t) #f #f)]))
(define parse-type/id (parse/id parse-type))

View File

@ -50,9 +50,10 @@
(ignore typechecker:ignore #:mark)
(ignore-some typechecker:ignore-some #:mark)
(ignore-some-expr typechecker:ignore-some)
(contract-def typechecker:contract-def)
(contract-def typechecker:contract-def) ; -> Contract-Def (struct in type-contract.rkt)
(contract-def/provide typechecker:contract-def/provide)
(external-check typechecker:external-check)
(casted-expr typechecker:casted-expr) ; Type -> Void, takes the original type of the casted expr
(with-type typechecker:with-type #:mark)
(type-ascription type-ascription)
(type-inst type-inst)
@ -74,5 +75,10 @@
(tr:class:local-table tr:class:local-table)
(tr:class:name-table tr:class:name-table)
(tr:class:def tr:class:def)
)
(tr:unit tr:unit #:mark)
(tr:unit:body-exp-def-type tr:unit:body-exp-def-type)
(tr:unit:invoke tr:unit:invoke)
(tr:unit:invoke:expr tr:unit:invoke:expr)
(tr:unit:compound tr:unit:compound)
(tr:unit:from-context tr:unit:from-context #:mark)
(unsafe-provide unsafe-provide #:mark))

View File

@ -5,19 +5,19 @@
(require
"../utils/utils.rkt"
syntax/parse
(rep type-rep filter-rep object-rep)
(rep type-rep prop-rep object-rep)
(utils tc-utils)
(env type-name-env row-constraint-env)
(rep rep-utils)
(types resolve union utils printer)
(prefix-in t: (types abbrev numeric-tower))
(prefix-in t: (types abbrev numeric-tower subtype))
(private parse-type syntax-properties)
racket/match racket/syntax racket/list
racket/format
racket/dict
racket/dict racket/set
syntax/flatten-begin
(only-in (types abbrev) -Bottom)
(static-contracts instantiate optimize structures combinators)
(only-in (types abbrev) -Bottom -Boolean)
(static-contracts instantiate optimize structures combinators constraints)
;; TODO make this from contract-req
(prefix-in c: racket/contract)
(contract-req)
@ -39,14 +39,26 @@
;; submod for testing
(module* test-exports #f (provide type->contract))
;; has-contrat-def-property? : Syntax -> Boolean
(define (has-contract-def-property? stx)
(syntax-parse stx
#:literal-sets (kernel-literals)
[(define-values (_) e)
(and (contract-def-property #'e)
#t)]
[_ #f]))
(struct contract-def (type flat? maker? typed-side) #:prefab)
;; get-contract-def-property : Syntax -> (U False Contract-Def)
;; Checks if the given syntax needs to be fixed up for contract generation
;; and if yes it returns the information stored in the property
(define (get-contract-def-property stx)
(syntax-parse stx
#:literal-sets (kernel-literals)
[(define-values (_) e) (contract-def-property #'e)]
[(define-values (_) e)
(and (contract-def-property #'e)
((contract-def-property #'e)))]
[_ #f]))
;; type->contract-fail : Syntax Type #:ctc-str String
@ -111,7 +123,7 @@
(λ (#:reason [reason #f]) (set! failure-reason reason))))
(syntax-parse stx
#:literal-sets (kernel-literals)
[(define-values ctc-id _)
[(define-values (ctc-id) _)
;; no need for ignore, the optimizer doesn't run on this code
(cond [failure-reason
#`(define-syntax (#,untyped-id stx)
@ -122,10 +134,15 @@
"type" #,(pretty-format-type type #:indent 8)))]
[else
(match-define (list defs ctc) result)
(define maybe-inline-val
(should-inline-contract?/cache ctc cache))
#`(begin #,@defs
(define ctc-id #,ctc)
#,@(if maybe-inline-val
null
(list #`(define-values (ctc-id) #,ctc)))
(define-module-boundary-contract #,untyped-id
#,orig-id ctc-id
#,orig-id
#,(or maybe-inline-val #'ctc-id)
#:pos-source #,blame-id
#:srcloc (vector (quote #,(syntax-source orig-id))
#,(syntax-line orig-id)
@ -133,6 +150,16 @@
#,(syntax-position orig-id)
#,(syntax-span orig-id))))])]))
;; Syntax (Dict Static-Contract (Cons Id Syntax)) -> (Option Syntax)
;; A helper for generate-contract-def/provide that helps inline contract
;; expressions when needed to cooperate with the contract system's optimizations
(define (should-inline-contract?/cache ctc-stx cache)
(and (identifier? ctc-stx)
(let ([match? (assoc ctc-stx (hash-values cache) free-identifier=?)])
(and match?
(should-inline-contract? (cdr match?))
(cdr match?)))))
;; The below requires are needed since they provide identifiers that
;; may appear in the residual program.
@ -147,7 +174,10 @@
typed-racket/utils/opaque-object
typed-racket/utils/evt-contract
typed-racket/utils/sealing-contract
unstable/contract racket/contract/parametric))
typed-racket/utils/promise-not-name-contract
typed-racket/utils/simple-result-arrow
racket/sequence
racket/contract/parametric))
;; Should the above requires be included in the output?
;; This box is only used for contracts generated for `require/typed`
@ -160,7 +190,7 @@
(define sc-cache (make-hash))
(with-new-name-tables
(for/list ((e (in-list forms)))
(if (not (get-contract-def-property e))
(if (not (has-contract-def-property? e))
e
(begin (set-box! include-extra-requires? #t)
(generate-contract-def e ctc-cache sc-cache))))))
@ -186,6 +216,15 @@
ctc-cache sc-cache)))]
[_ form]))))
;; get-max-contract-kind
;; static-contract -> (or/c 'flat 'chaperone 'impersonator)
;; recurse into a contract finding the max
;; kind (e.g. flat < chaperone < impersonator)
(define (get-max-contract-kind sc)
(define (get-restriction sc)
(sc->constraints sc get-restriction))
(kind-max-max (contract-restrict-value (get-restriction sc))))
;; To avoid misspellings
(define impersonator-sym 'impersonator)
(define chaperone-sym 'chaperone)
@ -267,9 +306,6 @@
(define (same sc)
(triple sc sc sc))
;; Keep track of the bound names and don't cache types where those are free
(define bound-names (make-parameter null))
;; Macro to simplify (and avoid reindentation) of the match below
;;
;; The sc-cache hashtable is used to memoize static contracts. The keys are
@ -284,7 +320,9 @@
[else
(define sc (match type match-clause ...))
(define fvs (fv type))
(unless (or (ormap (λ (n) (member n fvs)) (bound-names))
;; Only cache closed terms, otherwise open terms may show up
;; out of context.
(unless (or (not (null? fv))
;; Don't cache types with applications of Name types because
;; it does the wrong thing for recursive references
(has-name-app? type))
@ -361,6 +399,32 @@
(if numeric-sc
(apply or/sc numeric-sc (map t->sc non-numeric))
(apply or/sc (map t->sc elems)))]
[(Intersection: ts)
(define-values (chaperones/impersonators others)
(for/fold ([cs/is null] [others null])
([elem (in-immutable-set ts)])
(define c (t->sc elem))
(if (equal? flat-sym (get-max-contract-kind c))
(values cs/is (cons c others))
(values (cons c cs/is) others))))
(cond
[(> (length chaperones/impersonators) 1)
(fail #:reason (~a "Intersection type contract contains"
" more than 1 non-flat contract: "
type))]
[else
(apply and/sc (append others chaperones/impersonators))])]
[(and t (Function: arrs))
#:when (any->bool? arrs)
;; Avoid putting (-> any T) contracts on struct predicates (where Boolean <: T)
;; Optimization: if the value is typed, we can assume it's not wrapped
;; in a type-unsafe chaperone/impersonator and use the unsafe contract
(let* ([unsafe-spp/sc (flat/sc #'struct-predicate-procedure?)]
[safe-spp/sc (flat/sc #'struct-predicate-procedure?/c)]
[optimized/sc (if (from-typed? typed-side)
unsafe-spp/sc
safe-spp/sc)])
(or/sc optimized/sc (t->sc/fun t)))]
[(and t (Function: _)) (t->sc/fun t)]
[(Set: t) (set/sc (t->sc t))]
[(Sequence: ts) (apply sequence/sc (map t->sc ts))]
@ -396,6 +460,7 @@
[(Prompt-TagTop:) (only-untyped prompt-tag?/sc)]
[(Continuation-Mark-KeyTop:) (only-untyped continuation-mark-key?/sc)]
[(ClassTop:) (only-untyped class?/sc)]
[(UnitTop:) (only-untyped unit?/sc)]
[(StructTypeTop:) (struct-type/sc null)]
;; TODO Figure out how this should work
;[(StructTop: s) (struct-top/sc s)]
@ -418,13 +483,11 @@
(case typed-side
[(both) (recursive-sc
(list both-n*)
(parameterize ([bound-names (cons n (bound-names))])
(list (loop b 'both rv)))
(list (loop b 'both rv))
(recursive-sc-use both-n*))]
[(typed untyped)
(define (rec b side rv)
(parameterize ([bound-names (cons n (bound-names))])
(loop b side rv)))
(loop b side rv))
;; TODO not fail in cases that don't get used
(define untyped (rec b 'untyped rv))
(define typed (rec b 'typed rv))
@ -514,6 +577,25 @@
(if seal/sc
(and/sc seal/sc sc-for-class)
sc-for-class)]
[(Unit: imports exports init-depends results)
(define (traverse sigs)
(for/list ([sig (in-list sigs)])
(define mapping
(map
(match-lambda
[(cons id type) (cons id (t->sc type))])
(Signature-mapping sig)))
(signature-spec (Signature-name sig) (map car mapping) (map cdr mapping))))
(define imports-specs (traverse imports))
(define exports-specs (traverse exports))
(define init-depends-ids (map Signature-name init-depends))
(match results
[(? AnyValues?)
(fail #:reason (~a "cannot generate contract for unit type"
" with unknown return values"))]
[(Values: (list (Result: rngs _ _) ...))
(unit/sc imports-specs exports-specs init-depends-ids (map t->sc rngs))])]
[(Struct: nm par (list (fld: flds acc-ids mut?) ...) proc poly? pred?)
(cond
[(dict-ref recursive-values nm #f)]
@ -537,7 +619,14 @@
[(Syntax: t)
(syntax/sc (t->sc t))]
[(Value: v)
(flat/sc #`(flat-named-contract '#,v (lambda (x) (equal? x '#,v))) v)]
(if (and (c:flat-contract? v)
;; numbers used as contracts compare with =, but TR
;; requires an equal? check
(not (number? v))
;; regexps don't match themselves when used as contracts
(not (regexp? v)))
(flat/sc #`(quote #,v))
(flat/sc #`(flat-named-contract '#,v (lambda (x) (equal? x '#,v))) v))]
[(Param: in out)
(parameter/sc (t->sc in) (t->sc out))]
[(Hashtable: k v)
@ -560,17 +649,21 @@
;; and call the given thunk or raise an error
(define (handle-range arr convert-arr)
(match arr
;; functions with no filters or objects
[(arr: dom (Values: (list (Result: rngs (FilterSet: (Top:) (Top:)) (Empty:)) ...)) rst drst kws)
;; functions with no props or objects
[(arr: dom (Values: (list (Result: rngs
(PropSet: (TrueProp:)
(TrueProp:))
(Empty:)) ...))
rst drst kws)
(convert-arr)]
;; Functions that don't return
[(arr: dom (Values: (list (Result: (== -Bottom) _ _) ...)) rst drst kws)
(convert-arr)]
;; functions with filters or objects
;; functions with props or objects
[(arr: dom (Values: (list (Result: rngs _ _) ...)) rst drst kws)
(if (from-untyped? typed-side)
(fail #:reason (~a "cannot generate contract for function type"
" with filters or objects."))
" with props or objects."))
(convert-arr))]
[(arr: dom (? ValuesDots?) rst drst kws)
(fail #:reason (~a "cannot generate contract for function type"
@ -612,7 +705,7 @@
(map conv opt-kws))))
(define range (map t->sc rngs))
(define rest (and rst (listof/sc (t->sc/neg rst))))
(function/sc (process-dom mand-args) opt-args mand-kws opt-kws rest range))
(function/sc (from-typed? typed-side) (process-dom mand-args) opt-args mand-kws opt-kws rest range))
(handle-range first-arr convert-arr)]
[else
(define ((f case->) a)
@ -629,6 +722,7 @@
(and rst (listof/sc (t->sc/neg rst)))
(map t->sc rngs))
(function/sc
(from-typed? typed-side)
(process-dom (map t->sc/neg dom))
null
(map conv mand-kws)
@ -692,9 +786,8 @@
(define rv (for/fold ((rv recursive-values)) ((temp temporaries)
(v-nm vs-nm))
(hash-set rv v-nm (same (parametric-var/sc temp)))))
(parameterize ([bound-names (append (bound-names) vs-nm)])
(parametric->/sc temporaries
(t->sc b #:recursive-values rv)))))))
(parametric->/sc temporaries
(t->sc b #:recursive-values rv))))))
;; Generate a contract for a variable-arity polymorphic function type
(define (t->sc/polydots type fail typed-side recursive-values t->sc)
@ -732,12 +825,11 @@
([temp temporaries]
[v-nm vs-nm])
(hash-set rv v-nm (same (sealing-var/sc temp)))))
(parameterize ([bound-names (append (bound-names) vs-nm)])
;; Only the first three sets of constraints seem to be needed
;; since augment clauses don't make sense without a corresponding
;; public method too. This invariant has to be enforced though.
(sealing->/sc temporaries (take constraints 3)
(t->sc b #:recursive-values rv)))))))
;; Only the first three sets of constraints seem to be needed
;; since augment clauses don't make sense without a corresponding
;; public method too. This invariant has to be enforced though.
(sealing->/sc temporaries (take constraints 3)
(t->sc b #:recursive-values rv))))))
;; Predicate that checks for an App type with a recursive
;; Name type in application position
@ -745,7 +837,7 @@
(let/ec escape
(let loop ([type type])
(type-case
(#:Type loop #:Filter (sub-f loop) #:Object (sub-o loop))
(#:Type loop #:Prop (sub-f loop) #:Object (sub-o loop))
type
[#:App arg _ _
(match arg
@ -753,6 +845,15 @@
[_ type])]))
#f))
;; True if the arities `arrs` are what we'd expect from a struct predicate
(define (any->bool? arrs)
(match arrs
[(list (arr: (list (Univ:))
(Values: (list (Result: t _ _)))
#f #f '()))
(t:subtype -Boolean t)]
[_ #f]))
(module predicates racket/base
(require racket/extflonum)
(provide nonnegative? nonpositive?
@ -788,7 +889,7 @@
(define positive-integer/sc (numeric/sc Positive-Integer (and/c exact-integer? positive?)))
(define natural/sc (numeric/sc Natural exact-nonnegative-integer?))
(define negative-integer/sc (numeric/sc Negative-Integer (and/c exact-integer? negative?)))
(define nonpositive-integer/sc (numeric/sc Nonpositive-Integer (and/c exact-integer? nonpostive?)))
(define nonpositive-integer/sc (numeric/sc Nonpositive-Integer (and/c exact-integer? nonpositive?)))
(define integer/sc (numeric/sc Integer exact-integer?))
(define positive-rational/sc (numeric/sc Positive-Rational (and/c t:exact-rational? positive?)))
(define nonnegative-rational/sc (numeric/sc Nonnegative-Rational (and/c t:exact-rational? nonnegative?)))

View File

@ -154,10 +154,11 @@
(define (type-stxs->ids+defs type-stxs typed-side)
(for/lists (_1 _2) ([t (in-list type-stxs)])
(define ctc-id (generate-temporary))
(define contract-def `#s(contract-def ,t #f #f ,typed-side))
(values ctc-id
#`(define-values (#,ctc-id)
#,(contract-def-property
#'#f `#s(contract-def ,t #f #f ,typed-side))))))
#'#f (λ () contract-def))))))
(define (wt-core stx)
(define-syntax-class typed-id

View File

@ -1,67 +1,4 @@
#lang racket/base
;;TODO use contract-req
(require "rep-utils.rkt" "free-variance.rkt" racket/contract/base
racket/lazy-require)
;; TODO use something other than lazy-require.
(lazy-require ["type-rep.rkt" (Type/c Univ? Bottom?)]
["object-rep.rkt" (Path?)])
(provide Filter/c FilterSet/c name-ref/c hash-name filter-equal?)
(define (Filter/c-predicate? e)
(and (Filter? e) (not (NoFilter? e)) (not (FilterSet? e))))
(define Filter/c (flat-named-contract 'Filter Filter/c-predicate?))
(define FilterSet/c
(flat-named-contract
'FilterSet
(λ (e) (or (FilterSet? e) (NoFilter? e)))))
;; A Name-Ref is any value that represents an object.
;; As an identifier, it represents a free variable in the environment
;; As a list, it represents a De Bruijn indexed bound variable
(define name-ref/c (or/c identifier? (list/c integer? integer?)))
(define (hash-name v) (if (identifier? v) (hash-id v) (list v)))
(define ((length>=/c len) l)
(and (list? l)
(>= (length l) len)))
(def-filter Bot () [#:fold-rhs #:base])
(def-filter Top () [#:fold-rhs #:base])
(def-filter TypeFilter ([t (and/c Type/c (not/c Univ?) (not/c Bottom?))] [p Path?])
[#:intern (list (Rep-seq t) (Rep-seq p))]
[#:frees (λ (f) (combine-frees (map f (list t p))))]
[#:fold-rhs (*TypeFilter (type-rec-id t) (object-rec-id p))])
(def-filter NotTypeFilter ([t (and/c Type/c (not/c Univ?) (not/c Bottom?))] [p Path?])
[#:intern (list (Rep-seq t) (Rep-seq p))]
[#:frees (λ (f) (combine-frees (map f (list t p))))]
[#:fold-rhs (*NotTypeFilter (type-rec-id t) (object-rec-id p))])
;; implication
(def-filter ImpFilter ([a Filter/c] [c Filter/c]))
(def-filter OrFilter ([fs (and/c (length>=/c 2)
(listof (or/c TypeFilter? NotTypeFilter? ImpFilter?)))])
[#:intern (map Rep-seq fs)]
[#:fold-rhs (*OrFilter (map filter-rec-id fs))]
[#:frees (λ (f) (combine-frees (map f fs)))])
(def-filter AndFilter ([fs (and/c (length>=/c 2)
(listof (or/c OrFilter? TypeFilter? NotTypeFilter? ImpFilter?)))])
[#:intern (map Rep-seq fs)]
[#:fold-rhs (*AndFilter (map filter-rec-id fs))]
[#:frees (λ (f) (combine-frees (map f fs)))])
(def-filter FilterSet ([thn Filter/c] [els Filter/c])
[#:fold-rhs (*FilterSet (filter-rec-id thn) (filter-rec-id els))])
;; represents no info about the filters of this expression
;; should only be used for parsing type annotations and expected types
(def-filter NoFilter () [#:fold-rhs #:base])
(define (filter-equal? a b) (= (Rep-seq a) (Rep-seq b)))
(require "prop-rep.rkt")
(provide (all-from-out "prop-rep.rkt"))

View File

@ -5,7 +5,7 @@
;;
;; See "Logical Types for Untyped Languages" pg.3
(require "rep-utils.rkt" "free-variance.rkt" "filter-rep.rkt" "../utils/utils.rkt" (contract-req))
(require "rep-utils.rkt" "free-variance.rkt" "prop-rep.rkt" "../utils/utils.rkt" (contract-req))
(provide object-equal?)
(def-pathelem CarPE () [#:fold-rhs #:base])
@ -25,16 +25,4 @@
[#:frees (λ (f) (combine-frees (map f p)))]
[#:fold-rhs (*Path (map pathelem-rec-id p) v)])
;; represents no info about the object of this expression
;; should only be used for parsing type annotations and expected types
(def-object NoObject () [#:fold-rhs #:base])
(define (object-equal? o1 o2) (= (Rep-seq o1) (Rep-seq o2)))
#|
(dlo LEmpty () [#:fold-rhs #:base])
(dlo LPath ([p (listof PathElem?)] [idx index/c])
[#:frees (λ (f) (combine-frees (map f p)))]
[#:fold-rhs (*LPath (map pathelem-rec-id p) idx)])
|#

View File

@ -0,0 +1,56 @@
#lang racket/base
(require "../utils/utils.rkt" "rep-utils.rkt" "free-variance.rkt")
(provide hash-name prop-equal?)
(begin-for-cond-contract
(require racket/contract/base racket/lazy-require)
(lazy-require ["type-rep.rkt" (Type/c Univ? Bottom?)]
["object-rep.rkt" (Path?)]))
(provide-for-cond-contract name-ref/c)
;; A Name-Ref is any value that represents an object.
;; As an identifier, it represents a free variable in the environment
;; As a list, it represents a De Bruijn indexed bound variable
(define-for-cond-contract name-ref/c
(or/c identifier? (list/c integer? integer?)))
(define (hash-name v) (if (identifier? v) (hash-id v) (list v)))
(define-for-cond-contract ((length>=/c len) l)
(and (list? l)
(>= (length l) len)))
;; the trivially "true" proposition
(def-prop TrueProp () [#:fold-rhs #:base])
;; the absurd, "false" proposition
(def-prop FalseProp () [#:fold-rhs #:base])
(def-prop TypeProp ([p Path?] [t (and/c Type/c (not/c Univ?) (not/c Bottom?))])
[#:intern (list (Rep-seq t) (Rep-seq p))]
[#:frees (λ (f) (combine-frees (map f (list t p))))]
[#:fold-rhs (*TypeProp (object-rec-id p) (type-rec-id t))])
(def-prop NotTypeProp ([p Path?] [t (and/c Type/c (not/c Univ?) (not/c Bottom?))])
[#:intern (list (Rep-seq t) (Rep-seq p))]
[#:frees (λ (f) (combine-frees (map f (list t p))))]
[#:fold-rhs (*NotTypeProp (object-rec-id p) (type-rec-id t))])
(def-prop OrProp ([fs (and/c (length>=/c 2)
(listof (or/c TypeProp? NotTypeProp?)))])
[#:intern (map Rep-seq fs)]
[#:fold-rhs (*OrProp (map prop-rec-id fs))]
[#:frees (λ (f) (combine-frees (map f fs)))])
(def-prop AndProp ([fs (and/c (length>=/c 2)
(listof (or/c OrProp? TypeProp? NotTypeProp?)))])
[#:intern (map Rep-seq fs)]
[#:fold-rhs (*AndProp (map prop-rec-id fs))]
[#:frees (λ (f) (combine-frees (map f fs)))])
(def-prop PropSet ([thn Prop?] [els Prop?])
[#:fold-rhs (*PropSet (prop-rec-id thn) (prop-rec-id els))])
(define (prop-equal? a b) (= (Rep-seq a) (Rep-seq b)))

View File

@ -7,7 +7,6 @@
"interning.rkt"
racket/lazy-require
racket/stxparam
racket/unsafe/ops
(for-syntax
racket/match
(except-in syntax/parse id identifier keyword)
@ -21,7 +20,7 @@
(lazy-require
["../types/printer.rkt" (print-type print-filter print-object print-pathelem)])
["../types/printer.rkt" (print-type print-prop print-object print-pathelem)])
(provide == defintern hash-id (for-syntax fold-target))
@ -33,9 +32,9 @@
(define-struct Rep (seq free-vars free-idxs stx) #:transparent
#:methods gen:equal+hash
[(define (equal-proc x y recur)
(eq? (unsafe-Rep-seq x) (unsafe-Rep-seq y)))
(define (hash-proc x recur) (unsafe-Rep-seq x))
(define (hash2-proc x recur) (unsafe-Rep-seq x))])
(eq? (Rep-seq x) (Rep-seq y)))
(define (hash-proc x recur) (Rep-seq x))
(define (hash2-proc x recur) (Rep-seq x))])
;; evil tricks for hygienic yet unhygienic-looking reference
;; in say def-type for type-ref-id
@ -136,7 +135,7 @@
#:defaults
([frees.f1 (combiner #'Rep-free-vars #'flds.fields)]
[frees.f2 (combiner #'Rep-free-idxs #'flds.fields)]))
;; This tricky beast is for defining the type/filter/etc.'s
;; This tricky beast is for defining the type/prop/etc.'s
;; part of the fold. The make-prim-type's given
;; rec-ids are bound in this expression's context.
(~optional [#:fold-rhs (~var fold-rhs (fold-pat #'name.fold))]
@ -205,7 +204,7 @@
provides))])))
;; rec-ids are identifiers that are of the folded type, so we recur on them.
;; kws is e.g. '(#:Type #:Filter #:Object #:PathElem)
;; kws is e.g. '(#:Type #:Prop #:Object #:PathElem)
(define-for-syntax (mk-fold hashtable rec-ids kws)
(lambda (stx)
(define new-hashtable (make-hasheq))
@ -218,7 +217,7 @@
(syntax/loc this-syntax (pats ...))
(lambda () #'e)
this-syntax))
;; Match on a type (or filter etc) case with keyword k
;; Match on a type (or prop etc) case with keyword k
;; pats are the unignored patterns (say for rator rand)
;; and e is the expression that will run as fold-rhs.
(pattern
@ -352,23 +351,18 @@
;; [unsyntax (*1)]
(mk-fold ht
rec-ids
;; '(#:Type #:Filter #:Object #:PathElem)
;; '(#:Type #:Prop #:Object #:PathElem)
'(i.kw ...)))
(list i.hashtable ...))))))]))
(make-prim-type [Type def-type #:Type type-case print-type type-name-ht type-rec-id #:key]
[Filter def-filter #:Filter filter-case print-filter filter-name-ht filter-rec-id]
[Prop def-prop #:Prop prop-case print-prop prop-name-ht prop-rec-id]
[Object def-object #:Object object-case print-object object-name-ht object-rec-id]
[PathElem def-pathelem #:PathElem pathelem-case print-pathelem pathelem-name-ht pathelem-rec-id])
;; NOTE: change these if the definitions above change, or everything will segfault
(define-syntax-rule (unsafe-Rep-seq v) (unsafe-struct*-ref v 0))
(define-syntax-rule (unsafe-Type-key v) (unsafe-struct*-ref v 4))
(provide unsafe-Rep-seq unsafe-Type-key)
(define (Rep-values rep)
(match rep
[(? (lambda (e) (or (Filter? e)
[(? (lambda (e) (or (Prop? e)
(Object? e)
(PathElem? e)))
(app (lambda (v) (vector->list (struct->vector v))) (list-rest tag seq fv fi stx vals)))
@ -392,7 +386,7 @@
(provide/cond-contract
[rename rep-equal? type-equal? (Type? Type? . -> . boolean?)]
[rename rep<? type<? (Type? Type? . -> . boolean?)]
[rename rep<? filter<? (Filter? Filter? . -> . boolean?)]
[rename rep<? prop<? (Prop? Prop? . -> . boolean?)]
[struct Rep ([seq exact-nonnegative-integer?]
[free-vars (hash/c symbol? variance?)]
[free-idxs (hash/c symbol? variance?)]

View File

@ -7,10 +7,11 @@
;; TODO use contract-req
(require (utils tc-utils)
"rep-utils.rkt" "object-rep.rkt" "filter-rep.rkt" "free-variance.rkt"
racket/match racket/list
"rep-utils.rkt" "object-rep.rkt" "prop-rep.rkt" "free-variance.rkt"
racket/match racket/list racket/set
racket/contract
racket/lazy-require
racket/promise
(for-syntax racket/base syntax/parse))
(provide Mu-name:
@ -18,10 +19,11 @@
PolyDots-names:
PolyRow-names: PolyRow-fresh:
Type-seq
-unsafe-intersect
Mu-unsafe: Poly-unsafe:
PolyDots-unsafe:
Mu? Poly? PolyDots? PolyRow?
Filter? Object?
Prop? Object?
Type/c Type/c?
Values/c SomeValues/c
Bottom?
@ -52,32 +54,33 @@
;; Ugly hack - should use units
(lazy-require
("../types/union.rkt" (Un))
("../types/resolve.rkt" (resolve-app)))
("../types/union.rkt" (Un))
("../types/overlap.rkt" (overlap?))
("../types/resolve.rkt" (resolve-app)))
(define name-table (make-weak-hasheq))
(define Type/c?
(λ (e)
(and (Type? e)
(not (Scope? e))
(not (arr? e))
(not (fld? e))
(not (Values? e))
(not (ValuesDots? e))
(not (AnyValues? e))
(not (Result? e)))))
(not (Result? e))
(not (Signature? e)))))
;; (or/c Type/c Values? Results?)
;; Anything that can be treated as a Values by sufficient expansion
(define Values/c?
(λ (e)
(and (Type? e)
(not (Scope? e))
(not (arr? e))
(not (fld? e))
(not (ValuesDots? e))
(not (AnyValues? e)))))
(not (AnyValues? e))
(not (Signature? e)))))
(define Type/c (flat-named-contract 'Type Type/c?))
(define Values/c (flat-named-contract 'Values Values/c?))
@ -90,19 +93,6 @@
;; Type is defined in rep-utils.rkt
;; t must be a Type
(def-type Scope ([t (or/c Type/c Scope?)]) [#:key (Type-key t)])
(define (scope-depth k)
(flat-named-contract
(format "Scope of depth ~a" k)
(lambda (sc)
(define (f k sc)
(cond [(= 0 k) (Type/c? sc)]
[(not (Scope? sc)) #f]
[else (f (sub1 k) (Scope-t sc))]))
(f k sc))))
;; this is ONLY used when a type error ocurrs
(def-type Error () [#:frees #f] [#:fold-rhs #:base])
@ -236,48 +226,43 @@
[(Keyword) 'keyword]
[else #f]))])
;; body is a Scope
(def-type Mu ([body (scope-depth 1)]) #:no-provide [#:frees (λ (f) (f body))]
[#:fold-rhs (*Mu (*Scope (type-rec-id (Scope-t body))))]
(def-type Mu ([body Type/c]) #:no-provide [#:frees (λ (f) (f body))]
[#:fold-rhs (*Mu (type-rec-id body))]
[#:key (Type-key body)])
;; n is how many variables are bound here
;; body is a Scope
;; body is a type
(def-type Poly (n body) #:no-provide
[#:contract (->i ([n natural-number/c]
[body (n) (scope-depth n)])
[#:contract (->i ([n natural-number/c]
[body Type/c])
(#:syntax [stx (or/c #f syntax?)])
[result Poly?])]
[#:frees (λ (f) (f body))]
[#:fold-rhs (let ([body* (remove-scopes n body)])
(*Poly n (add-scopes n (type-rec-id body*))))]
[#:fold-rhs (*Poly n (type-rec-id body))]
[#:key (Type-key body)])
;; n is how many variables are bound here
;; there are n-1 'normal' vars and 1 ... var
;; body is a Scope
(def-type PolyDots (n body) #:no-provide
[#:contract (->i ([n natural-number/c]
[body (n) (scope-depth n)])
[body Type/c])
(#:syntax [stx (or/c #f syntax?)])
[result PolyDots?])]
[#:key (Type-key body)]
[#:frees (λ (f) (f body))]
[#:fold-rhs (let ([body* (remove-scopes n body)])
(*PolyDots n (add-scopes n (type-rec-id body*))))])
[#:fold-rhs (*PolyDots n (type-rec-id body))])
;; interp. A row polymorphic function type
;; constraints are row absence constraints, represented
;; as a set for each of init, field, methods
(def-type PolyRow (constraints body) #:no-provide
[#:contract (->i ([constraints (list/c list? list? list? list?)]
[body (scope-depth 1)])
[body Type/c])
(#:syntax [stx (or/c #f syntax?)])
[result PolyRow?])]
[#:frees (λ (f) (f body))]
[#:fold-rhs (let ([body* (remove-scopes 1 body)])
(*PolyRow constraints
(add-scopes 1 (type-rec-id body*))))]
[#:fold-rhs (*PolyRow constraints
(type-rec-id body))]
[#:key (Type-key body)])
;; pred : identifier
@ -291,9 +276,9 @@
[#:frees (λ (f) (f ty))]
[#:fold-rhs (*Keyword kw (type-rec-id ty) required?)])
(def-type Result ([t Type/c] [f FilterSet?] [o Object?])
(def-type Result ([t Type/c] [f PropSet?] [o Object?])
[#:frees (λ (frees) (combine-frees (map frees (list t f o))))]
[#:fold-rhs (*Result (type-rec-id t) (filter-rec-id f) (object-rec-id o))])
[#:fold-rhs (*Result (type-rec-id t) (prop-rec-id f) (object-rec-id o))])
(def-type Values ([rs (listof Result?)])
[#:intern (map Rep-seq rs)]
@ -301,7 +286,7 @@
[#:fold-rhs (*Values (map type-rec-id rs))])
(def-type AnyValues ([f Filter/c])
(def-type AnyValues ([f Prop?])
[#:fold-rhs #:base])
(def-type ValuesDots ([rs (listof Result?)] [dty Type/c] [dbound (or/c symbol? natural-number/c)])
@ -466,6 +451,55 @@
(define d* (remove-duplicates d))
(if (and (pair? d*) (null? (cdr d*))) (car d*) d*))])
;; Intersection
(def-type Intersection ([elems (and/c (set/c Type/c)
(λ (s) (>= (set-count s) 2)))])
[#:intern (for/set ([e (in-immutable-set elems)])
(Rep-seq e))]
[#:frees (λ (f) (combine-frees (for/list ([elem (in-immutable-set elems)])
(f elem))))]
[#:fold-rhs (let ([elems (for/list ([elem (in-immutable-set elems)])
(type-rec-id elem))])
(apply -unsafe-intersect elems))]
[#:key (let ()
(define d
(let loop ([ts (set->list elems)] [res null])
(cond [(null? ts) res]
[else
(define k (Type-key (car ts)))
(cond [(not k) (list #f)]
[(pair? k) (loop (cdr ts) (append k res))]
[else (loop (cdr ts) (cons k res))])])))
(define d* (remove-duplicates d))
(if (and (pair? d*) (null? (cdr d*))) (car d*) d*))])
;; constructor for intersections
;; in general, intersections should be built
;; using the 'intersect' operator, which worries
;; about actual subtyping, etc...
(define (-unsafe-intersect . ts)
(let loop ([elems (set)]
[ts ts])
(match ts
[(list)
(cond
[(set-empty? elems) (Univ)]
;; size = 1 ?
[(= 1 (set-count elems)) (set-first elems)]
;; size > 1, build an intersection
[else (*Intersection elems)])]
[(cons t ts)
(match t
[(? Bottom?) t]
[(Univ:) (loop elems ts)]
[(Intersection: ts*) (loop (set-union elems ts*) ts)]
[t (cond
[(for/or ([elem (in-immutable-set elems)]) (not (overlap? elem t)))
(*Union (list))]
[else (loop (set-add elems t) ts)])])])))
(def-type Univ () [#:frees #f] [#:fold-rhs #:base])
;; in : Type
@ -555,6 +589,35 @@
;; cls : Class
(def-type Instance ([cls Type/c]) [#:key 'instance])
;; interp:
;; name is the id of the signature
;; extends is the extended signature or #f
;; mapping maps variables in a signature to their types
;; This is not a type because signatures are not values
(def-type Signature ([name identifier?]
[extends (or/c identifier? #f)]
[mapping (listof (cons/c identifier? (or/c promise? Type/c)))])
[#:frees (lambda (f) null)]
[#:fold-rhs (*Signature name extends mapping)])
;; The supertype of all units, ie values recognized by the
;; predicate unit?
(def-type UnitTop () [#:fold-rhs #:base] [#:key 'unit])
;; interp: imports is the list of imported signatures
;; exports is the list of exported signatures
;; init-depends is the list of init-depend signatures
;; result is the type of the body of the unit
(def-type Unit ([imports (listof Signature?)]
[exports (listof Signature?)]
[init-depends (listof Signature?)]
[result SomeValues/c])
[#:frees (lambda (f) (f result))]
[#:fold-rhs (*Unit (map type-rec-id imports)
(map type-rec-id exports)
(map type-rec-id init-depends)
(type-rec-id result))])
;; sequences
;; includes lists, vectors, etc
;; tys : sequence produces this set of values at each step
@ -604,24 +667,11 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (add-scopes n t)
(if (zero? n) t
(add-scopes (sub1 n) (*Scope t))))
(define (remove-scopes n sc)
(if (zero? n)
sc
(match sc
[(Scope: sc*) (remove-scopes (sub1 n) sc*)]
[_ (int-err "Tried to remove too many scopes: ~a" sc)])))
(define ((sub-f st) e)
(filter-case (#:Type st
#:Filter (sub-f st)
#:PathElem (sub-pe st))
e))
(prop-case (#:Type st
#:Prop (sub-f st)
#:PathElem (sub-pe st))
e))
(define ((sub-o st) e)
@ -637,11 +687,11 @@
(define ((sub-t st) e)
(type-case (#:Type st
#:Filter (sub-f st))
#:Prop (sub-f st))
e))
;; abstract-many : Names Type -> Scope^n
;; abstract-many : Names Type -> Type
;; where n is the length of names
(define (abstract-many names ty)
;; mapping : dict[Type -> Natural]
@ -658,7 +708,7 @@
(f (+ (cdr pr) outer)))]
[else default]))
(type-case
(#:Type sb #:Filter (sub-f sb) #:Object (sub-o sb))
(#:Type sb #:Prop (sub-f sb) #:Object (sub-o sb))
ty
[#:F name* (transform name* *B ty)]
;; necessary to avoid infinite loops
@ -681,27 +731,23 @@
[#:ListDots dty dbound
(*ListDots (sb dty)
(transform dbound values dbound))]
[#:Mu (Scope: body) (*Mu (*Scope (loop (add1 outer) body)))]
[#:PolyRow constraints body*
(let ([body (remove-scopes 1 body*)])
(*PolyRow constraints
(add-scopes 1 (loop (+ 1 outer) body))))]
[#:PolyDots n body*
(let ([body (remove-scopes n body*)])
(*PolyDots n (add-scopes n (loop (+ n outer) body))))]
[#:Poly n body*
(let ([body (remove-scopes n body*)])
(*Poly n (add-scopes n (loop (+ n outer) body))))])))
[#:Mu body (*Mu (loop (add1 outer) body))]
[#:PolyRow constraints body
(*PolyRow constraints (loop (+ 1 outer) body))]
[#:PolyDots n body
(*PolyDots n (loop (+ n outer) body))]
[#:Poly n body
(*Poly n (loop (+ n outer) body))])))
(define n (length names))
(define mapping (for/list ([nm (in-list names)]
[i (in-range n 0 -1)])
(cons nm (sub1 i))))
(add-scopes n (nameTo mapping ty)))
(nameTo mapping ty))
;; instantiate-many : List[Type] Scope^n -> Type
;; instantiate-many : List[Type] Type -> Type
;; where n is the length of types
;; all of the types MUST be Fs
(define (instantiate-many images sc)
(define (instantiate-many images ty)
;; mapping : dict[Natural -> Type]
(define (replace mapping type)
(let loop ([outer 0] [ty type])
@ -716,7 +762,7 @@
(define (sb t) (loop outer t))
(define sf (sub-f sb))
(type-case
(#:Type sb #:Filter sf #:Object (sub-o sb))
(#:Type sb #:Prop sf #:Object (sub-o sb))
ty
[#:B idx (transform idx values ty)]
;; necessary to avoid infinite loops
@ -738,21 +784,18 @@
[#:ListDots dty dbound
(*ListDots (sb dty)
(transform dbound F-n dbound))]
[#:Mu (Scope: body) (*Mu (*Scope (loop (add1 outer) body)))]
[#:PolyRow constraints body*
(let ([body (remove-scopes 1 body*)])
(*PolyRow constraints (add-scopes 1 (loop (+ 1 outer) body))))]
[#:PolyDots n body*
(let ([body (remove-scopes n body*)])
(*PolyDots n (add-scopes n (loop (+ n outer) body))))]
[#:Poly n body*
(let ([body (remove-scopes n body*)])
(*Poly n (add-scopes n (loop (+ n outer) body))))])))
[#:Mu body (*Mu (loop (add1 outer) body))]
[#:PolyRow constraints body
(*PolyRow constraints (loop (+ 1 outer) body))]
[#:PolyDots n body
(*PolyDots n (loop (+ n outer) body))]
[#:Poly n body
(*Poly n (loop (+ n outer) body))])))
(define n (length images))
(define mapping (for/list ([img (in-list images)]
[i (in-range n 0 -1)])
(cons (sub1 i) img)))
(replace mapping (remove-scopes n sc)))
(replace mapping ty))
(define (abstract name ty)
(abstract-many (list name) ty))
@ -769,8 +812,8 @@
;; the 'smart' destructor
(define (Mu-body* name t)
(match t
[(Mu: scope)
(instantiate (*F name) scope)]))
[(Mu: body)
(instantiate (*F name) body)]))
;; the 'smart' constructor
;;
@ -793,10 +836,10 @@
;; the 'smart' destructor
(define (Poly-body* names t)
(match t
[(Poly: n scope)
[(Poly: n body)
(unless (= (length names) n)
(int-err "Wrong number of names: expected ~a got ~a" n (length names)))
(instantiate-many (map *F names) scope)]))
(instantiate-many (map *F names) body)]))
;; the 'smart' constructor
(define (PolyDots* names body)
@ -808,10 +851,10 @@
;; the 'smart' destructor
(define (PolyDots-body* names t)
(match t
[(PolyDots: n scope)
[(PolyDots: n body)
(unless (= (length names) n)
(int-err "Wrong number of names: expected ~a got ~a" n (length names)))
(instantiate-many (map *F names) scope)]))
(instantiate-many (map *F names) body)]))
;; Constructor and destructor for row polymorphism
;;
@ -826,15 +869,15 @@
(define (PolyRow-body* names t)
(match t
[(PolyRow: constraints scope)
(instantiate-many (map *F names) scope)]))
[(PolyRow: constraints body)
(instantiate-many (map *F names) body)]))
(print-struct #t)
(define-match-expander Mu-unsafe:
(lambda (stx)
(syntax-case stx ()
[(_ bp) #'(? Mu? (app (lambda (t) (Scope-t (Mu-body t))) bp))])))
[(_ bp) #'(? Mu? (app (lambda (t) (Mu-body t)) bp))])))
(define-match-expander Poly-unsafe:
(lambda (stx)

View File

@ -28,7 +28,8 @@
[(define (sc-map v f) v)
(define (sc-traverse v f) (void))
(define (sc->contract v f) #'any/c)
(define (sc->constraints v f) (simple-contract-restrict 'flat))]
(define (sc->constraints v f) (simple-contract-restrict 'flat))
(define (sc-terminal-kind v) 'flat)]
#:methods gen:custom-write [(define write-proc any-write-proc)])
(define-match-expander any/sc:

View File

@ -5,7 +5,6 @@
(require "../structures.rkt" "../constraints.rkt"
racket/list racket/match
unstable/contract
racket/contract
(for-template racket/base racket/contract/base)
(for-syntax racket/base syntax/parse))
@ -14,8 +13,8 @@
(contract-out
[case->/sc ((listof arr-combinator?) . -> . static-contract?)]
[arr/sc (-> (listof static-contract?)
(maybe/c static-contract?)
(maybe/c (listof static-contract?))
(or/c static-contract? #f)
(or/c (listof static-contract?) #f)
static-contract?)])
case->/sc:
arr/sc:

View File

@ -5,14 +5,13 @@
(require "../structures.rkt" "../constraints.rkt"
racket/list racket/match
unstable/contract
racket/contract
(for-template racket/base racket/contract/base)
(for-syntax racket/base syntax/parse))
(provide
(contract-out
[prompt-tag/sc ((listof static-contract?) (maybe/c (listof static-contract?)) . -> . static-contract?)])
[prompt-tag/sc ((listof static-contract?) (or/c (listof static-contract?) #f) . -> . static-contract?)])
prompt-tag/sc:)
(struct prompt-tag-combinator combinator ()

View File

@ -6,7 +6,7 @@
(require "simple.rkt" "structural.rkt"
(for-template racket/base racket/list racket/set racket/promise
racket/class racket/async-channel))
racket/class racket/unit racket/async-channel))
(provide (all-defined-out))
(define identifier?/sc (flat/sc #'identifier?))
@ -34,5 +34,6 @@
(define continuation-mark-key?/sc (flat/sc #'continuation-mark-key?))
(define class?/sc (flat/sc #'class?))
(define unit?/sc (flat/sc #'unit?))
(define struct-type?/sc (flat/sc #'struct-type?))

View File

@ -6,12 +6,13 @@
(require "../structures.rkt" "../constraints.rkt"
racket/list racket/match
racket/contract
(for-template racket/base racket/contract/base)
(for-template racket/base racket/contract/base "../../utils/simple-result-arrow.rkt")
(for-syntax racket/base syntax/parse))
(provide
(contract-out
[function/sc (-> (listof static-contract?)
[function/sc (-> boolean?
(listof static-contract?)
(listof static-contract?)
(listof (list/c keyword? static-contract?))
(listof (list/c keyword? static-contract?))
@ -21,7 +22,7 @@
->/sc:)
(struct function-combinator combinator (indices mand-kws opt-kws)
(struct function-combinator combinator (indices mand-kws opt-kws typed-side?)
#:property prop:combinator-name "->/sc"
#:methods gen:equal+hash [(define (equal-proc a b recur) (function-sc-equal? a b recur))
(define (hash-proc v recur) (function-sc-hash v recur))
@ -30,6 +31,7 @@
[(define (sc->contract v f) (function-sc->contract v f))
(define (sc-map v f) (function-sc-map v f))
(define (sc-traverse v f) (function-sc-map v f) (void))
(define (sc-terminal-kind v) (function-sc-terminal-kind v))
(define (sc->constraints v f) (function-sc-constraints v f))])
(define (split-function-args ctcs mand-args-end opt-args-end
@ -44,7 +46,10 @@
(and range-end (drop (take ctcs range-end) rest-end))))
(define (function-sc->contract sc recur)
(match-define (function-combinator args indices mand-kws opt-kws) sc)
(match-define (function-combinator args indices mand-kws opt-kws typed-side?) sc)
(define-values (mand-scs opt-scs mand-kw-scs opt-kw-scs rest-sc range-scs)
(apply split-function-args args indices))
(define-values (mand-ctcs opt-ctcs mand-kw-ctcs opt-kw-ctcs rest-ctc range-ctcs)
(apply split-function-args (map recur args) indices))
@ -61,14 +66,24 @@
#`(values #,@range-ctcs)
#'any))
#`((#,@mand-ctcs #,@mand-kws-stx)
(#,@opt-ctcs #,@opt-kws-stx)
#,@rest-ctc-stx
. ->* . #,range-ctc))
(cond
[(and (null? mand-kws) (null? opt-kws)
(null? opt-ctcs)
(not rest-ctc)
;; currently simple-result-> only handles up to arity 3
(member (length mand-ctcs) '(0 1 2 3))
(and range-ctcs (= 1 (length range-ctcs)))
(for/and ([a args]) (eq? 'flat (sc-terminal-kind a)))
(not typed-side?))
#`(simple-result-> #,@range-ctcs #,(length mand-ctcs))]
[else
#`((#,@mand-ctcs #,@mand-kws-stx)
(#,@opt-ctcs #,@opt-kws-stx)
#,@rest-ctc-stx
. ->* . #,range-ctc)]))
(define (function/sc mand-args opt-args mand-kw-args opt-kw-args rest range)
(define (function/sc typed-side? mand-args opt-args mand-kw-args opt-kw-args rest range)
(define mand-args-end (length mand-args))
(define opt-args-end (+ mand-args-end (length opt-args)))
(define mand-kw-args-end (+ opt-args-end (length mand-kw-args)))
@ -90,14 +105,15 @@
(or range null))
end-indices
mand-kws
opt-kws))
opt-kws
typed-side?))
(define-match-expander ->/sc:
(syntax-parser
[(_ mand-args opt-args mand-kw-args opt-kw-args rest range)
#'(and (? function-combinator?)
(app (match-lambda
[(function-combinator args indices mand-kws opt-kws)
[(function-combinator args indices mand-kws opt-kws typed-side?)
(define-values (mand-args* opt-args* mand-kw-args* opt-kw-args* rest* range*)
(apply split-function-args args indices))
(list
@ -109,13 +125,13 @@
(list mand-args opt-args mand-kw-args opt-kw-args rest range)))]))
(define (function-sc-map v f)
(match-define (function-combinator args indices mand-kws opt-kws) v)
(match-define (function-combinator args indices mand-kws opt-kws typed-side?) v)
(define-values (mand-args opt-args mand-kw-args opt-kw-args rest-arg range-args)
(apply split-function-args args indices))
(define new-args
(append
(append
(map (lambda (arg) (f arg 'contravariant))
(append mand-args opt-args mand-kw-args opt-kw-args (if rest-arg (list rest-arg) null)))
(if range-args
@ -124,26 +140,49 @@
empty)))
(function-combinator new-args indices mand-kws opt-kws))
(function-combinator new-args indices mand-kws opt-kws typed-side?))
(define (function-sc-terminal-kind v)
(match-define (function-combinator args indices mand-kws opt-kws typed-side?) v)
(define-values (mand-args opt-args mand-kw-args opt-kw-args rest-arg range-args)
(apply split-function-args args indices))
(if (and (not rest-arg)
(null? (append mand-kw-args mand-args opt-kw-args opt-args))
typed-side?)
;; currently we only handle this trivial case
;; we could probably look at the actual kind of `range-args` as well
(if (not range-args) 'flat #f)
#f))
(define (function-sc-constraints v f)
(match-define (function-combinator args indices mand-kws opt-kws) v)
(merge-restricts* 'chaperone (map f args)))
(match-define (function-combinator args indices mand-kws opt-kws typed-side?) v)
(define-values (mand-args opt-args mand-kw-args opt-kw-args rest-arg range-args)
(apply split-function-args args indices))
(if (and (not rest-arg)
(null? (append mand-kw-args mand-args opt-kw-args opt-args))
typed-side?)
;; arity-0 functions end up being flat contracts when they're
;; from the typed side and the result is flat
(if range-args
(merge-restricts* 'flat (map f range-args))
(merge-restricts* 'flat null))
(merge-restricts* 'chaperone (map f args))))
(define (function-sc-equal? a b recur)
(match-define (function-combinator a-args a-indices a-mand-kws a-opt-kws) a)
(match-define (function-combinator b-args b-indices b-mand-kws b-opt-kws) b)
(match-define (function-combinator a-args a-indices a-mand-kws a-opt-kws a-typed-side?) a)
(match-define (function-combinator b-args b-indices b-mand-kws b-opt-kws b-typed-side?) b)
(and
(equal? a-typed-side? b-typed-side?)
(recur a-indices b-indices)
(recur a-mand-kws b-mand-kws)
(recur a-opt-kws b-opt-kws)
(recur a-args b-args)))
(define (function-sc-hash v recur)
(match-define (function-combinator v-args v-indices v-mand-kws v-opt-kws) v)
(match-define (function-combinator v-args v-indices v-mand-kws v-opt-kws typed-side?) v)
(+ (recur v-indices) (recur v-mand-kws) (recur v-opt-kws) (recur v-args)))
(define (function-sc-hash2 v recur)
(match-define (function-combinator v-args v-indices v-mand-kws v-opt-kws) v)
(match-define (function-combinator v-args v-indices v-mand-kws v-opt-kws typed-side?) v)
(+ (recur v-indices) (recur v-mand-kws) (recur v-opt-kws) (recur v-args)))

View File

@ -33,6 +33,25 @@
(struct simple-contract static-contract (syntax kind name)
#:transparent
#:methods gen:equal+hash
[(define (equal-proc s1 s2 recur)
(and ;; only check s-expression equality because it's
;; unlikely that TR will compile contracts that are
;; s-exp equal but aren't actually the same contract
(recur (syntax->datum (simple-contract-syntax s1))
(syntax->datum (simple-contract-syntax s2)))
(recur (simple-contract-kind s1)
(simple-contract-kind s2))
(recur (simple-contract-name s1)
(simple-contract-name s2))))
(define (hash-proc sc hash-code)
(hash-code (list (syntax->datum (simple-contract-syntax sc))
(simple-contract-kind sc)
(simple-contract-name sc))))
(define (hash2-proc sc hash-code)
(hash-code (list (syntax->datum (simple-contract-syntax sc))
(simple-contract-kind sc)
(simple-contract-name sc))))]
#:methods gen:sc
[(define (sc-map v f) v)
(define (sc-traverse v f) (void))

View File

@ -8,14 +8,15 @@
racket/match
(for-syntax racket/base racket/syntax syntax/stx syntax/parse)
racket/set
unstable/contract
racket/sequence
(for-template racket/base
racket/contract/base
racket/set
racket/async-channel
unstable/contract
racket/sequence
racket/promise
"../../utils/evt-contract.rkt")
"../../utils/evt-contract.rkt"
"../../utils/promise-not-name-contract.rkt")
racket/contract
racket/async-channel)
@ -153,7 +154,7 @@
((set/sc (#:covariant #:chaperone)) set/c #:flat)
((vector/sc . (#:invariant)) vector/c #:chaperone)
((vectorof/sc (#:invariant)) vectorof #:chaperone)
((promise/sc (#:covariant)) (λ (x) (and/c (promise/c x) (not/c promise/name?))) #:chaperone)
((promise/sc (#:covariant)) promise-not-name/c #:chaperone)
((syntax/sc (#:covariant #:flat)) syntax/c #:flat)
((hash/sc (#:invariant #:flat) (#:invariant)) hash/c #:chaperone)
((box/sc (#:invariant)) box/c #:chaperone)

View File

@ -0,0 +1,109 @@
#lang racket/base
;; Static contracts for unit contracts
(require "../structures.rkt" "../constraints.rkt"
racket/list racket/match
racket/dict
racket/contract
racket/syntax
(for-template racket/base racket/unit)
(for-syntax racket/base syntax/parse))
(provide
(contract-out
[struct signature-spec ([name identifier?]
[members (listof identifier?)]
[scs (listof static-contract?)])]
[unit/sc (-> (listof signature-spec?)
(listof signature-spec?)
(listof identifier?)
(listof static-contract?)
static-contract?)]))
(struct signature-spec (name members scs) #:transparent)
(struct unit-combinator combinator ()
#:transparent
#:property prop:combinator-name "unit/sc"
#:methods gen:sc
[(define (sc-map v f)
(match v
[(unit-combinator unit-spec)
(unit-combinator (unit-spec-sc-map f unit-spec))]))
(define (sc-traverse v f)
(match v
[(unit-combinator unit-spec)
(unit-spec-sc-map f unit-spec)
(void)]))
(define (sc->contract v f)
(unit/sc->contract v f))
(define (sc->constraints v f)
(merge-restricts* 'chaperone (map f (unit-spec->list (combinator-args v)))))])
(define unit-spec->list
(match-lambda
[(unit-spec imports exports init-depends invoke)
(flatten (append (filter-map signature-spec-scs imports)
(filter-map signature-spec-scs exports)
;; init-depends do not show up because
;; there are no contracts attached
(filter-map (lambda (x) x) invoke)))]))
(struct unit-spec (imports exports init-depends invoke)
#:transparent
#:property prop:sequence unit-spec->list)
(define (unit-spec-sc-map f seq)
(match seq
[(unit-spec imports exports init-depends invokes)
(unit-spec
(map (signature-spec-sc-map f) imports)
(map (signature-spec-sc-map f) exports)
;; leave init-depends alone since they don't contain contracts
init-depends
(map (lambda (invoke) (and invoke (f invoke 'covariant))) invokes))]))
(define ((signature-spec-sc-map f) seq)
(match seq
[(signature-spec name (list ids ...) (list scs ...))
(signature-spec
name
ids
(map (lambda (sc) (and sc (f sc 'invariant))) scs))]))
(define (unit/sc->contract v f)
(match v
[(unit-combinator
(unit-spec (list imports ...)
(list exports ...)
(list deps ...)
(list invoke/scs ...)))
(define (sig-spec->syntax sig-spec)
(match sig-spec
[(signature-spec name members scs)
(define member-stx
(map (lambda (id sc) #`(#,id #,(f sc))) members scs))
#`(#,name #,@member-stx)]))
(define (invokes->contract lst)
(cond
;; just a single contract
[(= 1 (length lst))
#`#,(f (first lst))]
;; values contract
[else
#`(values #,@(map f lst))]))
#`(unit/c
(import #,@(map sig-spec->syntax imports))
(export #,@(map sig-spec->syntax exports))
(init-depend #,@deps)
#,(invokes->contract invoke/scs))]))
(define (unit/sc imports exports init-depends invoke)
(unit-combinator (unit-spec imports exports init-depends invoke)))

View File

@ -61,7 +61,8 @@
contract-restrict-recursive-values
contract-restrict?
)
contract-restrict-value
kind-max-max)
(module structs racket/base
(require racket/contract

View File

@ -23,7 +23,8 @@
[instantiate
(parametric->/c (a) ((static-contract? (-> #:reason (or/c #f string?) a))
(contract-kind? #:cache hash?)
. ->* . (or/c a (list/c (listof syntax?) syntax?))))]))
. ->* . (or/c a (list/c (listof syntax?) syntax?))))]
[should-inline-contract? (-> syntax? boolean?)]))
;; Providing these so that tests can work directly with them.
(module* internals #f
@ -47,9 +48,42 @@
(contract-restrict-recursive-values (compute-constraints sc kind)))
cache))))
;; computes the definitions that are in / used by `sc`
;; `(get-all-name-defs)` is not what we want directly, since it also includes
;; definitions that were optimized away
;; we restrict it to only variables bound in `sc`
(define (compute-defs sc)
(define all-name-defs (get-all-name-defs))
;; all-name-defs maps lists of ids to defs
;; we want to match if any id in the list matches
(define (ref b) (for/first ([(k v) (in-dict all-name-defs)]
#:when (for/or ([k* (in-list k)])
(free-identifier=? b k*)))
(cons k v)))
(define bound '())
;; ignores its second argument (variance, passed by sc-traverse)
(let loop ([sc sc] [_ #f])
(match sc
[(name/sc: name*)
(unless (member name* bound free-identifier=?)
(set! bound (cons name* bound))
;; traverse what `name` refers to
(define r (ref name*))
;; ref returns a rib, get the one definition we want
(define target (for/first ([k (car r)]
[v (cdr r)]
#:when (free-identifier=? name* k))
v))
(loop target #f))]
[else (sc-traverse sc loop)]))
(for*/hash ([b (in-list bound)]
[v (in-value (ref b))]
#:when v)
(values (car v) (cdr v))))
(define (compute-constraints sc max-kind)
(define memo-table (make-hash))
(define name-defs (get-all-name-defs))
(define name-defs (compute-defs sc))
(define (recur sc)
(cond [(hash-ref memo-table sc #f)]
[else
@ -96,7 +130,9 @@
(define bound-names (make-parameter null))
;; sc-queue : records the order in which to return syntax objects
(define sc-queue null)
(define (recur sc)
;; top-level? is #t only for the first call and not for recursive
;; calls, which helps for inlining
(define (recur sc [top-level? #f])
(cond [(and cache (hash-ref cache sc #f)) => car]
[(arr/sc? sc) (make-contract sc)]
[(or (parametric->/sc? sc) (sealing->/sc? sc))
@ -111,7 +147,14 @@
(make-contract sc)]
[else
(define ctc (make-contract sc))
(cond [cache
(cond [(and ;; when a contract benefits from inlining
;; (e.g., ->) and this contract appears
;; directly in a define-module-boundary-contract
;; position (i.e, top-level? is #t) then
;; don't generate a new identifier for it
(or (not (should-inline-contract? ctc))
(not top-level?))
cache)
(define fresh-id (generate-temporary))
(hash-set! cache sc (cons fresh-id ctc))
(set! sc-queue (cons sc sc-queue))
@ -137,8 +180,8 @@
(recur body)))]
[(? sc? sc)
(sc->contract sc recur)]))
(define ctc (recur sc))
(define name-defs (get-all-name-defs))
(define ctc (recur sc #t))
(define name-defs (compute-defs sc))
;; These are extra contract definitions for the name static contracts
;; that are used for this type. Since these are shared across multiple
;; contracts from a single contract fixup pass, we use the name-defined
@ -163,6 +206,17 @@
#`(define #,id #,ctc)))
ctc))
;; Determine whether the given contract syntax should be inlined or not.
(define (should-inline-contract? stx)
(or
;; no need to generate an extra def for things that are already identifiers
(identifier? stx)
;; ->* are handled specially by the contract system
(let ([sexp (syntax-e stx)])
(and (pair? sexp)
(or (free-identifier=? (car sexp) #'->)
(free-identifier=? (car sexp) #'->*))))))
;; determine if a given name is free in the sc
(define (name-free-in? name sc)
(let/ec escape

View File

@ -93,6 +93,7 @@
(fail))
;; All the checks passed
(function/sc
#t
(take longest-args (length shortest-args))
(drop longest-args (length shortest-args))
empty
@ -110,7 +111,7 @@
(define (trusted-side-reduce sc)
(match sc
[(->/sc: mand-args opt-args mand-kw-args opt-kw-args rest-arg (list (any/sc:) ...))
(function/sc mand-args opt-args mand-kw-args opt-kw-args rest-arg #f)]
(function/sc #t mand-args opt-args mand-kw-args opt-kw-args rest-arg #f)]
[(arr/sc: args rest (list (any/sc:) ...))
(arr/sc args rest #f)]
[(none/sc:) any/sc]

View File

@ -1,6 +1,7 @@
#lang racket/base
(require "utils/utils.rkt"
syntax/kerncase
syntax/stx
racket/pretty racket/promise racket/lazy-require
(env type-name-env type-alias-env mvar-env)
@ -9,7 +10,8 @@
(for-syntax racket/base)
(for-template racket/base))
(lazy-require [typed-racket/optimizer/optimizer (optimize-top)])
(lazy-require [typed-racket/typecheck/tc-toplevel (tc-toplevel-form tc-module)])
(lazy-require [typed-racket/typecheck/tc-toplevel (tc-module)])
(lazy-require [typed-racket/typecheck/toplevel-trampoline (tc-toplevel-start)])
(provide maybe-optimize init-current-type-names
tc-module/full
@ -36,7 +38,7 @@
(define-logger online-check-syntax)
(define (tc-setup orig-stx stx expand-ctxt do-expand checker k)
(define (tc-setup orig-stx stx expand-ctxt do-expand stop-forms k)
(set-box! typed-context? #t)
;(start-timing (syntax-property stx 'enclosing-module-name))
(with-handlers
@ -52,11 +54,11 @@
;; reinitialize disappeared uses
[disappeared-use-todo null]
[disappeared-bindings-todo null])
(define fully-expanded-stx (disarm* (do-expand stx expand-ctxt (list #'module*))))
(define expanded-stx (disarm* (do-expand stx expand-ctxt stop-forms)))
(when (print-syntax?)
(pretty-print (syntax->datum fully-expanded-stx)))
(pretty-print (syntax->datum expanded-stx)))
(do-time "Local Expand Done")
(let ([exprs (syntax->list (syntax-local-introduce fully-expanded-stx))])
(let ([exprs (syntax->list (syntax-local-introduce expanded-stx))])
(when (pair? exprs)
(log-message online-check-syntax-logger
'info
@ -66,17 +68,24 @@
;; expansion errors to happen with out paying that cost
(do-standard-inits)
(do-time "Initialized Envs")
(find-mutated-vars fully-expanded-stx mvar-env)
(parameterize ([orig-module-stx (or (orig-module-stx) orig-stx)]
[expanded-module-stx fully-expanded-stx])
(do-time "Starting `checker'")
(call-with-values (λ () (checker fully-expanded-stx))
(λ results
(do-time "Typechecking Done")
(apply k fully-expanded-stx results)))))))
(find-mutated-vars expanded-stx mvar-env)
(k expanded-stx))))
(define (tc-toplevel/full orig-stx stx k)
(tc-setup orig-stx stx 'top-level local-expand/capture* tc-toplevel-form k))
;; for top-level use
(define (tc-toplevel/full orig-stx stx)
(tc-setup orig-stx stx 'top-level
local-expand/capture* (kernel-form-identifier-list)
(λ (head-expanded-stx)
(do-time "Trampoline the top-level checker")
(tc-toplevel-start (or (orig-module-stx) orig-stx) head-expanded-stx))))
(define (tc-module/full orig-stx stx k)
(tc-setup orig-stx stx 'module-begin local-expand tc-module k))
(tc-setup orig-stx stx 'module-begin local-expand (list #'module*)
(λ (fully-expanded-stx)
(do-time "Starting `checker'")
(parameterize ([orig-module-stx (or (orig-module-stx) orig-stx)]
[expanded-module-stx fully-expanded-stx])
(call-with-values (λ () (tc-module fully-expanded-stx))
(λ results
(do-time "Typechecking Done")
(apply k fully-expanded-stx results)))))))

View File

@ -3,9 +3,9 @@
(require "../utils/utils.rkt"
racket/match (prefix-in - (contract-req))
racket/format
(types utils union subtype filter-ops abbrev)
(types utils union subtype prop-ops abbrev)
(utils tc-utils)
(rep type-rep object-rep filter-rep)
(rep type-rep object-rep prop-rep)
(typecheck error-message))
(provide/cond-contract
@ -21,7 +21,7 @@
(define (print-object o)
(match o
[(or (NoObject:) (Empty:)) "no object"]
[(or #f (Empty:)) "no object"]
[_ (format "object ~a" o)]))
;; If expected is #f, then just return tr1
@ -45,37 +45,36 @@
(value-string expected) (value-string actual)
"mismatch in number of values"))
;; fix-filter: FilterSet [FilterSet] -> FilterSet
;; Turns NoFilter into the actual filter; leaves other filters alone.
(define (fix-filter f [f2 -top-filter])
(match f
[(NoFilter:) f2]
[else f]))
;; fix-props:
;; PropSet [PropSet] -> PropSet
;; or
;; Prop [Prop] -> Prop
;; Turns #f prop/propset into the actual prop; leaves other props alone.
(define (fix-props p1 [p2 -tt-propset])
(or p1 p2))
;; fix-object: Object [Object] -> Object
;; Turns NoObject into the actual object; leaves other objects alone.
(define (fix-object o [o2 -empty-obj])
(match o
[(NoObject:) o2]
[else o]))
;; Turns #f into the actual object; leaves other objects alone.
(define (fix-object o1 [o2 -empty-obj])
(or o1 o2))
;; fix-results: tc-results -> tc-results
;; Turns NoObject/NoFilter into the Empty/TopFilter
;; Turns #f Prop or Obj into the Empty/Trivial
(define (fix-results r)
(match r
[(tc-any-results: f) (tc-any-results (fix-filter f -top))]
[(tc-results: ts fs os)
(ret ts (map fix-filter fs) (map fix-object os))]
[(tc-results: ts fs os dty dbound)
(ret ts (map fix-filter fs) (map fix-object os) dty dbound)]))
[(tc-any-results: f) (tc-any-results (fix-props f -tt))]
[(tc-results: ts ps os)
(ret ts (map fix-props ps) (map fix-object os))]
[(tc-results: ts ps os dty dbound)
(ret ts (map fix-props ps) (map fix-object os) dty dbound)]))
(define (fix-results/bottom r)
(match r
[(tc-any-results: f) (tc-any-results (fix-filter f -bot))]
[(tc-results: ts fs os)
(ret ts (for/list ([f fs]) (fix-filter f -bot-filter)) (map fix-object os))]
[(tc-results: ts fs os dty dbound)
(ret ts (for/list ([f fs]) (fix-filter f -bot-filter)) (map fix-object os) dty dbound)]))
[(tc-any-results: f) (tc-any-results (fix-props f -ff))]
[(tc-results: ts ps os)
(ret ts (for/list ([p ps]) (fix-props p -ff-propset)) (map fix-object os))]
[(tc-results: ts ps os dty dbound)
(ret ts (for/list ([p ps]) (fix-props p -ff-propset)) (map fix-object os) dty dbound)]))
@ -84,74 +83,74 @@
;; (Type Results -> Type)
;; (Type Type -> Type))
(define (check-below tr1 expected)
(define (filter-set-better? f1 f2)
(match* (f1 f2)
[(f f) #t]
[(f (NoFilter:)) #t]
[((FilterSet: f1+ f1-) (FilterSet: f2+ f2-))
(and (implied-atomic? f2+ f1+)
(implied-atomic? f2- f1-))]
(define (prop-set-better? p1 p2)
(match* (p1 p2)
[(p p) #t]
[(p #f) #t]
[((PropSet: p1+ p1-) (PropSet: p2+ p2-))
(and (implies-atomic? p1+ p2+)
(implies-atomic? p1- p2-))]
[(_ _) #f]))
(define (object-better? o1 o2)
(match* (o1 o2)
[(o o) #t]
[(o (or (NoObject:) (Empty:))) #t]
[(o (or #f (Empty:))) #t]
[(_ _) #f]))
(define (filter-better? f1 f2)
(or (NoFilter? f2)
(implied-atomic? f2 f1)))
(define (prop-better? p1 p2)
(or (not p2)
(implies-atomic? p1 p2)))
(match* (tr1 expected)
;; This case has to be first so that bottom (exceptions, etc.) can be allowed in cases
;; where multiple values are expected.
;; We can ignore the filters and objects in the actual value because they would never be about a value
;; We can ignore the props and objects in the actual value because they would never be about a value
[((tc-result1: (? (lambda (t) (type-equal? t (Un))))) _)
(fix-results/bottom expected)]
[((tc-any-results: f1) (tc-any-results: f2))
(unless (filter-better? f1 f2)
(type-mismatch f2 f1 "mismatch in filter"))
(tc-any-results (fix-filter f2 f1))]
[((tc-any-results: p1) (tc-any-results: p2))
(unless (prop-better? p1 p2)
(type-mismatch p2 p1 "mismatch in proposition"))
(tc-any-results (fix-props p2 p1))]
[((or (tc-results: _ (list (FilterSet: fs+ fs-) ...) _)
(tc-results: _ (list (FilterSet: fs+ fs-) ...) _ _ _))
(tc-any-results: f2))
(define merged-filter (apply -and (map -or fs+ fs-)))
(unless (filter-better? merged-filter f2)
(type-mismatch f2 merged-filter "mismatch in filter"))
(tc-any-results (fix-filter f2 merged-filter))]
[((or (tc-results: _ (list (PropSet: fs+ fs-) ...) _)
(tc-results: _ (list (PropSet: fs+ fs-) ...) _ _ _))
(tc-any-results: p2))
(define merged-prop (apply -and (map -or fs+ fs-)))
(unless (prop-better? merged-prop p2)
(type-mismatch p2 merged-prop "mismatch in proposition"))
(tc-any-results (fix-props p2 merged-prop))]
[((tc-result1: t1 f1 o1) (tc-result1: t2 f2 o2))
[((tc-result1: t1 p1 o1) (tc-result1: t2 p2 o2))
(cond
[(not (subtype t1 t2))
(expected-but-got t2 t1)]
[(and (not (filter-set-better? f1 f2))
[(and (not (prop-set-better? p1 p2))
(object-better? o1 o2))
(type-mismatch f2 f1 "mismatch in filter")]
[(and (filter-set-better? f1 f2)
(type-mismatch p2 p1 "mismatch in proposition")]
[(and (prop-set-better? p1 p2)
(not (object-better? o1 o2)))
(type-mismatch (print-object o2) (print-object o1) "mismatch in object")]
[(and (not (filter-set-better? f1 f2))
[(and (not (prop-set-better? p1 p2))
(not (object-better? o1 o2)))
(type-mismatch (format "`~a' and `~a'" f2 (print-object o2))
(format "`~a' and `~a'" f1 (print-object o1))
"mismatch in filter and object")])
(ret t2 (fix-filter f2 f1) (fix-object o2 o1))]
(type-mismatch (format "`~a' and `~a'" p2 (print-object o2))
(format "`~a' and `~a'" p1 (print-object o1))
"mismatch in proposition and object")])
(ret t2 (fix-props p2 p1) (fix-object o2 o1))]
;; case where expected is like (Values a ... a) but got something else
[((tc-results: t1 f1 o1) (tc-results: t2 f2 o2 dty dbound))
[((tc-results: t1 p1 o1) (tc-results: t2 p2 o2 dty dbound))
(value-mismatch expected tr1)
(fix-results expected)]
;; case where you have (Values a ... a) but expected something else
[((tc-results: t1 f1 o1 dty dbound) (tc-results: t2 f2 o2))
[((tc-results: t1 p1 o1 dty dbound) (tc-results: t2 p2 o2))
(value-mismatch expected tr1)
(fix-results expected)]
[((tc-results: t1 f1 o1 dty1 dbound)
(tc-results: t2 (list (or (NoFilter:) (FilterSet: (Top:) (Top:))) ...)
(list (or (NoObject:) (Empty:)) ...) dty2 dbound))
[((tc-results: t1 p1 o1 dty1 dbound)
(tc-results: t2 (list (or #f (PropSet: (TrueProp:) (TrueProp:))) ...)
(list (or #f (Empty:)) ...) dty2 dbound))
(cond
[(= (length t1) (length t2))
(unless (andmap subtype t1 t2)
@ -162,7 +161,7 @@
(value-mismatch expected tr1)])
(fix-results expected)]
[((tc-results: t1 f1 o1 dty1 dbound) (tc-results: t2 f2 o2 dty2 dbound))
[((tc-results: t1 p1 o1 dty1 dbound) (tc-results: t2 p2 o2 dty2 dbound))
(cond
[(= (length t1) (length t2))
(unless (andmap subtype t1 t2)
@ -173,9 +172,9 @@
(value-mismatch expected tr1)])
(fix-results expected)]
[((tc-results: t1 f1 o1)
(tc-results: t2 (list (or (NoFilter:) (FilterSet: (Top:) (Top:))) ...)
(list (or (NoObject:) (Empty:)) ...)))
[((tc-results: t1 p1 o1)
(tc-results: t2 (list (or #f (PropSet: (TrueProp:) (TrueProp:))) ...)
(list (or #f (Empty:)) ...)))
(unless (= (length t1) (length t2))
(value-mismatch expected tr1))
(unless (for/and ([t (in-list t1)] [s (in-list t2)]) (subtype t s))
@ -189,7 +188,7 @@
(expected-but-got (stringify t2) (stringify t1)))
(fix-results expected)]
[((tc-results: t1 f1 o1) (tc-results: t2 f2 o2)) (=> continue)
[((tc-results: t1 p1 o1) (tc-results: t2 p2 o2)) (=> continue)
(if (= (length t1) (length t2))
(continue)
(value-mismatch expected tr1))
@ -204,5 +203,5 @@
(expected-but-got t2 t1))
expected]
[((tc-results: ts fs os dty dbound) (tc-results: ts* fs* os* dty* dbound*))
(int-err "dotted types with different bounds/filters/objects in check-below nyi: ~a ~a" tr1 expected)]
(int-err "dotted types with different bounds/propositions/objects in check-below nyi: ~a ~a" tr1 expected)]
[(a b) (int-err "unexpected input for check-below: ~a ~a" a b)]))

View File

@ -461,7 +461,7 @@
(define-values (alias-names alias-map) (get-type-alias-info type-aliases))
(register-all-type-aliases alias-names alias-map)
;; Filter top level expressions into several groups, each processed
;; Prop top level expressions into several groups, each processed
;; into appropriate data structures
;;
;; Augment annotations go in their own table, because they're
@ -550,21 +550,10 @@
#:when (set-member? (hash-ref parse-info 'private-fields) name))
(hash-set! private-field-types name (list type)))
;; Hash<Syntax -> Listof<Listof<Syntax>, Listof<Type>>>
;; Maps the outermost `let-values` expressions introduced by the expansion of
;; `define-values` within the class body to a list of identifier syntaxes
;; that represent variables and a list of corresponding types.
;; The variables temporarily hold the values of the initializer expression;
;; a field mutator is called on each one in the body of the `let-values`.
;; Typechecking of these calls is done in `check-field-set!s` and requires
;; the types of the initial values.
(define inits-temporaries-types (make-hasheq))
(define synthesized-init-val-stxs
(synthesize-private-field-types private-field-stxs
local-private-field-table
private-field-types
inits-temporaries-types))
private-field-types))
;; Detect mutation of private fields for occurrence typing
(for ([stx (in-sequences
@ -609,8 +598,7 @@
(with-lexical-env/extend-types lexical-names/top-level lexical-types/top-level
(check-field-set!s (hash-ref parse-info 'initializer-body)
synthesized-init-val-stxs
inits
inits-temporaries-types))
inits))
(do-timestamp "checked field initializers")
(define checked-method-types
(with-lexical-env/extend-types lexical-names lexical-types
@ -995,7 +983,7 @@
(do-timestamp (format "finished method ~a" external-name))
(cons (list external-name pre-method-type) checked)]
;; Only try to type-check if these names are in the
;; filter when it's provided. This allows us to, say, only
;; prop when it's provided. This allows us to, say, only
;; type-check pubments/augments.
[(set-member? names-to-check external-name)
(do-timestamp (format "started checking method ~a" external-name))
@ -1035,11 +1023,11 @@
(tc-expr/t xformed-stx)])))
;; check-field-set!s : Syntax Listof<Syntax> Dict<Symbol, Type>
;; Dict<Syntax, List<Listof<Syntax>, Listof<Type>> -> Void
;; -> Void
;; Check that fields are initialized to the correct type
;; FIXME: use syntax classes for matching and clearly separate the handling
;; of field initialization and set! uses
(define (check-field-set!s stx synthed-stxs inits inits-temporaries-types)
(define (check-field-set!s stx synthed-stxs inits)
(for ([form (syntax->list stx)])
(syntax-parse form
#:literal-sets (kernel-literals)
@ -1106,12 +1094,21 @@
(tc-expr/check processed (ret type)))]
;; multiple private fields
[(let-values ([(names:id ...) val-expr]) begins ... (#%plain-app _))
(match-define (list t-names t-types)
(hash-ref inits-temporaries-types form (list empty empty)))
;; This seems like it's duplicating work since the synthesis pass
;; earlier had to do this, but it needs to be re-checked in this context
;; so that it has the right environment. An earlier approach did
;; check this only in the synthesis stage, but caused some regressions.
(define temp-names (syntax->list #'(names ...)))
(define init-types
(match (tc-expr #'val-expr)
[(tc-results: xs ) xs]))
(unless (= (length temp-names) (length init-types))
(tc-error/expr "wrong number of values: expected ~a but got ~a"
(length temp-names) (length init-types)))
;; Extend lexical type env with temporaries introduced in the
;; expansion of the field initialization or setter
(with-lexical-env/extend-types t-names t-types
(check-field-set!s #'(begins ...) synthed-stxs inits inits-temporaries-types))]
(with-lexical-env/extend-types temp-names init-types
(check-field-set!s #'(begins ...) synthed-stxs inits))]
[_ (void)])))
;; setter->type : Id -> Type
@ -1144,11 +1141,11 @@
[else
(tc-expr/check init-val (ret init-type))])))
;; synthesize-private-field-types : Listof<Syntax> Dict Hash Hash -> Listof<Syntax>
;; synthesize-private-field-types : Listof<Syntax> Dict Hash -> Listof<Syntax>
;; Given top-level expressions in the class, synthesize types from
;; the initialization expressions for private fields. Returns the initial
;; value expressions that were type synthesized.
(define (synthesize-private-field-types stxs locals types inits-temporaries-types)
(define (synthesize-private-field-types stxs locals types)
(for/fold ([synthed-stxs null])
([stx (in-list stxs)])
(syntax-parse stx
@ -1186,23 +1183,18 @@
(define field-names (map syntax-e (syntax-e (tr:class:def-property stx))))
(define temporary-stxs (syntax-e #'(initial-value-name ...)))
(define init-types
(match (tc-expr/check #'initial-values #f)
[(tc-results: xs ) xs]))
(unless (= (length field-names) (length init-types))
(tc-error/expr "wrong number of values: expected ~a but got ~a"
(length field-names) (length init-types)))
(define temporaries-types
(for/list
([name (in-list field-names)]
[temp-stx (in-list temporary-stxs)]
[type (in-list init-types)])
(define type-table-val (generalize type))
(unless (hash-has-key? types name)
(hash-set! types name (list type-table-val)))
(cons temp-stx type-table-val)))
(hash-set! inits-temporaries-types stx
(list (map car temporaries-types)
(map cdr temporaries-types)))
;; this gets re-checked later, so don't throw any errors yet
(match (tc-expr/check? #'initial-values #f)
[(tc-results: xs ) xs]
;; We have to return something here so use the most conservative type
[#f (make-list (length field-names) Univ)]))
(for ([name (in-list field-names)]
[temp-stx (in-list temporary-stxs)]
[type (in-list init-types)])
(define type-table-val (generalize type))
(unless (hash-has-key? types name)
(hash-set! types name (list type-table-val)))
(cons temp-stx type-table-val))
(cons #'initial-values synthed-stxs)])))
;; Syntax -> Dict<Symbol, Id> Dict<Symbol, Id>
@ -1605,7 +1597,7 @@
(make-PolyRow ns constraints (method->function type))]
[_ (tc-error/expr #:return -Bottom "expected a function type for method")]))
;; process-method-syntax : Syntax (Option Type) -> Syntax
;; process-method-syntax : Syntax Type (Option Type) -> Syntax
;; Register types for identifiers in a method that don't come with types and
;; propagate syntax properties as needed
(define (process-method-syntax stx self-type method-type)

View File

@ -11,7 +11,7 @@
(utils tc-utils)
(for-syntax racket/base syntax/parse)
(for-template racket/base)
(rep type-rep filter-rep object-rep))
(rep type-rep prop-rep object-rep))
(import tc-if^ tc-lambda^ tc-app^ tc-let^ tc-expr^)
(export check-subforms^)
@ -42,14 +42,14 @@
;; syntax tc-result1 type -> tc-results
;; The result of applying the function to a single argument of the type of its first argument
(define (get-range-result stx t filter-type)
(define (get-range-result stx t prop-type)
(let loop ((t t))
(match t
[(Function: (list _ ... (arr: (list arg1) _ _ #f (list (Keyword: _ _ #f) ...)) _ ...))
#:when (subtype filter-type arg1)
#:when (subtype prop-type arg1)
(tc/funapp #'here #'(here) t (list (ret arg1)) #f)]
[(Function: (list _ ... (arr: '() _ (? values rest) #f (list (Keyword: _ _ #f) ...)) _ ...))
#:when (subtype filter-type rest)
#:when (subtype prop-type rest)
(tc/funapp #'here #'(here) t (list (ret rest)) #f)]
[(? needs-resolving? t)
(loop (resolve t))]
@ -58,17 +58,17 @@
;; This clause should raise an error via the check-below test
[_
(cond [;; a redundant test, but it ensures an error message below
(not (subtype t (-> filter-type Univ)))
(not (subtype t (-> prop-type Univ)))
(parameterize ([current-orig-stx stx])
(check-below t (-> filter-type Univ)))]
[else (int-err "get-range-result: should not happen. type ~a filter ~a"
t filter-type)])
(check-below t (-> prop-type Univ)))]
[else (int-err "get-range-result: should not happen. type ~a prop ~a"
t prop-type)])
(ret (Un))])))
;; Syntax Type -> (Option Type)
;; Extract the type for the filter in a predicate type, or #f if
;; Extract the type for the prop in a predicate type, or #f if
;; the type is an invalid predicate type.
(define (get-filter-type stx pred-type)
(define (get-prop-type stx pred-type)
(cond [;; make sure the predicate has an appropriate type
(subtype pred-type (-> Univ Univ))
(define fun-type
@ -78,10 +78,10 @@
(match fun-type
;; FIXME: Almost all predicates fall into this case, but it may
;; be worth being more precise here for some rare code.
[(PredicateFilter: fs)
(match fs
[(FilterSet: (TypeFilter: ft (Path: '() '(0 0))) _) ft]
[(Bot:) (Un)]
[(PredicateProp: ps)
(match ps
[(PropSet: (TypeProp: (Path: '() '(0 0)) ft) _) ft]
[(FalseProp:) (Un)]
[_ Univ])]
[_ Univ])]
[else
@ -98,12 +98,12 @@
(hash-ref predicate-map key))
(match-define (list handler-stx handler-type)
(hash-ref handler-map key))
(define filter-type
(get-filter-type predicate-stx predicate-type))
(define prop-type
(get-prop-type predicate-stx predicate-type))
;; if the predicate doesn't check, then don't bother
;; with the RHS and return no result
(if filter-type
(get-range-result handler-stx handler-type filter-type)
(if prop-type
(get-range-result handler-stx handler-type prop-type)
(ret (Un)))))
(find-syntax form

View File

@ -0,0 +1,753 @@
#lang racket/unit
;; This module provides a unit for type-checking units
;; The general strategy for typechecking all of the racket/unit forms
;; is to match the entire expanded syntax and parse out the relevant
;; pieces of information.
;;
;; Each typing rule knows the expected expansion of the form being checked
;; and specifically parses that syntax. This implementation is extremely
;; brittle and will require changes should the expansion of any of the unit
;; forms change.
;;
;; For unit forms the general idea is to parse expanded syntax to find information
;; related to:
;; - imports
;; - exports
;; - init-depend
;; - subexpressions that require typechecking
;; And use these pieces to typecheck the entire form
;;
;; For the `unit` form imports, exports, and init-depends are parsed to generate
;; the type of the expression and to typecheck the body of the unit since imported signatures
;; introduce bindings of variables to types, and exported variables must be defined
;; with subtypes of their expected types.
;;
;; The `invoke-unit` expansion is more complex and depends on whether or not
;; imports were specified. In the case of no imports, the strategy is simply to
;; find the expression being invoked and ensure it has the type of a unit with
;; no imports. When there are imports to an `invoke-unit` form, the syntax contains
;; local definitions of units defined using `unit-from-context`. These forms
;; are parsed to determine which imports were declared to check subtyping on the
;; invoked expression and to ensure that imports pulled from the current context
;; have the correct types.
;;
;; The `compound-unit` expansion contains information about the imports and exports
;; of each unit expression being linked. Additionally the typed `compound-unit` macro
;; attaches a syntax property that specifies the exact linking structure of the compound
;; unit. These pieces of information enable the calculation of init-depends for the entire
;; compound unit and to properly check subtyping on each linked expression.
;;
;; `unit-from-context` is handled similarly to `invoke-unit`, the expansion is exactly
;; that of a unit created using the `unit` form, but lacks the annotations that are placed
;; there by the typed `unit` macro. In this case the body of the unit is searched for
;; syntax corresponding to definitions which are checked against the declared exports
;; to ensure the form is well typed.
;;
;; The handling of the various `infer` forms (invoke-unit/infer compound-unit/infer)
;; is generally identical to the corresponding form lacking inference, however, in these
;; cases typechecking can be more lax. In particular, the unit implementation knows that
;; only valid unit expressions are used in these forms and so there is no need to typecheck
;; each unit subexpression unless it is needed to determine the result type. The
;; `compund-unit/infer` form, however, requires the cooperation of the unit implementation
;; to attach a syntax property that specified the init-depends of the compound unit, otherwise
;; this information is extremely difficult to obtain from the syntax alone.
(require "../utils/utils.rkt"
syntax/id-set
racket/set
racket/dict
racket/format
racket/list
racket/match
racket/syntax
syntax/id-table
syntax/parse
syntax/stx
syntax/strip-context
racket/unit-exptime
"signatures.rkt"
(private parse-type syntax-properties type-annotation)
(only-in (base-env base-special-env) make-template-identifier)
(env lexical-env tvar-env global-env
signature-env)
(types utils abbrev union subtype resolve generalize signatures)
(typecheck check-below internal-forms)
(utils tc-utils)
(rep type-rep)
(for-syntax racket/base racket/unit-exptime syntax/parse)
(for-template racket/base
racket/unsafe/undefined
(submod "internal-forms.rkt" forms)))
(import tc-let^ tc-expr^)
(export check-unit^)
;; Syntax class definitions
;; variable annotations are modified by the expansion of the typed unit
;; macro in order to allow annotations on exported variables, this
;; syntax class allows conversion back to the usual internal syntax
;; for type annotations which may be used by tc-letrec/values
(define-syntax-class unit-body-annotation
#:literal-sets (kernel-literals)
#:literals (void values :-internal cons)
(pattern
(#%expression
(begin
(#%plain-app void (#%plain-lambda () var-int))
(begin
(quote-syntax
(:-internal var:id t) #:local)
(#%plain-app values))))
#:attr name #'var
#:attr fixed-form (quasisyntax/loc this-syntax
(begin
(quote-syntax (:-internal var-int t) #:local)
(#%plain-app values)))))
;; Syntax class matching the syntax of the rhs of definitions within unit bodies
;; The typed unit macro attaches the lambda to allow unit typechecking to associate
;; variable names with their definitions which are otherwise challenging to recover
(define-syntax-class unit-body-definition
#:literal-sets (kernel-literals)
#:literals (void)
(pattern
(#%expression
(begin
(#%plain-app void (#%plain-lambda () var:id ... (#%plain-app void)))
e))
#:with vars #'(var ...)
#:with body #'e))
;; Process the syntax of annotations and definitions from a unit
;; produces two values representing the names and the exprs corresponding
;; to each definition or annotation
;; Note:
;; - definitions may produce multiple names via define-values
;; - annotations produce no names
(define (process-ann/def-for-letrec ann/defs)
(for/fold ([names #`()]
[exprs #`()])
([a/d (in-list ann/defs)])
(syntax-parse a/d
[a:unit-body-annotation
(define name (attribute a.name))
;; TODO:
;; Duplicate annotations from imports
;; are not currently detected due to a bug
;; in tc/letrec-values
;; See Problem Report: 15145
(define fixed (attribute a.fixed-form))
(values #`(#,@names ()) #`(#,@exprs #,fixed))]
[d:unit-body-definition
(values #`(#,@names d.vars) #`(#,@exprs d.body))])))
;; A Sig-Info is a (sig-info identifier? (listof identifier?) (listof identifier?))
;; name is the identifier corresponding to the signature this sig-info represents
;; externals is the list of external names for variables in the signature
;; internals is the list of internal names for variables in the signature
;; Note:
;; - external names are those attached to signatures stored in static information
;; and in the Siganture representation
;; - internal names are the internal renamings of those variables in fully expanded
;; unit syntax, this renaming is performed by the untyped unit macro
;; - All references within a unit body use the internal names
(struct sig-info (name externals internals) #:transparent)
;; Process the various pieces of the fully expanded unit syntax to produce
;; sig-info structures for the unit's imports and exports, and a list of the
;; identifiers corresponding to init-depends of the unit
(define (process-unit-syntax import-sigs import-internal-ids import-tags
export-sigs export-temp-ids export-temp-internal-map
init-depend-tags)
;; build a mapping of import-tags to import signatures
;; since init-depends are referenced by the tags only in the expanded syntax
;; this map is used to determine the actual signatures corresponding to the
;; given signature tags of the init-depends
(define tag-map (make-immutable-free-id-table (map cons import-tags import-sigs)))
(define lookup-temp (λ (temp) (free-id-table-ref export-temp-internal-map temp #f)))
(values (for/list ([sig-id (in-list import-sigs)]
[sig-internal-ids (in-list import-internal-ids)])
(sig-info sig-id
(map car (Signature-mapping (lookup-signature/check sig-id)))
sig-internal-ids))
;; export-temp-ids is a flat list which must be processed
;; sequentially to map them to the correct internal/external identifiers
(let-values ([(_ si)
(for/fold ([temp-ids export-temp-ids]
[sig-infos '()])
([sig (in-list export-sigs)])
(define external-ids
(map car (Signature-mapping (lookup-signature/check sig))))
(define len (length external-ids))
(values (drop temp-ids len)
(cons (sig-info sig
external-ids
(map lookup-temp (take temp-ids len)))
sig-infos)))])
(reverse si))
(map (λ (x) (free-id-table-ref tag-map x #f)) init-depend-tags)))
;; The following three syntax classes are used to parse specific pieces of
;; information from parts of the expansion of units
;; Needed to parse out signature names, and signature-tags from the unit syntax
;; the tags are used to lookup init-depend signatures
(define-syntax-class sig-vector
#:literal-sets (kernel-literals)
#:literals (vector-immutable cons)
(pattern (#%plain-app
vector-immutable
(#%plain-app cons
(quote sig:id)
(#%plain-app vector-immutable sig-tag tag-rest ...))
...)
#:with sigs #'(sig ...)
#:with sig-tags #'(sig-tag ...)))
(define-syntax-class init-depend-list
#:literal-sets (kernel-literals)
#:literals (list cons)
(pattern (#%plain-app list (#%plain-app cons _ sig-tag) ...)
#:with init-depend-tags #'(sig-tag ...)))
(define-syntax-class export-table
#:literal-sets (kernel-literals)
#:literals (make-immutable-hash list cons vector-immutable check-not-unsafe-undefined unbox)
(pattern (#%plain-app
make-immutable-hash
(#%plain-app
list
(#%plain-app
cons
signature-tag:id
(#%plain-app
vector-immutable
(#%plain-lambda ()
(#%plain-app check-not-unsafe-undefined (#%plain-app unbox export-temp-id) external-id))
...))
...))
#:attr export-temp-ids (map syntax->list (syntax->list #'((export-temp-id ...) ...)))))
;; This syntax class matches the whole expansion of unit forms
(define-syntax-class unit-expansion
#:literal-sets (kernel-literals)
#:attributes (body-stx
import-internal-ids
import-sigs
import-sig-tags
export-sigs
export-temp-ids
init-depend-tags)
(pattern (#%plain-app
make-unit:id
name:expr
import-vector:sig-vector
export-vector:sig-vector
list-dep:init-depend-list
(let-values (_ ...)
(let-values (_ ...)
(#%expression
(#%plain-lambda ()
(let-values (((export-temp-id:id) _) ...)
(#%plain-app
values
(#%plain-lambda (import-table:id)
(let-values (((import:id ...) _) ...)
unit-body:expr))
et:export-table
_ ...)))))))
#:attr import-sigs (syntax->list #'import-vector.sigs)
#:attr import-sig-tags (syntax->list #'import-vector.sig-tags)
#:attr export-sigs (syntax->list #'export-vector.sigs)
#:attr export-temp-ids (syntax->list #'(export-temp-id ...))
#:attr init-depend-tags (syntax->list #'list-dep.init-depend-tags)
#:attr import-internal-ids (map syntax->list (syntax->list #'((import ...) ...)))
#:with body-stx #'unit-body))
;; Extract the identifiers referenced in unit-from-context and invoke-unit forms
;; in order to typecheck them in the current environment
(define (extract-definitions stx)
(trawl-for-property
stx
(lambda (stx) (syntax-parse stx [((int:id) ref:id) #t] [_ #f]))
(lambda (stx) (syntax-parse stx [((int:id) ref:id) #'ref]))))
;; Syntax inside the expansion of units that allows recovering a mapping
;; from temp-ids of exports to their internal identifiers
(define-syntax-class export-temp-internal-map-elem
#:literal-sets (kernel-literals)
#:literals (set-box!)
(pattern (#%plain-app set-box! temp-id:id internal-id:id)))
(define export-map-elem?
(syntax-parser [e:export-temp-internal-map-elem #t]
[_ #f]))
(define extract-export-map-elem
(syntax-parser [e:export-temp-internal-map-elem (cons #'e.temp-id #'e.internal-id)]))
;; get a reference to the actual `invoke-unit/core` function to properly parse
;; the fully expanded syntax of `invoke-unit` forms
(define invoke-unit/core (make-template-identifier 'invoke-unit/core 'racket/unit))
;; Syntax class for all the various expansions of invoke-unit forms
;; This also includes the syntax for the invoke-unit/infer forms
(define-syntax-class invoke-unit-expansion
#:literal-sets (kernel-literals)
(pattern (#%plain-app iu/c unit-expr)
#:when (free-identifier=? #'iu/c invoke-unit/core)
#:attr units '()
#:attr expr #'unit-expr
#:attr imports '())
(pattern
(let-values ()
body:invoke-unit-linkings)
#:attr units (attribute body.units)
#:attr expr (attribute body.expr)
#:attr imports (attribute body.imports)))
(define-syntax-class invoke-unit-linkings
#:literal-sets (kernel-literals)
(pattern
(let-values ([(u-temp:id)
(let-values ([(deps) _]
[(sig-provider) _] ...
[(temp) ie:invoked-expr])
_ ...)])
(#%plain-app iu/c (#%plain-app values _)))
#:when (free-identifier=? #'iu/c invoke-unit/core)
#:attr units '()
#:attr expr (if (tr:unit:invoke:expr-property #'ie) #'ie #'ie.invoke-expr)
#:attr imports '())
(pattern
(let-values ([(temp-id) u:unit-expansion])
rest:invoke-unit-linkings)
#:attr units (cons #'u (attribute rest.units))
#:attr expr (attribute rest.expr)
#:attr imports (append (attribute u.export-sigs) (attribute rest.imports))))
;; This should be used ONLY when an invoke/infer is used with the link clause ...
(define-syntax-class invoked-expr
#:literal-sets (kernel-literals)
#:literals (values)
(pattern
(let-values ([(deps2:id) _]
[(local-unit-id:id) unit:id] ...
[(invoke-temp) invoke-unit])
_ ...)
#:attr invoke-expr #'invoke-unit)
(pattern invoke-expr:expr))
;; Compound Unit syntax classes
(define-syntax-class compound-unit-expansion
#:literal-sets (kernel-literals)
#:literals (vector-immutable cons)
(pattern
(let-values ([(deps:id) _]
[(local-unit-name) unit-expr] ...)
(~seq (#%plain-app check-unit _ ...)
(#%plain-app check-sigs _
(#%plain-app
vector-immutable
(#%plain-app cons (quote import-sig:id) _) ...)
(#%plain-app
vector-immutable
(#%plain-app cons (quote export-sig:id) _) ...)
_)
(let-values ([(fht) _]
[(rht) _])
_ ...)) ...
(#%plain-app
make-unit:id
name:expr
import-vector:sig-vector
export-vector:sig-vector
deps-ref
internals))
#:attr unit-exprs (syntax->list #'(unit-expr ...))
#:attr unit-imports (map syntax->list (syntax->list #'((import-sig ...) ...)))
#:attr unit-exports (map syntax->list (syntax->list #'((export-sig ...) ...)))
#:attr compound-imports (syntax->list #'import-vector.sigs)
#:attr compound-exports (syntax->list #'export-vector.sigs)))
;; A cu-expr-info represents an element of the link clause in
;; a compound-unit form
;; - expr : the unit expression being linked
;; - import-sigs : the Signatures specified as imports for this link-element
;; - import-links : the symbols that correspond to the link-bindings
;; imported by this unit
;; - export-sigs : the Signatures specified as exports for this link-element
;; - export-links : the symbols corresponding to the link-bindings exported
;; by this unit
(struct cu-expr-info (expr import-sigs import-links export-sigs export-links)
#:transparent)
;; parse-compound-unit : Syntax -> (Values (Listof (Cons Symbol Id))
;; (Listof Symbol)
;; (Listof Signature)
;; (Listof Signature)
;; (Listof cu-expr-info))
;; Returns a mapping of link-ids to sig-ids, a list of imported sig ids
;; a list of exported link-ids
(define (parse-compound-unit stx)
(define (list->sigs l) (map lookup-signature/check l))
(syntax-parse stx
[cu:compound-unit-expansion
(define link-binding-info (tr:unit:compound-property stx))
(match-define (list cu-import-syms unit-export-syms unit-import-syms)
link-binding-info)
(define compound-imports (attribute cu.compound-imports))
(define compound-exports (attribute cu.compound-exports))
(define unit-exprs (attribute cu.unit-exprs))
(define unit-imports (attribute cu.unit-imports))
(define unit-exports (attribute cu.unit-exports))
;; Map signature ids to link binding symbols
(define mapping
(let ()
(define link-syms (append cu-import-syms (flatten unit-export-syms)))
(define sig-ids (append compound-imports (flatten unit-exports)))
(map cons link-syms (map lookup-signature/check sig-ids))))
(define cu-exprs
(for/list ([unit-expr (in-list unit-exprs)]
[import-sigs (in-list unit-imports)]
[import-links (in-list unit-import-syms)]
[export-sigs (in-list unit-exports)]
[export-links (in-list unit-export-syms)])
(cu-expr-info unit-expr
(list->sigs import-sigs) import-links
(list->sigs export-sigs) export-links)))
(values
mapping
cu-import-syms
(list->sigs compound-imports)
(list->sigs compound-exports)
cu-exprs)]))
;; Sig-Info -> (listof (pairof identifier? Type))
;; GIVEN: signature information
;; RETURNS: a mapping from internal names to types
(define (make-local-type-mapping si)
(define sig (lookup-signature/check (sig-info-name si)))
(define internal-names (sig-info-internals si))
(define sig-types
(map cdr (Signature-mapping sig)))
(map cons internal-names sig-types))
;; Syntax Option<TCResults> -> TCResults
;; Type-check a unit form
(define (check-unit form [expected #f])
(define expected-type
(match expected
[(tc-result1: type) (resolve type)]
[_ #f]))
(match expected-type
[(? Unit? unit-type)
(ret (parse-and-check-unit form unit-type))]
[_ (ret (parse-and-check-unit form #f))]))
;; Syntax Option<TCResultss> -> TCResults
(define (check-invoke-unit form [expected #f])
(define expected-type
(match expected
[(tc-result1: type) (resolve type)]
[_ #f]))
(ret (parse-and-check-invoke form expected-type)))
(define (check-compound-unit form [expected #f])
(define infer? (eq? (tr:unit:compound-property form) 'infer))
(define expected-type
(match expected
[(tc-result1: type) (resolve type)]
[_ #f]))
(if infer?
(ret (parse-and-check-compound/infer form expected-type))
(ret (parse-and-check-compound form expected-type))))
(define (check-unit-from-context form [expected #f])
(define expected-type
(match expected
[(tc-result1: type) (resolve type)]
[_ #f]))
(ret (parse-and-check-unit-from-context form expected-type)))
(define (parse-and-check-unit-from-context form expected-type)
(syntax-parse form
[u:unit-expansion
(define export-sigs (map lookup-signature/check (attribute u.export-sigs)))
(define body-stx (attribute u.body-stx))
(for ([sig (in-list export-sigs)])
(define ids (extract-definitions body-stx))
(define types (map cdr (Signature-mapping sig)))
(for ([type (in-list types)]
[id (in-list ids)])
(define lexical-type (lookup-type/lexical id))
(unless (subtype lexical-type type)
(tc-error/fields "type mismatch in unit-from-context export"
"expected" type
"given" lexical-type
"exported variable" (syntax-e id)
"exported-signature" (syntax-e (Signature-name sig))
#:stx form
#:delayed #t))))
(-unit null export-sigs null (-values (list -Void)))]))
(define (parse-and-check-compound form expected-type)
(define-values (link-mapping
import-syms
import-sigs
export-sigs
cu-exprs)
(parse-compound-unit form))
(define (lookup-link-id id) (dict-ref link-mapping id #f))
(define-values (check _ init-depends)
(for/fold ([check -Void]
[seen-init-depends import-syms]
[calculated-init-depends '()])
([form (in-list cu-exprs)])
(match-define (cu-expr-info unit-expr-stx
import-sigs
import-links
export-sigs
export-links)
form)
(define unit-expected-type
(-unit import-sigs
export-sigs
(map lookup-link-id (set-intersect seen-init-depends import-links))
ManyUniv))
(define unit-expr-type (tc-expr/t unit-expr-stx))
(check-below unit-expr-type unit-expected-type)
(define-values (body-type new-init-depends)
(match unit-expr-type
[(Unit: _ _ ini-deps ty)
;; init-depends here are strictly subsets of the units imports
;; but these may not exactly match with the provided links
;; so all of the extended signatures must be traversed to find the right
;; signatures for init-depends
(define extended-imports
(map cons import-links
(map (λ (l) (map Signature-name (flatten-sigs l))) import-sigs)))
(define init-depend-links
(for*/list ([sig-name (in-list (map Signature-name ini-deps))]
[(import-link import-family) (in-dict extended-imports)]
#:when (member sig-name import-family free-identifier=?))
import-link))
;; new init-depends are the init-depends of this unit that
;; overlap with the imports to the compound-unit
(values ty (set-intersect import-syms init-depend-links))]
;; unit-expr was not actually a unit, but we want to delay the errors
[_ (values #f '())]))
(values body-type
;; Add the exports to the list of seen-init-depends
(set-union seen-init-depends export-links)
;; Add the new-init-depends to those already calculated
(set-union calculated-init-depends new-init-depends))))
(if check
(-unit import-sigs
export-sigs
(map lookup-link-id init-depends)
check)
;; Error case when one of the links was not a unit
-Bottom))
(define (parse-and-check-compound/infer form expected-type)
(define init-depend-refs (syntax-property form 'unit:inferred-init-depends))
(syntax-parse form
[cu:compound-unit-expansion
(define unit-exprs (attribute cu.unit-exprs))
(define compound-imports (map lookup-signature/check (attribute cu.compound-imports)))
(define compound-exports (map lookup-signature/check (attribute cu.compound-exports)))
(define import-vector (apply vector compound-imports))
(define import-length (vector-length import-vector))
(unless (and (list? init-depend-refs)
(andmap (λ (i) (and (exact-nonnegative-integer? i) (< i import-length)))
init-depend-refs))
(int-err "malformed syntax property attached to compound-unit/infer form"))
(define compound-init-depends
(map (lambda (i) (vector-ref import-vector i)) init-depend-refs))
(define resulting-unit-expr (last unit-exprs))
(define final-unit-invoke-type (tc-expr/t resulting-unit-expr))
;; This type should always be a unit
(match-define (Unit: _ _ _ compound-invoke-type) final-unit-invoke-type)
(-unit compound-imports compound-exports compound-init-depends compound-invoke-type)]))
(define (parse-and-check-invoke form expected-type)
(syntax-parse form
[iu:invoke-unit-expansion
(define infer? (eq? 'infer (tr:unit:invoke-property form)))
(define invoked-unit (attribute iu.expr))
(define import-sigs (map lookup-signature/check (attribute iu.imports)))
(define linking-units (attribute iu.units))
(define unit-expr-type (tc-expr/t invoked-unit))
;; TODO: Better error message/handling when the folling check-below "fails"
(unless infer?
(check-below unit-expr-type (-unit import-sigs null import-sigs ManyUniv)))
(for ([unit (in-list linking-units)]
[sig (in-list import-sigs)])
(define ids (extract-definitions unit))
(define types (map cdr (Signature-mapping sig)))
(for ([type (in-list types)]
[id (in-list ids)])
(define lexical-type (lookup-type/lexical id))
(unless (subtype lexical-type type)
(tc-error/fields "type mismatch in invoke-unit import"
"expected" type
"given" lexical-type
"imported variable" (syntax-e id)
"imported signature" (syntax-e (Signature-name sig))
#:stx form
#:delayed? #t))))
(cond
[(Unit? unit-expr-type)
(define result-type (Unit-result unit-expr-type))
(match result-type
[(Values: (list (Result: t _ _) ...)) t]
[(AnyValues: f) ManyUniv]
[(ValuesDots: (list (Result: t _ _) ...) _ _) t])]
[else -Bottom])]))
;; Parse and check unit syntax
(define (parse-and-check-unit form expected)
(syntax-parse form
[u:unit-expansion
;; extract the unit body syntax
(define body-stx #'u.body-stx)
(define import-sigs (attribute u.import-sigs))
(define import-internal-ids (attribute u.import-internal-ids))
(define import-tags (attribute u.import-sig-tags))
(define export-sigs (attribute u.export-sigs))
(define export-temp-ids (attribute u.export-temp-ids))
(define init-depend-tags (attribute u.init-depend-tags))
(define export-temp-internal-map
(make-immutable-free-id-table
(trawl-for-property body-stx export-map-elem? extract-export-map-elem)))
(define-values (imports-info exports-info init-depends)
(process-unit-syntax import-sigs import-internal-ids import-tags
export-sigs export-temp-ids export-temp-internal-map
init-depend-tags))
;; Get Signatures to build Unit type
(define import-signatures (map lookup-signature/check (map sig-info-name imports-info)))
(define export-signatures (map lookup-signature/check (map sig-info-name exports-info)))
(define init-depend-signatures (map lookup-signature/check init-depends))
(unless (distinct-signatures? import-signatures)
(tc-error/expr "unit expressions must import distinct signatures"))
;; this check for exports may be unnecessary
;; the unit macro seems to check it as well
(unless (distinct-signatures? export-signatures)
(tc-error/expr "unit expresssions must export distinct signatures"))
(define local-sig-type-map
(apply append (map make-local-type-mapping imports-info)))
(define export-signature-type-map
(map (lambda (si)
(cons (sig-info-name si) (make-local-type-mapping si)))
exports-info))
;; Thunk to pass to tc/letrec-values to check export subtyping
;; These subtype checks can only be checked within the dynamic extent
;; of the call to tc/letrec-values because they need to lookup
;; variables in the type environment as modified by typechecking
(define (check-exports-thunk)
(for* ([sig-mapping (in-list export-signature-type-map)]
[sig (in-value (car sig-mapping))]
[mapping (in-value (cdr sig-mapping))]
[(id expected-type) (in-dict mapping)])
(define id-lexical-type (lookup-type/lexical id))
(unless (subtype id-lexical-type expected-type)
(tc-error/fields "type mismatch in unit export"
"expected" expected-type
"given" id-lexical-type
"exported variable" (syntax-e id)
"exported signature" (syntax-e sig)
#:delayed? #t))))
(define import-name-map
(append-map (lambda (si) (map cons (sig-info-externals si) (sig-info-internals si)))
imports-info))
(define export-name-map
(append-map (lambda (si) (map cons (sig-info-externals si) (sig-info-internals si)))
exports-info))
(define body-forms
(trawl-for-property body-stx tr:unit:body-exp-def-type-property))
(define last-form
(or (and (not (empty? body-forms)) (last body-forms))))
;; get expression forms, if the body was empty or ended with
;; a definition insert a `(void)` expression to be typechecked
;; This is necessary because we defer to tc/letrec-values for typechecking
;; unit bodies, but a unit body may contain only definitions whereas letrec bodies
;; cannot, in this case we insert dummy syntax representing a call to the void
;; function in order to correctly type the body of the unit.
(define expression-forms
(let ([exprs
(filter
(lambda (stx) (eq? (tr:unit:body-exp-def-type-property stx) 'expr))
body-forms)])
(cond
[(or (not last-form) (eq? (tr:unit:body-exp-def-type-property last-form) 'def/type))
(append exprs (list #'(#%plain-app void)))]
[else exprs])))
;; Filter out the annotation and definition syntax from the unit body
;; For the purposes of typechecking, annotations and definitions
;; are essentially lifted to the top of the body and all expressions
;; are placed at the end (possibly with the addition of a (void) expression
;; as described above), since the types of definitions and annotations
;; must scope over the entire body of the unit, this is valid for purposes
;; of typechecking
(define annotation/definition-forms
(filter
(lambda (stx) (eq? (tr:unit:body-exp-def-type-property stx) 'def/type))
body-forms))
(define-values (ann/def-names ann/def-exprs)
(process-ann/def-for-letrec annotation/definition-forms))
(define signature-annotations
(for/list ([(k v) (in-dict local-sig-type-map)])
(cons k (-> v))))
(define unit-type
(with-lexical-env/extend-types
(map car signature-annotations)
(map cdr signature-annotations)
;; Typechecking a unit body is structurally similar to that of
;; checking a let-body, so we resuse the machinary for checking
;; let expressions
(define res (tc/letrec-values ann/def-names
ann/def-exprs
(quasisyntax/loc form (#,@expression-forms))
#f
check-exports-thunk))
(define invoke-type
(match res
[(tc-results: tps) (-values tps)]))
(-unit import-signatures
export-signatures
init-depend-signatures
invoke-type)))
unit-type]))
;; Based on the function of the same name in check-class-unit.rkt
;; trawl-for-property : Syntax (Syntax -> Any) [(Syntax -> A)] -> (Listof A)
;; Search through the given syntax for pieces of syntax that satisfy
;; the accessor predicate, then apply the extractor function to all such syntaxes
(define (trawl-for-property form accessor [extractor values])
(define (recur-on-all stx-list)
(apply append (map (λ (stx) (trawl-for-property stx accessor extractor)) stx-list)))
(syntax-parse form
#:literal-sets (kernel-literals)
[stx
#:when (accessor #'stx)
(list (extractor form))]
[_
(define list? (syntax->list form))
(if list? (recur-on-all list?) '())]))

View File

@ -1,16 +0,0 @@
#lang racket/base
(require
(for-syntax syntax/parse racket/base
"renamer.rkt"
"../utils/tc-utils.rkt"))
(provide def-export)
(define-syntax (def-export stx)
(syntax-parse stx
[(def-export export-id:identifier id:identifier cnt-id:identifier)
#'(define-syntax export-id
(let ([c #'cnt-id])
(if (unbox typed-context?)
(renamer #'id c)
(renamer c))))]))

Some files were not shown because too many files have changed in this diff Show More