aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/ek/actualize.h2
-rw-r--r--include/ek/ast.h14
-rw-r--r--include/ek/debug.h1
-rw-r--r--include/ek/scope.h58
-rw-r--r--src/actualize.c273
-rw-r--r--src/ast.c20
-rw-r--r--src/debug.c13
-rw-r--r--src/lower.c2
-rw-r--r--src/parser.y11
-rw-r--r--src/scope.c112
-rw-r--r--tests/generic.ek11
-rw-r--r--tests/trait_expand.ek2
12 files changed, 422 insertions, 97 deletions
diff --git a/include/ek/actualize.h b/include/ek/actualize.h
index 9dc5948..1519287 100644
--- a/include/ek/actualize.h
+++ b/include/ek/actualize.h
@@ -27,6 +27,8 @@
*/
int types_match(struct type *a, struct type *b);
+int type_lists_match(struct type *a, struct type *b);
+
/**
* Analyze raw AST, assumed to be file scope.
* The analysis phase collects top level objects, types, procedures, etc.
diff --git a/include/ek/ast.h b/include/ek/ast.h
index 59101ba..c1276b5 100644
--- a/include/ek/ast.h
+++ b/include/ek/ast.h
@@ -398,6 +398,7 @@ static inline bool is_primitive(struct type *t)
#define return_a2(x, kind) *({assert((x)->k == kind); &(x)->a2;})
#define return_a3(x, kind) *({assert((x)->k == kind); &(x)->a3;})
+#define return_id(x, kind) *({assert((x)->k == kind); &(x)->id;})
#define return_t0(x, kind) *({assert((x)->k == kind); &(x)->t0;})
#define return_t1(x, kind) *({assert((x)->k == kind); &(x)->t1;})
/* note that this one is in ast, the other two are in type */
@@ -609,7 +610,7 @@ static inline bool is_primitive(struct type *t)
#define gen_const_bool(i, loc) \
gen_ast(AST_CONST_BOOL, NULL, NULL, NULL, NULL, NULL, NULL, i, loc)
-#define init_args(x) return_t1(x, AST_INIT)
+#define init_args(x) return_t2(x, AST_INIT)
#define init_body(x) return_a0(x, AST_INIT)
#define init_id(x) return_s(x, AST_INIT)
#define gen_init(id, targs, body, loc) \
@@ -637,11 +638,14 @@ static inline bool is_primitive(struct type *t)
#define tgen_ptr(base, loc) \
tgen1(TYPE_PTR, base, loc)
-#define construct_id(x) return_t0(x, TYPE_CONSTRUCT)
-#define construct_atypes(x) return_t1(x, TYPE_CONSTRUCT)
+#define construct_id(x) return_id(x, TYPE_CONSTRUCT)
+#define construct_atypes(x) return_t0(x, TYPE_CONSTRUCT)
#define tgen_construct(id, atypes, loc) \
tgen_str1(TYPE_CONSTRUCT, id, atypes, loc)
+#define tstruct_params(x) return_t0(x, TYPE_STRUCT)
+#define ttrait_params(x) return_t0(x, TYPE_TRAIT)
+
struct ast *clone_ast(struct ast *n);
struct ast *clone_ast_list(struct ast *l);
@@ -654,8 +658,8 @@ void ast_dump(int depth, struct ast *node);
void type_dump_list(struct type *root);
void type_dump(struct type *node);
-void ast_append(struct ast *list, struct ast *elem);
-void type_append(struct type *list, struct type *elem);
+void ast_append(struct ast **list, struct ast *elem);
+void type_append(struct type **list, struct type *elem);
struct ast *ast_prepend(struct ast *list, struct ast *elem);
struct type *type_prepend(struct type *list, struct type *elem);
diff --git a/include/ek/debug.h b/include/ek/debug.h
index 19ed044..72b5e9e 100644
--- a/include/ek/debug.h
+++ b/include/ek/debug.h
@@ -106,6 +106,7 @@ void semantic_warn(struct file_ctx ctx, struct ast *node, const char *fmt,
void semantic_error(struct file_ctx ctx, struct ast *node, const char *fmt,
...);
void type_error(struct file_ctx ctx, struct type *type, const char *fmt, ...);
+void loc_error(struct file_ctx ctx, struct src_loc loc, const char *fmt, ...);
/**
* Print internal error.
diff --git a/include/ek/scope.h b/include/ek/scope.h
index b747288..a119451 100644
--- a/include/ek/scope.h
+++ b/include/ek/scope.h
@@ -41,12 +41,11 @@ struct visible {
struct visible *next;
};
-/** Actualized nodes visible to scope. */
-struct actual {
- /** Actualized AST node. */
+struct expanded {
struct ast *node;
- /** Next actual node. */
- struct actual *next;
+ struct type *types;
+ struct ast *expd;
+ struct expanded *next;
};
struct type_defs {
@@ -82,11 +81,7 @@ struct scope {
/** List of child scopes. */
struct scope *children;
- /**
- * List of generic structs with actual arguments to generate before
- * lowering
- */
- struct actual *actuals;
+ struct expanded *expanded;
struct visible *symbols;
struct visible *macros;
@@ -103,30 +98,6 @@ struct scope {
struct scope *create_scope();
/**
- * Create actuals list.
- * Since actuals are shared in the file scope, many scopes may share the actuals
- * list. Only the file scope is allowed to destroy the actual list.
- *
- * @return Empty actual list.
- */
-struct actual *create_actuals();
-
-/**
- * Destroy the list of actuals and actuals in list.
- *
- * @param actuals List of actuals to destroy.
- */
-void destroy_actuals(struct actual *actuals);
-
-/**
- * Destroy list of visibles.
- *
- * @param scope Scope list belongs to.
- * @param visible List of visibles to destroy.
- */
-void destroy_visible(struct visible *visible);
-
-/**
* Destroy scope.
* Destroys all lists the scope owns and frees the scope.
*
@@ -188,16 +159,6 @@ unsigned scope_flags(struct scope *scope, enum scope_flags flags);
void scope_add_scope(struct scope *parent, struct scope *child);
/**
- * Add actualized AST node to scope.
- * Will make the actualized node visible to all scopes in the file scope.
- *
- * @param scope Scope to add \p node to.
- * @param node Actualized AST node.
- * @return \c 0 when succesful, non-zero otherwise.
- */
-int scope_add_actual(struct scope *scope, struct ast *node);
-
-/**
* Add variable to scope.
* Propagates public variables up the file scope chain as references.
*
@@ -247,7 +208,7 @@ int scope_add_macro(struct scope *scope, struct ast *macro);
*/
int scope_add_trait(struct scope *scope, struct ast *trait);
-int scope_resolve(struct scope *scope);
+int scope_add_expd_struct(struct scope *scope, struct ast *def, struct type *types, struct ast *expanded);
/**
* Find a variable with ID in \p scope.
@@ -322,6 +283,8 @@ struct ast *scope_find_alias(struct scope *scope, char *id);
*/
struct ast *scope_find_trait(struct scope *scope, char *id);
+struct ast *scope_find_expd_struct(struct scope *scope, struct ast *def, struct type *types);
+
/**
* Find a variable with ID visible to \p scope.
*
@@ -382,7 +345,12 @@ struct ast *file_scope_find_alias(struct scope *scope, char *id);
*/
struct ast *file_scope_find_trait(struct scope *scope, char *id);
+struct ast *file_scope_find_expd_struct(struct scope *scope, struct ast *def, struct type *types);
+
#define foreach_visible(iter, init) \
for (struct visible *iter = init; iter; iter = iter->next)
+#define foreach_expanded(iter, init) \
+ for (struct expanded *iter = init; iter; iter = iter->next)
+
#endif /* SCOPE_H */
diff --git a/src/actualize.c b/src/actualize.c
index 4fd6fc6..591333a 100644
--- a/src/actualize.c
+++ b/src/actualize.c
@@ -465,7 +465,7 @@ static struct ast *analyze_type_expand(struct scope *scope,
return body;
}
-static int implements_trait(struct ast *body, char *id)
+static int has_trait(struct ast *body, char *id)
{
foreach_node(n, body) {
/* traits don't currently take generic parameters I guess? */
@@ -509,8 +509,18 @@ int analyze_root(struct scope *scope, struct ast *tree)
static int structs_match(struct type *a, struct type *b)
{
- /* dunno, let's go with this for now */
- return a->d == b->d;
+ if (a->d != b->d)
+ return 0;
+
+ return type_lists_match(tstruct_params(a), tstruct_params(b));
+}
+
+static int traits_match(struct type *a, struct type *b)
+{
+ if (a->d != b->d)
+ return 0;
+
+ return type_lists_match(ttrait_params(a), ttrait_params(b));
}
int types_match(struct type *a, struct type *b)
@@ -528,6 +538,9 @@ int types_match(struct type *a, struct type *b)
if (is_void(a) && is_void(b))
return 1;
+ if (a->k == TYPE_TRAIT)
+ return traits_match(a, b);
+
if (a->k == TYPE_STRUCT)
return structs_match(a, b);
@@ -544,6 +557,20 @@ int types_match(struct type *a, struct type *b)
return 0;
}
+int type_lists_match(struct type *a, struct type *b)
+{
+ while (a && b) {
+ if (!types_match(a, b))
+ return 0;
+
+ a = a->n;
+ b = b->n;
+ }
+
+ /* if we succesfully checked all types, lists match */
+ return a == NULL && b == NULL;
+}
+
static int _replace_id(struct ast *node, void *data)
{
if (!node)
@@ -580,6 +607,180 @@ static int replace_id(struct ast *body, struct ast *id,
return ast_visit(_replace_id, NULL, body, pair);
}
+static int implements(struct type *trait, struct type *type)
+{
+ assert(type->d);
+ struct ast *def = type->d;
+
+ assert(trait->d);
+ struct ast *trait_def = trait->d;
+ assert(trait_def->k == AST_TRAIT_DEF);
+
+ /* empty traits are implemented implicitly */
+ if (!trait_body(trait_def))
+ return 1;
+
+ foreach_node(n, def) {
+ if (n->k != AST_TYPE_EXPAND)
+ continue;
+
+ if (!same_id(type_expand_id(n), trait_id(trait_def)))
+ continue;
+
+ assert(n->t);
+ if (n->t->d != trait_def)
+ continue;
+
+ return 1;
+ }
+
+ /** @todo look up possible continuations for type */
+ return 0;
+}
+
+static int should_implement_list(struct scope *scope, struct ast *params, struct src_loc loc, struct type *types)
+{
+ while (params && types) {
+ assert(params->k == AST_VAR_DEF);
+ struct type *t = var_type(params);
+ if (!implements(t, types)) {
+ char *type1 = type_str(types);
+ char *type2 = type_str(t);
+ type_error(scope->fctx, types,
+ "%s does not implement %s",
+ type1,
+ type2);
+ free(type1);
+ free(type2);
+ return 0;
+ }
+
+ params = params->n;
+ types = types->n;
+ }
+
+ if (params == NULL && types == NULL)
+ return 1;
+
+ if (params != NULL) {
+ loc_error(scope->fctx, loc,
+ "too few type params");
+ return 0;
+ }
+
+ if (types != NULL) {
+ loc_error(scope->fctx, loc,
+ "too many type params");
+ return 0;
+ }
+
+ return 0;
+}
+
+static int _reset(struct ast *node, void *data)
+{
+ UNUSED(data);
+ ast_clear_flags(node, AST_FLAG_INIT | AST_FLAG_ACTUAL);
+ /* clear type info */
+ node->t = NULL;
+ return 0;
+}
+
+static int reset(struct ast *body)
+{
+ return ast_visit(_reset, NULL, body, NULL);
+}
+
+static int expand_type(struct ast *expd, struct ast *params, struct type *types)
+{
+ /* we're getting expanded so remove our params to avoid redefining them
+ * later */
+ if (expd->k == AST_STRUCT_DEF)
+ struct_params(expd) = NULL;
+ else
+ trait_params(expd) = NULL;
+
+ assert(expd->scope->parent);
+ struct scope *p = expd->scope->parent;
+
+ reset(expd);
+
+ foreach_node(n, params) {
+ struct ast *p = clone_ast(n);
+ var_type(p) = clone_type(types);
+
+ if (expd->k == AST_STRUCT_DEF)
+ ast_append(&struct_params(expd), p);
+ else
+ ast_append(&trait_params(expd), p);
+
+ types = types->n;
+ }
+
+ /* if our trait system works, would theoretically already know that this
+ * will succeed (and I guess we wouldn't need to actualize anything) but
+ * this seems like the simplest solution for now */
+ struct act_state state = {0};
+ int ret = actualize(&state, p, expd);
+ assert(ret == 0);
+ return ret;
+}
+
+static struct ast *maybe_expand_struct(struct scope *scope, struct ast *def, struct src_loc loc, struct type *args)
+{
+ assert(def->k == AST_STRUCT_DEF);
+ if (struct_params(def) == NULL) {
+ if (args == NULL)
+ return def;
+
+ loc_error(scope->fctx, loc,
+ "passing types to non-generic struct %s",
+ struct_id(def));
+ return NULL;
+ }
+
+ struct ast *exists = file_scope_find_expd_struct(scope, def, args);
+ if (exists)
+ return exists;
+
+ if (!should_implement_list(scope, struct_params(def), loc, args))
+ return NULL;
+
+ struct ast *expd = clone_ast(def);
+ if (expand_type(expd, struct_params(def), args))
+ return NULL;
+
+ scope_add_expd_struct(scope, def, args, expd);
+ return expd;
+}
+
+static struct ast *maybe_expand_trait(struct scope *scope, struct ast *def, struct src_loc loc, struct type *args)
+{
+ assert(def->k == AST_TRAIT_DEF);
+ if (trait_params(def) == NULL) {
+ if (args != NULL) {
+ loc_error(scope->fctx, loc,
+ "passing types to non-generic trait %s",
+ trait_id(def));
+ return NULL;
+ }
+
+ return def;
+ }
+
+ loc_error(scope->fctx, loc, "unimplemented trait expansion");
+ return NULL;
+}
+
+static struct ast *maybe_expand_type(struct scope *scope, struct ast *def, struct src_loc loc, struct type *args)
+{
+ assert(def->k == AST_STRUCT_DEF || def->k == AST_TRAIT_DEF);
+ if (def->k == AST_STRUCT_DEF)
+ return maybe_expand_struct(scope, def, loc, args);
+ else
+ return maybe_expand_trait(scope, def, loc, args);
+}
+
static int actualize_macro_def(struct act_state *state,
struct scope *scope, struct ast *n)
{
@@ -822,7 +1023,7 @@ static int actualize_proc_sign(struct scope *scope, struct ast *proc)
continue;
}
- type_append(callable_ptypes(callable), p->t);
+ type_append(&callable_ptypes(callable), p->t);
}
set_type(proc, callable);
@@ -860,7 +1061,7 @@ static int actualize_proc(struct act_state *state,
struct ast *body = proc_body(proc);
struct ast *r = gen_return(NULL, NULL, NULL_LOC());
r->scope = body->scope;
- ast_append(block_body(body), r);
+ ast_append(&block_body(body), r);
}
else if (ast_block_last(proc_body(proc))->k != AST_RETURN) {
/* TODO: something more sophisticated than this */
@@ -1005,8 +1206,11 @@ static int actualize_var(struct act_state *state,
if (init && actualize_list(state, scope, init))
return -1;
- if (type && actualize_type_list(state, scope, type))
- return -1;
+ if (type) {
+ type = clone_type(type);
+ if (actualize_type_list(state, scope, type))
+ return -1;
+ }
if (init && type) {
/* make sure the asked type and the actualized types match */
@@ -1182,6 +1386,29 @@ static int actualize_ttrait(struct act_state *state, struct scope *scope,
return 0;
}
+static int actualize_tconstruct(struct act_state *state,
+ struct scope *scope,
+ struct type *t)
+{
+ assert(t->k == TYPE_CONSTRUCT);
+ struct ast *d = file_scope_find_type(scope, construct_id(t));
+ if (!d) {
+ type_error(scope->fctx, t, "no such type");
+ return -1;
+ }
+
+ if (actualize_type_list(state, scope, construct_atypes(t)))
+ return -1;
+
+ d = maybe_expand_type(scope, d, t->loc, construct_atypes(t));
+ if (!d)
+ return -1;
+
+ replace_type(t, d->t);
+ t->d = d;
+ return 0;
+}
+
static int actualize_type(struct act_state *state,
struct scope *scope,
struct type *t)
@@ -1203,6 +1430,7 @@ static int actualize_type(struct act_state *state,
case TYPE_CALLABLE: return actualize_callable(state, scope, t);
case TYPE_STRUCT: return actualize_tstruct(state, scope, t);
case TYPE_TRAIT: return actualize_ttrait(state, scope, t);
+ case TYPE_CONSTRUCT: return actualize_tconstruct(state, scope, t);
case TYPE_VOID: return 0; /* void is by default actualized */
default:
@@ -1367,6 +1595,7 @@ static int actualize_alias(struct act_state *state, struct scope *scope,
return -1;
}
+ alias->t = alias_type(alias);
ast_set_flags(alias, AST_FLAG_ACTUAL);
return 0;
}
@@ -1722,7 +1951,7 @@ static int actualize_trait(struct act_state *state, struct scope *scope,
continue;
/* don't re-expand already implemented traits */
- if (implements_trait(trait_body(node), type_expand_id(n))) {
+ if (has_trait(trait_body(node), type_expand_id(n))) {
/* not sure about this, but at least we don't have stray
* type expands everywhere */
n->k = AST_EMPTY;
@@ -1815,12 +2044,30 @@ static int actualize_struct(struct act_state *state,
else
node->t = tgen_struct(id, node, node->loc);
+ /* iterate over types and make aliases to them */
+ foreach_node(n, struct_params(node)) {
+ char *id = var_id(n);
+ struct type *type = var_type(n);
+ struct act_state type_state = {0};
+ if (actualize_type(&type_state, scope, type))
+ return -1;
+
+ type_append(&tstruct_params(node->t), type);
+
+ struct ast *alias = gen_alias(strdup(id), clone_type(type), n->loc);
+ if (analyze_visibility(struct_scope, alias))
+ return -1;
+
+ struct act_state state = {0};
+ if (actualize(&state, struct_scope, alias))
+ return -1;
+ }
foreach_node(n, struct_body(node)) {
if (n->k != AST_TYPE_EXPAND)
continue;
- if (implements_trait(struct_body(node), type_expand_id(n))) {
+ if (has_trait(struct_body(node), type_expand_id(n))) {
n->k = AST_EMPTY;
continue;
}
@@ -1969,6 +2216,7 @@ static int init_sort(const struct init_helper *a, const struct init_helper *b)
return strcmp(a->id, b->id);
}
+
static int actualize_init(struct act_state *state,
struct scope *scope, struct ast *node)
{
@@ -1980,6 +2228,13 @@ static int actualize_init(struct act_state *state,
return -1;
}
+ if (actualize_type_list(state, scope, init_args(node)))
+ return -1;
+
+ def = maybe_expand_type(scope, def, node->loc, init_args(node));
+ if (!def)
+ return -1;
+
struct vec init_args = vec_create(sizeof(struct init_helper));
struct vec struct_members = vec_create(sizeof(struct init_helper));
diff --git a/src/ast.c b/src/ast.c
index d734252..890196b 100644
--- a/src/ast.c
+++ b/src/ast.c
@@ -153,9 +153,14 @@ unsigned ast_flags(struct ast *node, enum ast_flags flags)
return node->f & flags;
}
-void ast_append(struct ast *list, struct ast *elem)
+void ast_append(struct ast **list, struct ast *elem)
{
- struct ast *cur = list;
+ struct ast *cur = *list;
+ if (!cur) {
+ *list = elem;
+ return;
+ }
+
while (cur->n)
cur = cur->n;
@@ -168,9 +173,14 @@ struct ast *ast_prepend(struct ast *list, struct ast *elem)
return elem;
}
-void type_append(struct type *list, struct type *elem)
+void type_append(struct type **list, struct type *elem)
{
- struct type *cur = list;
+ struct type *cur = *list;
+ if (!cur) {
+ *list = elem;
+ return;
+ }
+
while (cur->n)
cur = cur->n;
@@ -681,7 +691,7 @@ size_t align3k(size_t o)
{
size_t rem = o % 3;
if (rem)
- o += rem;
+ o += 3 - rem;
return o;
}
diff --git a/src/debug.c b/src/debug.c
index 734ce0e..0d11473 100644
--- a/src/debug.c
+++ b/src/debug.c
@@ -146,6 +146,19 @@ void type_error(struct file_ctx fctx, struct type *node,
va_end(args);
}
+void loc_error(struct file_ctx fctx, struct src_loc loc,
+ const char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ struct src_issue issue;
+ issue.level = SRC_ERROR;
+ issue.loc = loc;
+ issue.fctx = fctx;
+ _issue(issue, fmt, args);
+ va_end(args);
+}
+
void semantic_warn(struct file_ctx fctx, struct ast *node, const char *fmt,
...)
{
diff --git a/src/lower.c b/src/lower.c
index b9f9665..630cb1d 100644
--- a/src/lower.c
+++ b/src/lower.c
@@ -345,6 +345,8 @@ static int lower_var(struct lower_state *s, struct ast *v,
struct retval *retval)
{
assert(v->k == AST_VAR_DEF);
+ assert(var_init(v));
+
struct retval input = retval_create();
if (lower_expr(s, var_init(v), &input)) {
retval_destroy(&input);
diff --git a/src/parser.y b/src/parser.y
index 06599f2..a9c4776 100644
--- a/src/parser.y
+++ b/src/parser.y
@@ -155,6 +155,7 @@
/* optional stuff */
%nterm <node> opt_exprs proc_decl member opt_members
%nterm <type> opt_types opt_sign_decls sign_decls sign_decl sign_var_decl
+%nterm <node> opt_construct_args
%nterm <node> opt_behaviours behaviours behaviour
/* reverse lists */
@@ -512,7 +513,7 @@ macro
ast_set_flags($6, AST_FLAG_UNHYGIENIC);
}
| "define" ID "(" references "..." id ")" body {
- ast_append($4, $6);
+ ast_append(&$4, $6);
$$ = gen_macro_def($[ID], $4, $8, src_loc(@$));
ast_set_flags($$, AST_FLAG_VARIADIC);
ast_set_flags($8, AST_FLAG_UNHYGIENIC);
@@ -544,12 +545,16 @@ construct_args
: rev_construct_args { $$ = reverse_ast_list($1); }
| rev_construct_args "," { $$ = reverse_ast_list($1); }
+opt_construct_args
+ : construct_args
+ | { $$ = NULL; }
+
construct
- : APPLY "{" construct_args "}" {
+ : APPLY "{" opt_construct_args "}" {
/** @todo add type info? */
$$ = gen_init($1, NULL, $3, src_loc(@$));
}
- | APPLY "[" opt_types "]" "{" construct_args "}" {
+ | APPLY "[" opt_types "]" "{" opt_construct_args "}" {
$$ = gen_init($1, $3, $6, src_loc(@$));
}
diff --git a/src/scope.c b/src/scope.c
index 4316a40..5cd7075 100644
--- a/src/scope.c
+++ b/src/scope.c
@@ -32,13 +32,22 @@ struct scope *create_scope()
return scope;
}
-void destroy_visible(struct visible *visible)
+static void destroy_visible(struct visible *visible)
{
struct visible *prev = visible, *cur;
if (prev)
do {
cur = prev->next;
- /* destroy AST nodes globally somewhere? */
+ free(prev);
+ } while ((prev = cur));
+}
+
+static void destroy_expanded(struct expanded *expanded)
+{
+ struct expanded *prev = expanded, *cur;
+ if (prev)
+ do {
+ cur = prev->next;
free(prev);
} while ((prev = cur));
}
@@ -57,6 +66,8 @@ void destroy_scope(struct scope *scope)
destroy_visible(scope->macros);
destroy_visible(scope->types);
+ destroy_expanded(scope->expanded);
+
struct scope *prev = scope->children, *cur;
if (prev)
do {
@@ -82,6 +93,9 @@ unsigned scope_flags(struct scope *scope, enum scope_flags flags)
static struct visible *create_visible(char *id, struct ast *node)
{
struct visible *visible = calloc(1, sizeof(struct visible));
+ if (!visible)
+ return NULL;
+
visible->id = id;
visible->node = node;
return visible;
@@ -99,6 +113,22 @@ struct visible *create_type(struct scope *scope, char *id, struct ast *type)
return n;
}
+struct expanded *create_expanded(struct scope *scope, struct ast *def, struct type *types, struct ast *expd)
+{
+ struct expanded *n = calloc(1, sizeof(struct expanded));
+ if (!n)
+ return NULL;
+
+ n->node = def;
+ n->types = types;
+ n->expd = expd;
+
+ n->next = scope->expanded;
+ scope->expanded = n;
+
+ return n;
+}
+
struct visible *create_var(struct scope *scope, char *id, struct ast *var)
{
struct visible *n = create_visible(id, var);
@@ -227,6 +257,19 @@ int scope_add_trait(struct scope *scope, struct ast *trait)
return 0;
}
+int scope_add_expd_struct(struct scope *scope, struct ast *def, struct type *types, struct ast *expd)
+{
+ assert(def->k == AST_STRUCT_DEF);
+ assert(file_scope_find_expd_struct(scope, def, types) == NULL);
+
+ create_expanded(scope, def, types, expd);
+ if (scope->parent &&
+ scope_flags(scope, SCOPE_FILE) && ast_flags(def, AST_FLAG_PUBLIC))
+ return scope_add_expd_struct(scope->parent, def, types, expd);
+
+ return 0;
+}
+
static struct ast *scope_find_visible(struct visible *v, char *id)
{
if (!v)
@@ -241,6 +284,22 @@ static struct ast *scope_find_visible(struct visible *v, char *id)
return NULL;
}
+static struct ast *scope_find_expanded(struct expanded *e, struct ast *def, struct type *types)
+{
+ if (!e)
+ return NULL;
+
+ foreach_expanded(n, e) {
+ if (n->node != def)
+ continue;
+
+ if (type_lists_match(n->types, types))
+ return n->expd;
+ }
+
+ return NULL;
+}
+
struct ast *scope_find_type(struct scope *scope, char *id)
{
return scope_find_visible(scope->types, id);
@@ -352,13 +411,36 @@ struct ast *file_scope_find_var(struct scope *scope, char *id)
return NULL;
}
+struct ast *scope_find_expd_struct(struct scope *scope, struct ast *def, struct type *types)
+{
+ assert(def->k == AST_STRUCT_DEF);
+ struct ast *exists = scope_find_expanded(scope->expanded, def, types);
+ if (!exists)
+ return NULL;
+
+ assert(exists->k == AST_STRUCT_DEF);
+ return exists;
+}
+
+struct ast *file_scope_find_expd_struct(struct scope *scope, struct ast *def, struct type *types)
+{
+ assert(def->k == AST_STRUCT_DEF);
+ struct ast *found = scope_find_expd_struct(scope, def, types);
+ if (found)
+ return found;
+
+ if (!scope_flags(scope, SCOPE_FILE))
+ return file_scope_find_expd_struct(scope->parent, def, types);
+
+ return NULL;
+}
+
void scope_add_scope(struct scope *parent, struct scope *child)
{
assert(parent);
assert(child);
if (!scope_flags(child, SCOPE_FILE)) {
- child->actuals = parent->actuals;
child->fctx = parent->fctx;
}
@@ -366,27 +448,3 @@ void scope_add_scope(struct scope *parent, struct scope *child)
child->next = parent->children;
parent->children = child;
}
-
-static int add_actual(struct actual *actuals, struct ast *node)
-{
- if (!actuals->node) {
- /* fill empty first element */
- actuals->node = node;
- return 0;
- }
-
- /* TODO: check that there isn't already an actual like ours */
- struct actual *actual = calloc(1, sizeof(struct actual));
- if (!actual)
- return -1;
-
- actual->next = actuals->next;
- actual->node = node;
- actuals->next = actual;
- return 0;
-}
-
-int scope_add_actual(struct scope *scope, struct ast *node)
-{
- return add_actual(scope->actuals, node);
-}
diff --git a/tests/generic.ek b/tests/generic.ek
index 7e17d97..065477e 100644
--- a/tests/generic.ek
+++ b/tests/generic.ek
@@ -1,7 +1,14 @@
+define any[] {}
+
typedef i27 {}
-typedef a[any T] {}
+typedef i9 {}
+
+typedef a[any T] {
+ T b;
+}
main()
{
- a![i27] a;
+ a![i27] a = a![i27]{.b = 10};
+ a![i9] b = a![i9]{.b = 20 as i9};
}
diff --git a/tests/trait_expand.ek b/tests/trait_expand.ek
index 2d9f90d..8ee413e 100644
--- a/tests/trait_expand.ek
+++ b/tests/trait_expand.ek
@@ -14,6 +14,6 @@ typedef c {
main()
{
- c c;
+ c c = c!{};
c.a();
}