summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLeo Tenenbaum <pommicket@gmail.com>2020-04-26 18:06:04 -0400
committerLeo Tenenbaum <pommicket@gmail.com>2020-04-26 18:06:04 -0400
commitb3b39e6c33a0bbb7d5e47221e902700a5cf5b685 (patch)
tree7578ece653334fbe84995369b1e6a1ea77c61913
parent1cceedc5ca053b018bc98e96a7dd77d17dde1ada (diff)
use struct member!!!
-rw-r--r--identifiers.c6
-rw-r--r--main.c2
-rw-r--r--misc.c7
-rw-r--r--test.toc21
-rw-r--r--types.c116
-rw-r--r--types.h13
6 files changed, 149 insertions, 16 deletions
diff --git a/identifiers.c b/identifiers.c
index 828bed6..cb4de6f 100644
--- a/identifiers.c
+++ b/identifiers.c
@@ -96,6 +96,12 @@ static char *ident_to_str(Identifier i) {
return str;
}
+static String ident_to_string(Identifier i) {
+ String ret;
+ ret.str = i->str;
+ ret.len = i->len;
+ return ret;
+}
static inline void fprint_ident_str(FILE *out, char *s) {
fwrite(s, 1, ident_str_len(s), out);
diff --git a/main.c b/main.c
index 4fc4bd3..bc01e30 100644
--- a/main.c
+++ b/main.c
@@ -8,8 +8,6 @@
/*
@TODO:
-use
- - use with struct members (e.g. SuperPoint ::= struct { use p: Point; })
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
diff --git a/misc.c b/misc.c
index 4e82ff8..6224ddf 100644
--- a/misc.c
+++ b/misc.c
@@ -48,6 +48,13 @@ size_t str_copy(char *dest, size_t destsz, const char *src) {
return destsz-1;
}
+static char *str_dup(char *s) {
+ size_t bufsz = strlen(s)+1;
+ char *ret = malloc(bufsz);
+ memcpy(ret, s, bufsz);
+ return ret;
+}
+
static char *cstr(char const *str, size_t len) {
char *ret = malloc(len+1);
memcpy(ret, str, len);
diff --git a/test.toc b/test.toc
index 644f701..010f7d5 100644
--- a/test.toc
+++ b/test.toc
@@ -1,9 +1,22 @@
#include "std/io.toc";
+Point ::= struct {
+ x, y : int;
+}
+
+Point3D ::= struct {
+ use p : Point;
+ z : int;
+}
+
main ::= fn() {
- x:[]int;
- p:=&x;
- p.len = 12;
- puti(p.len);
+ use s: Point3D;
+ x = 12;
+ z = 18;
+ y = -1238;
+ puti(x);
+ puti(y);
+ puti(z);
+ puti(p.x);
}
main();
diff --git a/types.c b/types.c
index 056b800..2480924 100644
--- a/types.c
+++ b/types.c
@@ -15,6 +15,7 @@ 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 Status expr_must_usable(Typer *tr, Expression *e);
static inline Identifiers *typer_get_idents(Typer *tr) {
return tr->block == NULL ? tr->globals : &tr->block->idents;
@@ -84,12 +85,65 @@ static size_t compiler_alignof_builtin(BuiltinType b) {
return 0;
}
+static inline char *get_struct_name(StructDef *s) {
+ return s->name ? ident_to_str(s->name) : str_dup("anonymous struct");
+}
+
+/* adds fields of add to to */
+static Status struct_add_used_struct(Typer *tr, StructDef *to, StructDef *add, Declaration *use_decl) {
+ Location use_where = use_decl->where;
+ if (!struct_resolve(tr, add))
+ return false;
+ arr_foreach(add->body.stmts, Statement, stmt) {
+ assert(stmt->kind == STMT_DECL);
+ Declaration *decl = stmt->decl;
+ if (decl->flags & DECL_USE) {
+ assert(decl->type.kind == TYPE_STRUCT);
+ if (!struct_add_used_struct(tr, to, decl->type.struc, decl))
+ return false;
+ }
+ arr_foreach(decl->idents, Identifier, ip) {
+ Identifier i = *ip;
+ /* @OPTIM: only hash once */
+ Identifier previously_existing = ident_translate(i, &to->body.idents);
+ if (previously_existing) {
+ /* uh oh */
+ UsedFrom *uf = previously_existing->used_from;
+ char *struct_name = get_struct_name(to);
+ char *member_name = ident_to_str(previously_existing);
+ if (uf) {
+ /* conflicting uses */
+ Declaration *first_use = uf->use_decl;
+ err_print(first_use->where, "Conflicting used structs, while dealing with %s. %s was imported by this use statement...", struct_name, i);
+ info_print(use_where, "... and also by this use statement.");
+ } else {
+ /* declared a field, then used something which contains something of the same name */
+ Declaration *first_decl = previously_existing->decl;
+ char *used_struct_name = get_struct_name(add);
+ err_print(use_where, "used struct conflicts with field %s of %s (%s is also a member of %s).", member_name, struct_name, member_name, used_struct_name);
+ info_print(first_decl->where, "%s was declared here as a field.", member_name);
+ free(used_struct_name);
+ }
+ free(struct_name);
+ free(member_name);
+ return false;
+ }
+ Identifier new_ident = ident_translate_forced(i, &to->body.idents);
+ new_ident->decl = i->decl;
+ UsedFrom *uf = new_ident->used_from = typer_malloc(tr, sizeof *new_ident->used_from);
+ uf->use_decl = use_decl;
+ uf->struc = add;
+ }
+ }
+ return true;
+}
+
/* new_stmts is what s->body.stmts will become after typing is done */
-static Status add_block_to_struct(Typer *tr, Block *b, StructDef *s, Statement **new_stmts) {
+static Status struct_add_block(Typer *tr, Block *b, StructDef *s, Statement **new_stmts) {
arr_foreach(b->stmts, Statement, stmt) {
if (stmt->kind == STMT_EXPR) {
if (stmt->expr->kind == EXPR_BLOCK) {
- if (!add_block_to_struct(tr, stmt->expr->block, s, new_stmts))
+ if (!struct_add_block(tr, stmt->expr->block, s, new_stmts))
return false;
continue;
}
@@ -125,11 +179,30 @@ static Status add_block_to_struct(Typer *tr, Block *b, StructDef *s, Statement *
field->where = d->where;
field->name = *ident;
field->type = decl_type_at_index(d, i);
+ (*ident)->used_from = NULL;
++i;
}
-
typer_arr_add(tr, *new_stmts, *stmt);
}
+ if (flags & DECL_USE) {
+ /* add everything in the used struct to the namespace */
+ if (flags & DECL_IS_CONST) {
+ /* @TODO(eventually) */
+ err_print(d->where, "You can't use constant stuff in a struct.");
+ return false;
+ }
+ if (d->type.kind != TYPE_STRUCT) {
+ /* i don't think this can ever happen right now */
+ err_print(d->where, "You can only use structs inside a struct.");
+ return false;
+ }
+ if (arr_len(d->idents) > 1) {
+ err_print(d->where, "use declarations can only declare one thing. Every single used identifier would have conflicting definitions otherwise.");
+ return false;
+ }
+ if (!struct_add_used_struct(tr, s, d->type.struc, d))
+ return false;
+ }
if (b != &s->body) {
/* we need to translate d's identifiers to s's scope */
arr_foreach(d->idents, Identifier, ip) {
@@ -215,15 +288,17 @@ static size_t compiler_sizeof(Type *t) {
}
static Status struct_resolve(Typer *tr, StructDef *s) {
+ if (s->flags & STRUCT_DEF_RESOLVING_FAILED)
+ return false; /* silently fail; do not try to resolve again, because there'll be duplicate errors */
if (!(s->flags & STRUCT_DEF_RESOLVED)) {
s->flags |= STRUCT_DEF_RESOLVING;
{ /* resolving stuff */
if (!types_block(tr, &s->body))
- return false;
+ goto fail;
s->fields = NULL;
Statement *new_stmts = NULL;
- if (!add_block_to_struct(tr, &s->body, s, &new_stmts))
- return false;
+ if (!struct_add_block(tr, &s->body, s, &new_stmts))
+ goto fail;
s->body.stmts = new_stmts;
/* set the field of each declaration, so that we can lookup struct members quickly */
Field *field = s->fields;
@@ -248,10 +323,10 @@ static Status struct_resolve(Typer *tr, StructDef *s) {
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;
+ goto fail;
} 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;
+ goto fail;
}
size_t falign = compiler_alignof(f->type);
if (falign > total_align)
@@ -271,6 +346,9 @@ static Status struct_resolve(Typer *tr, StructDef *s) {
s->flags |= STRUCT_DEF_RESOLVED;
}
return true;
+fail:
+ s->flags |= STRUCT_DEF_RESOLVING_FAILED;
+ return false;
}
@@ -3148,12 +3226,34 @@ static Status types_expr(Typer *tr, Expression *e) {
if (!struct_resolve(tr, struc)) return false;
Identifier struct_ident = ident_get_with_len(&struc->body.idents, rhs->ident_str.str, rhs->ident_str.len);
+ UsedFrom *uf = struct_ident->used_from;
+ if (uf) {
+ /* foo.baz => (foo.bar).baz */
+ Expression *old_lhs = lhs;
+ lhs = e->binary.lhs = typer_malloc(tr, sizeof *lhs);
+ lhs->kind = EXPR_BINARY_OP;
+ lhs->flags = 0;
+ lhs->binary.op = BINARY_DOT;
+ lhs->binary.lhs = old_lhs;
+ Expression *middle = lhs->binary.rhs = typer_calloc(tr, 1, sizeof *lhs->binary.rhs);
+ middle->kind = EXPR_IDENT;
+ middle->flags = 0;
+ assert(arr_len(uf->use_decl->idents) == 1);
+ middle->ident_str = ident_to_string(uf->use_decl->idents[0]);
+ e->flags &= (ExprFlags)~(ExprFlags)EXPR_FOUND_TYPE;
+ /* re-type now that we know where it's from */
+ return types_expr(tr, e);
+ }
if (struct_ident && !(struct_ident->decl->flags & DECL_IS_CONST)) {
Field *field = struct_ident->decl->field;
field += ident_index_in_decl(struct_ident, struct_ident->decl);
e->binary.field = field;
*t = *field->type;
} else {
+ /*
+ this call will fail if rhs->ident_str isn't a member of the structure.
+ kind of weird to have that check here but whatever
+ */
if (!get_struct_constant(struct_type->struc, rhs->ident_str, e))
return false;
}
diff --git a/types.h b/types.h
index d81df36..d811a20 100644
--- a/types.h
+++ b/types.h
@@ -174,13 +174,21 @@ typedef struct VarArg {
Value val;
} VarArg;
+typedef struct {
+ struct StructDef *struc;
+ struct Declaration *use_decl; /* field declaration which uses the identifier */
+} UsedFrom;
+
typedef struct IdentSlot {
char *str;
size_t len;
/* where this identifier was declared */
struct Declaration *decl; /* if NULL, a declaration hasn't been found for it yet */
struct Identifiers *idents;
- struct Namespace *nms; /* only exists after typing, and only for namespace-level declarations (i.e. not local variables) */
+ union {
+ struct Namespace *nms; /* only exists after typing, and only for namespace-level declarations (i.e. not local variables) */
+ UsedFrom *used_from; /* for stuff used inside structs -- NULL if this is actually in the struct body */
+ };
} IdentSlot;
typedef struct StrHashTableSlot {
@@ -504,7 +512,8 @@ enum {
STRUCT_DEF_CGEN_DECLARED = 0x02,
STRUCT_DEF_CGEN_DEFINED = 0x04,
STRUCT_DEF_RESOLVED = 0x08,
- STRUCT_DEF_RESOLVING = 0x10
+ STRUCT_DEF_RESOLVING = 0x10,
+ STRUCT_DEF_RESOLVING_FAILED = 0x20
};
typedef U8 StructFlags;
typedef struct StructDef {