typed-racket/typed-racket-test/unit-tests/static-contract-optimizer-tests.rkt
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

350 lines
10 KiB
Racket

#lang racket/base
(require "test-utils.rkt"
racket/list racket/format rackunit
(static-contracts instantiate optimize combinators structures)
(for-syntax racket/base syntax/parse))
(provide tests)
(gen-test-main)
(define-syntax (check-optimize stx)
(syntax-parse stx
[(_ argument* #:pos positive-expected* #:neg negative-expected*)
#'(test-suite (~a 'argument*)
(test-case "Trusted Positive"
(check-optimize-helper argument* positive-expected* #t #f))
(test-case "Trusted Negative"
(check-optimize-helper argument* negative-expected* #f #t)))]))
(define (check-optimize-helper argument expected trusted-positive trusted-negative)
(define trusted-side
(cond
[(and trusted-positive trusted-negative) 'both]
[trusted-positive 'positive]
[trusted-negative 'negative]
[else 'neither]))
(with-check-info*
(list (make-check-info 'original argument)
(make-check-info 'trusted trusted-side)
(make-check-expected expected))
(λ ()
(let ([opt (optimize argument
#:trusted-positive trusted-positive
#:trusted-negative trusted-negative)])
(with-check-info* (list (make-check-actual opt))
(lambda ()
(unless (equal? opt expected)
(fail-check))))))))
(define-syntax (check-syntax stx)
(syntax-parse stx
[(_ argument* expected*)
#'(test-case (~a 'argument*)
(define argument argument*)
(define expected expected*)
(with-check-info*
(list (make-check-info 'original argument)
(make-check-expected expected))
(λ ()
(let ([ctc (syntax->datum
(cadr
(instantiate
(optimize argument #:trusted-positive #t)
(λ (#:reason [reason #f]) (error 'nyi))
'impersonator)))])
(with-check-info* (list (make-check-actual ctc))
(λ ()
(unless (equal? ctc expected)
(fail-check))))))))]))
;; Ids with unique identity so that equals works
(define foo-id #'foo)
(define bar-id #'bar)
(define syntax-tests
(test-suite "Optimized Syntax Tests"
(check-syntax list?/sc
'any/c)
(check-syntax (arr/sc null #f (list list?/sc))
'(-> any))
(check-syntax (hash/sc list?/sc list?/sc)
'(hash/c list? list?))
))
(define optimizer-tests
(test-suite "Optimizer Tests"
;; Lists
(check-optimize (listof/sc any/sc)
#:pos any/sc
#:neg list?/sc)
(check-optimize (listof/sc none/sc)
#:pos any/sc
#:neg empty-list/sc)
;; Heterogeneous Lists
;; TODO fix ability to test equality here
#;
(check-optimize (list/sc any/sc)
#:pos any/sc
#:neg (list-length/sc 1))
(check-optimize (list/sc none/sc)
#:pos any/sc
#:neg none/sc)
(check-optimize (list/sc)
#:pos any/sc
#:neg empty-list/sc)
;; Sets
(check-optimize (set/sc any/sc)
#:pos any/sc
#:neg set?/sc)
(check-optimize (set/sc none/sc)
#:pos any/sc
#:neg empty-set/sc)
;; Vectors
(check-optimize (vectorof/sc any/sc)
#:pos any/sc
#:neg vector?/sc)
(check-optimize (vectorof/sc none/sc)
#:pos (vectorof/sc none/sc)
#:neg (vectorof/sc none/sc))
;; Heterogeneous Vectors
;; TODO fix ability to test equality here
#;
(check-optimize (vector/sc any/sc)
#:pos any/sc
#:neg (vector-length/sc 1))
(check-optimize (vector/sc none/sc)
#:pos (vector/sc none/sc)
#:neg (vector/sc none/sc))
;; TODO fix ability to test equality here
#;
(check-optimize (vector/sc)
#:pos any/sc
#:neg empty-vector/sc)
(check-optimize (vector/sc set?/sc)
#:pos (vector/sc set?/sc)
#:neg (vector/sc set?/sc))
;; HashTables
(check-optimize (hash/sc any/sc any/sc)
#:pos any/sc
#:neg hash?/sc)
(check-optimize (hash/sc none/sc any/sc)
#:pos (hash/sc none/sc any/sc)
#:neg (hash/sc none/sc any/sc))
(check-optimize (hash/sc any/sc none/sc)
#:pos (hash/sc any/sc none/sc)
#:neg (hash/sc any/sc none/sc))
;; And
(check-optimize (and/sc set?/sc)
#:pos any/sc
#:neg set?/sc)
(check-optimize (and/sc set?/sc any/sc)
#:pos any/sc
#:neg set?/sc)
(check-optimize (and/sc set?/sc none/sc)
#:pos any/sc
#:neg none/sc)
(check-optimize (and/sc)
#:pos any/sc
#:neg any/sc)
(check-optimize (and/sc any/sc any/sc)
#:pos any/sc
#:neg any/sc)
;; Or
(check-optimize (or/sc set?/sc)
#:pos any/sc
#:neg set?/sc)
(check-optimize (or/sc set?/sc any/sc)
#:pos any/sc
#:neg any/sc)
(check-optimize (or/sc set?/sc none/sc)
#:pos any/sc
#:neg set?/sc)
(check-optimize (or/sc)
#:pos any/sc
#:neg none/sc)
(check-optimize (or/sc none/sc none/sc)
#:pos any/sc
#:neg none/sc)
;; None
(check-optimize none/sc
#:pos any/sc
#:neg none/sc)
;; Boxes
(check-optimize (box/sc any/sc)
#:pos any/sc
#:neg box?/sc)
(check-optimize (box/sc none/sc)
#:pos (box/sc none/sc)
#:neg (box/sc none/sc))
(check-optimize (box/sc set?/sc)
#:pos (box/sc set?/sc)
#:neg (box/sc set?/sc))
;; Syntax Objects
(check-optimize (syntax/sc any/sc)
#:pos any/sc
#:neg syntax?/sc)
(check-optimize (syntax/sc none/sc)
#:pos any/sc
#:neg none/sc)
(check-optimize (syntax/sc set?/sc)
#:pos any/sc
#:neg (syntax/sc set?/sc))
;; Promises
(check-optimize (promise/sc any/sc)
#:pos any/sc
#:neg promise?/sc)
(check-optimize (promise/sc none/sc)
#:pos any/sc
#:neg (promise/sc none/sc))
(check-optimize (promise/sc set?/sc)
#:pos any/sc
#:neg (promise/sc set?/sc))
(check-optimize (promise/sc (box/sc set?/sc))
#:pos (promise/sc (box/sc set?/sc))
#:neg (promise/sc (box/sc set?/sc)))
(check-optimize
(function/sc #t
(list (listof/sc any/sc))
(list)
(list)
(list)
#f
(list (listof/sc any/sc)))
#:pos
(function/sc #t
(list list?/sc)
(list)
(list)
(list)
#f
#f)
#:neg
(function/sc #t
(list any/sc)
(list)
(list)
(list)
#f
(list list?/sc)))
(check-optimize
(function/sc #t
(list (listof/sc any/sc))
(list)
(list)
(list)
#f
(list any/sc))
#:pos
(function/sc #t
(list list?/sc)
(list)
(list)
(list)
#f
#f)
#:neg
(function/sc #t
(list any/sc)
(list)
(list)
(list)
#f
(list any/sc)))
(check-optimize (case->/sc empty)
#:pos (case->/sc empty)
#:neg (case->/sc empty))
(check-optimize (parameter/sc list?/sc set?/sc)
#:pos (parameter/sc list?/sc any/sc)
#:neg (parameter/sc any/sc set?/sc))
(check-optimize
(case->/sc (list (arr/sc (list (listof/sc any/sc)) (listof/sc (set/sc any/sc)) (list (listof/sc any/sc)))))
#:pos (case->/sc (list (arr/sc (list list?/sc) (listof/sc set?/sc) #f)))
#:neg (case->/sc (list (arr/sc (list any/sc) any/sc (list list?/sc)))))
(check-optimize
(object/sc #t (list (member-spec 'field 'x (listof/sc any/sc))))
#:pos (object/sc #t (list (member-spec 'field 'x list?/sc)))
#:neg (object/sc #t (list (member-spec 'field 'x list?/sc))))
(check-optimize
(object/sc #f (list (member-spec 'field 'x (listof/sc any/sc))))
#:pos (object/sc #f (list (member-spec 'field 'x list?/sc)))
#:neg (object/sc #f (list (member-spec 'field 'x list?/sc))))
(check-optimize
(class/sc #t (list (member-spec 'field 'x (listof/sc any/sc))) null)
#:pos (class/sc #t (list (member-spec 'field 'x list?/sc)) null)
#:neg (class/sc #t (list (member-spec 'field 'x list?/sc)) null))
(check-optimize
(class/sc #f (list (member-spec 'field 'x (listof/sc any/sc))) null)
#:pos (class/sc #f (list (member-spec 'field 'x list?/sc)) null)
#:neg (class/sc #f (list (member-spec 'field 'x list?/sc)) null))
(check-optimize
(recursive-sc (list foo-id bar-id)
(list (listof/sc (recursive-sc-use foo-id))
(listof/sc (recursive-sc-use bar-id)))
(recursive-sc-use foo-id))
#:pos (recursive-sc (list foo-id)
(list (listof/sc (recursive-sc-use foo-id)))
(recursive-sc-use foo-id))
#:neg (recursive-sc (list foo-id)
(list (listof/sc (recursive-sc-use foo-id)))
(recursive-sc-use foo-id)))
(check-optimize
(recursive-sc (list foo-id bar-id)
(list (listof/sc any/sc )
(listof/sc any/sc))
(recursive-sc-use foo-id))
#:pos any/sc
#:neg list?/sc)
(check-optimize (cons/sc any/sc list?/sc)
#:pos any/sc
#:neg (cons/sc any/sc list?/sc))
(check-optimize
(case->/sc
(list
(arr/sc empty #f (list set?/sc))
(arr/sc (list identifier?/sc) #f (list (listof/sc set?/sc)))))
#:pos (function/sc #t
(list)
(list identifier?/sc)
(list)
(list)
#f
#f)
#:neg (case->/sc
(list
(arr/sc empty #f (list set?/sc))
(arr/sc (list any/sc) #f (list (listof/sc set?/sc))))))
))
(define tests
(test-suite "Static Contracts"
syntax-tests
optimizer-tests))