From 5277f2222417b5366e1ef6e0871c90e3946b1742 Mon Sep 17 00:00:00 2001
From: Leo Tenenbaum <pommicket@gmail.com>
Date: Fri, 1 May 2020 15:49:30 -0400
Subject: &void

---
 cgen.c           |  21 ++-----
 copy.c           |   2 -
 eval.c           | 103 +++++++++++++++++----------------
 foreign.c        |  10 ++--
 infer.c          |   1 -
 instance_table.c |  10 ++--
 main.c           |   4 +-
 parse.c          |  27 +++++----
 test.toc         |  10 +---
 toc.c            |   3 +
 types.c          | 170 +++++++++++++++++++++++++++++--------------------------
 types.h          |   9 +--
 12 files changed, 188 insertions(+), 182 deletions(-)

diff --git a/cgen.c b/cgen.c
index 5927858..188a16e 100644
--- a/cgen.c
+++ b/cgen.c
@@ -169,7 +169,6 @@ static void cgen_defs_decl(CGenerator *g, Declaration *d);
 	case TYPE_PTR:														\
 		f(g, type->ptr);												\
 		break;															\
-	case TYPE_VOID:														\
 	case TYPE_BUILTIN:													\
 	case TYPE_UNKNOWN:													\
 		break;															\
