This is of use when writing bindings for higher-level languages that
support exception-handling.

An example of using this is:
 
https://bitbucket.org/pypy/pypy/commits/6b48e7ef126a50f0bd181f59a827244e0b3e2a00
where I use it in my experimental PyPy backend in order to raise
meaningful RPython exceptions whenever I do something bogus that leads
to a NULL back from the libgccjit API.

This makes it *much* easier to track down problems when developing
client code that uses libgccjit.

Committed to trunk as r219363.

gcc/jit/ChangeLog:
        * docs/topics/contexts.rst (Error-handling): Document new
        entrypoint gcc_jit_context_get_last_error.
        * docs/_build/texinfo/libgccjit.texi: Regenerate.
        * jit-recording.c (gcc::jit::recording::context::context):
        Initialize new fields "m_last_error_str" and
        "m_owns_last_error_str".
        (gcc::jit::recording::context::~context): Clean up
        m_last_error_str, if needed.
        (gcc::jit::recording::context::add_error_va): Update
        m_last_error_str and m_owns_last_error_str, freeing the old
        value if appropriate.
        (gcc::jit::recording::context::get_last_error): New function.
        * jit-recording.h (gcc::jit::recording::context::get_last_error):
        New function.
        (gcc::jit::recording::context): New fields m_last_error_str and
        m_owns_last_error_str.
        * libgccjit.c (gcc_jit_context_get_last_error): New function.
        * libgccjit.h (gcc_jit_context_get_last_error): New declaration.
        * libgccjit.map (gcc_jit_context_get_last_error): New function.

gcc/testsuite/ChangeLog:
        * jit.dg/test-error-block-in-wrong-function.c (verify_code):
        Verify the result of gcc_jit_context_get_last_error.
        * jit.dg/test-error-null-passed-to-api.c (verify_code): Likewise.
---
 gcc/jit/docs/topics/contexts.rst                   | 18 ++++++++++++++-
 gcc/jit/jit-recording.c                            | 27 +++++++++++++++++++---
 gcc/jit/jit-recording.h                            |  6 +++++
 gcc/jit/libgccjit.c                                | 14 +++++++++++
 gcc/jit/libgccjit.h                                | 10 ++++++++
 gcc/jit/libgccjit.map                              |  1 +
 .../jit.dg/test-error-block-in-wrong-function.c    |  3 +++
 .../jit.dg/test-error-null-passed-to-api.c         |  2 ++
 8 files changed, 77 insertions(+), 4 deletions(-)

diff --git a/gcc/jit/docs/topics/contexts.rst b/gcc/jit/docs/topics/contexts.rst
index 6099c69..3dc313c 100644
--- a/gcc/jit/docs/topics/contexts.rst
+++ b/gcc/jit/docs/topics/contexts.rst
@@ -123,7 +123,9 @@ In general, if an error occurs when using an API 
entrypoint, the
 entrypoint returns NULL.  You don't have to check everywhere for NULL
 results, since the API handles a NULL being passed in for any
 argument by issuing another error.  This typically leads to a cascade of
-followup error messages, but is safe (albeit verbose).
+followup error messages, but is safe (albeit verbose).  The first error
+message is usually the one to pay attention to, since it is likely to
+be responsible for all of the rest:
 
 .. function:: const char *\
               gcc_jit_context_get_first_error (gcc_jit_context *ctxt)
@@ -135,6 +137,20 @@ followup error messages, but is safe (albeit verbose).
 
    If no errors occurred, this will be NULL.
 
+If you are wrapping the C API for a higher-level language that supports
+exception-handling, you may instead by interested in the last error that
+occurred on the context, so that you can embed this in an exception:
+
+.. function:: const char *\
+              gcc_jit_context_get_last_error (gcc_jit_context *ctxt)
+
+   Returns the last error message that occurred on the context.
+
+   The returned string is valid for the rest of the lifetime of the
+   context.
+
+   If no errors occurred, this will be NULL.
+
 Debugging
 ---------
 
diff --git a/gcc/jit/jit-recording.c b/gcc/jit/jit-recording.c
index a872063..a9ff300 100644
--- a/gcc/jit/jit-recording.c
+++ b/gcc/jit/jit-recording.c
@@ -175,6 +175,8 @@ recording::context::context (context *parent_ctxt)
     m_error_count (0),
     m_first_error_str (NULL),
     m_owns_first_error_str (false),
+    m_last_error_str (NULL),
+    m_owns_last_error_str (false),
     m_mementos (),
     m_compound_types (),
     m_functions (),
@@ -230,6 +232,10 @@ recording::context::~context ()
 
   if (m_owns_first_error_str)
     free (m_first_error_str);
