|
117 | 117 | [(arr/sc: args rest (list (any/sc:) ...))
|
118 | 118 | (arr/sc args rest #f)]
|
119 | 119 | [(none/sc:) any/sc]
|
120 |
| - [(app sc-terminal-kind 'flat) any/sc] |
| 120 | + [(or/sc: (? flat-terminal-kind?) ...) any/sc] |
| 121 | + [(? flat-terminal-kind?) any/sc] |
121 | 122 | [else sc]))
|
122 | 123 |
|
| 124 | +(define (flat-terminal-kind? sc) |
| 125 | + (eq? 'flat (sc-terminal-kind sc))) |
123 | 126 |
|
| 127 | +;; The side of a static contract describes the source of the values that |
| 128 | +;; the contract needs to check. |
| 129 | +;; - 'positive : values exported by the server module |
| 130 | +;; - 'negative : values imported from a client module |
| 131 | +;; - 'both : values from both server & client |
| 132 | +(define (side? v) |
| 133 | + (memq v '(positive negative both))) |
| 134 | + |
| 135 | +;; A _weak side_ is a side that is currently unsafe to optimize |
| 136 | +;; Example: |
| 137 | +;; when optimizing an `(or/sc scs ...)` on the 'positive side, |
| 138 | +;; each of the `scs` should be optimized on the '(weak positive) side, |
| 139 | +;; and their sub-contracts --- if any --- may be optimized on the 'positive side |
| 140 | +(define (weak-side? x) |
| 141 | + (match x |
| 142 | + [(list 'weak (? side?)) |
| 143 | + #true] |
| 144 | + [_ |
| 145 | + #false])) |
| 146 | + |
| 147 | +(define (strengthen-side side) |
| 148 | + (if (weak-side? side) |
| 149 | + (second side) |
| 150 | + side)) |
| 151 | + |
| 152 | +(define (weaken-side side) |
| 153 | + (if (weak-side? side) |
| 154 | + side |
| 155 | + `(weak ,side))) |
124 | 156 |
|
125 | 157 | (define (invert-side v)
|
126 |
| - (case v |
127 |
| - [(positive) 'negative] |
128 |
| - [(negative) 'positive] |
129 |
| - [(both) 'both])) |
| 158 | + (if (weak-side? v) |
| 159 | + (weaken-side (invert-side v)) |
| 160 | + (case v |
| 161 | + [(positive) 'negative] |
| 162 | + [(negative) 'positive] |
| 163 | + [(both) 'both]))) |
130 | 164 |
|
131 | 165 | (define (combine-variance side var)
|
132 | 166 | (case var
|
133 | 167 | [(covariant) side]
|
134 | 168 | [(contravariant) (invert-side side)]
|
135 | 169 | [(invariant) 'both]))
|
136 | 170 |
|
| 171 | +;; update-side : sc? weak-side? -> weak-side? |
| 172 | +;; Change the current side to something safe & strong-as-possible |
| 173 | +;; for optimizing the sub-contracts of the given `sc`. |
| 174 | +(define (update-side sc side) |
| 175 | + (match sc |
| 176 | + [(or/sc: scs ...) |
| 177 | + #:when (not (andmap flat-terminal-kind? scs)) |
| 178 | + (weaken-side side)] |
| 179 | + [(? guarded-sc?) |
| 180 | + (strengthen-side side)] |
| 181 | + [_ |
| 182 | + ;; Keep same side by default. |
| 183 | + ;; This is precisely safe for "unguarded" static contracts like and/sc |
| 184 | + ;; and conservatively safe for everything else. |
| 185 | + side])) |
| 186 | + |
| 187 | +;; guarded-sc? : sc? -> boolean? |
| 188 | +;; Returns #true if the given static contract represents a type with a "real" |
| 189 | +;; type constructor. E.g. list/sc is "real" and or/sc is not. |
| 190 | +(define (guarded-sc? sc) |
| 191 | + (match sc |
| 192 | + [(or (? flat-terminal-kind?) |
| 193 | + (->/sc: _ _ _ _ _ _) |
| 194 | + (arr/sc: _ _ _) |
| 195 | + (async-channel/sc: _) |
| 196 | + (box/sc: _) |
| 197 | + (channel/sc: _) |
| 198 | + (cons/sc: _ _) |
| 199 | + (continuation-mark-key/sc: _) |
| 200 | + (evt/sc: _) |
| 201 | + (hash/sc: _ _) |
| 202 | + (immutable-hash/sc: _ _) |
| 203 | + (list/sc: _ ...) |
| 204 | + (listof/sc: _) |
| 205 | + (mutable-hash/sc: _ _) |
| 206 | + (parameter/sc: _ _) |
| 207 | + (promise/sc: _) |
| 208 | + (prompt-tag/sc: _ _) |
| 209 | + (sequence/sc: _ ...) |
| 210 | + (set/sc: _) |
| 211 | + (struct/sc: _ _) |
| 212 | + (syntax/sc: _) |
| 213 | + (vector/sc: _ ...) |
| 214 | + (vectorof/sc: _) |
| 215 | + (weak-hash/sc: _ _)) |
| 216 | + #true] |
| 217 | + [_ |
| 218 | + #false])) |
| 219 | + |
137 | 220 | (define (remove-unused-recursive-contracts sc)
|
138 | 221 | (define root (generate-temporary))
|
139 | 222 | (define main-table (make-free-id-table))
|
|
208 | 291 | ;; If we trust a specific side then we drop all contracts protecting that side.
|
209 | 292 | (define (optimize sc #:trusted-positive [trusted-positive #f] #:trusted-negative [trusted-negative #f])
|
210 | 293 | ;; single-step: reduce and trusted-side-reduce if appropriate
|
211 |
| - (define (single-step sc side) |
| 294 | + (define (single-step sc maybe-weak-side) |
212 | 295 | (define trusted
|
213 |
| - (case side |
214 |
| - [(positive) trusted-positive] |
215 |
| - [(negative) trusted-negative] |
216 |
| - [(both) (and trusted-positive trusted-negative)])) |
| 296 | + (if (weak-side? maybe-weak-side) |
| 297 | + #false |
| 298 | + (case maybe-weak-side |
| 299 | + [(positive) trusted-positive] |
| 300 | + [(negative) trusted-negative] |
| 301 | + [(both) (and trusted-positive trusted-negative)]))) |
217 | 302 |
|
218 | 303 | (reduce
|
219 | 304 | (if trusted
|
|
223 | 308 | ;; full-pass: single-step at every static contract subpart
|
224 | 309 | (define (full-pass sc)
|
225 | 310 | (define ((recur side) sc variance)
|
226 |
| - (define new-side (combine-variance side variance)) |
227 |
| - (single-step (sc-map sc (recur new-side)) new-side)) |
| 311 | + (define curr-side (combine-variance side variance)) |
| 312 | + (define next-side (update-side sc curr-side)) |
| 313 | + (single-step (sc-map sc (recur next-side)) curr-side)) |
228 | 314 | ((recur 'positive) sc 'covariant))
|
229 | 315 |
|
230 | 316 | ;; Do full passes until we reach a fix point, and then remove all unneccessary recursive parts
|
|
0 commit comments