Initial implementation of struct-type contract generation.

Currently errors on all reflective access to struct types.

original commit: d3167c1ce4250b82991ec3984e9245300f8020ce
This commit is contained in:
Sam Tobin-Hochstadt 2014-07-10 15:28:45 -04:00
parent 6a7ff0c342
commit 2c836801af
7 changed files with 103 additions and 9 deletions

View File

@ -332,6 +332,7 @@
[(Prompt-TagTop:) (only-untyped prompt-tag?/sc)]
[(Continuation-Mark-KeyTop:) (only-untyped continuation-mark-key?/sc)]
[(ClassTop:) (only-untyped class?/sc)]
[(StructTypeTop:) (struct-type/sc null)]
;; TODO Figure out how this should work
;[(StructTop: s) (struct-top/sc s)]
@ -451,6 +452,11 @@
(recursive-sc (list nm*) (list (struct/sc nm (ormap values mut?) fields))
(recursive-sc-use nm*))]
[else (flat/sc #`(flat-named-contract '#,(syntax-e pred?) (lambda (x) (#,pred? x))))])]
[(StructType: s)
(if (from-untyped? typed-side)
(fail #:reason (~a "cannot import structure types from"
"untyped code"))
(struct-type/sc null))]
[(Syntax: (Base: 'Symbol _ _ _)) identifier?/sc]
[(Syntax: t)
(syntax/sc (t->sc t))]

View File

@ -34,3 +34,5 @@
(define continuation-mark-key?/sc (flat/sc #'continuation-mark-key?))
(define class?/sc (flat/sc #'class?))
(define struct-type?/sc (flat/sc #'struct-type?))

View File

@ -6,13 +6,19 @@
racket/list racket/match
unstable/contract
racket/contract
(for-template racket/base racket/contract/base)
(for-template racket/base racket/contract/base "../../utils/struct-type-c.rkt")
(for-syntax racket/base syntax/parse))
(provide
(contract-out
[struct/sc (identifier? boolean? (listof static-contract?) . -> . static-contract?)])
struct/sc:)
[struct/sc (identifier? boolean? (listof static-contract?) . -> . static-contract?)]
;; #f as argument indicates StructTypeTop, which should fail on
;; all reflective operations.
[struct-type/sc (any/c . -> . static-contract?)])
struct/sc:
struct-type/sc:)
(struct struct-combinator combinator (name mut?)
@ -47,3 +53,31 @@
(syntax-parser
[(_ name fields)
#'(struct-combinator fields name _)]))
;; FIXME: Currently ignores the structure type and fails on all
;; reflective use.
(struct struct-type/sc combinator ()
#:transparent
#:property prop:combinator-name "struct-type/sc"
#:methods gen:sc
[(define (sc-map v f)
(match v
[(struct-type/sc args)
(struct-type/sc (map (λ (a) (f a 'covariant)) args))]))
(define (sc-traverse v f)
(match v
[(struct-type/sc args)
(for-each (λ (a) (f a 'covariant)) args)
(void)]))
(define (sc->contract v f)
(match v
[(struct-type/sc args)
#`(struct-type/c #f)]))
(define (sc->constraints v f)
(match v
[(struct-type/sc args) (simple-contract-restrict 'chaperone)]))])
(define-match-expander struct-type/sc:
(syntax-parser
[(_ args)
#'(struct-type/sc args)]))

View File

@ -14,14 +14,14 @@
(struct recursive-sc-use ([name identifier?]))
(struct combinator ([args sequence?]))
(struct static-contract ())
[sc-map (static-contract? (static-contract? variance/c . -> . static-contract?) . -> . static-contract?)]
[sc-map
(static-contract? (static-contract? variance/c . -> . static-contract?) . -> . static-contract?)]
[sc-traverse (static-contract? (static-contract? variance/c . -> . any/c) . -> . void?)]
[sc->contract (static-contract? (static-contract? . -> . syntax?) . -> . syntax?)]
[sc->constraints (static-contract? (static-contract? . -> . contract-restrict?) . -> . contract-restrict?)]
[sc->constraints
(static-contract? (static-contract? . -> . contract-restrict?) . -> . contract-restrict?)]
[sc-terminal-kind (static-contract? . -> . (or/c #f contract-kind?))]
[sc? predicate/c]
)
[sc? predicate/c])
prop:combinator-name
gen:sc)

View File

@ -10,7 +10,7 @@
(submod "private/type-contract.rkt" predicates)
"utils/utils.rkt"
(for-syntax "utils/utils.rkt")
"utils/any-wrap.rkt" unstable/contract racket/contract/parametric)
"utils/any-wrap.rkt" "utils/struct-type-c.rkt" unstable/contract racket/contract/parametric)
(provide (rename-out [module-begin #%module-begin]
[top-interaction #%top-interaction])

View File

@ -0,0 +1,40 @@
#lang racket/base
(provide struct-type/c)
(require racket/contract/base racket/contract/combinator)
;; struct-type/c generates contracts which protect structure type
;; descriptors. These descriptors can be used reflectively to create
;; constructors, accessors, mutators, predicates, and other
;; structure-related functions.
;; Currently, this is a very simple implentation which always rejects
;; all reflective access. A better implementation would check that the
;; procedures created by reflective access to the structure obey
;; appropriate invariants.
(define (val-first-projection b)
(define (fail neg-party v)
(raise-blame-error
(blame-swap b) #:missing-party neg-party
v
"Attempted to use a struct type reflectively in untyped code: ~v" v))
(λ (v)
(λ (neg-party)
(chaperone-struct-type
v
;; the below interposition functions could be improved to fail later,
;; when the functions they produce are actually used.
;; interposition for `struct-type-info`
(λ _ (fail neg-party v))
;; interposition for `struct-type-make-constructor`
(λ _ (fail neg-party v))
;; guard for interposition on subtypes
(λ _ (fail neg-party v))))))
(define (struct-type/c sty) ;; currently ignores sty
(make-chaperone-contract
#:name "struct-type/c"
#:first-order struct-type?
#:projection (λ (blame) (λ (val) (((val-first-projection blame) val) #f)))
#:val-first-projection val-first-projection))

View File

@ -0,0 +1,12 @@
#lang racket/load
(module m1 typed/racket
(struct x ())
(define: y : Struct-TypeTop struct:x)
(provide struct:x y))
(module m2 racket
(require 'm1)
struct:x y)
(require 'm2)