From: Axy Date: Wed, 6 May 2026 00:34:07 +0000 (+0200) Subject: Exceptions and defer X-Git-Url: https://git.uwuaxy.net/sitemap.xml?a=commitdiff_plain;h=6c64dfa774bd9f1685a15ad35d74d63d1237ec7e;p=axy%2Fft%2Fc-cera.git Exceptions and defer --- diff --git a/Makefile b/Makefile index 7c584c5..1631561 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ NAME=ccera.a -SRCS=src/arc.c src/eval.c src/value_destroy.c src/value_get.c src/value_lifetime.c src/value_new.c +SRCS=src/arc.c src/defer2.c src/defer.c src/eval.c src/panic.c src/value_destroy.c src/value_get.c src/value_lifetime.c src/value_new.c src/value_readf.c HEADERS=src/ccera.h @@ -10,8 +10,9 @@ THIS=Makefile OBJS=${SRCS:src/%.c=${BUILDDIR}/%.o} -CFLAGS=-Wall -Wextra -Werror -g -pthread -flto -ffat-lto-objects -#CFLAGS=-Wall -Wextra -Werror -g -pthread -O3 -flto -ffat-lto-objects +# no malloc leak detection as our union shenanigans trigger false positives +CFLAGS=-Wall -Wextra -Werror -g -pthread -flto -ffat-lto-objects -fanalyzer -Wno-analyzer-malloc-leak +CFLAGS += -O3 CC=gcc @@ -19,27 +20,27 @@ AR=ar MAKEFLAGS += --no-print-directory -all : ${NAME} +all: ${NAME} -${BUILDDIR}/%.o : src/%.c ${HEADERS} ${THIS} | ${BUILDDIR} +${BUILDDIR}/%.o: src/%.c ${HEADERS} ${THIS} | ${BUILDDIR} ${CC} ${CFLAGS} -c -o $@ $< -${BUILDDIR} : +${BUILDDIR}: mkdir ${BUILDDIR} -${NAME} : ${OBJS} ${THIS} +${NAME}: ${OBJS} ${THIS} ${AR} rcs $@ ${OBJS} -clean : +clean: rm -f ${OBJS} -fclean : clean +fclean: clean rm -f ${NAME} rm -fd ${BUILDDIR} -re : fclean all +re: fclean all -remakefile : clean +remakefile: clean sed -i "s?^SRCS=.*?$$(echo -n SRCS=; echo -n src/*.c)?" ${THIS} sed -i "s?^HEADERS=.*?$$(echo -n HEADERS=; echo -n src/*.h)?" ${THIS} @@ -48,7 +49,7 @@ remakefile : clean @norminette ${SRCS} ${HEADERS} > .norm || touch .norm_fail norm: .norm - @cat .norm + @cat .norm | sed "s/.*OK!$$//" | grep . @test ! -e .norm_fail norm-watch: @@ -57,4 +58,10 @@ norm-watch: build-watch: watch -c -n 1 make -.PHONY : all clean fclean re remakefile norm norm-watch build-watch +a.out: test.c ${NAME} ${THIS} + ${CC} ${CFLAGS} test.c ccera.a + +test: a.out + valgrind ./a.out + +.PHONY: all clean fclean re remakefile norm norm-watch build-watch test diff --git a/src/arc.c b/src/arc.c index e453239..af02b81 100644 --- a/src/arc.c +++ b/src/arc.c @@ -6,7 +6,7 @@ /* By: agilliar +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2026/04/29 16:40:27 by agilliar #+# #+# */ -/* Updated: 2026/05/04 02:01:19 by agilliar ### ########.fr */ +/* Updated: 2026/05/05 20:40:03 by agilliar ### ########.fr */ /* */ /* ************************************************************************** */ @@ -18,23 +18,19 @@ t_arc arc_create(t_usize size) t_usize *res; if (__builtin_add_overflow(size, sizeof(t_usize), &size)) - return (NULL); + panic("Arc alloc too big to create"); res = malloc(size); if (!res) - return (NULL); + panic("Malloc failed while creating arc"); res[0] = 1; return ((void *) &res[1]); } -t_arc arc_copy(t_arc arc) +void arc_copy(t_arc arc) { - if (!arc) - return (NULL); if (__atomic_add_fetch(((t_usize *)arc) - 1, 1, __ATOMIC_RELAXED) - < ((t_usize) 1) << (sizeof(t_usize) * 8 - 1)) - return (arc); - arc_destroy(arc, noop); - return (NULL); + >= ((t_usize) 1) << (sizeof(t_usize) * 8 - 1)) + panic("Arc counter overflowed"); } void arc_destroy(t_arc arc, t_drop destructor) @@ -45,7 +41,7 @@ void arc_destroy(t_arc arc, t_drop destructor) return ; __atomic_thread_fence(__ATOMIC_ACQUIRE); destructor(arc); - free(arc - 8); + free(((char *)arc) - 8); } bool arc_is_unique(t_arc arc) diff --git a/src/ccera.h b/src/ccera.h index 9465c6e..7f3fc35 100644 --- a/src/ccera.h +++ b/src/ccera.h @@ -6,7 +6,7 @@ /* By: agilliar +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2026/04/29 16:08:57 by agilliar #+# #+# */ -/* Updated: 2026/05/04 03:58:26 by agilliar ### ########.fr */ +/* Updated: 2026/05/06 02:32:20 by agilliar ### ########.fr */ /* */ /* ************************************************************************** */ @@ -23,6 +23,44 @@ typedef uintptr_t t_usize; typedef intptr_t t_isize; +struct s_defer_pair +{ + void (*f)(void *); + void *dat; + bool err; +}; + +# define MAX_DEFERS 8 + +typedef struct s_defer +{ + struct s_defer *prev; + struct s_defer *next; + t_usize len; + struct s_defer_pair defers[MAX_DEFERS]; +} t_defer; + +void defer_new(t_defer *store); +void defer_delete(t_defer *store); +void defer_re(t_defer *store); + +void defer(void *f, void *dat); +void errdefer(void *f, void *dat); +void defer_exit(t_defer *until); + +typedef struct s_panic_info +{ + void *data; + intptr_t jmp[5]; + t_defer *defer; +} t_panic_info; + +void *catch(void (f)(void *), void *dat); + +void panic(void *err) + __attribute__((noreturn, nonnull(1))); +bool panicking(void); + typedef union u_value t_value; typedef t_value (*t_builtin)(t_value); @@ -59,14 +97,14 @@ typedef struct s_value_list { t_usize len; enum e_value_ptr discrim; - t_value list[0]; + t_value list[]; } t_value_list; typedef struct s_value_bytes { t_usize len; enum e_value_ptr discrim; - char bytes[0]; + char bytes[]; } t_value_bytes; t_value value_builtin_new(t_builtin builtin); @@ -80,7 +118,7 @@ t_value_list *value_list_get(t_value value); t_value_bytes *value_bytes_get(t_value value); t_isize value_int_get(t_value value); -void value_destroy(t_value value); +void value_destroy(t_value *value); t_value value_copy(t_value value); t_value_list *value_list_unique(t_value *value); @@ -94,14 +132,12 @@ typedef void *(*t_unpack)(t_value); typedef void (*t_drop)(void *); t_arc arc_create(t_usize size); -t_arc arc_copy(t_arc arc); +void arc_copy(t_arc arc); void arc_destroy(t_arc arc, void (*destructor)(void *)); bool arc_is_unique(t_arc arc); void noop(void *ptr); -struct s_exec_context; - // The expression value is a tuple of an evaluatable and the argument to give it // // Value-evaluatables are laid out as follows: diff --git a/src/defer.c b/src/defer.c new file mode 100644 index 0000000..25320ba --- /dev/null +++ b/src/defer.c @@ -0,0 +1,72 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* defer.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: agilliar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2026/05/05 17:31:36 by agilliar #+# #+# */ +/* Updated: 2026/05/06 02:31:46 by agilliar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "ccera.h" +#include + +t_defer **defer_store(void); + +void defer(void *f, void *dat) +{ + t_defer *store; + + store = *defer_store(); + if (store->len == MAX_DEFERS) + { + ((void (*)(void *))f)(dat); + panic("Maximum defers exeeded in scope"); + } + store->defers[store->len].f = f; + store->defers[store->len].dat = dat; + store->defers[store->len].err = false; + store->len++; +} + +void errdefer(void *f, void *dat) +{ + t_defer *store; + + store = *defer_store(); + if (store->len == MAX_DEFERS) + { + ((void (*)(void *))f)(dat); + panic("Maximum defers exeeded in scope"); + } + store->defers[store->len].f = f; + store->defers[store->len].dat = dat; + store->defers[store->len].err = true; + store->len++; +} + +void defer_exit(t_defer *until) +{ + t_usize i; + t_defer *store; + struct s_defer_pair curr; + + store = *defer_store(); + while (store) + { + i = 0; + while (i < store->len) + { + curr = store->defers[i++]; + if (!panicking() && curr.err) + continue ; + curr.f(curr.dat); + } + if (store == until) + break ; + store = store->prev; + } + *defer_store() = store; +} diff --git a/src/defer2.c b/src/defer2.c new file mode 100644 index 0000000..9a85883 --- /dev/null +++ b/src/defer2.c @@ -0,0 +1,47 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* defer2.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: agilliar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2026/05/06 02:23:38 by agilliar #+# #+# */ +/* Updated: 2026/05/06 02:32:07 by agilliar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "ccera.h" +#include + +t_defer **defer_store(void) +{ + static __thread t_defer *store = NULL; + + return (&store); +} + +void defer_new(t_defer *store) +{ + store->len = 0; + store->prev = *defer_store(); + if (store->prev) + store->prev->next = store; + store->next = NULL; + *defer_store() = store; +} + +void defer_delete(t_defer *store) +{ + if (store == *defer_store()) + *defer_store() = store->prev; + if (store->next) + store->next->prev = store->prev; + if (store->prev) + store->prev->next = store->next; +} + +void defer_re(t_defer *store) +{ + defer_delete(store); + defer_new(store); +} diff --git a/src/eval.c b/src/eval.c index 597e6ce..2636220 100644 --- a/src/eval.c +++ b/src/eval.c @@ -6,32 +6,9 @@ /* By: agilliar +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2026/05/04 01:21:34 by agilliar #+# #+# */ -/* Updated: 2026/05/04 04:12:32 by agilliar ### ########.fr */ +/* Updated: 2026/05/05 05:31:52 by agilliar ### ########.fr */ /* */ /* ************************************************************************** */ #include "ccera.h" #include - -static bool to_state(t_value *value) -{ - t_value_list *expr; - t_value_list *state; - - expr = value_list_unique(value); - if (!expr || expr->len != 2) - return (false); - state = value_list_unique(&expr->list[0]); - if (!state || state->len != 2) - return (false); - if (!value_list_unique(&state->list[0])) - return (false); - return (true); -} - -t_value expr_eval(t_value expr) -{ - to_state(&expr); - value_destroy(expr); - return (value_undefined_new()); -} diff --git a/src/panic.c b/src/panic.c new file mode 100644 index 0000000..1b9fe7c --- /dev/null +++ b/src/panic.c @@ -0,0 +1,56 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* panic.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: agilliar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2026/05/05 15:38:33 by agilliar #+# #+# */ +/* Updated: 2026/05/05 21:25:00 by agilliar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "ccera.h" +#include +#include +#include + +static t_panic_info *panic_store(void) +{ + static __thread t_panic_info store; + + return (&store); +} + +void *catch(void (f)(void *), void *dat) +{ + void *res; + t_defer defer_store; + t_panic_info prev; + + prev = *panic_store(); + panic_store()->defer = &defer_store; + defer_new(&defer_store); + if (__builtin_setjmp(panic_store()->jmp)) + { + res = panic_store()->data; + *panic_store() = prev; + return (res); + } + f(dat); + *panic_store() = prev; + return (NULL); +} + +__attribute__((noreturn, nonnull(1))) +void panic(void *err) +{ + defer_exit(panic_store()->defer); + panic_store()->data = err; + __builtin_longjmp(panic_store()->jmp, 1); +} + +bool panicking(void) +{ + return (panic_store()->data != NULL); +} diff --git a/src/value_destroy.c b/src/value_destroy.c index b67f20d..1e05c9a 100644 --- a/src/value_destroy.c +++ b/src/value_destroy.c @@ -6,7 +6,7 @@ /* By: agilliar +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2026/04/30 19:40:43 by agilliar #+# #+# */ -/* Updated: 2026/05/04 01:17:51 by agilliar ### ########.fr */ +/* Updated: 2026/05/05 20:54:09 by agilliar ### ########.fr */ /* */ /* ************************************************************************** */ @@ -30,12 +30,13 @@ static void value_list_del(struct s_value_list *list) i = 0; while (i < list->len) - value_destroy(list->list[i++]); + value_destroy(&list->list[i++]); } -void value_destroy(t_value value) +void value_destroy(t_value *value) { - value_drop_arc(&value, (t_unpack) value_builtin_get, noop); - value_drop_arc(&value, (t_unpack) value_list_get, (t_drop) value_list_del); - value_drop_arc(&value, (t_unpack) value_bytes_get, noop); + value_drop_arc(value, (t_unpack) value_builtin_get, noop); + value_drop_arc(value, (t_unpack) value_list_get, (t_drop) value_list_del); + value_drop_arc(value, (t_unpack) value_bytes_get, noop); + *value = value_undefined_new(); } diff --git a/src/value_get.c b/src/value_get.c index 7b79dc7..43e3a19 100644 --- a/src/value_get.c +++ b/src/value_get.c @@ -6,7 +6,7 @@ /* By: agilliar +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2026/04/30 17:27:40 by agilliar #+# #+# */ -/* Updated: 2026/05/04 01:18:03 by agilliar ### ########.fr */ +/* Updated: 2026/05/05 21:32:17 by agilliar ### ########.fr */ /* */ /* ************************************************************************** */ @@ -20,7 +20,7 @@ struct s_value_builtin *value_builtin_get(t_value value) if ((value.discrim & VALUE_MASK) != VALUE_PTR) return (NULL); res = value.ptr - offsetof(struct s_value_builtin, builtin); - if (res->discrim != VALUE_PTR_BUILTIN) + if (!value.ptr || res->discrim != VALUE_PTR_BUILTIN) return (NULL); return (res); } @@ -32,7 +32,7 @@ struct s_value_list *value_list_get(t_value value) if ((value.discrim & VALUE_MASK) != VALUE_PTR) return (NULL); res = value.ptr - offsetof(struct s_value_list, list); - if (res->discrim != VALUE_PTR_LIST) + if (!value.ptr || res->discrim != VALUE_PTR_LIST) return (NULL); return (res); } @@ -44,7 +44,7 @@ struct s_value_bytes *value_bytes_get(t_value value) if ((value.discrim & VALUE_MASK) != VALUE_PTR) return (NULL); res = value.ptr - offsetof(struct s_value_bytes, bytes); - if (res->discrim != VALUE_PTR_BYTES) + if (!value.ptr || res->discrim != VALUE_PTR_BYTES) return (NULL); return (res); } diff --git a/src/value_lifetime.c b/src/value_lifetime.c index c5ad951..c2850cb 100644 --- a/src/value_lifetime.c +++ b/src/value_lifetime.c @@ -6,7 +6,7 @@ /* By: agilliar +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2026/05/04 02:05:48 by agilliar #+# #+# */ -/* Updated: 2026/05/04 03:27:11 by agilliar ### ########.fr */ +/* Updated: 2026/05/05 20:37:17 by agilliar ### ########.fr */ /* */ /* ************************************************************************** */ @@ -61,7 +61,7 @@ t_value_list *value_list_unique(t_value *value) new_list->list[i] = value_copy(list->list[i]); i++; } - value_destroy(*value); + value_destroy(value); *value = new; return (new_list); } @@ -82,7 +82,7 @@ t_value_bytes *value_bytes_unique(t_value *value) if (!new_bytes) return (NULL); memcpy(new_bytes->bytes, bytes->bytes, bytes->len); - value_destroy(*value); + value_destroy(value); *value = new; return (new_bytes); } diff --git a/src/value_new.c b/src/value_new.c index 7dd45ad..589cabd 100644 --- a/src/value_new.c +++ b/src/value_new.c @@ -6,7 +6,7 @@ /* By: agilliar +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2026/04/30 16:25:42 by agilliar #+# #+# */ -/* Updated: 2026/05/04 01:49:28 by agilliar ### ########.fr */ +/* Updated: 2026/05/05 21:25:48 by agilliar ### ########.fr */ /* */ /* ************************************************************************** */ @@ -19,8 +19,6 @@ t_value value_builtin_new(t_builtin builtin) t_value res_raw; res = arc_create(sizeof(struct s_value_builtin)); - if (!res) - return (value_undefined_new()); res->discrim = VALUE_PTR_BUILTIN; res->builtin = builtin; res_raw.ptr = &res->builtin; @@ -35,11 +33,9 @@ t_value value_list_new(t_usize len) t_usize n; if (__builtin_mul_overflow(len, sizeof(t_value), &n) - || __builtin_add_overflow(n, sizeof(struct s_value_list), &n)) - return (value_undefined_new()); + || __builtin_add_overflow(n, sizeof(t_value_list), &n)) + panic("List size overflowed"); res = arc_create(n); - if (!res) - return (value_undefined_new()); res->discrim = VALUE_PTR_LIST; res->len = len; n = 0; @@ -57,10 +53,8 @@ t_value value_bytes_new(t_usize len) t_usize n; if (__builtin_add_overflow(len, sizeof(struct s_value_bytes), &n)) - return (value_undefined_new()); + panic("Bytes size overflowed"); res = arc_create(n); - if (!res) - return (value_undefined_new()); res->discrim = VALUE_PTR_BYTES; res->len = len; memset(&res->bytes, 0, len); diff --git a/src/value_readf.c b/src/value_readf.c new file mode 100644 index 0000000..aa6cc56 --- /dev/null +++ b/src/value_readf.c @@ -0,0 +1,26 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* value_readf.c :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: agilliar +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2026/05/05 15:21:41 by agilliar #+# #+# */ +/* Updated: 2026/05/05 16:58:28 by agilliar ### ########.fr */ +/* */ +/* ************************************************************************** */ + +#include "ccera.h" + +// i: int +// (: list +// ): end list +// .: list rem discard +// _: skip +// b: bytes +// l: list +// v: value +// value_readf("((li)v)") +//bool value_readf(const char *f, void **out, bool move) +//{ +//} diff --git a/test.c b/test.c index e15714d..ff6d963 100644 --- a/test.c +++ b/test.c @@ -6,7 +6,7 @@ /* By: agilliar +#+ +:+ +#+ */ /* +#+#+#+#+#+ +#+ */ /* Created: 2026/04/30 17:19:58 by agilliar #+# #+# */ -/* Updated: 2026/05/04 04:13:07 by agilliar ### ########.fr */ +/* Updated: 2026/05/06 02:32:34 by agilliar ### ########.fr */ /* */ /* ************************************************************************** */ @@ -14,19 +14,41 @@ #include #include -int main(void) +void main2(void *s) { - char *s = "Hello cera!"; + t_defer defer_store __attribute__((cleanup(defer_exit))); + defer_new(&defer_store); + t_value arg = value_bytes_new(strlen(s) + 1); + defer(value_destroy, &arg); + //panic("A"); t_value state = value_list_new(0); + defer(value_destroy, &state); + //panic("B"); t_value idx = value_int_new(0); t_value fn = value_list_new(2); + defer(value_destroy, &fn); + //panic("C"); t_value expr = value_list_new(2); + defer(value_destroy, &expr); + //panic("D"); + defer_re(&defer_store); value_list_get(fn)->list[0] = state; value_list_get(fn)->list[1] = idx; value_list_get(expr)->list[0] = fn; value_list_get(expr)->list[1] = arg; + defer(value_destroy, &expr); + //panic("E"); t_value val = value_copy(expr); - printf("%p\n", expr_eval(expr).ptr); - value_destroy(val); + defer(value_destroy, &val); + //panic("F"); +} + +int main(void) +{ + if (char *err = catch(main2, "Hello cera!")) + { + printf("%s\n", err); + return (1); + } } diff --git a/vgcore.2443515 b/vgcore.2443515 new file mode 100644 index 0000000..51ffaba Binary files /dev/null and b/vgcore.2443515 differ