+
+  if (m_owns_last_error_str)
+    if (m_last_error_str != m_first_error_str)
+      free (m_last_error_str);
 }
 
 /* Add the given mememto to the list of those tracked by this
@@ -984,9 +990,12 @@ recording::context::add_error_va (location *loc, const 
char *fmt, va_list ap)
       m_first_error_str = const_cast <char *> (errmsg);
       m_owns_first_error_str = has_ownership;
     }
-  else
-    if (has_ownership)
-      free (malloced_msg);
+
+  if (m_owns_last_error_str)
+    if (m_last_error_str != m_first_error_str)
+      free (m_last_error_str);
+  m_last_error_str = const_cast <char *> (errmsg);
+  m_owns_last_error_str = has_ownership;
 
   m_error_count++;
 }
@@ -1003,6 +1012,18 @@ recording::context::get_first_error () const
   return m_first_error_str;
 }
 
+/* Get the message for the last error that occurred on this context, or
+   NULL if no errors have occurred on it.
+
+   Implements the post-error-checking part of
+   gcc_jit_context_get_last_error.  */
+
+const char *
+recording::context::get_last_error () const
+{
+  return m_last_error_str;
+}
+
 /* Lazily generate and record a recording::type representing an opaque
    struct named "FILE".
 
diff --git a/gcc/jit/jit-recording.h b/gcc/jit/jit-recording.h
index dddf0db..9417993 100644
--- a/gcc/jit/jit-recording.h
+++ b/gcc/jit/jit-recording.h
@@ -235,6 +235,9 @@ public:
   const char *
   get_first_error () const;
 
+  const char *
+  get_last_error () const;
+
   bool errors_occurred () const
   {
     if (m_parent_ctxt)
@@ -261,6 +264,9 @@ private:
   char *m_first_error_str;
   bool m_owns_first_error_str;
 
+  char *m_last_error_str;
+  bool m_owns_last_error_str;
+
   char *m_str_options[GCC_JIT_NUM_STR_OPTIONS];
   int m_int_options[GCC_JIT_NUM_INT_OPTIONS];
   bool m_bool_options[GCC_JIT_NUM_BOOL_OPTIONS];
diff --git a/gcc/jit/libgccjit.c b/gcc/jit/libgccjit.c
index c043961..34f201e 100644
--- a/gcc/jit/libgccjit.c
+++ b/gcc/jit/libgccjit.c
@@ -2164,6 +2164,20 @@ gcc_jit_context_get_first_error (gcc_jit_context *ctxt)
 /* Public entrypoint.  See description in libgccjit.h.
 
    After error-checking, the real work is done by the
+   gcc::jit::recording::context::get_last_error method in
+   jit-recording.c.  */
+
+const char *
+gcc_jit_context_get_last_error (gcc_jit_context *ctxt)
+{
+  RETURN_NULL_IF_FAIL (ctxt, NULL, NULL, "NULL context");
+
+  return ctxt->get_last_error ();
+}
+
+/* Public entrypoint.  See description in libgccjit.h.
+
+   After error-checking, the real work is done by the
    gcc::jit::result::get_code method in jit-result.c.  */
 
 void *
diff --git a/gcc/jit/libgccjit.h b/gcc/jit/libgccjit.h
index 91ca409..953c665 100644
--- a/gcc/jit/libgccjit.h
+++ b/gcc/jit/libgccjit.h
@@ -279,6 +279,16 @@ gcc_jit_context_set_logfile (gcc_jit_context *ctxt,
 extern const char *
 gcc_jit_context_get_first_error (gcc_jit_context *ctxt);
 
+/* To be called after a compile, this gives the last error message
+   that occurred on the context.
+
+   The returned string is valid for the rest of the lifetime of the
+   context.
+
+   If no errors occurred, this will be NULL.  */
+extern const char *
+gcc_jit_context_get_last_error (gcc_jit_context *ctxt);
+
 /* Locate a given function within the built machine code.
    This will need to be cast to a function pointer of the
    correct type before it can be called. */
diff --git a/gcc/jit/libgccjit.map b/gcc/jit/libgccjit.map
index 267898c..dc2fa6f 100644
--- a/gcc/jit/libgccjit.map
+++ b/gcc/jit/libgccjit.map
@@ -36,6 +36,7 @@
     gcc_jit_context_enable_dump;
     gcc_jit_context_get_builtin_function;
     gcc_jit_context_get_first_error;
+    gcc_jit_context_get_last_error;
     gcc_jit_context_get_type;
     gcc_jit_context_get_int_type;
     gcc_jit_context_new_array_access;
diff --git a/gcc/testsuite/jit.dg/test-error-block-in-wrong-function.c 
b/gcc/testsuite/jit.dg/test-error-block-in-wrong-function.c
index b7342a3..285fcb0 100644
--- a/gcc/testsuite/jit.dg/test-error-block-in-wrong-function.c
+++ b/gcc/testsuite/jit.dg/test-error-block-in-wrong-function.c
@@ -62,4 +62,7 @@ verify_code (gcc_jit_context *ctxt, gcc_jit_result *result)
                      " source block initial is in function test_fn"
                      " whereas target block block_within_other_fn"
                      " is in function other_fn");
+  /* Example of a testcase in which the last error != first error.  */
+  CHECK_STRING_VALUE (gcc_jit_context_get_last_error (ctxt),
+                     "unterminated block in other_fn: block_within_other_fn");
 }
diff --git a/gcc/testsuite/jit.dg/test-error-null-passed-to-api.c 
b/gcc/testsuite/jit.dg/test-error-null-passed-to-api.c
index ea4390b..2fab453 100644
--- a/gcc/testsuite/jit.dg/test-error-null-passed-to-api.c
+++ b/gcc/testsuite/jit.dg/test-error-null-passed-to-api.c
@@ -27,5 +27,7 @@ verify_code (gcc_jit_context *ctxt, gcc_jit_result *result)
   /* Verify that the correct error message was emitted.  */
   CHECK_STRING_VALUE (gcc_jit_context_get_first_error (ctxt),
                      "gcc_jit_context_new_function: NULL return_type");
+  CHECK_STRING_VALUE (gcc_jit_context_get_last_error (ctxt),
+                     "gcc_jit_context_new_function: NULL return_type");
 }
 
-- 
1.8.5.3

Reply via email to