Subject: [bug#40016] [PATCH] inferior: Distinguish inferior exceptions.

From: Ludovic Courtès <[email-masked]>
Date: Tue, 10 Mar 2020 17:03:05 +0100

This avoids ambiguities when looking at a backtrace where the exception
was actually thrown by an inferior in a very different context.

* guix/inferior.scm (&inferior-exception): New condition type.
(read-repl-response): Add optional 'inferior' parameter.  Raise
'&inferior-exception' instead of rethrowing to KEY when receiving an
'exception' message.
(read-inferior-response): Pass INFERIOR to 'read-repl-response'.
* tests/inferior.scm ("&inferior-exception"): New test.
---
 guix/inferior.scm  | 21 +++++++++++++++++----
 tests/inferior.scm | 13 ++++++++++++-
 2 files changed, 29 insertions(+), 5 deletions(-)

diff --git a/guix/inferior.scm b/guix/inferior.scm
index 0236fb61ad..6b685ece30 100644
--- a/guix/inferior.scm
+++ b/guix/inferior.scm
@@ -63,6 +63,9 @@
             inferior-eval
             inferior-eval-with-store
             inferior-object?
+            inferior-exception?
+            inferior-exception-arguments
+            inferior-exception-inferior
             read-repl-response
 
             inferior-packages
@@ -195,8 +198,15 @@ equivalent.  Return #f if the inferior could not be launched."
 
 (set-record-type-printer! <inferior-object> write-inferior-object)
 
-(define (read-repl-response port)
-  "Read a (guix repl) response from PORT and return it as a Scheme object."
+;; Reified exception thrown by an inferior.
+(define-condition-type &inferior-exception &error
+  inferior-exception?
+  (arguments  inferior-exception-arguments)       ;key + arguments
+  (inferior   inferior-exception-inferior))       ;<inferior> | #f
+
+(define* (read-repl-response port #:optional inferior)
+  "Read a (guix repl) response from PORT and return it as a Scheme object.
+Raise '&inferior-exception' when an exception is read from PORT."
   (define sexp->object
     (match-lambda
       (('value value)
@@ -208,10 +218,13 @@ equivalent.  Return #f if the inferior could not be launched."
     (('values objects ...)
      (apply values (map sexp->object objects)))
     (('exception key objects ...)
-     (apply throw key (map sexp->object objects)))))
+     (raise (condition (&inferior-exception
+                        (arguments (cons key (map sexp->object objects)))
+                        (inferior inferior)))))))
 
 (define (read-inferior-response inferior)
-  (read-repl-response (inferior-socket inferior)))
+  (read-repl-response (inferior-socket inferior)
+                      inferior))
 
 (define (send-inferior-request exp inferior)
   (write exp (inferior-socket inferior))
diff --git a/tests/inferior.scm b/tests/inferior.scm
index f54b6d6037..b4417d8629 100644
--- a/tests/inferior.scm
+++ b/tests/inferior.scm
@@ -1,5 +1,5 @@
 ;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2018, 2019 Ludovic Courtès <[email-masked]>
+;;; Copyright © 2018, 2019, 2020 Ludovic Courtès <[email-masked]>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -61,6 +61,17 @@
            (close-inferior inferior)
            (list a (inferior-object? b))))))
 
+(test-equal "&inferior-exception"
+  '(a b c d)
+  (let ((inferior (open-inferior %top-builddir
+                                 #:command "scripts/guix")))
+    (guard (c ((inferior-exception? c)
+               (close-inferior inferior)
+               (and (eq? inferior (inferior-exception-inferior c))
+                    (inferior-exception-arguments c))))
+      (inferior-eval '(throw 'a 'b 'c 'd) inferior)
+      'badness)))
+
 (test-equal "inferior-packages"
   (take (sort (fold-packages (lambda (package lst)
                                (cons (list (package-name package)
-- 
2.25.1