extenders excludes inline defrecord/deftype impls
deftype/defrecord inline protocol methods went through extend-type -> register-method, so a record implementing a protocol inline showed up in (extenders P) — the JVM only lists extend/extend-type/extend-protocol registrations there (inline impls compile into the class). Add register-inline-method: it registers for dispatch under the record tag but skips the extender mark. The mark lives inside type-registry so the per-case corpus prune restores it. Closes corpus lists-extended-type + seq-of-tags.
This commit is contained in:
parent
10d2b992f7
commit
ccc76fd69f
4 changed files with 45 additions and 14 deletions
|
|
@ -176,11 +176,29 @@
|
||||||
((and (> (string-length type-name) 10) (string=? (substring type-name 0 10) "java.util.")) (substring type-name 10 (string-length type-name)))
|
((and (> (string-length type-name) 10) (string=? (substring type-name 0 10) "java.util.")) (substring type-name 10 (string-length type-name)))
|
||||||
(else type-name))))
|
(else type-name))))
|
||||||
(and (hashtable-ref host-type-set base #f) base)))
|
(and (hashtable-ref host-type-set base #f) base)))
|
||||||
|
;; An extend/extend-type/extend-protocol registration marks the tag as an
|
||||||
|
;; extender of the protocol (recorded inside type-registry so the per-case prune
|
||||||
|
;; restores it). deftype/defrecord inline impls go through register-inline-method
|
||||||
|
;; and skip the mark: the JVM compiles inline protocol methods into the class, so
|
||||||
|
;; extenders excludes them.
|
||||||
|
(define extend-mark "__jolt_extend__")
|
||||||
|
(define (mark-extend! tag proto-name)
|
||||||
|
(let ((ti (hashtable-ref type-registry tag #f)))
|
||||||
|
(when ti (let ((pi (hashtable-ref ti proto-name #f)))
|
||||||
|
(when pi (hashtable-set! pi extend-mark #t))))))
|
||||||
(define (register-method type-name proto-name method-name fn)
|
(define (register-method type-name proto-name method-name fn)
|
||||||
(let ((host (canonical-host-tag type-name)))
|
(let* ((host (canonical-host-tag type-name))
|
||||||
(register-protocol-method (or host (string-append (chez-current-ns) "." type-name)) proto-name method-name fn)
|
(tag (or host (string-append (chez-current-ns) "." type-name))))
|
||||||
|
(register-protocol-method tag proto-name method-name fn)
|
||||||
|
(mark-extend! tag proto-name)
|
||||||
jolt-nil))
|
jolt-nil))
|
||||||
|
|
||||||
|
;; register-inline-method: a deftype/defrecord inline impl. Registers for dispatch
|
||||||
|
;; under the ns-qualified record tag but does NOT mark it as an extender.
|
||||||
|
(define (register-inline-method type-name proto-name method-name fn)
|
||||||
|
(register-protocol-method (string-append (chez-current-ns) "." type-name) proto-name method-name fn)
|
||||||
|
jolt-nil)
|
||||||
|
|
||||||
;; protocol-dispatch: look up the impl by the value's type tag (record) or host
|
;; protocol-dispatch: look up the impl by the value's type tag (record) or host
|
||||||
;; candidates, invoke it; reified objects carry instance-local methods.
|
;; candidates, invoke it; reified objects carry instance-local methods.
|
||||||
(define (protocol-dispatch proto-name method-name obj rest-args)
|
(define (protocol-dispatch proto-name method-name obj rest-args)
|
||||||
|
|
@ -245,14 +263,19 @@
|
||||||
(cond ((< i 0) s) ((char=? (string-ref s i) #\.) (substring s (+ i 1) (string-length s))) (else (loop (- i 1))))))
|
(cond ((< i 0) s) ((char=? (string-ref s i) #\.) (substring s (+ i 1) (string-length s))) (else (loop (- i 1))))))
|
||||||
(define (memp pred lst) (cond ((null? lst) #f) ((pred (car lst)) lst) (else (memp pred (cdr lst)))))
|
(define (memp pred lst) (cond ((null? lst) #f) ((pred (car lst)) lst) (else (memp pred (cdr lst)))))
|
||||||
|
|
||||||
;; extenders: type-tags implementing a protocol, as symbols (extends? reads this).
|
;; extenders: type-tags that extend a protocol via extend/extend-type/extend-
|
||||||
|
;; protocol, as symbols (extends? reads this). Inline deftype/defrecord impls are
|
||||||
|
;; excluded — only tags carrying the extend mark count, matching the JVM.
|
||||||
(define (extenders proto)
|
(define (extenders proto)
|
||||||
(let* ((pn (jolt-get proto (keyword #f "name") jolt-nil))
|
(let* ((pn (jolt-get proto (keyword #f "name") jolt-nil))
|
||||||
(pn-str (if (symbol-t? pn) (symbol-t-name pn) pn))
|
(pn-str (if (symbol-t? pn) (symbol-t-name pn) pn))
|
||||||
(out '()))
|
(out '()))
|
||||||
(vector-for-each
|
(vector-for-each
|
||||||
(lambda (tag) (when (let ((ti (hashtable-ref type-registry tag #f))) (and ti (hashtable-ref ti pn-str #f)))
|
(lambda (tag)
|
||||||
(set! out (cons (jolt-symbol jolt-nil tag) out))))
|
(let ((ti (hashtable-ref type-registry tag #f)))
|
||||||
|
(when ti (let ((pi (hashtable-ref ti pn-str #f)))
|
||||||
|
(when (and pi (hashtable-ref pi extend-mark #f))
|
||||||
|
(set! out (cons (jolt-symbol jolt-nil tag) out)))))))
|
||||||
(hashtable-keys type-registry))
|
(hashtable-keys type-registry))
|
||||||
(if (null? out) jolt-nil (list->cseq out))))
|
(if (null? out) jolt-nil (list->cseq out))))
|
||||||
|
|
||||||
|
|
@ -305,6 +328,7 @@
|
||||||
(def-var! "clojure.core" "make-protocol" make-protocol)
|
(def-var! "clojure.core" "make-protocol" make-protocol)
|
||||||
(def-var! "clojure.core" "register-protocol-methods!" register-protocol-methods!)
|
(def-var! "clojure.core" "register-protocol-methods!" register-protocol-methods!)
|
||||||
(def-var! "clojure.core" "register-method" register-method)
|
(def-var! "clojure.core" "register-method" register-method)
|
||||||
|
(def-var! "clojure.core" "register-inline-method" register-inline-method)
|
||||||
(def-var! "clojure.core" "protocol-dispatch" (lambda (pn mn obj rest) (protocol-dispatch pn mn obj rest)))
|
(def-var! "clojure.core" "protocol-dispatch" (lambda (pn mn obj rest) (protocol-dispatch pn mn obj rest)))
|
||||||
(def-var! "clojure.core" "satisfies?" jolt-satisfies?)
|
(def-var! "clojure.core" "satisfies?" jolt-satisfies?)
|
||||||
(def-var! "clojure.core" "extenders" extenders)
|
(def-var! "clojure.core" "extenders" extenders)
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
;; reset between cases so there is no leakage — same isolation a fresh process gives.
|
;; reset between cases so there is no leakage — same isolation a fresh process gives.
|
||||||
;;
|
;;
|
||||||
;; chez --script host/chez/run-corpus.ss
|
;; chez --script host/chez/run-corpus.ss
|
||||||
;; JOLT_CHEZ_ZJ_FLOOR=N override the regression floor (default 2720)
|
;; JOLT_CHEZ_ZJ_FLOOR=N override the regression floor (default 2722)
|
||||||
;; JOLT_CORPUS_LIMIT=N every-Nth stride (fast iteration; floor drops to 0)
|
;; JOLT_CORPUS_LIMIT=N every-Nth stride (fast iteration; floor drops to 0)
|
||||||
;; JOLT_DUMP_CRASH_LABELS=1 list crash + allowlisted labels
|
;; JOLT_DUMP_CRASH_LABELS=1 list crash + allowlisted labels
|
||||||
(import (chezscheme))
|
(import (chezscheme))
|
||||||
|
|
@ -89,7 +89,6 @@
|
||||||
'("getMessage on a thrown string"
|
'("getMessage on a thrown string"
|
||||||
"type of record" "chunked-seq? always false"
|
"type of record" "chunked-seq? always false"
|
||||||
"^Type tag on var"
|
"^Type tag on var"
|
||||||
"lists extended type" "seq of tags"
|
|
||||||
"close on throw" "macroexpand-1"
|
"close on throw" "macroexpand-1"
|
||||||
"bean is the map" "proxy resolves nil"
|
"bean is the map" "proxy resolves nil"
|
||||||
"*in* is bound" "*in* bound"
|
"*in* is bound" "*in* bound"
|
||||||
|
|
@ -193,7 +192,7 @@
|
||||||
|
|
||||||
;; Regression floor: fail on any NEW divergence or if pass drops below the floor.
|
;; Regression floor: fail on any NEW divergence or if pass drops below the floor.
|
||||||
(define base-floor (let ((s (getenv "JOLT_CHEZ_ZJ_FLOOR")))
|
(define base-floor (let ((s (getenv "JOLT_CHEZ_ZJ_FLOOR")))
|
||||||
(if s (string->number s) 2720)))
|
(if s (string->number s) 2722)))
|
||||||
(define floor (if limit 0 base-floor))
|
(define floor (if limit 0 base-floor))
|
||||||
(when (or (> (length diverged) 0) (< pass floor))
|
(when (or (> (length diverged) 0) (< pass floor))
|
||||||
(printf "REGRESSION: pass ~a < floor ~a or ~a new divergence(s)\n"
|
(printf "REGRESSION: pass ~a < floor ~a or ~a new divergence(s)\n"
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -307,13 +307,17 @@
|
||||||
(:volatile-mutable mt)))
|
(:volatile-mutable mt)))
|
||||||
true false)))
|
true false)))
|
||||||
fields)
|
fields)
|
||||||
|
;; inline impls register for dispatch but are NOT extenders of the
|
||||||
|
;; protocol (the JVM compiles them into the class) — register-inline-method,
|
||||||
|
;; not extend-type.
|
||||||
impl (fn [proto specs]
|
impl (fn [proto specs]
|
||||||
`(extend-type ~tname ~proto
|
`(do
|
||||||
~@(map (fn [spec]
|
~@(map (fn [spec]
|
||||||
(let [argv (nth spec 1)
|
(let [argv (nth spec 1)
|
||||||
inst (first argv)
|
inst (first argv)
|
||||||
binds (vec (mapcat (fn [f] [f `(get ~inst ~(keyword (name f)))]) fields))]
|
binds (vec (mapcat (fn [f] [f `(get ~inst ~(keyword (name f)))]) fields))]
|
||||||
`(~(first spec) ~argv (let [~@binds] ~@(drop 2 spec)))))
|
`(register-inline-method ~(name tname) ~(name proto) ~(name (first spec))
|
||||||
|
(fn ~argv (let [~@binds] ~@(drop 2 spec))))))
|
||||||
specs)))]
|
specs)))]
|
||||||
`(do
|
`(do
|
||||||
(def ~tname (make-deftype-ctor (quote ~tname) [~@field-kws] [~@field-tags] [~@field-muts]))
|
(def ~tname (make-deftype-ctor (quote ~tname) [~@field-kws] [~@field-tags] [~@field-muts]))
|
||||||
|
|
@ -445,8 +449,11 @@
|
||||||
;; each method body sees the record fields, bound from the instance (the
|
;; each method body sees the record fields, bound from the instance (the
|
||||||
;; method's first param), matching Clojure's defrecord method scope. vec the
|
;; method's first param), matching Clojure's defrecord method scope. vec the
|
||||||
;; spliced binding seq so ~@ splices its elements, not the lazy-seq itself.
|
;; spliced binding seq so ~@ splices its elements, not the lazy-seq itself.
|
||||||
|
;; inline impls register for dispatch but are NOT extenders of the
|
||||||
|
;; protocol (the JVM compiles them into the class) — register-inline-method,
|
||||||
|
;; not extend-type.
|
||||||
impl (fn [proto specs]
|
impl (fn [proto specs]
|
||||||
`(extend-type ~name-sym ~proto
|
`(do
|
||||||
~@(map (fn [spec]
|
~@(map (fn [spec]
|
||||||
(let [argv (nth spec 1)
|
(let [argv (nth spec 1)
|
||||||
inst (first argv)
|
inst (first argv)
|
||||||
|
|
@ -455,7 +462,8 @@
|
||||||
;; instead of going through the runtime tag guard.
|
;; instead of going through the runtime tag guard.
|
||||||
hinted (assoc argv 0 (vary-meta inst assoc :tag (name name-sym)))
|
hinted (assoc argv 0 (vary-meta inst assoc :tag (name name-sym)))
|
||||||
binds (vec (mapcat (fn [f] [f `(get ~inst ~(keyword (name f)))]) fields))]
|
binds (vec (mapcat (fn [f] [f `(get ~inst ~(keyword (name f)))]) fields))]
|
||||||
`(~(first spec) ~hinted (let [~@binds] ~@(drop 2 spec)))))
|
`(register-inline-method ~(name name-sym) ~(name proto) ~(name (first spec))
|
||||||
|
(fn ~hinted (let [~@binds] ~@(drop 2 spec))))))
|
||||||
specs)))]
|
specs)))]
|
||||||
`(do
|
`(do
|
||||||
;; deftype already defines ->name (= the ctor); no (name. …) interop needed,
|
;; deftype already defines ->name (= the ctor); no (name. …) interop needed,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue