From 7ee5fc2721e40471f01f9377dd901ded4b969a33 Mon Sep 17 00:00:00 2001 From: Leo Tenenbaum Date: Sat, 25 Apr 2020 15:01:47 -0400 Subject: fixed problems with using future types --- eval.c | 4 -- main.c | 4 ++ test.toc | 46 +++++++++++++++++- tests/test.sh | 1 + tests/types.toc | 48 ++++++++++++++++++ tests/types_expected | 13 +++++ toc.c | 2 +- types.c | 135 ++++++++++++++++++++++++++++----------------------- types.h | 37 +++++++------- 9 files changed, 205 insertions(+), 85 deletions(-) create mode 100644 tests/types.toc create mode 100644 tests/types_expected diff --git a/eval.c b/eval.c index 88e6f13..9a65ff9 100644 --- a/eval.c +++ b/eval.c @@ -4,11 +4,7 @@ You should have received a copy of the GNU General Public License along with toc. If not, see . */ -static Status types_block(Typer *tr, Block *b); -static Status types_decl(Typer *tr, Declaration *d); -static Status type_resolve(Typer *tr, Type *t, Location where); static Status eval_block(Evaluator *ev, Block *b, Value *v); -static Status eval_expr(Evaluator *ev, Expression *e, Value *v); static Status eval_address_of(Evaluator *ev, Expression *e, void **ptr); static Value get_builtin_val(BuiltinVal val); diff --git a/main.c b/main.c index 2d42a45..0ac2443 100644 --- a/main.c +++ b/main.c @@ -15,10 +15,14 @@ use compile to a temp file, then move it if compilation succeeds fix including something twice - just use the non-namespacey version if it exists or pick one namespace to use everywhere otherwise &void +null simplify eval macros with val_to_u/i64 #if should not create a block &&, || start making a standard library... (printf; stringbuilder would be nice to have) +improve type_to_str: + Foo ::= struct(t::Type) {} + type_to_str(Foo(int)) switch - #fallthrough enums diff --git a/test.toc b/test.toc index 2a171ed..0b84b8a 100644 --- a/test.toc +++ b/test.toc @@ -1,6 +1,48 @@ #include "std/io.toc", io; -use io; +#include "std/mem.toc"; + main ::= fn() { - puts("hi"); + nums := news(int, 10); + for x, i := &nums { + *x = i*i; + } + l := slice_to_ll(nums); + p := &l; + while p { + io.puti(p.head); + p = p.tail; + } + f: Foo; + f.k = -173; + f.b = new(Bar); + f.b.f.b = new(Bar); + f.b.f.b.f.k = 9; + io.puti(f.k); + io.puti(f.b.f.k); + io.puti(f.b.f.b.f.k); +} + +slice_to_ll ::= fn(t::=, slice: []t) use ll: LinkedList(t) { + head = slice[0]; + if slice.len == 1 { + tail = 0 as &LinkedList(t); + } else { + tail = new(LinkedList(t)); + *tail = slice_to_ll(slice[1:]); + } +} + +LinkedList ::= struct (of :: Type) { + head: of; + tail: &LinkedList(of); +} + +Foo ::= struct { + k: int; + b: &Bar; +} + +Bar ::= struct { + f: Foo; } diff --git a/tests/test.sh b/tests/test.sh index e6e8fe5..1168e5b 100755 --- a/tests/test.sh +++ b/tests/test.sh @@ -2,6 +2,7 @@ tests='bf control_flow +types defer sizeof new diff --git a/tests/types.toc b/tests/types.toc new file mode 100644 index 0000000..4ef55e8 --- /dev/null +++ b/tests/types.toc @@ -0,0 +1,48 @@ +#include "io.toc", io; +#include "mem.toc"; + + +main ::= fn() { + nums := news(int, 10); + for x, i := &nums { + *x = i*i; + } + l := slice_to_ll(nums); + p := &l; + while p { + io.puti(p.head); + p = p.tail; + } + f: Foo; + f.k = -173; + f.b = new(Bar); + f.b.f.b = new(Bar); + f.b.f.b.f.k = 9; + io.puti(f.k); + io.puti(f.b.f.k); + io.puti(f.b.f.b.f.k); +} + +slice_to_ll ::= fn(t::=, slice: []t) use ll: LinkedList(t) { + head = slice[0]; + if slice.len == 1 { + tail = 0 as &LinkedList(t); + } else { + tail = new(LinkedList(t)); + *tail = slice_to_ll(slice[1:]); + } +} + +LinkedList ::= struct (of :: Type) { + head: of; + tail: &LinkedList(of); +} + +Foo ::= struct { + k: int; + b: &Bar; +} + +Bar ::= struct { + f: Foo; +} diff --git a/tests/types_expected b/tests/types_expected new file mode 100644 index 0000000..eef051c --- /dev/null +++ b/tests/types_expected @@ -0,0 +1,13 @@ +0 +1 +4 +9 +16 +25 +36 +49 +64 +81 +-173 +0 +9 diff --git a/toc.c b/toc.c index 5536dd8..6089c20 100644 --- a/toc.c +++ b/toc.c @@ -130,9 +130,9 @@ static Location token_location(File *file, Token *t); #include "tokenizer.c" #include "parse.c" #include "foreign.c" -#include "eval.c" #include "infer.c" #include "types.c" +#include "eval.c" static void cgen_decls_file(CGenerator *g, ParsedFile *f); static void cgen_sdecls_file(CGenerator *g, ParsedFile *f); #include "cgen.c" diff --git a/types.c b/types.c index af52f60..2be1237 100644 --- a/types.c +++ b/types.c @@ -4,8 +4,17 @@ You should have received a copy of the GNU General Public License along with toc. If not, see . */ static Status types_stmt(Typer *tr, Statement *s); +static Status types_block(Typer *tr, Block *b); +static Status types_decl(Typer *tr, Declaration *d); static Status type_resolve(Typer *tr, Type *t, Location where); - +static Status eval_expr(Evaluator *ev, Expression *e, Value *v); +static void val_cast(Value *vin, Type *from, Value *vout, Type *to); +static U64 val_to_u64(Value v, BuiltinType v_type); +static I64 val_to_i64(Value v, BuiltinType v_type); +static bool val_truthiness(Value v, Type *t); +static Value val_zero(Type *t); +static Status eval_stmt(Evaluator *ev, Statement *stmt); +static Status struct_resolve(Typer *tr, StructDef *s); static inline Identifiers *typer_get_idents(Typer *tr) { return tr->block == NULL ? tr->globals : &tr->block->idents; @@ -140,64 +149,6 @@ static Status add_block_to_struct(Typer *tr, Block *b, StructDef *s, Statement * return true; } -static Status struct_resolve(Typer *tr, StructDef *s) { - if (!(s->flags & STRUCT_DEF_RESOLVED)) { - { /* resolving stuff */ - if (!types_block(tr, &s->body)) - return false; - s->fields = NULL; - Statement *new_stmts = NULL; - if (!add_block_to_struct(tr, &s->body, s, &new_stmts)) - return false; - s->body.stmts = new_stmts; - /* set the field of each declaration, so that we can lookup struct members quickly */ - Field *field = s->fields; - arr_foreach(new_stmts, Statement, stmt) { - assert(stmt->kind == STMT_DECL); - Declaration *decl = stmt->decl; - if (!(decl->flags & DECL_IS_CONST)) { - assert(!(decl->flags & DECL_HAS_EXPR)); - decl->field = field; - field += arr_len(decl->idents); - } - } - s->instance_id = 0; - } - /* find offsets and size */ - /* assume the align of a struct is the greatest align out of its children's */ - { - if (s->flags & STRUCT_DEF_FINDING_OFFSETS) { - err_print(s->where, "Circular dependency in struct!"); - return false; - } - s->flags |= STRUCT_DEF_FINDING_OFFSETS; - size_t bytes = 0; - size_t total_align = 1; - arr_foreach(s->fields, Field, f) { - size_t size = compiler_sizeof(f->type); - if (size == SIZE_MAX) { - info_print(f->where, "... while descending into this field of a struct."); - return false; - } - size_t falign = compiler_alignof(f->type); - if (falign > total_align) - total_align = falign; - /* align */ - bytes += ((falign - bytes) % falign + falign) % falign; /* = -bytes mod falign */ - assert(bytes % falign == 0); - f->offset = bytes; - /* add size */ - bytes += size; - } - bytes += ((total_align - bytes) % total_align + total_align) % total_align; /* = -bytes mod align */ - s->size = bytes; - s->align = total_align; - } - s->flags |= STRUCT_DEF_RESOLVED; - } - return true; -} - static size_t compiler_alignof(Type *t) { assert(t->flags & TYPE_IS_RESOLVED); switch (t->kind) { @@ -246,6 +197,11 @@ static size_t compiler_sizeof(Type *t) { case TYPE_SLICE: return sizeof v.slice; case TYPE_STRUCT: { + /* these two ifs are purely for struct_resolve, so that it can detect use of future structs in a non-pointery way */ + if (t->struc->flags & STRUCT_DEF_RESOLVING) + return SIZE_MAX-1; + if (!(t->struc->flags & STRUCT_DEF_RESOLVED)) + return SIZE_MAX; return t->struc->size; } break; case TYPE_VOID: @@ -258,6 +214,65 @@ static size_t compiler_sizeof(Type *t) { return 0; } +static Status struct_resolve(Typer *tr, StructDef *s) { + if (!(s->flags & STRUCT_DEF_RESOLVED)) { + s->flags |= STRUCT_DEF_RESOLVING; + { /* resolving stuff */ + if (!types_block(tr, &s->body)) + return false; + s->fields = NULL; + Statement *new_stmts = NULL; + if (!add_block_to_struct(tr, &s->body, s, &new_stmts)) + return false; + s->body.stmts = new_stmts; + /* set the field of each declaration, so that we can lookup struct members quickly */ + Field *field = s->fields; + arr_foreach(new_stmts, Statement, stmt) { + assert(stmt->kind == STMT_DECL); + Declaration *decl = stmt->decl; + if (!(decl->flags & DECL_IS_CONST)) { + assert(!(decl->flags & DECL_HAS_EXPR)); + decl->field = field; + field += arr_len(decl->idents); + } + } + s->instance_id = 0; + } + /* find offsets and size */ + /* assume the align of a struct is the greatest align out of its children's */ + { + size_t bytes = 0; + size_t total_align = 1; + arr_foreach(s->fields, Field, f) { + size_t size = compiler_sizeof(f->type); + if (size == SIZE_MAX) { + err_print(f->where, "Use of type that hasn't been declared yet (but not as a pointer/slice).\n" + "Either make this field a pointer or put the declaration of its type before this struct."); + return false; + } else if (size == SIZE_MAX-1) { + err_print(f->where, "Circular dependency in structs! You will need to make this member a pointer."); + return false; + } + size_t falign = compiler_alignof(f->type); + if (falign > total_align) + total_align = falign; + /* align */ + bytes += ((falign - bytes) % falign + falign) % falign; /* = -bytes mod falign */ + assert(bytes % falign == 0); + f->offset = bytes; + /* add size */ + bytes += size; + } + bytes += ((total_align - bytes) % total_align + total_align) % total_align; /* = -bytes mod align */ + s->size = bytes; + s->align = total_align; + } + s->flags &= (StructFlags)~(StructFlags)STRUCT_DEF_RESOLVING; + s->flags |= STRUCT_DEF_RESOLVED; + } + return true; +} + /* are a and b EXACTLY equal (not counting flags)? */ static bool type_eq_exact(Type *a, Type *b) { @@ -2000,7 +2015,6 @@ static Status types_expr(Typer *tr, Expression *e) { } else { char *s = cstr(i_str, i_len); err_print(e->where, "Conflicting declarations for identifier %s.", s); - free(s); char *also = ""; if (previous_use_which_uses_i) { /* i was use'd twice */ @@ -2010,6 +2024,7 @@ static Status types_expr(Typer *tr, Expression *e) { /* i was declared then used. */ info_print(ident_decl_location(translated), "%s was declared here.", s); } + free(s); info_print(use->expr.where, "...and %simported by this use statement.", also); return false; } diff --git a/types.h b/types.h index 9a19d5b..fd02a05 100644 --- a/types.h +++ b/types.h @@ -58,6 +58,19 @@ typedef uint64_t U64; #define U32_FMT "%" PRIu32 #define U64_FMT "%" PRIu64 +typedef int8_t I8; +#define I8_MAX INT8_MAX +typedef int16_t I16; +#define I16_MAX INT16_MAX +typedef int32_t I32; +#define I32_MAX INT32_MAX +typedef int64_t I64; +#define I64_MAX INT64_MAX +#define I8_FMT "%" PRId8 +#define I16_FMT "%" PRId16 +#define I32_FMT "%" PRId32 +#define I64_FMT "%" PRId64 + #if __STDC_VERSION__ >= 199901 #include #elif defined __cplusplus @@ -75,19 +88,6 @@ typedef U8 bool; #define Status bool WarnUnusedResult -typedef int8_t I8; -#define I8_MAX INT8_MAX -typedef int16_t I16; -#define I16_MAX INT16_MAX -typedef int32_t I32; -#define I32_MAX INT32_MAX -typedef int64_t I64; -#define I64_MAX INT64_MAX -#define I8_FMT "%" PRId8 -#define I16_FMT "%" PRId16 -#define I32_FMT "%" PRId32 -#define I64_FMT "%" PRId64 - /* NOTE: if you change these, make sure you change hash_tables.c */ typedef float F32; typedef double F64; @@ -500,16 +500,17 @@ typedef Block *BlockPtr; enum { STRUCT_DEF_FOUND_OFFSETS = 0x01, - STRUCT_DEF_FINDING_OFFSETS = 0x02, - STRUCT_DEF_CGEN_DECLARED = 0x04, - STRUCT_DEF_CGEN_DEFINED = 0x08, - STRUCT_DEF_RESOLVED = 0x10 + STRUCT_DEF_CGEN_DECLARED = 0x02, + STRUCT_DEF_CGEN_DEFINED = 0x04, + STRUCT_DEF_RESOLVED = 0x08, + STRUCT_DEF_RESOLVING = 0x10 }; +typedef U8 StructFlags; typedef struct StructDef { /* these two only exist after resolving (before then, it's scope.stmts) */ Field *fields; Location where; - U8 flags; + StructFlags flags; /* use this instead of fields when looking up a field, because it will include "use"d things. this only consists of statements which are declarations after typing (and not #ifs, -- cgit v1.2.3