Skip to content

Commit 04ac810

Browse files
devs6186claude
andcommitted
fix(metacall): implement metacall_error_last/clear with thread-local store
metacall_error_last() and metacall_error_clear() were stubs that returned 1 and did nothing. Any caller that checked the last error after a failed metacall received no information, making the published API unusable. Implement a thread-local value* (metacall_error_last_v) in metacall_error.c using the PORTABILITY_THREAD_LOCAL macro so the per-thread last-error slot works on GCC/Clang (__thread) and MSVC (__declspec(thread)) without changes to the calling code. metacall_error_last() now delegates to metacall_error_from_value() on the stored copy. metacall_error_clear() destroys the stored value and resets the slot. metacall_error_set_last() is the new internal entry point that saves a deep copy (via value_type_copy, which increments the exception reference count) so the caller retains ownership of the original. In metacallfv_s, after function_call returns, detect TYPE_THROWABLE via type_id_throwable() and call metacall_error_set_last(). This covers the common call path reached by metacallv/metacallv_s/metacallfv/metacallfv_s. The copy approach means the returned value and the internal slot are independent; destroying one does not invalidate the other. The existing metacall_python_exception_test already exercises metacall_error_from_value() directly. With this change, metacall_error_last() becomes available as an errno-style alternative for callers that do not inspect return values. Fixes #141 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 44c62f9 commit 04ac810

3 files changed

Lines changed: 48 additions & 4 deletions

File tree

source/metacall/include/metacall/metacall_error.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,18 @@ METACALL_API int metacall_error_last(metacall_exception ex);
106106
*/
107107
METACALL_API void metacall_error_clear(void);
108108

109+
/**
110+
* @brief
111+
* Record a throwable value as the last error for the calling thread. Called
112+
* internally by metacallfv_s when a loader returns a TYPE_THROWABLE value.
113+
* May also be called by ports or loaders that want to set the last error
114+
* directly. Copies @v so the caller retains ownership of the original value.
115+
*
116+
* @param[in] v
117+
* Throwable value to record; must be of type TYPE_THROWABLE
118+
*/
119+
METACALL_API void metacall_error_set_last(void *v);
120+
109121
#ifdef __cplusplus
110122
}
111123
#endif

source/metacall/source/metacall.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1144,6 +1144,11 @@ void *metacallfv_s(void *func, void *args[], size_t size)
11441144

11451145
ret = function_call(f, args, size);
11461146

1147+
if (ret != NULL && type_id_throwable(value_type_id(ret)) == 0)
1148+
{
1149+
metacall_error_set_last(ret);
1150+
}
1151+
11471152
if (ret != NULL)
11481153
{
11491154
type t = signature_get_return(s);

source/metacall/source/metacall_error.c

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,14 @@
2424

2525
#include <reflect/reflect_value_type.h>
2626

27+
#include <portability/portability_compiler.h>
28+
2729
#include <log/log.h>
2830

31+
/* -- Private Variables -- */
32+
33+
static PORTABILITY_THREAD_LOCAL value metacall_error_last_v = NULL;
34+
2935
/* -- Methods -- */
3036

3137
void *metacall_error_throw(const char *label, int64_t code, const char *stacktrace, const char *message, ...)
@@ -92,13 +98,34 @@ int metacall_error_from_value(void *v, metacall_exception ex)
9298

9399
int metacall_error_last(metacall_exception ex)
94100
{
95-
// TODO
96-
(void)ex;
101+
if (metacall_error_last_v == NULL)
102+
{
103+
return 1;
104+
}
97105

98-
return 1;
106+
return metacall_error_from_value(metacall_error_last_v, ex);
99107
}
100108

101109
void metacall_error_clear(void)
102110
{
103-
// TODO
111+
if (metacall_error_last_v != NULL)
112+
{
113+
value_type_destroy(metacall_error_last_v);
114+
metacall_error_last_v = NULL;
115+
}
116+
}
117+
118+
void metacall_error_set_last(void *v)
119+
{
120+
if (v == NULL || type_id_throwable(value_type_id((value)v)) != 0)
121+
{
122+
return;
123+
}
124+
125+
if (metacall_error_last_v != NULL)
126+
{
127+
value_type_destroy(metacall_error_last_v);
128+
}
129+
130+
metacall_error_last_v = value_type_copy((value)v);
104131
}

0 commit comments

Comments
 (0)