cs: encode procedure methodness statically

When a `lambda` form is marked as a method (for arity error reporting)
through a property, translate that to a static flag on the procedure,
instead of a call to `procedure->method`.

The only way we have to attach static information is through the
procedure name, so the encoding already in place for "no name" and
"path-based name" is extended to support a method flag.
This commit is contained in:
Matthew Flatt 2020-02-17 08:59:57 -07:00
parent 5412a4c5fa
commit c7059c7c94
4 changed files with 99 additions and 27 deletions

View File

@ -548,9 +548,9 @@
(string->symbol (format "body of ~a" n))))
(let* ([c (#%$continuation-return-code k)]
[n (#%$code-name c)])
(if (special-procedure-name-string? n)
(if (path-or-empty-procedure-name-string? n)
#f
n)))]
(procedure-name-string->visible-name-string n))))]
[desc
(let* ([ci (#%$code-info (#%$continuation-return-code k))]
[src (and

View File

@ -40,19 +40,8 @@
[else
(let ([name (#%$code-name (#%$closure-code v))])
(and name
(cond
[(special-procedure-name-string? name)
;; "[" is "no name", and any other
;; "["-prefixed name is derived from the path
(let ([len (string-length name)])
(and (fx> len 1)
(string->symbol (substring name 1 len))))]
[(and (fx> (string-length name) 0)
(char=? #\] (string-ref name 0)))
;; Strip escape character
(string->symbol (substring name 1 (string-length name)))]
[else
(string->symbol name)])))])]
(let ([n (procedure-name-string->visible-name-string name)])
(and n (string->symbol n)))))])]
[(impersonator? v)
(object-name (impersonator-val v))]
[(procedure? v)
@ -76,9 +65,45 @@
(getprop (record-type-uid rtd) 'prefab-key+count #f)))
(object-name (record-rtd v)))))
;; Since a procedure name is the one way we have to attach static
;; information to `lambda` forms, it can encode more than just a name:
;; * Starting with "[" means a path-derived name, where that
;; distinction is used instack trace.
;; * Starting with "]" means that some other starting character
;; ws escaped.
;; * After "[" or "]", "!" means a method, and "^" means not-a-method.
(define (procedure-name-string->visible-name-string n)
(cond
[(not (string? n)) n]
[else
(let ([len (string-length n)])
(cond
[(fx= 0 len) n]
[else
(let ([strip-prefix
(lambda ()
(cond
[(fx= 1 len) ""]
[(char=? #\; (string-ref n 0))
(substring n 2 len)]
[(char=? #\^ (string-ref n 0))
(substring n 2 len)]
[else
(substring n 1 len)]))])
(cond
[(char=? #\[ (string-ref n 0))
(let ([n (strip-prefix)])
;; Empty means no name
(if (eqv? "" n)
#f
n))]
[(char=? #\] (string-ref n 0))
(strip-prefix)]
[else n]))]))]))
;; name starting with a square bracket is meant to
;; encode a path or "no name"
(define (special-procedure-name-string? n)
(define (path-or-empty-procedure-name-string? n)
(and (string? n)
(fx> (string-length n) 0)
(char=? #\[ (string-ref n 0))))

View File

@ -376,7 +376,7 @@
(let ([name (wrapper-procedure-data f)])
(and (#%vector? name)
(method-wrapper-vector? name)))
#f)]
(procedure-is-method-by-name? f))]
[(record? f)
(or (method-arity-error? f)
(let ([v (struct-property-ref prop:procedure (record-rtd f) #f)])
@ -391,8 +391,47 @@
[else (procedure-is-method? v)])))]
[else #f]))
(define-syntax-rule (|#%method-arity| e)
(procedure->method e))
(define (procedure-is-method-by-name? proc)
(let ([n (#%$code-name (#%$closure-code proc))])
(and n
(fx>= (string-length n) 2)
(or (char=? #\[ (string-ref n 0))
(char=? #\] (string-ref n 0)))
(char=? #\! (string-ref n 1)))))
(define-syntax (|#%method-arity| stx)
(syntax-case stx (|#%name|)
[(_ (|#%name| name e))
;; Encode method-arity property in the procedure name; see
;; "object-name.ss" for more information about encoding
(let ([n (#%symbol->string (#%syntax->datum #'name))])
(let ([new-name
(#%string->symbol
(cond
[(= 0 (string-length n))
;; "]" indicates encoded, and "!" indicates method
"]!"]
[(or (char=? #\[ (string-ref n 0))
(char=? #\] (string-ref n 0)))
;; Path-based, no name, or escaped:
(cond
[(= 1 (string-length n))
;; No name or empty, so change to method
(string-append n "!")]
[(char=? #\! (string-ref n 1))
;; Already marked as a method
n]
[(char=? #\^ (string-ref n 1))
;; Currently marked as "not a method"
(string-append (#%substring n 0 1) "!" (#%substring n 2 (string-length n)))]
[else
;; Currently a path-based name or escaped name
(string-append (#%substring n 0 1) "!" (#%substring n 1 (string-length n)))])]
[else
;; Add an escape so we can mark as a method:
(string-append "]!" n)]))])
#`(|#%name| #,(#%datum->syntax #'name new-name) e)))]
[(_ e) #'(procedure->method e)]))
;; ----------------------------------------
@ -462,13 +501,15 @@
(vector (or name (#%vector-ref v 0))
(#%vector-ref v 1)
'method)]
[name (vector name
[name (vector (or name (#%vector-ref v 0))
(#%vector-ref v 1))]
[else v])))]
[(#%procedure? proc)
(make-arity-wrapper-procedure proc
mask
(vector name proc))]
(if (procedure-is-method-by-name? proc)
(vector name proc 'method)
(vector name proc)))]
[(reduced-arity-procedure? proc)
(do-procedure-reduce-arity-mask (reduced-arity-procedure-proc proc)
mask

View File

@ -30,11 +30,17 @@
(define (add-property str)
(wrap-property-set (reannotate orig-s new-s)
'inferred-name
;; Hack: starting with "[" means
;; "derived from path". This distinction
;; is used when printing function names
;; in a stack trace.
(string->symbol (string-append-immutable "[" str))))
;; Starting with "[" means "derived from
;; path". This distinction is used when
;; printing function names in a stack trace.
;; Furthermore, "!" or "^" after "[" indicates
;; methodness or not, so add an explicit "^"
;; if necessary.
(let ([prefix (if (or (char=? (string-ref str 0) #\!)
(char=? (string-ref str 0) #\^))
"[^"
"[")])
(string->symbol (string-append-immutable prefix str)))))
(cond
[(and (or (path? src) (string? src)) line col)
(add-property
@ -54,7 +60,7 @@
;; suppress any other inferred name:
(wrap-property-set (reannotate orig-s new-s)
'inferred-name
;; Hack: "[" means "no name"
;; "[" means "no name"
'|[|)]
[else new-s])]))