diff options
-rw-r--r-- | allocator.c | 27 | ||||
-rw-r--r-- | arr.c | 7 | ||||
-rw-r--r-- | copy.c | 171 | ||||
-rw-r--r-- | eval.c | 56 | ||||
-rw-r--r-- | instance_table.c | 18 | ||||
-rw-r--r-- | test.toc | 13 | ||||
-rw-r--r-- | toc.c | 1 | ||||
-rw-r--r-- | types.c | 150 | ||||
-rw-r--r-- | types.h | 33 |
9 files changed, 304 insertions, 172 deletions
diff --git a/allocator.c b/allocator.c index e49ea3e..4394201 100644 --- a/allocator.c +++ b/allocator.c @@ -39,41 +39,36 @@ static void *allocr_malloc(Allocator *a, size_t bytes) { static void *allocr_calloc(Allocator *a, size_t n, size_t sz) { #if NO_ALLOCATOR - (void)a; - return err_calloc(n, sz); -#else + a = NULL; +#endif + if (a == NULL) return err_calloc(n, sz); /* OPTIM: use calloc */ size_t bytes = n * sz; void *data = allocr_malloc(a, bytes); memset(data, 0, bytes); return data; -#endif } /* OPTIM */ static void *allocr_realloc(Allocator *a, void *data, size_t old_size, size_t new_size) { #if NO_ALLOCATOR - (void)a; - (void)old_size; - return err_realloc(data, new_size); -#else + a = NULL; +#endif + if (a == NULL) return err_realloc(data, new_size); void *ret = allocr_malloc(a, new_size); memcpy(ret, data, old_size); return ret; -#endif } static void allocr_free(Allocator *a, void *data, size_t size) { #if NO_ALLOCATOR - (void)a; - (void)size; - free(data); -#else + a = NULL; +#endif + if (a == NULL) { + free(data); + } /* OPTIM */ - (void)a; (void)size; - (void)data; -#endif } static void allocr_free_all(Allocator *a) { @@ -106,6 +106,12 @@ static void arr_remove_last_(void **arr, size_t item_sz) { arr_hdr(*arr)->len--; (void)item_sz; } +static void arr_copya_(void **out, void *in, size_t item_sz, Allocator *a) { + size_t len = arr_len(in); + arr_resva_(out, len, item_sz, a); + memcpy(*out, in, len * item_sz); +} + #ifdef __GNUC__ #define typeof __typeof__ #endif @@ -139,6 +145,7 @@ You shouldn't rely on this, though, e.g. by doing #define arr_last(arr) arr_last_((void *)(arr), sizeof *(arr)) #define arr_foreach(arr, type, var) for (type *var = arr_len(arr) ? arr : NULL, *var##_foreach_end = arr_last(arr); var; var == var##_foreach_end ? var = NULL : var++) #define arr_remove_last(arr) arr_remove_last_((void **)(arr), sizeof **(arr)) +#define arr_copya(out, in, a) do { assert(sizeof *(in) == sizeof **(out)); arr_copya_((void **)(out), (in), sizeof **(out), (a)); } while(0) #ifdef TOC_DEBUG static void arr_test(void) { @@ -0,0 +1,171 @@ +/* these copy functions MUST be used before typing!!!! (except for copy_val) */ + +static void copy_expr(Allocator *a, Expression *out, Expression *in); +static void copy_block(Allocator *a, Block *out, Block *in); + +static void copy_val(Allocator *allocr, Value *out, Value *in, Type *t) { + switch (t->kind) { + case TYPE_BUILTIN: + case TYPE_FN: + case TYPE_PTR: + case TYPE_SLICE: + case TYPE_VOID: + case TYPE_UNKNOWN: + case TYPE_TYPE: + *out = *in; + break; + case TYPE_ARR: { + size_t bytes = t->arr.n * compiler_sizeof(t->arr.of); + out->arr = allocr_malloc(allocr, bytes); + memcpy(out->arr, in->arr, bytes); + } break; + case TYPE_TUPLE: { + size_t bytes = arr_len(t->tuple) * sizeof(*out->tuple); + out->tuple = allocr_malloc(allocr, bytes); + memcpy(out->tuple, in->tuple, bytes); + } break; + case TYPE_STRUCT: { + size_t bytes = compiler_sizeof(t); + out->struc = allocr_malloc(allocr, bytes); + memcpy(out->struc, in->struc, bytes); + } break; + case TYPE_USER: + copy_val(allocr, out, in, type_user_underlying(t)); + break; + } +} + +static void copy_type(Allocator *a, Type *out, Type *in) { + assert(!(in->flags & TYPE_IS_RESOLVED)); + *out = *in; + switch (in->kind) { + case TYPE_BUILTIN: + case TYPE_TYPE: + case TYPE_VOID: + case TYPE_UNKNOWN: + case TYPE_USER: + break; + case TYPE_FN: { + size_t ntypes = arr_len(in->fn.types); + out->fn.types = NULL; + arr_set_lena(&out->fn.types, ntypes, a); + for (size_t i = 0; i < ntypes; i++) { + copy_type(a, &out->fn.types[i], &in->fn.types[i]); + } + } break; + case TYPE_TUPLE: { + size_t ntypes = arr_len(in->tuple); + arr_set_lena(&out->tuple, ntypes, a); + for (size_t i = 0; i < ntypes; i++) { + copy_type(a, &out->tuple[i], &in->tuple[i]); + } + } break; + case TYPE_ARR: + out->arr.n_expr = allocr_malloc(a, sizeof *out->arr.n_expr); + copy_expr(a, out->arr.n_expr, in->arr.n_expr); + out->arr.of = allocr_malloc(a, sizeof *out->arr.of); + copy_type(a, out->arr.of, in->arr.of); + break; + case TYPE_PTR: + out->ptr = allocr_malloc(a, sizeof *out->ptr); + copy_type(a, out->ptr, in->ptr); + break; + case TYPE_SLICE: + out->ptr = allocr_malloc(a, sizeof *out->slice); + copy_type(a, out->slice, in->slice); + break; + case TYPE_STRUCT: + out->struc.fields = NULL; + size_t nfields = arr_len(in->struc.fields); + arr_set_lena(&out->struc.fields, nfields, a); + for (size_t i = 0; i < nfields; i++) { + Field *fout = &out->struc.fields[i]; + Field *fin = &in->struc.fields[i]; + *fout = *fin; + copy_type(a, fout->type, fin->type); + } + break; + } +} + +static void copy_expr(Allocator *a, Expression *out, Expression *in) { + *out = *in; + switch (in->kind) { + case EXPR_LITERAL_FLOAT: + case EXPR_LITERAL_INT: + case EXPR_LITERAL_STR: + case EXPR_LITERAL_CHAR: + case EXPR_LITERAL_BOOL: + case EXPR_IDENT: + break; + case EXPR_UNARY_OP: + copy_expr(a, out->unary.of = allocr_malloc(a, sizeof *out->unary.of), in->unary.of); + break; + case EXPR_BINARY_OP: + copy_expr(a, out->binary.lhs = allocr_malloc(a, sizeof *out->binary.lhs), in->binary.lhs); + copy_expr(a, out->binary.rhs = allocr_malloc(a, sizeof *out->binary.rhs), in->binary.rhs); + break; + case EXPR_IF: { + IfExpr *iin = &in->if_; + IfExpr *iout = &out->if_; + if (iin->cond) + copy_expr(a, iout->cond = allocr_malloc(a, sizeof *iout->cond), iin->cond); + if (iin->next_elif) + copy_expr(a, iout->next_elif = allocr_malloc(a, sizeof *iout->next_elif), iin->next_elif); + copy_block(a, &iout->body, &iin->body); + } break; + case EXPR_WHILE: { + WhileExpr *win = &in->while_; + WhileExpr *wout = &out->while_; + if (win->cond) + copy_expr(a, wout->cond = allocr_malloc(a, sizeof *wout->cond), win->cond); + copy_block(a, &wout->body, &win->body); + } break; + case EXPR_EACH: { + EachExpr *ein = &in->each; + EachExpr *eout = &out->each; + copy_type(a, &eout->type, &ein->type); + if (ein->flags & EACH_IS_RANGE) { + copy_expr(a, eout->range.from = allocr_malloc(a, sizeof *eout->range.from), ein->range.from); + if (ein->range.to) + copy_expr(a, eout->range.to = allocr_malloc(a, sizeof *eout->range.to), ein->range.to); + if (ein->range.step) + copy_expr(a, eout->range.step = allocr_malloc(a, sizeof *eout->range.step), ein->range.step); + } else { + copy_expr(a, eout->of = allocr_malloc(a, sizeof *eout->of), ein->of); + } + copy_block(a, &eout->body, &ein->body); + } break; + } +} + +static void copy_stmt(Allocator *a, Statement *out, Statement *in) { + *out = *in; + assert(!(in->decl.flags & DECL_FOUND_TYPE)); + switch (in->kind) { + case STMT_RET: + if (in->flags & RET_HAS_EXPR) + copy_expr(a, &out->ret.expr, &in->ret.expr); + break; + case STMT_EXPR: + copy_expr(a, &out->expr, &in->expr); + break; + case STMT_DECL: + copy_expr(a, &out->decl.expr, &in->decl.expr); + if (in->decl.flags & DECL_FOUND_VAL) { + copy_val(a, &out->decl.val, &in->decl.val, &in->decl.type); + } + if (in->decl.flags & DECL_ANNOTATES_TYPE) + copy_type(a, &out->decl.type, &in->decl.type); + break; + } +} + +static void copy_block(Allocator *a, Block *out, Block *in) { + *out = *in; + size_t nstmts = arr_len(in->stmts); + arr_set_lena(&out->stmts, nstmts, a); + for (size_t i = 0; i < nstmts; i++) { + copy_stmt(a, &out->stmts[i], &in->stmts[i]); + } +} @@ -348,50 +348,6 @@ static void fprint_val(FILE *f, Value v, Type *t) { } } -/* -allocr can be NULL -*/ -static void val_copy(Allocator *allocr, Value *dest, Value *src, Type *t) { - switch (t->kind) { - case TYPE_BUILTIN: - case TYPE_FN: - case TYPE_PTR: - case TYPE_SLICE: - case TYPE_VOID: - case TYPE_UNKNOWN: - case TYPE_TYPE: - *dest = *src; - break; - case TYPE_ARR: { - size_t bytes = t->arr.n * compiler_sizeof(t->arr.of); - if (allocr) - dest->arr = allocr_malloc(allocr, bytes); - else - dest->arr = err_malloc(bytes); - memcpy(dest->arr, src->arr, bytes); - } break; - case TYPE_TUPLE: { - size_t bytes = arr_len(t->tuple) * sizeof(*dest->tuple); - if (allocr) - dest->tuple = allocr_malloc(allocr, bytes); - else - dest->tuple = err_malloc(bytes); - memcpy(dest->tuple, src->tuple, bytes); - } break; - case TYPE_STRUCT: { - size_t bytes = compiler_sizeof(t); - if (allocr) - dest->struc = allocr_malloc(allocr, bytes); - else - dest->struc = err_malloc(bytes); - memcpy(dest->struc, src->struc, bytes); - } break; - case TYPE_USER: - val_copy(allocr, dest, src, type_user_underlying(t)); - break; - } -} - static void *val_ptr_to_free(Value *v, Type *t) { switch (t->kind) { case TYPE_BUILTIN: @@ -1468,7 +1424,7 @@ static bool eval_expr(Evaluator *ev, Expression *e, Value *v) { arr_foreach(p->idents, Identifier, i) { Type *type = p->type.kind == TYPE_TUPLE ? &p->type.tuple[idx++] : &p->type; IdentDecl *id = ident_decl(*i); - val_copy(NULL, &id->val, &args[arg], type); + copy_val(NULL, &id->val, &args[arg], type); id->flags |= IDECL_HAS_VAL; arg++; } @@ -1480,7 +1436,7 @@ static bool eval_expr(Evaluator *ev, Expression *e, Value *v) { IdentDecl *id = ident_decl(*i); if (d->flags & DECL_HAS_EXPR) { assert(d->expr.kind == EXPR_VAL); - val_copy(NULL, &id->val, &d->expr.val, type); + copy_val(NULL, &id->val, &d->expr.val, type); id->flags |= IDECL_HAS_VAL; } else { id->flags |= IDECL_HAS_VAL; @@ -1507,7 +1463,7 @@ static bool eval_expr(Evaluator *ev, Expression *e, Value *v) { return false; Value *element = arr_add(&tuple); Type *type = decl_type_at_index(d, i); - val_copy(NULL, element, &this_one, type); + copy_val(NULL, element, &this_one, type); void *to_free = val_ptr_to_free(element, type); if (to_free) *(void **)arr_add(&ev->to_free) = to_free; @@ -1608,7 +1564,7 @@ static bool eval_decl(Evaluator *ev, Declaration *d) { Type *type = decl_type_at_index(d, index); if (!is_const) { if (has_expr) { - val_copy(NULL, &id->val, &val, type); + copy_val(NULL, &id->val, &val, type); } else { id->val = val_zero(type); } @@ -1634,7 +1590,7 @@ static bool eval_stmt(Evaluator *ev, Statement *stmt) { Value r; if (!eval_expr(ev, &stmt->ret.expr, &r)) return false; - val_copy(NULL, &ev->ret_val, &r, &stmt->ret.expr.type); + copy_val(NULL, &ev->ret_val, &r, &stmt->ret.expr.type); } break; } return true; @@ -1655,7 +1611,7 @@ static bool eval_block(Evaluator *ev, Block *b, Type *t, Value *v) { if (!eval_expr(ev, b->ret_expr, &r)) return false; /* make a copy so that r's data isn't freed when we exit the block */ - val_copy(NULL, v, &r, &b->ret_expr->type); + copy_val(NULL, v, &r, &b->ret_expr->type); void *free_ptr = val_ptr_to_free(v, t); if (free_ptr) *(void **)arr_add(&prev_to_free) = free_ptr; diff --git a/instance_table.c b/instance_table.c index 5da4a77..948e4e5 100644 --- a/instance_table.c +++ b/instance_table.c @@ -225,10 +225,8 @@ static Instance *instance_table_adda(Allocator *a, HashTable *h, Value v, Type * bool *already_exists) { if (h->n * 2 >= h->cap) { U64 new_cap = h->cap * 2 + 3; - Instance **new_data = a ? allocr_malloc(a, (size_t)new_cap * sizeof *new_data) - : malloc((size_t)new_cap * sizeof *new_data); - bool *new_occupied = a ? allocr_calloc(a, (size_t)new_cap, sizeof *new_occupied) - : calloc((size_t)new_cap, sizeof *new_occupied); + Instance **new_data = allocr_malloc(a, (size_t)new_cap * sizeof *new_data); + bool *new_occupied = allocr_calloc(a, (size_t)new_cap, sizeof *new_occupied); Instance **old_data = h->data; bool *old_occupied = h->occupied; for (U64 i = 0; i < h->cap; i++) { @@ -246,13 +244,8 @@ static Instance *instance_table_adda(Allocator *a, HashTable *h, Value v, Type * } h->data = new_data; h->occupied = new_occupied; - if (a) { - allocr_free(a, old_occupied, h->cap * sizeof *old_occupied); - allocr_free(a, old_data, h->cap * sizeof *old_data); - } else { - free(old_occupied); - free(old_data); - } + allocr_free(a, old_occupied, h->cap * sizeof *old_occupied); + allocr_free(a, old_data, h->cap * sizeof *old_data); h->cap = new_cap; } Instance **data = h->data; @@ -271,8 +264,7 @@ static Instance *instance_table_adda(Allocator *a, HashTable *h, Value v, Type * if (already_exists) { /* create, because it doesn't exist */ *already_exists = false; - data[index] = a ? allocr_malloc(a, sizeof *data[index]) - : malloc(sizeof *data[index]); + data[index] = allocr_malloc(a, sizeof *data[index]); data[index]->val = v; h->occupied[index] = true; h->n++; @@ -8,14 +8,11 @@ puti @= fn(x: int) { // }; +stuff @= fn(t @ Type) int { + 4327834 as t as int +}; -main @= fn() { - a, b := f(10, 20); - c, d := f(10, 10+10); - puti(a); puti(b); - puti(c); puti(d); -f @= fn(a: int, x @ int) (int, int) { - x * a, x + a -}; +main @= fn() { + puti(stuff(int)); }; @@ -36,6 +36,7 @@ static Type *type_inner(Type *t) { #include "blockarr.c" #include "str.c" #include "instance_table.c" +#include "copy.c" #include "identifiers.c" #include "tokenizer.c" @@ -181,9 +181,7 @@ static bool expr_must_lval(Expression *e) { return false; } -static bool type_of_fn(Typer *tr, Expression *e, Type *t) { - assert(e->kind == EXPR_FN); - FnExpr *f = &e->fn; +static bool type_of_fn(Typer *tr, FnExpr *f, Location where, Type *t) { t->kind = TYPE_FN; t->fn.types = NULL; t->fn.constness = NULL; /* OPTIM: constant doesn't need to be a dynamic array */ @@ -216,13 +214,13 @@ static bool type_of_fn(Typer *tr, Expression *e, Type *t) { } } } - if (!type_resolve(tr, &f->ret_type, e->where)) + if (!type_resolve(tr, &f->ret_type, where)) return false; *ret_type = f->ret_type; size_t idx = 0; arr_foreach(f->params, Declaration, decl) { if (!types_decl(tr, decl)) return false; - if (!type_resolve(tr, &decl->type, e->where)) + if (!type_resolve(tr, &decl->type, where)) return false; U32 is_at_all_const = decl->flags & (DECL_IS_CONST | DECL_SEMI_CONST); if (is_at_all_const) { @@ -318,7 +316,7 @@ static bool type_of_ident(Typer *tr, Location where, Identifier i, Type *t) { } else { if ((d->flags & DECL_HAS_EXPR) && (d->expr.kind == EXPR_FN)) { /* allow using a function before declaring it */ - if (!type_of_fn(tr, &d->expr, t)) return false; + if (!type_of_fn(tr, &d->expr.fn, d->expr.where, t)) return false; return true; } else { if (location_after(d->where, where)) { @@ -591,78 +589,88 @@ static bool arg_is_const(Expression *arg, Constness constness) { } +static bool types_fn(Typer *tr, FnExpr *f, Type *t, Location where) { + FnExpr *prev_fn = tr->fn; + bool success = true; + { + HashTable z = {0}; + f->instances = z; + } + + assert(t->kind == TYPE_FN); + + /* don't type function body yet; we need to do that for every instance */ + if (t->fn.constness) + return true; + + tr->fn = f; + if (!fn_enter(f, SCOPE_CHECK_REDECL)) { + success = false; + goto ret; + } + bool block_success = true; + block_success = types_block(tr, &f->body); + fn_exit(f); + if (!block_success) { + success = false; + goto ret; + } + Expression *ret_expr = f->body.ret_expr; + Type *ret_type = t->fn.types; + bool has_named_ret_vals = f->ret_decls != NULL; + if (ret_expr) { + if (!types_expr(tr, ret_expr)) { + success = false; + goto ret; + } + if (!type_eq(ret_type, &ret_expr->type)) { + char *got = type_to_str(&ret_expr->type); + char *expected = type_to_str(ret_type); + err_print(ret_expr->where, "Returning type %s, but function returns type %s.", got, expected); + info_print(where, "Function declaration is here."); + free(got); free(expected); + success = false; + goto ret; + } + } else if (ret_type->kind != TYPE_VOID && !has_named_ret_vals) { + Statement *stmts = f->body.stmts; + if (arr_len(stmts)) { + Statement *last_stmt = (Statement *)stmts + (arr_len(stmts) - 1); + if (last_stmt->kind == STMT_RET) { + /* + last statement is a return, so it doesn't matter that the function has no return value + ideally this would handle if foo { return 5; } else { return 6; } + */ + success = true; + goto ret; + } + } + /* TODO: this should really be at the closing brace, and not the function declaration */ + char *expected = type_to_str(ret_type); + err_print(f->body.end, "No return value in function which returns %s.", expected); + free(expected); + info_print(where, "Function was declared here:"); + success = false; + goto ret; + } + ret: + tr->fn = prev_fn; + return success; +} + static bool types_expr(Typer *tr, Expression *e) { if (e->flags & EXPR_FOUND_TYPE) return true; Type *t = &e->type; t->flags = 0; t->kind = TYPE_UNKNOWN; /* default to unknown type (in the case of an error) */ e->flags |= EXPR_FOUND_TYPE; /* even if failed, pretend we found the type */ - bool success = true; switch (e->kind) { - case EXPR_FN: { - { - HashTable z = {0}; - e->fn.instances = z; - } - FnExpr *prev_fn = tr->fn; - FnExpr *f = &e->fn; - if (!type_of_fn(tr, e, t)) { - success = false; - goto fn_ret; - } - tr->fn = f; - if (!fn_enter(f, SCOPE_CHECK_REDECL)) + case EXPR_FN: + if (!type_of_fn(tr, &e->fn, e->where, &e->type)) + return false; + if (!types_fn(tr, &e->fn, &e->type, e->where)) return false; - bool block_success = true; - block_success = types_block(tr, &e->fn.body); - fn_exit(f); - if (!block_success) { - success = false; - goto fn_ret; - } - Expression *ret_expr = f->body.ret_expr; - assert(t->kind == TYPE_FN); - Type *ret_type = t->fn.types; - bool has_named_ret_vals = f->ret_decls != NULL; - if (ret_expr) { - if (!types_expr(tr, ret_expr)) { - success = false; - goto fn_ret; - } - if (!type_eq(ret_type, &ret_expr->type)) { - char *got = type_to_str(&ret_expr->type); - char *expected = type_to_str(ret_type); - err_print(ret_expr->where, "Returning type %s, but function returns type %s.", got, expected); - info_print(e->where, "Function declaration is here."); - free(got); free(expected); - success = false; - goto fn_ret; - } - } else if (ret_type->kind != TYPE_VOID && !has_named_ret_vals) { - Statement *stmts = e->fn.body.stmts; - if (arr_len(stmts)) { - Statement *last_stmt = (Statement *)stmts + (arr_len(stmts) - 1); - if (last_stmt->kind == STMT_RET) { - /* - last statement is a return, so it doesn't matter that the function has no return value - ideally this would handle if foo { return 5; } else { return 6; } - */ - success = true; - goto fn_ret; - } - } - /* TODO: this should really be at the closing brace, and not the function declaration */ - char *expected = type_to_str(ret_type); - err_print(f->body.end, "No return value in function which returns %s.", expected); - free(expected); - info_print(e->where, "Function was declared here:"); - success = false; - goto fn_ret; - } - fn_ret: - tr->fn = prev_fn; - if (!success) return false; - } break; + break; case EXPR_LITERAL_INT: t->kind = TYPE_BUILTIN; t->builtin = BUILTIN_I64; @@ -1597,7 +1605,7 @@ static bool types_decl(Typer *tr, Declaration *d) { success = false; goto ret; } - val_copy(tr->allocr, &d->val, &val, &d->type); + copy_val(tr->allocr, &d->val, &val, &d->type); d->flags |= DECL_FOUND_VAL; } } @@ -1,3 +1,5 @@ +/* NOTE: make sure you edit copy.c when you make a change to expression-related types or type-related types in this file! */ + typedef int64_t Integer; typedef uint64_t UInteger; typedef long double Floating; /* OPTIM: Switch to double, but make sure floating-point literals are right */ @@ -336,8 +338,8 @@ typedef struct Type { struct { struct Type *of; union { - U64 n; /* after typing */ - struct Expression *n_expr; /* before typing */ + U64 n; /* after resolving */ + struct Expression *n_expr; /* before resolving */ }; } arr; struct Type *ptr; @@ -431,19 +433,12 @@ typedef enum { } BinaryOp; typedef struct { - Value val; - struct { - U64 id; - } c; -} Instance; - -typedef struct { struct Expression *fn; union { struct Argument *args; struct Expression *arg_exprs; }; - Instance *instance; /* NULL = ordinary function, no compile time args */ + struct Instance *instance; /* NULL = ordinary function, no compile time args */ struct { IdentID id; } c; @@ -481,11 +476,12 @@ typedef struct EachExpr { Block body; union { struct { - struct Expression *from; - struct Expression *to; + struct Expression *from; /* can't be null */ + struct Expression *to; /* can be null */ union { - struct Expression *step; - Value *stepval; + /* (either) can be null */ + struct Expression *step; /* before typing */ + Value *stepval; /* after typing */ }; } range; struct Expression *of; @@ -515,6 +511,15 @@ typedef struct FnExpr { } c; } FnExpr; /* an expression such as fn(x: int) int { 2 * x } */ +typedef struct Instance { + Value val; + /* this holds the typed function */ + FnExpr fn; + struct { + U64 id; + } c; +} Instance; + typedef struct { Type type; struct Expression *expr; |