mirror of
https://git.savannah.gnu.org/git/guile.git
synced 2025-06-17 09:10:22 +02:00
abort always dispatches to VM bytecode, to detect same-invocation aborts
* libguile/control.h: * libguile/control.c (scm_c_make_prompt): Take an extra arg, a cookie. Continuations will be rewindable only if the abort has the same cookie as the prompt. (scm_at_abort): Redefine from scm_abort, and instead of taking rest args, take the abort values as a list directly. Also, don't allow rewinding, because we won't support rewinding the C stack with delimited continuations. * libguile/eval.c (eval): Adapt to scm_c_make_prompt change. * libguile/vm-engine.c (vm_engine): Use vp->cookie to get a unique value corresponding to this VM invocation. * libguile/vm-i-system.c (prompt): Pass the cookie to scm_c_make_prompt. (abort): Take an additional tail arg. * libguile/vm.c (vm_abort): Parse out the abort tail arg. This is for the @abort case, or the (apply abort ...) case. (make_vm): Initialize the cookie to 0. * libguile/vm.h (struct scm_vm): Add cookie. * module/ice-9/boot-9.scm (abort): Define here as a trampoline to @abort. Needed to make sure that a call to abort dispatches to a VM opcode, so the cookie will be the same. * module/language/tree-il.scm (<tree-il>): Add a "tail" field to <abort>, for the (apply abort ...) case, or (@abort tag args). Should be #<const ()> in the normal case. Add support throughout. * module/language/tree-il/analyze.scm (analyze-lexicals): Add abort-tail support here too. * module/language/tree-il/compile-glil.scm (flatten): Compile the tail argument appropriately. * module/language/tree-il/primitives.scm (*primitive-expand-table*): Fix @abort and abort cases to pass the tail arg to make-abort.
This commit is contained in:
parent
f828ab4f30
commit
2d026f04cc
12 changed files with 71 additions and 50 deletions
|
@ -27,7 +27,8 @@
|
||||||
|
|
||||||
|
|
||||||
SCM
|
SCM
|
||||||
scm_c_make_prompt (SCM vm, SCM k, scm_t_uint8 escape_only_p)
|
scm_c_make_prompt (SCM vm, SCM k, scm_t_uint8 escape_only_p,
|
||||||
|
scm_t_int64 vm_cookie)
|
||||||
{
|
{
|
||||||
scm_t_bits tag;
|
scm_t_bits tag;
|
||||||
SCM ret;
|
SCM ret;
|
||||||
|
@ -42,6 +43,7 @@ scm_c_make_prompt (SCM vm, SCM k, scm_t_uint8 escape_only_p)
|
||||||
regs->fp = SCM_VM_DATA (vm)->fp;
|
regs->fp = SCM_VM_DATA (vm)->fp;
|
||||||
regs->sp = SCM_VM_DATA (vm)->sp;
|
regs->sp = SCM_VM_DATA (vm)->sp;
|
||||||
regs->ip = SCM_VM_DATA (vm)->ip;
|
regs->ip = SCM_VM_DATA (vm)->ip;
|
||||||
|
regs->cookie = vm_cookie;
|
||||||
|
|
||||||
SCM_SET_CELL_OBJECT (ret, 1, k);
|
SCM_SET_CELL_OBJECT (ret, 1, k);
|
||||||
SCM_SET_CELL_WORD (ret, 2, (scm_t_bits)regs);
|
SCM_SET_CELL_WORD (ret, 2, (scm_t_bits)regs);
|
||||||
|
@ -109,9 +111,9 @@ scm_c_abort (SCM vm, SCM tag, size_t n, SCM *argv)
|
||||||
abort ();
|
abort ();
|
||||||
}
|
}
|
||||||
|
|
||||||
SCM_DEFINE (scm_abort, "abort", 1, 0, 1, (SCM tag, SCM args),
|
SCM_DEFINE (scm_at_abort, "@abort", 2, 0, 0, (SCM tag, SCM args),
|
||||||
"Abort to the nearest prompt with tag @var{tag}.")
|
"Abort to the nearest prompt with tag @var{tag}.")
|
||||||
#define FUNC_NAME s_scm_abort
|
#define FUNC_NAME s_scm_at_abort
|
||||||
{
|
{
|
||||||
SCM *argv;
|
SCM *argv;
|
||||||
size_t i, n;
|
size_t i, n;
|
||||||
|
@ -123,23 +125,11 @@ SCM_DEFINE (scm_abort, "abort", 1, 0, 1, (SCM tag, SCM args),
|
||||||
|
|
||||||
scm_c_abort (scm_the_vm (), tag, n, argv);
|
scm_c_abort (scm_the_vm (), tag, n, argv);
|
||||||
|
|
||||||
/* Oh, what, you're still here? The abort must have been reinstated. OK, pull
|
/* Oh, what, you're still here? The abort must have been reinstated. Actually,
|
||||||
args back from the stack, and keep going... */
|
that's quite impossible, given that we're already in C-land here, so...
|
||||||
|
abort! */
|
||||||
|
|
||||||
{
|
abort ();
|
||||||
SCM vals = SCM_EOL;
|
|
||||||
struct scm_vm *vp = SCM_VM_DATA (scm_the_vm ());
|
|
||||||
n = scm_to_size_t (vp->sp[0]);
|
|
||||||
for (i = 0; i < n; i++)
|
|
||||||
vals = scm_cons (vp->sp[-(i + 1)], vals);
|
|
||||||
/* The continuation call did reset the VM's registers, but then these values
|
|
||||||
were pushed on; so we need to pop them ourselves. */
|
|
||||||
vp->sp -= n + 1;
|
|
||||||
/* FIXME NULLSTACK */
|
|
||||||
|
|
||||||
return (scm_is_pair (vals) && scm_is_null (scm_cdr (vals)))
|
|
||||||
? scm_car (vals) : scm_values (vals);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#undef FUNC_NAME
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
|
|
@ -37,12 +37,15 @@ struct scm_prompt_registers
|
||||||
scm_t_uint8 *ip;
|
scm_t_uint8 *ip;
|
||||||
SCM *sp;
|
SCM *sp;
|
||||||
SCM *fp;
|
SCM *fp;
|
||||||
|
scm_t_int64 cookie;
|
||||||
scm_i_jmp_buf regs;
|
scm_i_jmp_buf regs;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
SCM_INTERNAL SCM scm_c_make_prompt (SCM vm, SCM k, scm_t_uint8 escape_only_p);
|
SCM_INTERNAL SCM scm_c_make_prompt (SCM vm, SCM k, scm_t_uint8 escape_only_p,
|
||||||
|
scm_t_int64 cookie);
|
||||||
SCM_INTERNAL SCM scm_c_abort (SCM vm, SCM tag, size_t n, SCM *argv) SCM_NORETURN;
|
SCM_INTERNAL SCM scm_c_abort (SCM vm, SCM tag, size_t n, SCM *argv) SCM_NORETURN;
|
||||||
|
SCM_INTERNAL SCM scm_at_abort (SCM tag, SCM args) SCM_NORETURN;
|
||||||
|
|
||||||
|
|
||||||
SCM_INTERNAL void scm_init_control (void);
|
SCM_INTERNAL void scm_init_control (void);
|
||||||
|
|
|
@ -429,7 +429,7 @@ eval (SCM x, SCM env)
|
||||||
{
|
{
|
||||||
SCM prompt, handler, res;
|
SCM prompt, handler, res;
|
||||||
|
|
||||||
prompt = scm_c_make_prompt (scm_the_vm (), eval (CAR (mx), env), 0);
|
prompt = scm_c_make_prompt (scm_the_vm (), eval (CAR (mx), env), 0, -1);
|
||||||
handler = eval (CDDR (mx), env);
|
handler = eval (CDDR (mx), env);
|
||||||
scm_i_set_dynwinds (scm_cons (prompt, scm_i_dynwinds ()));
|
scm_i_set_dynwinds (scm_cons (prompt, scm_i_dynwinds ()));
|
||||||
|
|
||||||
|
|
|
@ -47,7 +47,9 @@ VM_NAME (SCM vm, SCM program, SCM *argv, int nargs)
|
||||||
SCM *objects = NULL; /* constant objects */
|
SCM *objects = NULL; /* constant objects */
|
||||||
size_t object_count = 0; /* length of OBJECTS */
|
size_t object_count = 0; /* length of OBJECTS */
|
||||||
SCM *stack_limit = vp->stack_limit; /* stack limit address */
|
SCM *stack_limit = vp->stack_limit; /* stack limit address */
|
||||||
|
|
||||||
SCM dynstate = SCM_I_CURRENT_THREAD->dynamic_state;
|
SCM dynstate = SCM_I_CURRENT_THREAD->dynamic_state;
|
||||||
|
scm_t_int64 vm_cookie = vp->cookie++;
|
||||||
|
|
||||||
/* Internal variables */
|
/* Internal variables */
|
||||||
int nvalues = 0;
|
int nvalues = 0;
|
||||||
|
|
|
@ -1464,7 +1464,7 @@ VM_DEFINE_INSTRUCTION (83, prompt, "prompt", 4, 2, 0)
|
||||||
/* Push the prompt onto the dynamic stack. The setjmp itself has to be local
|
/* Push the prompt onto the dynamic stack. The setjmp itself has to be local
|
||||||
to this procedure. */
|
to this procedure. */
|
||||||
/* FIXME: do more error checking */
|
/* FIXME: do more error checking */
|
||||||
prompt = scm_c_make_prompt (vm, k, escape_only_p);
|
prompt = scm_c_make_prompt (vm, k, escape_only_p, vm_cookie);
|
||||||
scm_i_set_dynwinds (scm_cons (prompt, scm_i_dynwinds ()));
|
scm_i_set_dynwinds (scm_cons (prompt, scm_i_dynwinds ()));
|
||||||
if (SCM_PROMPT_SETJMP (prompt))
|
if (SCM_PROMPT_SETJMP (prompt))
|
||||||
{
|
{
|
||||||
|
@ -1509,7 +1509,7 @@ VM_DEFINE_INSTRUCTION (86, abort, "abort", 1, -1, -1)
|
||||||
{
|
{
|
||||||
unsigned n = FETCH ();
|
unsigned n = FETCH ();
|
||||||
SYNC_REGISTER ();
|
SYNC_REGISTER ();
|
||||||
if (sp - n - 1 <= SCM_FRAME_UPPER_ADDRESS (fp))
|
if (sp - n - 2 <= SCM_FRAME_UPPER_ADDRESS (fp))
|
||||||
goto vm_error_stack_underflow;
|
goto vm_error_stack_underflow;
|
||||||
vm_abort (vm, n);
|
vm_abort (vm, n);
|
||||||
/* vm_abort should not return */
|
/* vm_abort should not return */
|
||||||
|
|
|
@ -206,15 +206,25 @@ static void
|
||||||
vm_abort (SCM vm, size_t n)
|
vm_abort (SCM vm, size_t n)
|
||||||
{
|
{
|
||||||
size_t i;
|
size_t i;
|
||||||
SCM tag, *argv;
|
ssize_t tail_len;
|
||||||
|
SCM tag, tail, *argv;
|
||||||
|
|
||||||
|
/* FIXME: VM_ENABLE_STACK_NULLING */
|
||||||
|
tail = *(SCM_VM_DATA (vm)->sp--);
|
||||||
|
/* NULLSTACK (1) */
|
||||||
|
tail_len = scm_ilength (tail);
|
||||||
|
if (tail_len < 0)
|
||||||
|
abort ();
|
||||||
tag = SCM_VM_DATA (vm)->sp[-n];
|
tag = SCM_VM_DATA (vm)->sp[-n];
|
||||||
argv = alloca (n * sizeof (SCM));
|
argv = alloca ((n + tail_len) * sizeof (SCM));
|
||||||
for (i = 0; i < n; i++)
|
for (i = 0; i < n; i++)
|
||||||
argv[i] = SCM_VM_DATA (vm)->sp[-(n-1-i)];
|
argv[i] = SCM_VM_DATA (vm)->sp[-(n-1-i)];
|
||||||
|
for (; i < n + tail_len; i++, tail = scm_cdr (tail))
|
||||||
|
argv[i] = scm_car (tail);
|
||||||
|
/* NULLSTACK (n + 1) */
|
||||||
SCM_VM_DATA (vm)->sp -= n + 1;
|
SCM_VM_DATA (vm)->sp -= n + 1;
|
||||||
|
|
||||||
scm_c_abort (vm, tag, n, argv);
|
scm_c_abort (vm, tag, n + tail_len, argv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -386,6 +396,7 @@ make_vm (void)
|
||||||
vp->trace_level = 0;
|
vp->trace_level = 0;
|
||||||
for (i = 0; i < SCM_VM_NUM_HOOKS; i++)
|
for (i = 0; i < SCM_VM_NUM_HOOKS; i++)
|
||||||
vp->hooks[i] = SCM_BOOL_F;
|
vp->hooks[i] = SCM_BOOL_F;
|
||||||
|
vp->cookie = 0;
|
||||||
return scm_cell (scm_tc7_vm, (scm_t_bits)vp);
|
return scm_cell (scm_tc7_vm, (scm_t_bits)vp);
|
||||||
}
|
}
|
||||||
#undef FUNC_NAME
|
#undef FUNC_NAME
|
||||||
|
|
|
@ -51,6 +51,7 @@ struct scm_vm {
|
||||||
SCM hooks[SCM_VM_NUM_HOOKS]; /* hooks */
|
SCM hooks[SCM_VM_NUM_HOOKS]; /* hooks */
|
||||||
SCM options; /* options */
|
SCM options; /* options */
|
||||||
int trace_level; /* traces enabled if trace_level > 0 */
|
int trace_level; /* traces enabled if trace_level > 0 */
|
||||||
|
scm_t_int64 cookie; /* used to detect unrewindable continuations */
|
||||||
};
|
};
|
||||||
|
|
||||||
SCM_API SCM scm_the_vm_fluid;
|
SCM_API SCM scm_the_vm_fluid;
|
||||||
|
|
|
@ -404,6 +404,8 @@
|
||||||
;;; Delimited continuations
|
;;; Delimited continuations
|
||||||
(define (prompt tag thunk handler)
|
(define (prompt tag thunk handler)
|
||||||
(@prompt tag (thunk) handler))
|
(@prompt tag (thunk) handler))
|
||||||
|
(define (abort tag . args)
|
||||||
|
(@abort tag args))
|
||||||
|
|
||||||
;;; apply-to-args is functionally redundant with apply and, worse,
|
;;; apply-to-args is functionally redundant with apply and, worse,
|
||||||
;;; is less general than apply since it only takes two arguments.
|
;;; is less general than apply since it only takes two arguments.
|
||||||
|
|
|
@ -50,7 +50,7 @@
|
||||||
<dynref> dynref? make-dynref dynref-src dynref-fluid
|
<dynref> dynref? make-dynref dynref-src dynref-fluid
|
||||||
<dynset> dynset? make-dynset dynset-src dynset-fluid dynset-exp
|
<dynset> dynset? make-dynset dynset-src dynset-fluid dynset-exp
|
||||||
<prompt> prompt? make-prompt prompt-src prompt-tag prompt-body prompt-handler
|
<prompt> prompt? make-prompt prompt-src prompt-tag prompt-body prompt-handler
|
||||||
<abort> abort? make-abort abort-src abort-tag abort-args
|
<abort> abort? make-abort abort-src abort-tag abort-args abort-tail
|
||||||
|
|
||||||
parse-tree-il
|
parse-tree-il
|
||||||
unparse-tree-il
|
unparse-tree-il
|
||||||
|
@ -86,7 +86,7 @@
|
||||||
(<dynref> fluid)
|
(<dynref> fluid)
|
||||||
(<dynset> fluid exp)
|
(<dynset> fluid exp)
|
||||||
(<prompt> tag body handler)
|
(<prompt> tag body handler)
|
||||||
(<abort> tag args))
|
(<abort> tag args tail))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -192,8 +192,8 @@
|
||||||
((prompt ,tag ,body ,handler)
|
((prompt ,tag ,body ,handler)
|
||||||
(make-prompt loc (retrans tag) (retrans body) (retrans handler)))
|
(make-prompt loc (retrans tag) (retrans body) (retrans handler)))
|
||||||
|
|
||||||
((abort ,tag ,type ,args)
|
((abort ,tag ,args ,tail)
|
||||||
(make-abort loc (retrans tag) type (map retrans args)))
|
(make-abort loc (retrans tag) (map retrans args) (retrans tail)))
|
||||||
|
|
||||||
(else
|
(else
|
||||||
(error "unrecognized tree-il" exp)))))
|
(error "unrecognized tree-il" exp)))))
|
||||||
|
@ -276,8 +276,9 @@
|
||||||
((<prompt> tag body handler)
|
((<prompt> tag body handler)
|
||||||
`(prompt ,tag ,(unparse-tree-il body) ,(unparse-tree-il handler)))
|
`(prompt ,tag ,(unparse-tree-il body) ,(unparse-tree-il handler)))
|
||||||
|
|
||||||
((<abort> tag args)
|
((<abort> tag args tail)
|
||||||
`(abort ,(unparse-tree-il tag) ,(map unparse-tree-il args)))))
|
`(abort ,(unparse-tree-il tag) ,(map unparse-tree-il args)
|
||||||
|
,(unparse-tree-il tail)))))
|
||||||
|
|
||||||
(define (tree-il->scheme e)
|
(define (tree-il->scheme e)
|
||||||
(record-case e
|
(record-case e
|
||||||
|
@ -374,8 +375,9 @@
|
||||||
,(tree-il->scheme handler)))
|
,(tree-il->scheme handler)))
|
||||||
|
|
||||||
|
|
||||||
((<abort> tag args)
|
((<abort> tag args tail)
|
||||||
`(@abort ,(tree-il->scheme tag) ,@(map tree-il->scheme args)))))
|
`(apply abort ,(tree-il->scheme tag) ,@(map tree-il->scheme args)
|
||||||
|
,(tree-il->scheme tail)))))
|
||||||
|
|
||||||
|
|
||||||
(define (tree-il-fold leaf down up seed tree)
|
(define (tree-il-fold leaf down up seed tree)
|
||||||
|
@ -444,8 +446,8 @@ This is an implementation of `foldts' as described by Andy Wingo in
|
||||||
(up tree
|
(up tree
|
||||||
(loop tag (loop body (loop handler
|
(loop tag (loop body (loop handler
|
||||||
(down tree result))))))
|
(down tree result))))))
|
||||||
((<abort> tag args)
|
((<abort> tag args tail)
|
||||||
(up tree (loop tag (loop args (down tree result)))))
|
(up tree (loop tail (loop args (loop tag (down tree result))))))
|
||||||
(else
|
(else
|
||||||
(leaf tree result))))))
|
(leaf tree result))))))
|
||||||
|
|
||||||
|
@ -518,9 +520,10 @@ This is an implementation of `foldts' as described by Andy Wingo in
|
||||||
(let*-values (((seed ...) (foldts tag seed ...))
|
(let*-values (((seed ...) (foldts tag seed ...))
|
||||||
((seed ...) (foldts body seed ...)))
|
((seed ...) (foldts body seed ...)))
|
||||||
(foldts handler seed ...)))
|
(foldts handler seed ...)))
|
||||||
((<abort> tag args)
|
((<abort> tag args tail)
|
||||||
(let*-values (((seed ...) (foldts tag seed ...)))
|
(let*-values (((seed ...) (foldts tag seed ...))
|
||||||
(fold-values foldts args seed ...)))
|
((seed ...) (fold-values foldts args seed ...)))
|
||||||
|
(foldts tail seed ...)))
|
||||||
(else
|
(else
|
||||||
(values seed ...)))))
|
(values seed ...)))))
|
||||||
(up tree seed ...)))))))
|
(up tree seed ...)))))))
|
||||||
|
@ -599,9 +602,10 @@ This is an implementation of `foldts' as described by Andy Wingo in
|
||||||
(set! (prompt-body x) (lp body))
|
(set! (prompt-body x) (lp body))
|
||||||
(set! (prompt-handler x) (lp handler)))
|
(set! (prompt-handler x) (lp handler)))
|
||||||
|
|
||||||
((<abort> tag args)
|
((<abort> tag args tail)
|
||||||
(set! (abort-tag x) (lp tag))
|
(set! (abort-tag x) (lp tag))
|
||||||
(set! (abort-args x) (map lp args)))
|
(set! (abort-args x) (map lp args))
|
||||||
|
(set! (abort-tail x) (lp tail)))
|
||||||
|
|
||||||
(else #f))
|
(else #f))
|
||||||
|
|
||||||
|
@ -681,9 +685,10 @@ This is an implementation of `foldts' as described by Andy Wingo in
|
||||||
(set! (prompt-body x) (lp body))
|
(set! (prompt-body x) (lp body))
|
||||||
(set! (prompt-handler x) (lp handler)))
|
(set! (prompt-handler x) (lp handler)))
|
||||||
|
|
||||||
((<abort> tag args)
|
((<abort> tag args tail)
|
||||||
(set! (abort-tag x) (lp tag))
|
(set! (abort-tag x) (lp tag))
|
||||||
(set! (abort-args x) (map lp args)))
|
(set! (abort-args x) (map lp args))
|
||||||
|
(set! (abort-tail x) (lp tail)))
|
||||||
|
|
||||||
(else #f))
|
(else #f))
|
||||||
x)))
|
x)))
|
||||||
|
|
|
@ -351,8 +351,8 @@
|
||||||
((<prompt> tag body handler)
|
((<prompt> tag body handler)
|
||||||
(lset-union eq? (step tag) (step body) (step handler)))
|
(lset-union eq? (step tag) (step body) (step handler)))
|
||||||
|
|
||||||
((<abort> tag args)
|
((<abort> tag args tail)
|
||||||
(apply lset-union eq? (step tag) (map step args)))
|
(apply lset-union eq? (step tag) (step tail) (map step args)))
|
||||||
|
|
||||||
(else '())))
|
(else '())))
|
||||||
|
|
||||||
|
@ -525,8 +525,8 @@
|
||||||
(and cont-var (zero? (hashq-ref refcounts cont-var 0))))
|
(and cont-var (zero? (hashq-ref refcounts cont-var 0))))
|
||||||
(max (recur tag) (recur body) (recur handler))))
|
(max (recur tag) (recur body) (recur handler))))
|
||||||
|
|
||||||
((<abort> tag args)
|
((<abort> tag args tail)
|
||||||
(apply max (recur tag) (map recur args)))
|
(apply max (recur tag) (recur tail) (map recur args)))
|
||||||
|
|
||||||
(else n)))
|
(else n)))
|
||||||
|
|
||||||
|
|
|
@ -1111,9 +1111,10 @@
|
||||||
(and (eq? context 'drop) (not RA)))
|
(and (eq? context 'drop) (not RA)))
|
||||||
(emit-label POST))))
|
(emit-label POST))))
|
||||||
|
|
||||||
((<abort> src tag args)
|
((<abort> src tag args tail)
|
||||||
(comp-push tag)
|
(comp-push tag)
|
||||||
(for-each comp-push args)
|
(for-each comp-push args)
|
||||||
|
(comp-push tail)
|
||||||
(emit-code src (make-glil-call 'abort (length args)))
|
(emit-code src (make-glil-call 'abort (length args)))
|
||||||
;; so, the abort can actually return. if it does, the values will be on
|
;; so, the abort can actually return. if it does, the values will be on
|
||||||
;; the stack, then the MV marker, just as in an MV context.
|
;; the stack, then the MV marker, just as in an MV context.
|
||||||
|
|
|
@ -63,7 +63,7 @@
|
||||||
|
|
||||||
fluid-ref fluid-set!
|
fluid-ref fluid-set!
|
||||||
|
|
||||||
@prompt prompt abort
|
@prompt prompt @abort abort
|
||||||
|
|
||||||
struct? struct-vtable make-struct struct-ref struct-set!
|
struct? struct-vtable make-struct struct-ref struct-set!
|
||||||
|
|
||||||
|
@ -475,9 +475,15 @@
|
||||||
(else #f)))
|
(else #f)))
|
||||||
(else #f)))
|
(else #f)))
|
||||||
|
|
||||||
|
(hashq-set! *primitive-expand-table*
|
||||||
|
'@abort
|
||||||
|
(case-lambda
|
||||||
|
((src tag tail-args)
|
||||||
|
(make-abort src tag '() tail-args))
|
||||||
|
(else #f)))
|
||||||
(hashq-set! *primitive-expand-table*
|
(hashq-set! *primitive-expand-table*
|
||||||
'abort
|
'abort
|
||||||
(case-lambda
|
(case-lambda
|
||||||
((src tag . args)
|
((src tag . args)
|
||||||
(make-abort src tag args))
|
(make-abort src tag args (make-const #f '())))
|
||||||
(else #f)))
|
(else #f)))
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue