
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`.
350 lines
10 KiB
Racket
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))
|