@@ -286,7 +285,6 @@ static bool cgen_uses_ptr(Type *t) {
 	case TYPE_PTR:
 	case TYPE_FN:
 	case TYPE_SLICE:
-	case TYPE_VOID:
 	case TYPE_UNKNOWN:
 		return false;
 	case TYPE_EXPR:
@@ -326,16 +324,14 @@ static void cgen_type_pre(CGenerator *g, Type *t) {
 		case BUILTIN_BOOL: cgen_write(g, "bool"); break;
 		case BUILTIN_F32: cgen_write(g, "f32"); break;
 		case BUILTIN_F64: cgen_write(g, "f64"); break;
+		case BUILTIN_VOID: cgen_write(g, "void"); break;
 		case BUILTIN_NMS:
 		case BUILTIN_TYPE:
 		case BUILTIN_VARARGS:
 			assert(0); break;
 		} break;
 	case TYPE_PTR:
-		if (t->ptr->kind == TYPE_UNKNOWN)
-			cgen_write(g, "void"); /* #C &"foo", for example */
-		else
-			cgen_type_pre(g, t->ptr);
+		cgen_type_pre(g, t->ptr);
 		cgen_write(g, "(*");
 		break;
 	case TYPE_ARR:
@@ -353,7 +349,6 @@ static void cgen_type_pre(CGenerator *g, Type *t) {
 	case TYPE_SLICE:
 		cgen_write(g, "slice_");
 		break;
-	case TYPE_VOID: cgen_write(g, "void"); break;
 	case TYPE_STRUCT:
 		cgen_write(g, "struct ");
 		cgen_struct_name(g, t->struc);
@@ -416,7 +411,6 @@ static void cgen_type_post(CGenerator *g, Type *t) {
 			cgen_type_post(g, &t->fn.types[0]);
 	} break;
 	case TYPE_BUILTIN:
-	case TYPE_VOID:
 	case TYPE_UNKNOWN:
 	case TYPE_TUPLE:
 	case TYPE_SLICE:
@@ -543,7 +537,6 @@ static void cgen_val_ptr_pre(CGenerator *g, void *v, Type *t) {
 	case TYPE_FN:
 	case TYPE_UNKNOWN:
 	case TYPE_TUPLE:
-	case TYPE_VOID:
 	case TYPE_BUILTIN:
 	case TYPE_PTR:
 	case TYPE_STRUCT:
@@ -559,7 +552,6 @@ static void cgen_val_ptr(CGenerator *g, void *v, Type *t) {
 	assert(t->flags & TYPE_IS_RESOLVED);
 	switch (t->kind) {
 	case TYPE_TUPLE:
-	case TYPE_VOID:
 	case TYPE_EXPR:
 	case TYPE_UNKNOWN:
 		assert(0);
@@ -612,6 +604,7 @@ static void cgen_val_ptr(CGenerator *g, void *v, Type *t) {
 		case BUILTIN_TYPE:
 		case BUILTIN_NMS:
 		case BUILTIN_VARARGS:
+		case BUILTIN_VOID:
 			assert(0);
 			break;
 		}
@@ -829,7 +822,6 @@ static void cgen_set(CGenerator *g, Expression *set_expr, const char *set_str, E
 		assert(set_expr->kind == EXPR_TUPLE);
 		cgen_set_tuple(g, set_expr->tuple, NULL, NULL, to_expr);
 		break;
-	case TYPE_VOID:
 	case TYPE_EXPR:
 		assert(0);
 		break;
@@ -1003,7 +995,7 @@ static void cgen_expr_pre(CGenerator *g, Expression *e) {
 		
 		cgen_ident_id_to_str(ret_name, id);
 		char *p = ret_name + strlen(ret_name);
-		if (e->type.kind != TYPE_VOID) {
+		if (!type_is_void(&e->type)) {
 			if (e->type.kind == TYPE_TUPLE) {
 				for (unsigned long i = 0; i < arr_len(e->type.tuple); ++i) {
 					sprintf(p, "%lu", i);
@@ -1665,7 +1657,7 @@ static void cgen_expr(CGenerator *g, Expression *e) {
 	} break;
 	case EXPR_BLOCK:
 	case EXPR_IF:
-		if (e->type.kind != TYPE_VOID)
+		if (!type_is_void(&e->type))
 			cgen_ident_id(g, e->cgen.id);
 		break;
 	case EXPR_CALL:
@@ -1850,7 +1842,6 @@ static void cgen_zero_value(CGenerator *g, Type *t) {
 	case TYPE_STRUCT:
 		cgen_write(g, "{0}");
 		break;
-	case TYPE_VOID:
 	case TYPE_UNKNOWN:
 	case TYPE_TUPLE:
 	case TYPE_EXPR:
@@ -2104,7 +2095,7 @@ static void cgen_ret(CGenerator *g, Block *returning_from, Expression *ret_expr)
 		ret.type = f->ret_type;
 		#endif
 		cgen_writeln(g, " return;");
-	} else if (f->ret_type.kind == TYPE_VOID) {
+	} else if (type_is_void(&f->ret_type)) {
 		cgen_writeln(g, "return;");
 	} else {
 		cgen_writeln(g, "return ret_;");
diff --git a/copy.c b/copy.c
index 05b1eea..e34036c 100644
--- a/copy.c
+++ b/copy.c
@@ -53,7 +53,6 @@ static void copy_val(Allocator *a, Value *out, Value in, Type *t) {
 	case TYPE_FN:
 	case TYPE_PTR:
 	case TYPE_SLICE:
-	case TYPE_VOID:
 	case TYPE_UNKNOWN:
 		*out = in;
 		break;
@@ -122,7 +121,6 @@ static void copy_type(Copier *c, Type *out, Type *in) {
 	*out = *in;
 	switch (in->kind) {
 	case TYPE_BUILTIN:
-	case TYPE_VOID:
 	case TYPE_UNKNOWN:
 		break;
 	case TYPE_EXPR:
diff --git a/eval.c b/eval.c
index cda97da..1a2451d 100644
--- a/eval.c
+++ b/eval.c
@@ -37,6 +37,7 @@ static bool builtin_truthiness(Value v, BuiltinType b) {
 	case BUILTIN_BOOL: return v.boolv;
 	case BUILTIN_CHAR: return v.charv != 0;
 	case BUILTIN_VARARGS: return arr_len(v.varargs) != 0;
+	case BUILTIN_VOID:
 	case BUILTIN_TYPE:
 	case BUILTIN_NMS:
 		break;
@@ -47,7 +48,6 @@ static bool builtin_truthiness(Value v, BuiltinType b) {
 static bool val_truthiness(Value v, Type *t) {
 	assert(t->flags & TYPE_IS_RESOLVED);
 	switch (t->kind) {
-	case TYPE_VOID: return false;
 	case TYPE_UNKNOWN: assert(0); return false;
 	case TYPE_BUILTIN: return builtin_truthiness(v, t->builtin);
 	case TYPE_PTR: return v.ptr != NULL;
@@ -146,7 +146,6 @@ static void *val_get_ptr(Value *v, Type *t) {
 	switch (t->kind) {
 	case TYPE_PTR:
 	case TYPE_BUILTIN:
-	case TYPE_VOID:
 	case TYPE_UNKNOWN:
 	case TYPE_FN:
 	case TYPE_SLICE:
@@ -165,9 +164,6 @@ static void *val_get_ptr(Value *v, Type *t) {
 static void fprint_val_ptr(FILE *f, void *p, Type *t) {
 	assert(t->flags & TYPE_IS_RESOLVED);
 	switch (t->kind) {
-	case TYPE_VOID:
-		fprintf(f, "(void)");
-		break;
 	case TYPE_UNKNOWN:
 		fprintf(f, "???");
 		break;
@@ -185,6 +181,7 @@ static void fprint_val_ptr(FILE *f, void *p, Type *t) {
 		case BUILTIN_F64: fprintf(f, F64_FMT, *(F64 *)p); break;
 		case BUILTIN_CHAR: fprint_char_literal(f, *(char *)p); break;
 		case BUILTIN_BOOL: fprintf(f, "%s", *(bool *)p ? "true" : "false"); break;
+		case BUILTIN_VOID: fprintf(f, "(void)"); break;
 		case BUILTIN_VARARGS:
 			fprintf(f, "...(");
 			arr_foreach(*(VarArg **)p, VarArg, varg) {
@@ -278,7 +275,6 @@ static void *val_ptr_to_free(Value *v, Type *t) {
 	case TYPE_FN:
 	case TYPE_PTR:
 	case TYPE_SLICE:
-	case TYPE_VOID:
 	case TYPE_UNKNOWN:
 		return NULL;
 	case TYPE_ARR:
@@ -302,53 +298,55 @@ static inline void val_free_ptr(Value *v, Type *t) {
 	free(v);
 }
 
-#define builtin_casts_to_int(x)					\
-	case BUILTIN_I8:							\
-	vout->i8 = (I8)(I64)vin->x; break;			\
-	case BUILTIN_I16:							\
-	vout->i16 = (I16)(I64)vin->x; break;		\
-	case BUILTIN_I32:							\
-	vout->i32 = (I32)(I64)vin->x; break;		\
-	case BUILTIN_I64:							\
-	vout->i64 = (I64)vin->x; break;				\
-	case BUILTIN_U8:							\
-	vout->u8 = (U8)(U64)vin->x; break;			\
-	case BUILTIN_U16:							\
-	vout->u16 = (U16)(U64)vin->x; break;		\
-	case BUILTIN_U32:							\
-	vout->u32 = (U32)(U64)vin->x; break;		\
-	case BUILTIN_U64:							\
+#define builtin_casts_to_int(x) \
+	case BUILTIN_I8: \
+	vout->i8 = (I8)(I64)vin->x; break; \
+	case BUILTIN_I16: \
+	vout->i16 = (I16)(I64)vin->x; break; \
+	case BUILTIN_I32: \
+	vout->i32 = (I32)(I64)vin->x; break; \
+	case BUILTIN_I64: \
+	vout->i64 = (I64)vin->x; break; \
+	case BUILTIN_U8: \
+	vout->u8 = (U8)(U64)vin->x; break; \
+	case BUILTIN_U16: \
+	vout->u16 = (U16)(U64)vin->x; break; \
+	case BUILTIN_U32: \
+	vout->u32 = (U32)(U64)vin->x; break; \
+	case BUILTIN_U64: \
 	vout->u64 = (U64)vin->x; break
 
-#define builtin_casts_to_num(x)					\
-	builtin_casts_to_int(x);					\
-	case BUILTIN_F32:							\
-	vout->f32 = (F32)vin->x; break;				\
-	case BUILTIN_F64:							\
+#define builtin_casts_to_num(x) \
+	builtin_casts_to_int(x); \
+	case BUILTIN_F32: \
+	vout->f32 = (F32)vin->x; break; \
+	case BUILTIN_F64: \
 	vout->f64 = (F64)vin->x; break
 
-#define builtin_int_casts(low, up)							\
-	case BUILTIN_##up:										\
-	switch (to) {											\
-		builtin_casts_to_num(low);							\
-	case BUILTIN_CHAR: vout->charv = (char)vin->low; break;	\
-	case BUILTIN_BOOL: vout->boolv = vin->low != 0; break;	\
-	case BUILTIN_NMS:										\
-	case BUILTIN_TYPE:										\
-	case BUILTIN_VARARGS:									\
-		assert(0); break;									\
+#define builtin_int_casts(low, up) \
+	case BUILTIN_##up: \
+	switch (to) { \
+		builtin_casts_to_num(low); \
+	case BUILTIN_CHAR: vout->charv = (char)vin->low; break; \
+	case BUILTIN_BOOL: vout->boolv = vin->low != 0; break; \
+	case BUILTIN_NMS: \
+	case BUILTIN_VOID: \
+	case BUILTIN_TYPE: \
+	case BUILTIN_VARARGS: \
+		assert(0); break; \
 	} break
 
-#define builtin_float_casts(low, up)							\
-	case BUILTIN_##up:											\
-	switch (to) {												\
-		builtin_casts_to_num(low);								\
-	case BUILTIN_BOOL: vout->boolv = vin->low != 0.0f; break;	\
-	case BUILTIN_CHAR:											\
-	case BUILTIN_TYPE:											\
-	case BUILTIN_NMS:											\
-	case BUILTIN_VARARGS:										\
-		assert(0); break;										\
+#define builtin_float_casts(low, up) \
+	case BUILTIN_##up: \
+	switch (to) { \
+		builtin_casts_to_num(low); \
+	case BUILTIN_BOOL: vout->boolv = vin->low != 0.0f; break; \
+	case BUILTIN_CHAR: \
+	case BUILTIN_TYPE: \
+	case BUILTIN_NMS: \
+	case BUILTIN_VARARGS: \
+	case BUILTIN_VOID: \
+		assert(0); break; \
 	} break
 	
 static void val_builtin_cast(Value *vin, BuiltinType from, Value *vout, BuiltinType to) {
@@ -378,6 +376,7 @@ static void val_builtin_cast(Value *vin, BuiltinType from, Value *vout, BuiltinT
 		case BUILTIN_BOOL:
 		case BUILTIN_TYPE:
 		case BUILTIN_NMS:
+		case BUILTIN_VOID:
 		case BUILTIN_VARARGS:
 			assert(0); break;
 		}
@@ -385,6 +384,7 @@ static void val_builtin_cast(Value *vin, BuiltinType from, Value *vout, BuiltinT
 	case BUILTIN_TYPE:
 	case BUILTIN_NMS:
 	case BUILTIN_VARARGS:
+	case BUILTIN_VOID:
 		assert(0);
 		break;
 	}
@@ -400,7 +400,6 @@ static void val_cast(Value *vin, Type *from, Value *vout, Type *to) {
 	}
 	
 	switch (from->kind) {
-	case TYPE_VOID:
 	case TYPE_UNKNOWN:
 	case TYPE_TUPLE:
 	case TYPE_STRUCT:
@@ -427,7 +426,6 @@ static void val_cast(Value *vin, Type *from, Value *vout, Type *to) {
 		case TYPE_EXPR:
 		case TYPE_STRUCT:
 		case TYPE_SLICE:
-		case TYPE_VOID:
 		case TYPE_UNKNOWN:
 		case TYPE_TUPLE:
 		case TYPE_FN:
@@ -462,6 +460,7 @@ static void val_cast(Value *vin, Type *from, Value *vout, Type *to) {
 			case BUILTIN_TYPE:
 			case BUILTIN_NMS:
 			case BUILTIN_VARARGS:
+			case BUILTIN_VOID:
 				assert(0); break;
 			}
 			break;
@@ -537,6 +536,7 @@ static void eval_deref(Value *v, void *ptr, Type *type) {
 		case BUILTIN_TYPE:
 			v->type = *(Type **)ptr;
 			break;
+		case BUILTIN_VOID:
 		case BUILTIN_VARARGS:
 			assert(0);
 			break;
@@ -545,7 +545,6 @@ static void eval_deref(Value *v, void *ptr, Type *type) {
 	case TYPE_SLICE:
 		v->slice = *(Slice *)ptr;
 		break;
-	case TYPE_VOID:
 	case TYPE_UNKNOWN:
 	case TYPE_EXPR:
 		assert(0);
@@ -579,6 +578,7 @@ static void eval_deref_set(void *set, Value *to, Type *type) {
 		case BUILTIN_TYPE:
 			*(Type **)set = to->type;
 			break;
+		case BUILTIN_VOID:
 		case BUILTIN_VARARGS:
 			assert(0);
 			break;
@@ -587,7 +587,6 @@ static void eval_deref_set(void *set, Value *to, Type *type) {
 	case TYPE_SLICE:
 		*(Slice *)set = to->slice;
 		break;
-	case TYPE_VOID:
 	case TYPE_UNKNOWN:
 	case TYPE_EXPR:
 		assert(0);
@@ -1521,7 +1520,7 @@ static Status eval_expr(Evaluator *ev, Expression *e, Value *v) {
 			}
 		}
 		if (ev->returning) {
-			if (fn->ret_type.kind != TYPE_VOID && !fn->ret_decls)
+			if (!type_is_void(&fn->ret_type) && !fn->ret_decls)
 				*v = ev->ret_val;
 			ev->returning = NULL;
 		}
diff --git a/foreign.c b/foreign.c
index 788f87b..65de87b 100644
--- a/foreign.c
+++ b/foreign.c
@@ -102,9 +102,6 @@ static bool arg_list_start(av_alist *arg_list, void (*fn)(), Value *return_val,
 		warn_print(where, "Foreign function returns function pointer. If it returns a C-style function pointer, it won't be called properly by toc.");
 		av_start_ptr(*arg_list, fn, FnExpr *, &return_val->fn);
 		break;
-	case TYPE_VOID:
-		av_start_void(*arg_list, fn);
-		break;
 	case TYPE_PTR:
 		av_start_ptr(*arg_list, fn, void *, &return_val->ptr);
 		break;
@@ -153,6 +150,9 @@ static bool arg_list_start(av_alist *arg_list, void (*fn)(), Value *return_val,
 		case BUILTIN_NMS:
 			av_start_ptr(*arg_list, fn, Namespace *, &return_val->nms);
 			break;
+		case BUILTIN_VOID:
+			av_start_void(*arg_list, fn);
+			break;
 		case BUILTIN_VARARGS:
 			assert(0);
 			break;
@@ -197,7 +197,6 @@ static bool arg_list_start(av_alist *arg_list, void (*fn)(), Value *return_val,
 
 static bool arg_list_add(av_alist *arg_list, Value val, Type *type, Location where) {
 	switch (type->kind) {
-	case TYPE_VOID:
 	case TYPE_TUPLE:
 	case TYPE_UNKNOWN:
 	case TYPE_ARR: { /* @TODO: maybe just pass pointer for arr? */
@@ -262,6 +261,9 @@ static bool arg_list_add(av_alist *arg_list, Value val, Type *type, Location whe
 				arg_list_add(arg_list, arg->val, arg->type, where);
 			}
 			break;
+		case BUILTIN_VOID:
+			err_print(where, "Cannot pass type void to foreign function.");
+			return false;
 		}
 		break;
 	case TYPE_SLICE:
diff --git a/infer.c b/infer.c
index 8367dce..0bf5e0f 100644
--- a/infer.c
+++ b/infer.c
@@ -81,7 +81,6 @@ static bool infer_from_type(Typer *tr, Type *match, Type *to, Identifier *idents
 		}
 	}
 	switch (match->kind) {
-	case TYPE_VOID:
 	case TYPE_UNKNOWN:
 	case TYPE_BUILTIN:
 		break; /* nothing we can do here */
diff --git a/instance_table.c b/instance_table.c
index 924ac1d..870ff60 100644
--- a/instance_table.c
+++ b/instance_table.c
@@ -83,15 +83,13 @@ static U64 type_hash(Type *t) {
 											 0x8191b5178d728e8c,
 											 0x50da97f1211b2423,
 											 0xc3977306abd0ae6c,
-											 0x87ea684427e1c521,
-											 0xcee5fd6d6cbdfe23
+											 0x87ea684427e1c521
 	};
 	U64 hash = starters[t->kind];
 	assert(t->flags & TYPE_IS_RESOLVED);
 	switch (t->kind) {
 	case TYPE_BUILTIN:
 		return hash + (U64)t->builtin * 0x1307787dfff73417;
-	case TYPE_VOID:
 	case TYPE_UNKNOWN:
 		return hash;
 	case TYPE_TUPLE:
@@ -124,7 +122,6 @@ static U64 type_hash(Type *t) {
 static U64 val_ptr_hash(void *v, Type *t) {
 	assert(t->flags & TYPE_IS_RESOLVED);
 	switch (t->kind) {
-	case TYPE_VOID: return 0;
 	case TYPE_UNKNOWN: return 0;
 	case TYPE_BUILTIN:
 		switch (t->builtin) {
@@ -152,6 +149,8 @@ static U64 val_ptr_hash(void *v, Type *t) {
 			return type_hash(*(Type **)v);
 		case BUILTIN_NMS:
 			return (U64)*(Namespace **)v;
+		case BUILTIN_VOID:
+			return 0;
 		}
 		assert(0);
 		return 0;
@@ -224,6 +223,7 @@ static bool val_ptr_eq(void *u, void *v, Type *t) {
 		case BUILTIN_F64: return *(F64 *)u == *(F64 *)v;
 		case BUILTIN_BOOL: return *(bool *)u == *(bool *)v;
 		case BUILTIN_CHAR: return *(char *)u == *(char *)v;
+		case BUILTIN_VOID: return true;
 		case BUILTIN_VARARGS: {
 			VarArg *us = *(VarArg **)u, *vs = *(VarArg **)v;
 			size_t n = arr_len(us);
@@ -243,8 +243,6 @@ static bool val_ptr_eq(void *u, void *v, Type *t) {
 			return *(Namespace **)u == *(Namespace **)v;
 		}
 		break;
-	case TYPE_VOID:
-		return true;
 	case TYPE_UNKNOWN:
 		return false;
 	case TYPE_FN:
diff --git a/main.c b/main.c
index 704b534..0b2fd5c 100644
--- a/main.c
+++ b/main.c
@@ -8,11 +8,12 @@
 
 /* 
 @TODO:
-&void
 null
+fix eval pointer arithmetic - we're not multiplying by the size?
 fix including something twice - just use the non-namespacey version if it exists or pick one namespace to use everywhere otherwise
 	- maybe store info about namespaces which are secretly the same as inline blocks/other namespaces in the Typer
 &&, ||
+#no_warn
 start making a standard library... (printf; stringbuilder would be nice to have)
 improve type_to_str:
 	Foo ::= struct(t::Type) {}
@@ -23,6 +24,7 @@ enums
 unions
 ---
 switch to / add as an alternative: libffi
+	- better yet, inline assembly
 don't bother generating ret_ if nothing's deferred
 X ::= newtype(int); or something
 any odd number of "s for a string
diff --git a/parse.c b/parse.c
index 3780b40..b06e0e1 100644
--- a/parse.c
+++ b/parse.c
@@ -170,6 +170,7 @@ static int kw_to_builtin_type(Keyword kw) {
 	case KW_BOOL: return BUILTIN_BOOL;
 	case KW_CHAR: return BUILTIN_CHAR;
 	case KW_TYPE: return BUILTIN_TYPE;
+	case KW_VOID: return BUILTIN_VOID;
 	case KW_NAMESPACE: return BUILTIN_NMS;
 	/* don't allow .. => varargs because it's not a normal type */
 	default: return -1;
@@ -192,6 +193,7 @@ static Keyword builtin_type_to_kw(BuiltinType t) {
 	case BUILTIN_BOOL: return KW_BOOL;
 	case BUILTIN_CHAR: return KW_CHAR;
 	case BUILTIN_TYPE: return KW_TYPE;
+	case BUILTIN_VOID: return KW_VOID;
 	case BUILTIN_NMS: return KW_NAMESPACE;
 	case BUILTIN_VARARGS: return KW_DOTDOT;
 	}
@@ -203,8 +205,6 @@ static Keyword builtin_type_to_kw(BuiltinType t) {
 static size_t type_to_str_(Type *t, char *buffer, size_t bufsize) {
 	bool resolved = (t->flags & TYPE_IS_RESOLVED) != 0;
 	switch (t->kind) {
-	case TYPE_VOID:
-		return str_copy(buffer, bufsize, "void");
 	case TYPE_UNKNOWN:
 		return str_copy(buffer, bufsize, "???");
 	case TYPE_BUILTIN: {
@@ -234,7 +234,7 @@ static size_t type_to_str_(Type *t, char *buffer, size_t bufsize) {
 			written += type_to_str_(&param_types[i], buffer + written, bufsize - written);
 		}
 		written += str_copy(buffer + written, bufsize - written, ")");
-		if (ret_type->kind != TYPE_VOID) {
+		if (!type_is_builtin(ret_type, BUILTIN_VOID)) {
 			written += str_copy(buffer + written, bufsize - written, " ");
 			written += type_to_str_(ret_type, buffer + written, bufsize - written);
 		}
@@ -550,7 +550,8 @@ static Status parse_type(Parser *p, Type *type, Location *where) {
 				 && t->token->kw != KW_LPAREN
 				 && t->token->kw != KW_AMPERSAND)
 				|| t->token->kw == KW_AS) {
-				ret_type->kind = TYPE_VOID;
+				ret_type->kind = TYPE_BUILTIN;
+				ret_type->builtin = BUILTIN_VOID;
 				ret_type->flags = 0;
 			} else {
 				if (!parse_type(p, ret_type, NULL))
@@ -930,7 +931,8 @@ static Status parse_fn_expr(Parser *p, FnExpr *f) {
 	
 	if (token_is_kw(t->token, KW_LBRACE)) {
 		/* void function */
-		f->ret_type.kind = TYPE_VOID;
+		f->ret_type.kind = TYPE_BUILTIN;
+		f->ret_type.builtin = BUILTIN_VOID;
 		f->ret_type.flags = 0;
 	} else if (is_decl(t)) {
 		if (!parse_decl_list(p, &f->ret_decls, DECL_CAN_END_WITH_LBRACE | DECL_CAN_END_WITH_COMMA))
@@ -943,7 +945,8 @@ static Status parse_fn_expr(Parser *p, FnExpr *f) {
 		}
 		--t->token;	/* move back to { */
 		/* just set return type to void. the actual return type will be set by types.c:type_of_fn */
-		f->ret_type.kind = TYPE_VOID;
+		f->ret_type.kind = TYPE_BUILTIN;
+		f->ret_type.builtin = BUILTIN_VOID;
 		f->ret_type.flags = 0;
 	} else {
 		if (!parse_type(p, &f->ret_type, NULL)) {
@@ -1088,11 +1091,12 @@ static Status ctype_to_type(Allocator *a, CType *ctype, Type *type, Location whe
 	case CTYPE_DOUBLE:
 		type->builtin = BUILTIN_F64;
 		break;
-	case CTYPE_PTR:
+	case CTYPE_PTR: {
 		type->kind = TYPE_PTR;
-		type->ptr = allocr_calloc(a, 1, sizeof *type->ptr);
-		type->ptr->kind = TYPE_VOID;
-		break;
+		Type *p = type->ptr = allocr_calloc(a, 1, sizeof *type->ptr);
+		p->kind = TYPE_BUILTIN;
+		p->builtin = BUILTIN_VOID;
+	} break;
 	case CTYPE_UNSIGNED: assert(0); break;
 	}
 	if (size != 0) {
@@ -1537,7 +1541,8 @@ static Status parse_expr(Parser *p, Expression *e, Token *end) {
 			if (t->token == end) {
 				/* void */
 				ret_ctype->kind = CTYPE_NONE;
-				ret_type->kind = TYPE_VOID;
+				ret_type->kind = TYPE_BUILTIN;
+				ret_type->builtin = BUILTIN_VOID;
 				ret_type->flags = 0;
 			} else {
 				if (!parse_c_type(p, ret_ctype, ret_type))
diff --git a/test.toc b/test.toc
index 822084b..891b513 100644
--- a/test.toc
+++ b/test.toc
@@ -1,11 +1,7 @@
 #include "std/io.toc";
 
 main ::= fn() {
-	#if "hello" {
-		x := 5;
-	} else {
-		x : FOOTYPE = 6.3 + "foo";
-		_+3=bar;
-	}
-	puti(x);
+	x : &void = 18 as &&char;
+	puti(x as int);
+
 }
diff --git a/toc.c b/toc.c
index 6089c20..954f357 100644
--- a/toc.c
+++ b/toc.c
@@ -81,6 +81,9 @@ static void fprint_char_literal(FILE *f, char c) {
 static inline bool type_is_builtin(Type *t, BuiltinType b) {
 	return t->kind == TYPE_BUILTIN && t->builtin == b;
 }
+static inline bool type_is_void(Type *t) {
+	return t->kind == TYPE_BUILTIN && t->builtin == BUILTIN_VOID;
+}
 
 static inline bool type_is_slicechar(Type *t) {
 	return t->kind == TYPE_SLICE && type_is_builtin(t->slice, BUILTIN_CHAR);
diff --git a/types.c b/types.c
index 22126a0..a3fb651 100644
--- a/types.c
+++ b/types.c
@@ -59,6 +59,7 @@ static size_t compiler_sizeof_builtin(BuiltinType b) {
 	case BUILTIN_TYPE: return sizeof(Type *);
 	case BUILTIN_NMS: return sizeof(Namespace *);
 	case BUILTIN_VARARGS: return sizeof(VarArg *);
+	case BUILTIN_VOID: return 1; /* void ptr arithmetic */
 	}
 	assert(0);
 	return 0;
@@ -80,6 +81,7 @@ static size_t compiler_alignof_builtin(BuiltinType b) {
 	case BUILTIN_TYPE: return toc_alignof(Type *);
 	case BUILTIN_NMS: return toc_alignof(Namespace *);
 	case BUILTIN_VARARGS: return toc_alignof(VarArg *);
+	case BUILTIN_VOID: return 1;
 	}
 	assert(0);
 	return 0;
@@ -227,8 +229,6 @@ static size_t compiler_alignof(Type *t) {
 	switch (t->kind) {
 	case TYPE_BUILTIN:
 		return compiler_alignof_builtin(t->builtin);
-	case TYPE_VOID:
-		return 1;
 	case TYPE_FN:
 		return toc_alignof(FnExpr *);
 	case TYPE_PTR:
@@ -277,7 +277,6 @@ static size_t compiler_sizeof(Type *t) {
 			return SIZE_MAX;
 		return t->struc->size;
 	} break;
-	case TYPE_VOID:
 	case TYPE_UNKNOWN:
 		return 0;
 	case TYPE_EXPR:
@@ -359,7 +358,6 @@ static bool type_eq_exact(Type *a, Type *b) {
 	
 	if (a->kind != b->kind) return false;
 	switch (a->kind) {
-	case TYPE_VOID: return true;
 	case TYPE_UNKNOWN: return true;
 	case TYPE_BUILTIN:
 		return a->builtin == b->builtin;
@@ -429,7 +427,7 @@ static bool type_eq_implicit(Type *a, Type *b) {
 	}
 	if (a->kind == TYPE_PTR) {
 		/* &void casts to &anything */
-		if (a->ptr->kind == TYPE_VOID || b->ptr->kind == TYPE_VOID)
+		if (type_is_builtin(a->ptr, BUILTIN_VOID) || type_is_builtin(b->ptr, BUILTIN_VOID))
 			return true;
 	}
 	return type_eq_exact(a, b);
@@ -447,8 +445,13 @@ static Type *overriding_type(Type *a, Type *b) {
 		}
 		return b;
 	}
+
 	if (b->flags & TYPE_IS_FLEXIBLE)
 		return a;
+
+	if (a->kind == TYPE_PTR && type_is_builtin(a->ptr, BUILTIN_VOID)) 
+		return b;
+
 	/* doesn't matter */
 	return a;
 }
@@ -530,7 +533,6 @@ static Status expr_must_lval(Expression *e, const char *purpose) {
 static bool type_is_compileonly(Type *t) {
 	assert(t->flags & TYPE_IS_RESOLVED);
 	switch (t->kind) {
-	case TYPE_VOID:
 	case TYPE_UNKNOWN:
 		return false;
 	case TYPE_BUILTIN:
@@ -708,7 +710,7 @@ static Status type_of_fn(Typer *tr, FnExpr *f, Type *t, U16 flags) {
 		}
 	}
 	
-	if (f->ret_decls && !generic && f->ret_type.kind == TYPE_VOID /* haven't found return type yet */) {
+	if (f->ret_decls && !generic && type_is_builtin(&f->ret_type, BUILTIN_VOID) /* haven't found return type yet */) {
 		/* find return type */
 
 		arr_foreach(f->ret_decls, Declaration, d) {
@@ -980,7 +982,6 @@ static Status type_resolve(Typer *tr, Type *t, Location where) {
 		}
 	} break;
 	case TYPE_UNKNOWN:
-	case TYPE_VOID:
 	case TYPE_BUILTIN:
 		break;
 	}
@@ -993,7 +994,6 @@ static Status type_resolve(Typer *tr, Type *t, Location where) {
 static bool type_can_be_truthy(Type *t) {
 	assert(t->flags & TYPE_IS_RESOLVED);
 	switch (t->kind) {
-	case TYPE_VOID:
 	case TYPE_TUPLE:
 	case TYPE_ARR:
 	case TYPE_STRUCT:
@@ -1007,6 +1007,7 @@ static bool type_can_be_truthy(Type *t) {
 		switch (t->builtin) {
 		case BUILTIN_TYPE:
 		case BUILTIN_NMS:
+		case BUILTIN_VOID:
 			return false;
 		case BUILTIN_I8:
 		case BUILTIN_U8:
@@ -1045,7 +1046,6 @@ static CastStatus type_cast_status(Type *from, Type *to) {
 	switch (from->kind) {
 	case TYPE_UNKNOWN: return CAST_STATUS_NONE;
 	case TYPE_STRUCT:
-	case TYPE_VOID:
 		return CAST_STATUS_ERR;
 	case TYPE_BUILTIN:
 		switch (from->builtin) {
@@ -1076,6 +1076,7 @@ static CastStatus type_cast_status(Type *from, Type *to) {
 				case BUILTIN_TYPE:
 				case BUILTIN_NMS:
 				case BUILTIN_VARARGS:
+				case BUILTIN_VOID:
 					return CAST_STATUS_ERR;
 				}
 				assert(0);
@@ -1108,6 +1109,7 @@ static CastStatus type_cast_status(Type *from, Type *to) {
 			case BUILTIN_TYPE:
 			case BUILTIN_NMS:
 			case BUILTIN_VARARGS:
+			case BUILTIN_VOID:
 				return CAST_STATUS_ERR;
 			}
 			assert(0);
@@ -1121,6 +1123,7 @@ static CastStatus type_cast_status(Type *from, Type *to) {
 		case BUILTIN_TYPE:
 		case BUILTIN_NMS:
 		case BUILTIN_VARARGS:
+		case BUILTIN_VOID:
 			return CAST_STATUS_ERR;
 		}
 		break;
@@ -1206,7 +1209,7 @@ static Status types_fn(Typer *tr, FnExpr *f, Type *t, Instance *instance) {
 			success = false;
 			goto ret;
 		}
-	} else if (ret_type->kind != TYPE_VOID && !has_named_ret_vals) {
+	} else if (!type_is_builtin(ret_type, BUILTIN_VOID) && !has_named_ret_vals) {
 		Statement *stmts = f->body.stmts;
 		if (arr_len(stmts)) {
 			Statement *last_stmt = (Statement *)stmts + (arr_len(stmts) - 1);
@@ -2019,7 +2022,8 @@ static Status types_expr(Typer *tr, Expression *e) {
 			goto for_fail;
 		}
 		
-		t->kind = TYPE_VOID;
+		t->kind = TYPE_BUILTIN;
+		t->builtin = BUILTIN_VOID;
 		
 		typer_block_exit(tr);
 		}break;
@@ -2173,7 +2177,7 @@ static Status types_expr(Typer *tr, Expression *e) {
 		if (curr->body.ret_expr) {
 			*t = curr->body.ret_expr->type;
 		} else {
-			t->kind = TYPE_VOID;
+			t->kind = TYPE_BUILTIN; t->builtin = BUILTIN_VOID;
 			t->flags |= TYPE_IS_RESOLVED;
 		}
 		while (1) {
@@ -2199,7 +2203,7 @@ static Status types_expr(Typer *tr, Expression *e) {
 				if (nexti->body.ret_expr) {
 					*next_type = nexti->body.ret_expr->type;
 				} else {
-					next_type->kind = TYPE_VOID;
+					next_type->kind = TYPE_BUILTIN; next_type->builtin = BUILTIN_VOID;
 					next_type->flags = TYPE_IS_RESOLVED;
 				}
 				if (!type_eq_implicit(next_type, curr_type)) {
@@ -2217,7 +2221,7 @@ static Status types_expr(Typer *tr, Expression *e) {
 			}
 		}
 		
-		if (!has_else && t->kind != TYPE_VOID) {
+		if (!has_else && !type_is_builtin(t, BUILTIN_VOID)) {
 			err_print(e->where, "Non-void if block with no else.");
 			return false;
 		}
@@ -2235,7 +2239,8 @@ static Status types_expr(Typer *tr, Expression *e) {
 			err_print(w->body.ret_expr->where, "while loops can't return values -- you're missing a semicolon (;)");
 			return false;
 		}
-		t->kind = TYPE_VOID;
+		t->kind = TYPE_BUILTIN;
+		t->builtin = BUILTIN_VOID;
 	} break;
 	case EXPR_CALL: {
 		CallExpr *c = &e->call;
@@ -2829,7 +2834,8 @@ static Status types_expr(Typer *tr, Expression *e) {
 		if (b->ret_expr) {
 			*t = b->ret_expr->type;
 		} else {
-			t->kind = TYPE_VOID;
+			t->kind = TYPE_BUILTIN;
+			t->builtin = BUILTIN_VOID;
 		}
 	} break;
 	case EXPR_C: {
@@ -2931,10 +2937,6 @@ static Status types_expr(Typer *tr, Expression *e) {
 			assert(0); /* types_expr is what makes things UNARY_LEN */
 			break;
 		case UNARY_TYPEOF: {
-			if (of->type.kind == TYPE_VOID) {
-				err_print(of->where, "This has type void, but you're trying to apply typeof to it.");
-				return false;
-			}
 			if (type_is_builtin(&of->type, BUILTIN_VARARGS)) {
 				err_print(of->where, "You can't apply typeof to varargs.");
 				return false;
@@ -3061,7 +3063,8 @@ static Status types_expr(Typer *tr, Expression *e) {
 				switch (o) {
 				case BINARY_SET:
 					/* type of x = y is always void */
-					t->kind = TYPE_VOID;
+					t->kind = TYPE_BUILTIN;
+					t->builtin = BUILTIN_VOID;
 					break;
 				case BINARY_LT:
 				case BINARY_GT:
@@ -3094,7 +3097,7 @@ static Status types_expr(Typer *tr, Expression *e) {
 				o == BINARY_SET_SUB ||
 				o == BINARY_SET_MUL ||
 				o == BINARY_SET_DIV) {
-				t->kind = TYPE_VOID; /* actually, it's just void */
+				t->kind = TYPE_BUILTIN; t->builtin = BUILTIN_VOID; /* actually, it's just void */
 			}
 				
 			break;
@@ -3241,7 +3244,8 @@ static Status types_expr(Typer *tr, Expression *e) {
 					/* allow access of slice pointer */
 					t->kind = TYPE_PTR;
 					t->ptr = typer_calloc(tr, 1, sizeof *t->ptr);
-					t->ptr->kind = TYPE_VOID;
+					t->ptr->kind = TYPE_BUILTIN;
+					t->ptr->builtin = BUILTIN_VOID;
 					t->ptr->flags = TYPE_IS_RESOLVED;
 					break;
 				}
@@ -3396,7 +3400,7 @@ static Status types_block(Typer *tr, Block *b) {
 		if (s->kind == STMT_EXPR && (s->flags & STMT_EXPR_NO_SEMICOLON)) {
 			/* not voided */
 			Expression *e = s->expr;
-			if (e->type.kind == TYPE_VOID) {
+			if (type_is_builtin(&e->type, BUILTIN_VOID)) {
 				if (!(e->kind == EXPR_BLOCK
 					  || e->kind == EXPR_IF
 					  || e->kind == EXPR_WHILE
@@ -3427,6 +3431,7 @@ static Status types_block(Typer *tr, Block *b) {
 }
 
 static Status types_decl(Typer *tr, Declaration *d) {
+	Type *dtype = &d->type;
 	if (d->flags & DECL_FOUND_TYPE) return true;
 	bool success = true;
 
@@ -3438,15 +3443,14 @@ static Status types_decl(Typer *tr, Declaration *d) {
 	}
 	
 	if (d->flags & DECL_INFER) {
-		d->type.kind = TYPE_UNKNOWN;
-		d->type.flags = 0;
+		dtype->kind = TYPE_UNKNOWN;
+		dtype->flags = 0;
 		return true;
 	}
 	typer_arr_add(tr, tr->in_decls, d);
 	if (d->flags & DECL_ANNOTATES_TYPE) {
 		/* type supplied */
-		assert(d->type.kind != TYPE_VOID); /* there's no way to annotate void */
-		if (!type_resolve(tr, &d->type, d->where)) {
+		if (!type_resolve(tr, dtype, d->where)) {
 			success = false;
 			goto ret;
 		}
@@ -3458,8 +3462,8 @@ static Status types_decl(Typer *tr, Declaration *d) {
 		}
 		assert(d->expr.type.flags & TYPE_IS_RESOLVED);
 		if (d->flags & DECL_ANNOTATES_TYPE) {
-			if (!type_eq_implicit(&d->expr.type, &d->type)) {
-				char *decl_type = type_to_str(&d->type),
+			if (!type_eq_implicit(&d->expr.type, dtype)) {
+				char *decl_type = type_to_str(dtype),
 					*expr_type = type_to_str(&d->expr.type);
 				err_print(d->expr.where, "Declaration type %s does not match expression type %s.", decl_type, expr_type);
 				free(decl_type); free(expr_type);
@@ -3467,14 +3471,14 @@ static Status types_decl(Typer *tr, Declaration *d) {
 				goto ret;
 			}
 		} else {
-			if (d->expr.type.kind == TYPE_VOID) {
+			if (type_is_void(&d->expr.type)) {
 				/* e.g. x := (fn(){})(); */
 				err_print(d->expr.where, "Use of void value.");
 				success = false;
 				goto ret;
 			}
-			d->type = d->expr.type;
-			d->type.flags &= (TypeFlags)~(TypeFlags)TYPE_IS_FLEXIBLE; /* x := 5; => x is not flexible */
+			*dtype = d->expr.type;
+			dtype->flags &= (TypeFlags)~(TypeFlags)TYPE_IS_FLEXIBLE; /* x := 5; => x is not flexible */
 		}
 		bool need_value = (d->flags & DECL_IS_CONST) || !tr->block || tr->block->kind == BLOCK_NMS;
 		if (need_value) {
@@ -3484,26 +3488,62 @@ static Status types_decl(Typer *tr, Declaration *d) {
 					success = false;
 					goto ret;
 				}
-				copy_val(tr->allocr, &d->val, val, &d->type);
+				copy_val(tr->allocr, &d->val, val, dtype);
 				d->flags |= DECL_FOUND_VAL;
 			}
 		}
 	}
+
+	size_t n_idents; n_idents = arr_len(d->idents);
+
+	if (type_is_compileonly(dtype)) {
+		if (!(d->flags & DECL_IS_CONST)) {
+			char *s = type_to_str(dtype);
+			err_print(d->where, "Declarations with type %s must be constant.", s);
+			free(s);
+			success = false;
+			goto ret;
+		}
+	}
 	
-	for (size_t i = 0; i < arr_len(d->idents); ++i) {
-		Type *t = d->type.kind == TYPE_TUPLE ? &d->type.tuple[i] : &d->type;
-		if (type_is_compileonly(&d->type)) {
-			if (!(d->flags & DECL_IS_CONST)) {
-				char *s = type_to_str(&d->type);
-				err_print(d->where, "Declarations with type %s must be constant.", s);
-				free(s);
-				success = false;
-				goto ret;
-			}
+	if (dtype->kind == TYPE_TUPLE) {
+		if (n_idents != arr_len(dtype->tuple)) {
+			err_print(d->where, "Expected to have %lu things declared in declaration, but got %lu.", (unsigned long)arr_len(dtype->tuple), (unsigned long)n_idents);
+			success = false;
+			goto ret;
+		}
+	}
+	if (dtype->kind == TYPE_UNKNOWN) {
+		if (!d->where.file->ctx->have_errored) /* don't do an error if we haven't already done one, because it might be because of that */
+			err_print(d->where, "Can't determine type of declaration.");
+		success = false;
+		goto ret;
+	}
+	if (dtype->kind == TYPE_BUILTIN) {
+		if (dtype->builtin == BUILTIN_VARARGS && !(d->flags & DECL_IS_PARAM)) {
+			err_print(d->where, "Only parameters can be varargs.");
+			success = false;
+			goto ret;
+		} else if (dtype->builtin == BUILTIN_VOID) {
+			err_print(d->where, "The type of a declaration can't be void.");
+			success = false;
+			goto ret;
 		}
+	}
+	if (d->flags & DECL_IS_CONST) {
+		if (dtype->kind == TYPE_PTR) {
+			err_print(d->where, "You can't have a constant pointer.");
+			success = false;
+			goto ret;
+		}
+	}
+		
+	
+	for (int i = 0, len = (int)n_idents; i < len; ++i) {
+		Type *t = decl_type_at_index(d, i);
 		if (type_is_builtin(t, BUILTIN_TYPE)) {
 			if (d->flags & DECL_HAS_EXPR) {
-				Value *val = d->type.kind == TYPE_TUPLE ? &d->val.tuple[i] : &d->val;
+				Value *val = decl_val_at_index(d, i);
 				if (val->type->kind == TYPE_STRUCT && val->type->struc->params) {
 					/* don't resolve it because it's not really complete */
 				} else {
@@ -3522,34 +3562,7 @@ static Status types_decl(Typer *tr, Declaration *d) {
 			t->fn.constness = NULL;
 		}
 	}
-	
-	size_t n_idents; n_idents = arr_len(d->idents);
-	if (d->type.kind == TYPE_TUPLE) {
-		if (n_idents != arr_len(d->type.tuple)) {
-			err_print(d->where, "Expected to have %lu things declared in declaration, but got %lu.", (unsigned long)arr_len(d->type.tuple), (unsigned long)n_idents);
-			success = false;
-			goto ret;
-		}
-	}
-	if (d->type.kind == TYPE_UNKNOWN) {
-		if (!d->where.file->ctx->have_errored) /* don't do an error if we haven't already done one, because it might be because of that */
-			err_print(d->where, "Can't determine type of declaration.");
-		success = false;
-		goto ret;
-	}
-	if (type_is_builtin(&d->type, BUILTIN_VARARGS) && !(d->flags & DECL_IS_PARAM)) {
-		err_print(d->where, "Only parameters can be varargs.");
-		success = false;
-		goto ret;
-	}
-	if (d->flags & DECL_IS_CONST) {
-		if (d->type.kind == TYPE_PTR) {
-			err_print(d->where, "You can't have a constant pointer.");
-			success = false;
-			goto ret;
-		}
-	}
-		
+
 	if (d->flags & DECL_USE) {
 		int idx = 0;
 		if (arr_len(d->idents) > 1) {
@@ -3585,8 +3598,8 @@ static Status types_decl(Typer *tr, Declaration *d) {
 	d->flags |= DECL_FOUND_TYPE;
 	if (!success) {
 		/* use unknown type if we didn't get the type */
-		d->type.flags = TYPE_IS_RESOLVED;
-		d->type.kind = TYPE_UNKNOWN;
+		dtype->flags = TYPE_IS_RESOLVED;
+		dtype->kind = TYPE_UNKNOWN;
 	}
 	arr_remove_lasta(tr->in_decls, tr->allocr);
 	return success;
@@ -3700,7 +3713,7 @@ static Status types_stmt(Typer *tr, Statement *s) {
 		}
 	    r->referring_to = &tr->fn->body;
 		if (r->flags & RET_HAS_EXPR) {
-			if (tr->fn->ret_type.kind == TYPE_VOID) {
+			if (type_is_void(&tr->fn->ret_type)) {
 				err_print(s->where, "Return value in a void function.");
 				return false;
 			}
@@ -3717,8 +3730,7 @@ static Status types_stmt(Typer *tr, Statement *s) {
 				return false;
 			}
 		} else {
-			if (tr->fn->ret_type.kind != TYPE_VOID
-				&& !tr->fn->ret_decls) {
+			if (type_is_void(&tr->fn->ret_type) && !tr->fn->ret_decls) {
 				err_print(s->where, "No return value in non-void function.");
 				return false;
 			}
diff --git a/types.h b/types.h
index f3fff36..8ceaa32 100644
--- a/types.h
+++ b/types.h
@@ -303,6 +303,7 @@ typedef enum {
 	KW_FLOAT,
 	KW_F32,
 	KW_F64,
+	KW_VOID,
 	KW_TYPE,
 	KW_NAMESPACE,
 	KW_CHAR,
@@ -326,8 +327,8 @@ static const char *const keywords[KW_COUNT] = {
 	"if", "elif", "else", "while", "for", "return", "break",
 	"continue", "defer", "fn", "as", "struct",
 	"int", "i8", "i16", "i32", "i64",
-	"u8", "u16", "u32", "u64", "float", "f32", "f64", "Type",
-	"Namespace", "char", "bool", "true", "false", "nms", "use",
+	"u8", "u16", "u32", "u64", "float", "f32", "f64", "void",
+	"Type",	"Namespace", "char", "bool", "true", "false", "nms", "use",
 	"typeof", "sizeof", "alignof"
 };
 
@@ -398,7 +399,6 @@ typedef struct Tokenizer {
 
 typedef enum {
 	TYPE_UNKNOWN,
-	TYPE_VOID,
 	TYPE_BUILTIN,
 	TYPE_FN,
 	TYPE_TUPLE,
@@ -426,7 +426,8 @@ typedef enum {
 	BUILTIN_BOOL,
 	BUILTIN_TYPE,
 	BUILTIN_VARARGS,
-	BUILTIN_NMS
+	BUILTIN_NMS,
+	BUILTIN_VOID
 } BuiltinType;
 
 
-- 
cgit v1.2.3