mirror of
https://git.savannah.gnu.org/git/guile.git
synced 2025-04-30 03:40:34 +02:00
peval: Enable inlining for functions with kwargs
* module/language/tree-il/peval.scm (peval): Handle all lambda inlining the same, and extend with support for multiple clauses and keyword arguments. * test-suite/tests/peval.test ("case-lambda"): Enable kwarg inlining.
This commit is contained in:
parent
c758c99b5e
commit
f95bf6921e
2 changed files with 273 additions and 167 deletions
|
@ -1,6 +1,6 @@
|
||||||
;;; Tree-IL partial evaluator
|
;;; Tree-IL partial evaluator
|
||||||
|
|
||||||
;; Copyright (C) 2011-2014,2017,2019-2023 Free Software Foundation, Inc.
|
;; Copyright (C) 2011-2014,2017,2019-2024 Free Software Foundation, Inc.
|
||||||
|
|
||||||
;;;; This library is free software; you can redistribute it and/or
|
;;;; This library is free software; you can redistribute it and/or
|
||||||
;;;; modify it under the terms of the GNU Lesser General Public
|
;;;; modify it under the terms of the GNU Lesser General Public
|
||||||
|
@ -1554,7 +1554,83 @@ top-level bindings from ENV and return the resulting expression."
|
||||||
(make-primcall src name args)))))
|
(make-primcall src name args)))))
|
||||||
|
|
||||||
(($ <call> src orig-proc orig-args)
|
(($ <call> src orig-proc orig-args)
|
||||||
;; todo: augment the global env with specialized functions
|
(define (residualize-call)
|
||||||
|
(make-call src (for-call orig-proc) (map for-value orig-args)))
|
||||||
|
|
||||||
|
(define (singly-referenced-lambda? proc)
|
||||||
|
(match proc
|
||||||
|
(($ <lambda>) #t)
|
||||||
|
(($ <lexical-ref> _ _ sym)
|
||||||
|
(and (not (assigned-lexical? sym))
|
||||||
|
(= (lexical-refcount sym) 1)
|
||||||
|
(singly-referenced-lambda?
|
||||||
|
(operand-source (lookup sym)))))
|
||||||
|
(_ #f)))
|
||||||
|
|
||||||
|
(define (attempt-inlining proc names syms vals body)
|
||||||
|
(define inline-key (source-expression proc))
|
||||||
|
(define existing-counter (find-counter inline-key counter))
|
||||||
|
(define inlined-exp (make-let src names syms vals body))
|
||||||
|
|
||||||
|
(cond
|
||||||
|
((and=> existing-counter counter-recursive?)
|
||||||
|
;; A recursive call. Process again in tail context.
|
||||||
|
|
||||||
|
;; Mark intervening counters as recursive, so we can
|
||||||
|
;; handle a toplevel counter that recurses mutually with
|
||||||
|
;; some other procedure. Otherwise, the next time we see
|
||||||
|
;; the other procedure, the effort limit would be clamped
|
||||||
|
;; to 100.
|
||||||
|
(let lp ((counter counter))
|
||||||
|
(unless (eq? counter existing-counter)
|
||||||
|
(set-counter-recursive?! counter #t)
|
||||||
|
(lp (counter-prev counter))))
|
||||||
|
|
||||||
|
(log 'inline-recurse inline-key)
|
||||||
|
(loop inlined-exp env counter ctx))
|
||||||
|
((singly-referenced-lambda? orig-proc)
|
||||||
|
;; A lambda in the operator position of the source
|
||||||
|
;; expression. Process again in tail context.
|
||||||
|
(log 'inline-beta inline-key)
|
||||||
|
(loop inlined-exp env counter ctx))
|
||||||
|
(else
|
||||||
|
;; An integration at the top-level, the first
|
||||||
|
;; recursion of a recursive procedure, or a nested
|
||||||
|
;; integration of a procedure that hasn't been seen
|
||||||
|
;; yet.
|
||||||
|
(log 'inline-begin exp)
|
||||||
|
(let/ec k
|
||||||
|
(define (abort)
|
||||||
|
(log 'inline-abort exp)
|
||||||
|
(k (residualize-call)))
|
||||||
|
(define new-counter
|
||||||
|
(cond
|
||||||
|
;; These first two cases will transfer effort from
|
||||||
|
;; the current counter into the new counter.
|
||||||
|
(existing-counter
|
||||||
|
(make-recursive-counter recursive-effort-limit
|
||||||
|
operand-size-limit
|
||||||
|
existing-counter counter))
|
||||||
|
(counter
|
||||||
|
(make-nested-counter abort inline-key counter))
|
||||||
|
;; This case opens a new account, effectively
|
||||||
|
;; printing money. It should only do so once for
|
||||||
|
;; each call site in the source program.
|
||||||
|
(else
|
||||||
|
(make-top-counter effort-limit operand-size-limit
|
||||||
|
abort inline-key))))
|
||||||
|
(define result
|
||||||
|
(loop inlined-exp env new-counter ctx))
|
||||||
|
|
||||||
|
(when counter
|
||||||
|
;; The nested inlining attempt succeeded. Deposit the
|
||||||
|
;; unspent effort and size back into the current
|
||||||
|
;; counter.
|
||||||
|
(transfer! new-counter counter))
|
||||||
|
|
||||||
|
(log 'inline-end result exp)
|
||||||
|
result))))
|
||||||
|
|
||||||
(let revisit-proc ((proc (visit orig-proc 'operator)))
|
(let revisit-proc ((proc (visit orig-proc 'operator)))
|
||||||
(match proc
|
(match proc
|
||||||
(($ <primitive-ref> _ name)
|
(($ <primitive-ref> _ name)
|
||||||
|
@ -1563,167 +1639,193 @@ top-level bindings from ENV and return the resulting expression."
|
||||||
(augment-var-table-with-externally-introduced-lexicals
|
(augment-var-table-with-externally-introduced-lexicals
|
||||||
exp store))
|
exp store))
|
||||||
(for-tail exp)))
|
(for-tail exp)))
|
||||||
(($ <lambda> _ _
|
|
||||||
($ <lambda-case> _ req opt rest #f inits gensyms body #f))
|
(($ <lambda> _ _ clause)
|
||||||
;; Simple case: no keyword arguments.
|
;; A lambda. Attempt to find the matching clause, if
|
||||||
;; todo: handle the more complex cases
|
;; possible.
|
||||||
(let* ((nargs (length orig-args))
|
(define (inline-clause req opt rest kw inits gensyms body
|
||||||
(nreq (length req))
|
arity-mismatch)
|
||||||
(opt (or opt '()))
|
(define (bind name sym val binds)
|
||||||
(rest (if rest (list rest) '()))
|
(cons (vector name sym val) binds))
|
||||||
(nopt (length opt))
|
(define (has-binding? binds sym)
|
||||||
(key (source-expression proc)))
|
(match binds
|
||||||
(define (singly-referenced-lambda? orig-proc)
|
(() #f)
|
||||||
(match orig-proc
|
((#(n s v) . binds)
|
||||||
(($ <lambda>) #t)
|
(or (eq? s sym) (has-binding? binds sym)))))
|
||||||
(($ <lexical-ref> _ _ sym)
|
|
||||||
(and (not (assigned-lexical? sym))
|
;; The basic idea is that we are going to transform an
|
||||||
(= (lexical-refcount sym) 1)
|
;; expression like ((lambda (param ...) body) arg ...)
|
||||||
(singly-referenced-lambda?
|
;; into (let ((param arg) ...) body). However, we have to
|
||||||
(operand-source (lookup sym)))))
|
;; consider order of effects and scope: the args are
|
||||||
|
;; logically parallel, whereas initializer expressions for
|
||||||
|
;; params that don't have arguments are evaluated in
|
||||||
|
;; order, after the arguments. Therefore we have a set of
|
||||||
|
;; parallel bindings, abbreviated pbinds, which proceed
|
||||||
|
;; from the call site, and a set of serial bindings, the
|
||||||
|
;; sbinds, which result from callee initializers. We
|
||||||
|
;; collect these in reverse order as we parse arguments.
|
||||||
|
;; The result is an outer let for the parallel bindings
|
||||||
|
;; containing a let* of the serial bindings and then the
|
||||||
|
;; body.
|
||||||
|
|
||||||
|
(define (process-req req syms args pbinds sbinds)
|
||||||
|
(match req
|
||||||
|
(() (process-opt (or opt '()) syms inits args pbinds sbinds))
|
||||||
|
((name . req)
|
||||||
|
(match syms
|
||||||
|
((sym . syms)
|
||||||
|
(match args
|
||||||
|
(() (arity-mismatch))
|
||||||
|
((arg . args)
|
||||||
|
(process-req req syms args
|
||||||
|
(bind name sym arg pbinds)
|
||||||
|
sbinds))))))))
|
||||||
|
|
||||||
|
(define (keyword-arg? exp)
|
||||||
|
(match exp
|
||||||
|
(($ <const> _ (? keyword?)) #t)
|
||||||
|
(_ #f)))
|
||||||
|
(define (not-keyword-arg? exp)
|
||||||
|
(match exp
|
||||||
|
((or ($ <const> _ (not (? keyword?)))
|
||||||
|
($ <void>)
|
||||||
|
($ <primitive-ref>)
|
||||||
|
($ <lambda>))
|
||||||
|
#t)
|
||||||
(_ #f)))
|
(_ #f)))
|
||||||
(define (inlined-call)
|
|
||||||
(let ((req-vals (list-head orig-args nreq))
|
|
||||||
(opt-vals (let lp ((args (drop orig-args nreq))
|
|
||||||
(inits inits)
|
|
||||||
(out '()))
|
|
||||||
(match inits
|
|
||||||
(() (reverse out))
|
|
||||||
((init . inits)
|
|
||||||
(match args
|
|
||||||
(()
|
|
||||||
(lp '() inits (cons init out)))
|
|
||||||
((arg . args)
|
|
||||||
(lp args inits (cons arg out))))))))
|
|
||||||
(rest-vals (cond
|
|
||||||
((> nargs (+ nreq nopt))
|
|
||||||
(list (make-primcall
|
|
||||||
#f 'list
|
|
||||||
(drop orig-args (+ nreq nopt)))))
|
|
||||||
((null? rest) '())
|
|
||||||
(else (list (make-const #f '()))))))
|
|
||||||
(if (>= nargs (+ nreq nopt))
|
|
||||||
(make-let src
|
|
||||||
(append req opt rest)
|
|
||||||
gensyms
|
|
||||||
(append req-vals opt-vals rest-vals)
|
|
||||||
body)
|
|
||||||
;; The default initializers of optional arguments
|
|
||||||
;; may refer to earlier arguments, so in the general
|
|
||||||
;; case we must expand into a series of nested let
|
|
||||||
;; expressions.
|
|
||||||
;;
|
|
||||||
;; In the generated code, the outermost let
|
|
||||||
;; expression will bind all required arguments, as
|
|
||||||
;; well as the empty rest argument, if any. Each
|
|
||||||
;; optional argument will be bound within an inner
|
|
||||||
;; let.
|
|
||||||
(make-let src
|
|
||||||
(append req rest)
|
|
||||||
(append (list-head gensyms nreq)
|
|
||||||
(last-pair gensyms))
|
|
||||||
(append req-vals rest-vals)
|
|
||||||
(fold-right (lambda (var gensym val body)
|
|
||||||
(make-let src
|
|
||||||
(list var)
|
|
||||||
(list gensym)
|
|
||||||
(list val)
|
|
||||||
body))
|
|
||||||
body
|
|
||||||
opt
|
|
||||||
(list-head (drop gensyms nreq) nopt)
|
|
||||||
opt-vals)))))
|
|
||||||
|
|
||||||
|
(define (process-opt opt syms inits args pbinds sbinds)
|
||||||
|
(match opt
|
||||||
|
(() (process-rest syms inits args pbinds sbinds))
|
||||||
|
((name . opt)
|
||||||
|
(match inits
|
||||||
|
((init . inits)
|
||||||
|
(match syms
|
||||||
|
((sym . syms)
|
||||||
|
(cond
|
||||||
|
(kw
|
||||||
|
(match args
|
||||||
|
((or () ((? keyword-arg?) . _))
|
||||||
|
;; Optargs and kwargs; stop optarg dispatch at
|
||||||
|
;; first keyword.
|
||||||
|
(process-opt opt syms inits args pbinds
|
||||||
|
(bind name sym init sbinds)))
|
||||||
|
(((? not-keyword-arg? arg) . args)
|
||||||
|
;; Arg is definitely not a keyword; it is an
|
||||||
|
;; optarg.
|
||||||
|
(process-opt opt syms inits args
|
||||||
|
(bind name sym arg pbinds)
|
||||||
|
sbinds))
|
||||||
|
(_
|
||||||
|
;; We can't tell whether the arg is a keyword
|
||||||
|
;; or not! Annoying semantics, this.
|
||||||
|
(residualize-call))))
|
||||||
|
(else
|
||||||
|
;; No kwargs.
|
||||||
|
(match args
|
||||||
|
(()
|
||||||
|
(process-opt opt syms inits args pbinds
|
||||||
|
(bind name sym init sbinds)))
|
||||||
|
((arg . args)
|
||||||
|
(process-opt opt syms inits args
|
||||||
|
(bind name sym arg pbinds)
|
||||||
|
sbinds))))))))))))
|
||||||
|
|
||||||
|
(define (process-rest syms inits args pbinds sbinds)
|
||||||
|
(match rest
|
||||||
|
(#f
|
||||||
|
(match kw
|
||||||
|
((#f . kw)
|
||||||
|
(process-kw kw syms inits args pbinds sbinds))
|
||||||
|
(#f
|
||||||
|
(unless (and (null? syms) (null? inits))
|
||||||
|
(error "internal error"))
|
||||||
|
(match args
|
||||||
|
(() (finish pbinds sbinds body))
|
||||||
|
(_ (arity-mismatch))))))
|
||||||
|
(rest
|
||||||
|
(match syms
|
||||||
|
((sym . syms)
|
||||||
|
(let ((rest-val (make-primcall src 'list args)))
|
||||||
|
(unless (and (null? syms) (null? inits))
|
||||||
|
(error "internal error"))
|
||||||
|
(finish pbinds (bind rest sym rest-val sbinds)
|
||||||
|
body)))))))
|
||||||
|
|
||||||
|
(define (process-kw kw syms inits args pbinds sbinds)
|
||||||
|
;; Require that the ordered list of the keywords'
|
||||||
|
;; syms is the same as the remaining gensyms to bind.
|
||||||
|
;; Psyntax emits tree-il with this property, and it
|
||||||
|
;; is required by (and checked by) other parts of the
|
||||||
|
;; compiler, e.g. tree-il-to-cps lowering.
|
||||||
|
(unless (equal? syms (match kw (((k name sym) ...) sym)))
|
||||||
|
(error "internal error: unexpected kwarg syms"))
|
||||||
|
|
||||||
|
(define (process-kw-args positional? args pbinds)
|
||||||
|
(match args
|
||||||
|
(()
|
||||||
|
(process-kw-inits kw inits pbinds sbinds))
|
||||||
|
((($ <const> _ (? keyword? keyword)) arg . args)
|
||||||
|
(match (assq keyword kw)
|
||||||
|
((keyword name sym)
|
||||||
|
;; Because of side effects, we don't
|
||||||
|
;; optimize passing the same keyword arg
|
||||||
|
;; multiple times.
|
||||||
|
(if (has-binding? pbinds sym)
|
||||||
|
(residualize-call)
|
||||||
|
(process-kw-args #f args
|
||||||
|
(bind name sym arg pbinds))))
|
||||||
|
(#f (residualize-call))))
|
||||||
|
(((? not-keyword-arg?) . args)
|
||||||
|
(if positional?
|
||||||
|
(arity-mismatch)
|
||||||
|
(residualize-call)))
|
||||||
|
(_ (residualize-call))))
|
||||||
|
|
||||||
|
(define (process-kw-inits kw inits pbinds sbinds)
|
||||||
|
(match kw
|
||||||
|
(()
|
||||||
|
(unless (null? inits) (error "internal error"))
|
||||||
|
(finish pbinds sbinds body))
|
||||||
|
(((keyword name sym) . kw)
|
||||||
|
(match inits
|
||||||
|
((init . inits)
|
||||||
|
(process-kw-inits kw inits pbinds
|
||||||
|
(if (has-binding? pbinds sym)
|
||||||
|
sbinds
|
||||||
|
(bind name sym init sbinds))))))))
|
||||||
|
|
||||||
|
(process-kw-args #t args pbinds))
|
||||||
|
|
||||||
|
(define (finish pbinds sbinds body)
|
||||||
|
(match sbinds
|
||||||
|
(()
|
||||||
|
(match (reverse pbinds)
|
||||||
|
((#(name sym val) ...)
|
||||||
|
(attempt-inlining proc name sym val body))))
|
||||||
|
((#(name sym val) . sbinds)
|
||||||
|
(finish pbinds sbinds
|
||||||
|
(make-let src (list name) (list sym) (list val)
|
||||||
|
body)))))
|
||||||
|
|
||||||
|
;; Limitations:
|
||||||
|
;;
|
||||||
|
;; - #:key or #:rest, but not both.
|
||||||
|
;; - #:allow-other-keys unsupported.
|
||||||
(cond
|
(cond
|
||||||
((or (< nargs nreq) (and (null? rest) (> nargs (+ nreq nopt))))
|
((and kw (or rest (match kw ((aok? . _) aok?))))
|
||||||
;; An error, or effecting arguments.
|
(residualize-call))
|
||||||
(make-call src (for-call orig-proc) (map for-value orig-args)))
|
|
||||||
((or (and=> (find-counter key counter) counter-recursive?)
|
|
||||||
(singly-referenced-lambda? orig-proc))
|
|
||||||
;; A recursive call, or a lambda in the operator
|
|
||||||
;; position of the source expression. Process again in
|
|
||||||
;; tail context.
|
|
||||||
;;
|
|
||||||
;; In the recursive case, mark intervening counters as
|
|
||||||
;; recursive, so we can handle a toplevel counter that
|
|
||||||
;; recurses mutually with some other procedure.
|
|
||||||
;; Otherwise, the next time we see the other procedure,
|
|
||||||
;; the effort limit would be clamped to 100.
|
|
||||||
;;
|
|
||||||
(let ((found (find-counter key counter)))
|
|
||||||
(if (and found (counter-recursive? found))
|
|
||||||
(let lp ((counter counter))
|
|
||||||
(if (not (eq? counter found))
|
|
||||||
(begin
|
|
||||||
(set-counter-recursive?! counter #t)
|
|
||||||
(lp (counter-prev counter)))))))
|
|
||||||
|
|
||||||
(log 'inline-recurse key)
|
|
||||||
(loop (inlined-call) env counter ctx))
|
|
||||||
(else
|
(else
|
||||||
;; An integration at the top-level, the first
|
(process-req req gensyms orig-args '() '()))))
|
||||||
;; recursion of a recursive procedure, or a nested
|
|
||||||
;; integration of a procedure that hasn't been seen
|
(let lp ((clause clause))
|
||||||
;; yet.
|
(match clause
|
||||||
(log 'inline-begin exp)
|
;; No clause matches.
|
||||||
(let/ec k
|
(#f (residualize-call))
|
||||||
(define (abort)
|
(($ <lambda-case> src req opt rest kw inits gensyms body alt)
|
||||||
(log 'inline-abort exp)
|
(inline-clause req opt rest kw inits gensyms body
|
||||||
(k (make-call src (for-call orig-proc)
|
(lambda () (lp alt)))))))
|
||||||
(map for-value orig-args))))
|
|
||||||
(define new-counter
|
|
||||||
(cond
|
|
||||||
;; These first two cases will transfer effort
|
|
||||||
;; from the current counter into the new
|
|
||||||
;; counter.
|
|
||||||
((find-counter key counter)
|
|
||||||
=> (lambda (prev)
|
|
||||||
(make-recursive-counter recursive-effort-limit
|
|
||||||
operand-size-limit
|
|
||||||
prev counter)))
|
|
||||||
(counter
|
|
||||||
(make-nested-counter abort key counter))
|
|
||||||
;; This case opens a new account, effectively
|
|
||||||
;; printing money. It should only do so once
|
|
||||||
;; for each call site in the source program.
|
|
||||||
(else
|
|
||||||
(make-top-counter effort-limit operand-size-limit
|
|
||||||
abort key))))
|
|
||||||
(define result
|
|
||||||
(loop (inlined-call) env new-counter ctx))
|
|
||||||
|
|
||||||
(if counter
|
|
||||||
;; The nested inlining attempt succeeded.
|
|
||||||
;; Deposit the unspent effort and size back
|
|
||||||
;; into the current counter.
|
|
||||||
(transfer! new-counter counter))
|
|
||||||
|
|
||||||
(log 'inline-end result exp)
|
|
||||||
result)))))
|
|
||||||
(($ <lambda> src-proc meta orig-body)
|
|
||||||
;; If there are multiple cases and one matches nargs, omit all the others.
|
|
||||||
(or (and
|
|
||||||
orig-body
|
|
||||||
(lambda-case-alternate orig-body)
|
|
||||||
(let ((nargs (length orig-args)))
|
|
||||||
(let loop ((body orig-body))
|
|
||||||
(match body
|
|
||||||
(#f #f) ;; No matching case; an error.
|
|
||||||
(($ <lambda-case> src-case req opt rest kw inits gensyms case-body alt)
|
|
||||||
(cond (kw
|
|
||||||
;; FIXME: Not handling keyword cases.
|
|
||||||
#f)
|
|
||||||
((let ((nreq (length req)))
|
|
||||||
(if rest
|
|
||||||
(<= nreq nargs)
|
|
||||||
(<= nreq nargs (+ nreq (if opt (length opt) 0)))))
|
|
||||||
;; Keep only this case.
|
|
||||||
(revisit-proc
|
|
||||||
(make-lambda
|
|
||||||
src-proc meta
|
|
||||||
(make-lambda-case src-case req opt rest kw inits gensyms case-body #f))))
|
|
||||||
(else (loop alt))))))))
|
|
||||||
(make-call src (for-call orig-proc) (map for-value orig-args))))
|
|
||||||
(($ <let> _ _ _ vals _)
|
(($ <let> _ _ _ vals _)
|
||||||
;; Attempt to inline `let' in the operator position.
|
;; Attempt to inline `let' in the operator position.
|
||||||
;;
|
;;
|
||||||
|
@ -1747,10 +1849,10 @@ top-level bindings from ENV and return the resulting expression."
|
||||||
;; traverse through lambdas. In that case re-visit
|
;; traverse through lambdas. In that case re-visit
|
||||||
;; the procedure.
|
;; the procedure.
|
||||||
(proc (revisit-proc proc)))
|
(proc (revisit-proc proc)))
|
||||||
(make-call src (for-call orig-proc)
|
(residualize-call)))
|
||||||
(map for-value orig-args))))
|
|
||||||
(_
|
(_ (residualize-call)))))
|
||||||
(make-call src (for-call orig-proc) (map for-value orig-args))))))
|
|
||||||
(($ <lambda> src meta body)
|
(($ <lambda> src meta body)
|
||||||
(case ctx
|
(case ctx
|
||||||
((effect) (make-void #f))
|
((effect) (make-void #f))
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
;;;; tree-il.test --- test suite for compiling tree-il -*- scheme -*-
|
;;;; tree-il.test --- test suite for compiling tree-il -*- scheme -*-
|
||||||
;;;; Andy Wingo <wingo@pobox.com> --- May 2009
|
;;;; Andy Wingo <wingo@pobox.com> --- May 2009
|
||||||
;;;;
|
;;;;
|
||||||
;;;; Copyright (C) 2009-2014, 2017, 2020, 2022-2023 Free Software Foundation, Inc.
|
;;;; Copyright (C) 2009-2014, 2017, 2020, 2022-2024 Free Software Foundation, Inc.
|
||||||
;;;;
|
;;;;
|
||||||
;;;; This library is free software; you can redistribute it and/or
|
;;;; This library is free software; you can redistribute it and/or
|
||||||
;;;; modify it under the terms of the GNU Lesser General Public
|
;;;; modify it under the terms of the GNU Lesser General Public
|
||||||
|
@ -1523,10 +1523,14 @@
|
||||||
(const 0))
|
(const 0))
|
||||||
|
|
||||||
;; keyword cases survive
|
;; keyword cases survive
|
||||||
(pass-if (= 1 ((case-lambda* ((a b) 0) ((a #:key x) 1)) 0 #:x 1)))
|
(pass-if-peval ((case-lambda* ((a b) 0) ((a #:key x) 1)) 0 #:x 1)
|
||||||
(pass-if (= 0 ((case-lambda* ((a b c) 0) ((a #:key x) 1)) 0 #:x 1)))
|
(const 1))
|
||||||
(pass-if (= 0 ((case-lambda* ((a #:key x) 0) ((a b) 0)) 0 #:x 1)))
|
(pass-if-peval ((case-lambda* ((a b c) 0) ((a #:key x) 1)) 0 #:x 1)
|
||||||
(pass-if (= 1 ((case-lambda* ((a #:key x) 0) ((a b c) 1)) 0 1 2))))
|
(const 0))
|
||||||
|
(pass-if-peval ((case-lambda* ((a #:key x) 0) ((a b) 0)) 0 #:x 1)
|
||||||
|
(const 0))
|
||||||
|
(pass-if-peval ((case-lambda* ((a #:key x) 0) ((a b c) 1)) 0 1 2)
|
||||||
|
(const 1)))
|
||||||
|
|
||||||
(with-test-prefix "eqv?"
|
(with-test-prefix "eqv?"
|
||||||
(pass-if-peval (eqv? x #f)
|
(pass-if-peval (eqv? x #f)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue