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