Serene Runtime 1.0.0-dev
C runtime for the Serene programming language
Loading...
Searching...
No Matches
errors.h
Go to the documentation of this file.
1/* -*- C -*-
2 * Serene programming language
3 * Copyright (C) 2019-2026 Sameer Rahmani <lxsameer@lxsameer.com>
4 *
5 * This library is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with this library. If not, see <https://www.gnu.org/licenses/>.
17 */
18
19/** @file
20
21 Error handling for the runtime.
22
23 An `srn_error_t` pairs a `srn_error_tag_t`, naming the kind of failure, with a
24 human-readable message, and is allocated from a `srn_context_t`. Fallible
25 operations report failures as these explicit error values rather than through
26 return codes or out-of-band state, so an error travels with the data it
27 describes. When the Serene language layer is built, an error can also be lifted
28 into a `srn_value_t` so the failure is representable inside the language
29 itself.
30
31 ## IMPORTANT NOTE: ##
32
33 By Convention, all the data structures that in anyway may, interacting with
34 them may result in an error, must use the ERROR_HEADER macro as the first
35 member. For example:
36
37 ```c
38 typedef struct foo {
39 ERROR_HEADER
40 bar other;
41 ...
42 } foo;
43 ```
44 Following this convention let you use the error handling helpers from this
45 header uniformly across the runtime library.
46*/
47
48#pragma once
49
50/**
51 * The first member every fallible data structure embeds (see the file note
52 * above). Holds the error attached to a value, or null when the value is
53 * good.
54 */
55#include <stddef.h>
56#include <stdint.h>
57
58#ifdef __cplusplus
59extern "C" {
60#endif
61
62#define ERROR_HEADER srn_error_t *maybe_error
63#define TRACE_FRAME_DEPTH 32
64
65/**
66 * True when `x` -- a value carrying an `ERROR_HEADER` -- has an error
67 * attached. Takes the struct by value, the way these results are passed and
68 * checked.
69 */
70#define HAS_ERROR(x) ((x).maybe_error != nullptr)
71
72#define PUSH_TRACE(ctx, x) srn_error_trace_push(ctx, (x).maybe_error, __builtin_return_address(0))
73#define PUSH_AND_MOVE(ctx, from, to) \
74 { \
75 (to).maybe_error = (from).maybe_error; \
76 PUSH_TRACE(ctx, to); \
77 }
78
79typedef struct srn_context_t srn_context_t;
80
81#ifdef SRN_WITH_SERENE
82typedef struct srn_value_t srn_value_t;
83typedef struct srn_metadata_t srn_metadata_t;
84#endif
85
86// TODO(lxsameer): Move this function to an auto generated file alongside
87// the actual `srn_error_tag_name`, something like tablegen or something.
88
89/**
90 * The kind of a failure. Each `srn_error_t` carries one of these.
91 */
92typedef enum srn_error_tag_t {
102 /// The JIT could not parse a module's LLVM IR or bitcode.
104 /// A JIT symbol lookup found no symbol of that name.
106 /// A JIT symbol definition collided with a name already defined.
115
116 // IO operation failures, reactor backends will maps their platform
117 // errors to these tags.
138 /// The request itself is malformed (an empty POLL interest mask, for
139 /// example), so retrying it unchanged can never succeed.
141 /// The channel already has a full ring's worth of operations in flight.
142 /// Transient by construction, it clears as the worker consumes
143 /// completions, so the caller can retry.
149
150typedef enum srn_error_layer_t : uint8_t {
152#ifdef SRN_WITH_SERENE
153 SRN_ERR_PARSE_LAYER,
154#endif
156
157/**
158 * This is how we create a traceback for the C level code in the runtime lib. It's similar to how
159 * Zig handles it as well. The hard part is to remember to call `__builtin_return_address(0)` every
160 * time we return an error and add the address to this array. Later we can use `dladdr` on POSIX
161 * (and `SymFromAddr` on Windows) to resolve the function name, and look up the line number in
162 * DWARF data when printing the error.
163 */
168
169/**
170 * A runtime error, a tag classifying the failure and a human-readable
171 * message.
172 */
173typedef struct srn_error_t {
176 char *msg;
177 union {
178#ifdef SRN_WITH_SERENE
179 srn_metadata_t *metadata; // for every layer, except for runtime
180#endif
181 srn_trace_frames_t *frames; // for runtime only
182 } as;
184
185/**
186 * Append a return address to the trace of a runtime-layer error. Allocates
187 * `frames` from `ctx` on the first call. Silently does nothing when the trace
188 * is full or the error is not a runtime-layer error.
189 */
190void srn_error_trace_push(const srn_context_t *ctx, srn_error_t *err, void *addr);
191
192/**
193 * Build an error in `ctx`'s memory, tagged `tag` and carrying `msg`. `caller`
194 * is the return address of the call site (pass `__builtin_return_address(0)`)
195 * and is recorded as the first frame of the trace. The ERR macro supplies it
196 * automatically.
197 */
199srn_errors_make(const srn_context_t *ctx, srn_error_tag_t tag, const char *msg, void *caller);
200
201/**
202 * Build an error in `ctx`'s memory that carries the layer, tag, message and
203 * payload of `src`, with a trace of its own that starts at `caller`. `src`
204 * is not written to, so this is the way to take a trace on an error that is
205 * shared or immutable, such as the reactor's static IO errors. A runtime
206 * layer `src` with frames has them copied, so the earlier trace is kept. The
207 * ERR_FROM macro supplies `caller`.
208 */
209srn_error_t *srn_errors_from(const srn_context_t *ctx, const srn_error_t *src, void *caller);
210
211/**
212 * Allocate a `size`/`alignment` value in `ctx`, set its ERROR_HEADER (the
213 * first member, `srn_error_t *maybe_error`) to a fresh error, and return it.
214 * The result is a valid fallible value whose `HAS_ERROR` is true. Use the
215 * FAIL macro, which fills in `size`/`alignment` from the target type.
216 */
217[[nodiscard]] [[gnu::nonnull(1)]]
218void *srn_errors_fail(
219 const srn_context_t *ctx, size_t size, size_t alignment, srn_error_tag_t tag, const char *msg,
220 void *caller
221);
222
223#define ERR(ctx, err, msg) srn_errors_make(ctx, err, msg, __builtin_return_address(0))
224#define ERR_FROM(ctx, src) srn_errors_from(ctx, src, __builtin_return_address(0))
225#define FAIL(ctx, T, err, msg) \
226 ((T *)srn_errors_fail(ctx, sizeof(T), alignof(T), err, msg, __builtin_return_address(0)))
227
228/**
229 * Format `err` as a newly allocated string of the form `[TAG]: msg`, allocated
230 * in `ctx`. Returns `[UNKNOWN]: <null>` when `err` is null.
231 */
232[[nodiscard]] [[gnu::nonnull(1)]]
233char *srn_error_to_string(const srn_context_t *ctx, const srn_error_t *err);
234
235/**
236 * Map a POSIX `errno` to error tag.
237 * Note: an `errno` with no mapping yields `default_tag`, so the caller picks the
238 * fallback that fits its domain. Defined on POSIX platforms. a non-POSIX backend should supply its
239 * own native-code mapping.
240 */
242
243#ifdef SRN_WITH_SERENE
244/**
245 * Lift an existing `srn_error_t` into a Serene value, so a failure already
246 * produced by the substrate can be returned through the language layer.
247 */
249srn_errors_err_to_value(srn_context_t *ctx, srn_metadata_t *metadata, srn_error_t *err);
250/**
251 * Created an error as a value. In compare to `srn_errors_make` this function
252 * populates the `sr_value_t` data structure, so the returning value can be
253 * used in Serene lang directly.
254 */
255srn_value_t *srn_errors_make_error(
256 srn_context_t *ctx, srn_metadata_t *metadata, srn_error_tag_t tag, const char *msg
257);
258#endif
259
260#ifdef __cplusplus
261}
262#endif
srn_error_t * srn_errors_make(const srn_context_t *ctx, srn_error_tag_t tag, const char *msg, void *caller)
Build an error in ctx's memory, tagged tag and carrying msg.
Definition errors.c:196
void srn_error_trace_push(const srn_context_t *ctx, srn_error_t *err, void *addr)
Append a return address to the trace of a runtime-layer error.
Definition errors.c:177
void * srn_errors_fail(const srn_context_t *ctx, size_t size, size_t alignment, srn_error_tag_t tag, const char *msg, void *caller)
Allocate a size/alignment value in ctx, set its ERROR_HEADER (the first member, srn_error_t *maybe_er...
Definition errors.c:224
srn_error_t * srn_errors_from(const srn_context_t *ctx, const srn_error_t *src, void *caller)
Build an error in ctx's memory that carries the layer, tag, message and payload of src,...
Definition errors.c:207
srn_error_tag_t
The kind of a failure.
Definition errors.h:92
@ AL_LIMIT_REACHED
Definition errors.h:96
@ EPOLL_EVENT_FD_FAILED
Definition errors.h:113
@ NOT_IMPLEMENTED
Definition errors.h:94
@ BROKEN_PIPE
Definition errors.h:121
@ SM_BUFFER_ID_EXHAUSTED
Definition errors.h:97
@ HOST_UNREACHABLE
Definition errors.h:127
@ CORRUPTED_SEQ
Definition errors.h:99
@ CORRUPTED_ARRAY_LIST
Definition errors.h:100
@ EMPTY_NAMESPACE_NAME
Definition errors.h:108
@ JIT_IR_PARSE_FAILED
The JIT could not parse a module's LLVM IR or bitcode.
Definition errors.h:103
@ NETWORK_UNREACHABLE
Definition errors.h:128
@ STRING_LENGTH_LIMIT_EXCEEDED
Definition errors.h:110
@ IS_A_DIRECTORY
Definition errors.h:135
@ INVALID_ARGUMENT
The request itself is malformed (an empty POLL interest mask, for example), so retrying it unchanged ...
Definition errors.h:140
@ CHANNEL_BUSY
The channel already has a full ring's worth of operations in flight.
Definition errors.h:144
@ ABSURD
Definition errors.h:93
@ WRONG_RANGE
Definition errors.h:111
@ JIT_SYMBOL_ALREADY_DEFINED
A JIT symbol definition collided with a name already defined.
Definition errors.h:107
@ PERMISSION_DENIED
Definition errors.h:131
@ CONNECTION_RESET
Definition errors.h:119
@ UNKNOWN_IO_ERROR
Definition errors.h:147
@ TOO_MANY_OPEN_FILES
Definition errors.h:130
@ JIT_SYMBOL_NOT_FOUND
A JIT symbol lookup found no symbol of that name.
Definition errors.h:105
@ NOT_CONNECTED
Definition errors.h:125
@ CONNECTION_REFUSED
Definition errors.h:118
@ FAILED_TO_ADD_MODULE
Definition errors.h:101
@ INDEX_OUT_OF_BOUND
Definition errors.h:98
@ SEQ_LIMIT_REACHED
Definition errors.h:95
@ CONNECTION_ABORTED
Definition errors.h:120
@ ADDRESS_IN_USE
Definition errors.h:126
@ EPOLL_FAILED_TO_ADD_FD
Definition errors.h:114
@ EPOLL_INIT_FAILED
Definition errors.h:112
@ BAD_DESCRIPTOR
Definition errors.h:122
@ OUT_OF_MEMORY
Definition errors.h:132
@ NOT_FOUND
Definition errors.h:133
@ CANCELED
Definition errors.h:124
@ NOT_A_DIRECTORY
Definition errors.h:136
@ TIMED_OUT
Definition errors.h:123
@ IR_PARSE_ERROR
Definition errors.h:146
@ EMPTY_SYMBOL_NAME
Definition errors.h:109
@ MEMORY_MAP_FAILED
Definition errors.h:145
@ MESSAGE_TOO_LONG
Definition errors.h:129
@ ALREADY_EXISTS
Definition errors.h:134
@ NO_SPACE
Definition errors.h:137
srn_error_tag_t srn_errno_to_tag(int errno_value, srn_error_tag_t default_tag)
Map a POSIX errno to error tag.
char * srn_error_to_string(const srn_context_t *ctx, const srn_error_t *err)
Format err as a newly allocated string of the form [TAG]: msg, allocated in ctx.
Definition errors.c:133
#define TRACE_FRAME_DEPTH
Definition errors.h:63
srn_error_layer_t
Definition errors.h:150
@ SRN_ERR_RUNTIME_LAYER
Definition errors.h:151
A runtime error, a tag classifying the failure and a human-readable message.
Definition errors.h:173
union srn_error_t::@110141362101221075206211275212023153042233307220 as
srn_error_layer_t layer
Definition errors.h:174
char * msg
Definition errors.h:176
srn_error_tag_t tag
Definition errors.h:175
srn_trace_frames_t * frames
Definition errors.h:181
This is how we create a traceback for the C level code in the runtime lib.
Definition errors.h:164
uint16_t count
Definition errors.h:166
void * return_addrs[TRACE_FRAME_DEPTH]
Definition errors.h:165