aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKimplul <kimi.h.kuparinen@gmail.com>2023-03-27 03:18:48 +0300
committerKimplul <kimi.h.kuparinen@gmail.com>2023-03-27 03:18:48 +0300
commit78cf00cb506cb86112cb715e4d2d69ec11b20c42 (patch)
tree32a1fd3d1e60343cb1e654900c2d4dedca5e938b
parent9fb179ab999c3b98caabca09b30c409dc5dd3b3d (diff)
downloadek-78cf00cb506cb86112cb715e4d2d69ec11b20c42.tar.gz
ek-78cf00cb506cb86112cb715e4d2d69ec11b20c42.zip
change typeof to be a full time type
-rw-r--r--gen/gen_parser.c2
-rw-r--r--src/actualize.c186
-rw-r--r--src/ast.c8
-rw-r--r--src/compiler.c1
-rw-r--r--src/debug.c5
-rw-r--r--src/parser.y2
-rw-r--r--src/scope.c101
-rw-r--r--tests/callbacks.cu17
-rw-r--r--tests/calls.cu19
-rw-r--r--tests/loops.cu4
10 files changed, 280 insertions, 65 deletions
diff --git a/gen/gen_parser.c b/gen/gen_parser.c
index 2182c59..df39070 100644
--- a/gen/gen_parser.c
+++ b/gen/gen_parser.c
@@ -3566,7 +3566,7 @@ yyreduce:
#line 445 "src/parser.y"
{
(yyval.node) = gen_type(AST_TYPE_POINTER, NULL, NULL, NULL);
- (yyval.node)->next = (yyvsp[0].node);
+ (yyval.node)->_type.next = (yyvsp[0].node);
}
#line 3572 "gen/gen_parser.c"
break;
diff --git a/src/actualize.c b/src/actualize.c
index 8aec279..e79bfa2 100644
--- a/src/actualize.c
+++ b/src/actualize.c
@@ -494,6 +494,23 @@ int alias_match(struct ast_node *a, struct ast_node *b)
return types_match(a_act, b_act);
}
+static int typeof_match(struct ast_node *a, struct ast_node *b)
+{
+ struct ast_node *a_act = NULL, *b_act = NULL;
+ if (a->_type.kind == AST_TYPE_TYPEOF)
+ a_act = a->_type.typeo.actual;
+ else
+ a_act = a;
+
+
+ if (b->_type.kind == AST_TYPE_TYPEOF)
+ b_act = b->_type.typeo.actual;
+ else
+ b_act = b;
+
+ return types_match(a_act, b_act);
+}
+
int types_match(struct ast_node *a, struct ast_node *b)
{
if (!a && !b)
@@ -508,9 +525,17 @@ int types_match(struct ast_node *a, struct ast_node *b)
assert(a->node_type == AST_TYPE);
assert(b->node_type == AST_TYPE);
+ if (a->_type.kind == AST_TYPE_TYPEOF ||
+ b->_type.kind == AST_TYPE_TYPEOF) {
+ if (typeof_match(a, b))
+ return 1;
+ return 0;
+ }
+
/* handle special cases that should match even with different type kinds */
if (a->_type.kind == AST_TYPE_TEMPLATE ||
b->_type.kind == AST_TYPE_TEMPLATE) {
+ /* TODO: check template type name */
if (template_match(a, b))
return 1;
return 0;
@@ -676,6 +701,7 @@ static int actualize_proc_call(struct act_state *state,
struct ast_node *params = sign->_type.sign.params;
struct ast_node *args = call->_call.args;
+ /* fuck, analyze_proc gobbles up the return type typeof */
actualize_template_types(params, args);
if (actualize(state, tmp, sign))
@@ -773,6 +799,7 @@ static int actualize_call(struct act_state *state,
assert(call && call->node_type == AST_CALL);
/* check that arguments exist, make sure they have types etc. */
+ /* TODO: procedure callbacks? */
int ret = actualize(state, scope, call->_call.args);
if (ret)
return ret;
@@ -795,7 +822,7 @@ get_callable:
if (callable->node_type == AST_PROC
&& !ast_flags(callable->_proc.sign, AST_FLAG_ACTUAL)) {
/* since we're in types only mode, this should be fine */
- if (actualize(state, scope, callable))
+ if (actualize(state, callable->scope, callable))
return -1;
goto get_callable;
@@ -866,26 +893,27 @@ static int actualize_proc(struct act_state *state,
}
struct ast_node *sign = actual->_proc.sign;
- if (act_flags(state, ACT_ONLY_TYPES)) {
- struct scope *param_scope = create_scope();
- scope_add_scope(scope, param_scope);
+ struct scope *param_scope = create_scope();
+ scope_add_scope(scope, param_scope);
- /* procedure type is the signature */
- sign->scope = param_scope;
- actual->type = sign;
+ /* procedure type is the signature */
+ sign->scope = param_scope;
+ actual->type = sign;
- /* TODO: if we're actualizing the types here, how can we avoid
- * duplicates in the scope proc list? Or can we at all? */
+ /* TODO: if we're actualizing the types here, how can we avoid
+ * duplicates in the scope proc list? Or can we at all? */
- /* actualize types in signature */
- /* note to self: this could likely be made more explicit, but
- * essentially we only want to actualize the signature when
- * we're in the analysis phase. After that, the call to the proc
- * will initialize the types for us, so this step would
- * overwrite the type info. I think, at least. */
- return actualize(state, param_scope, sign);
- }
+ /* actualize types in signature */
+ /* note to self: this could likely be made more explicit, but
+ * essentially we only want to actualize the signature when
+ * we're in the analysis phase. After that, the call to the proc
+ * will initialize the types for us, so this step would
+ * overwrite the type info. I think, at least. */
+ if (actualize(state, param_scope, sign))
+ return -1;
+ if (act_flags(state, ACT_ONLY_TYPES))
+ return 0;
/*
struct ast_node *params = sign->_type.sign.params;
while (params) {
@@ -1215,21 +1243,11 @@ static int actualize_type(struct act_state *state,
if (actualize(state, scope, expr))
EXIT_ACT(-1);
- /* I suspect there are more ways than this that we can fail, but
- * this is a decent starting point */
- if (expr->type->_type.kind == AST_TYPE_TYPEOF) {
- semantic_error(scope->fctx, type,
- "couldn't actualize type expression\n");
- EXIT_ACT(-1);
- }
-
+ /* TODO: for now just trust that the expression is not looped or
+ * anything dumb like that, but I would feel better if I figure
+ * out some check */
assert(type->_type.next == NULL);
- /* clone node to make sure we don't accidentally step on
- * anyone's toes and double free anything */
- struct ast_node *clone = clone_ast_node(expr->type);
- *type = *clone;
- free(clone);
- destroy_ast_node(expr);
+ type->_type.typeo.actual = expr->type;
break;
}
@@ -1275,13 +1293,6 @@ static int actualize_type(struct act_state *state,
}
ast_set_flags(type, AST_FLAG_ACTUAL);
-
- if (type->_type.kind == AST_TYPE_TYPEOF) {
- semantic_error(scope->fctx, type,
- "couldn't convert expression to type");
- EXIT_ACT(-1);
- }
-
/* generally speaking */
EXIT_ACT(0);
}
@@ -1520,34 +1531,87 @@ static int init_struct(struct act_state *state, struct scope *scope,
return ret;
}
+static struct ast_node *actual_type(struct ast_node *type)
+{
+ assert(type->node_type == AST_TYPE);
+ if (type->_type.kind == AST_TYPE_ALIAS)
+ return actual_type(type->_type.alias.actual);
+
+ if (type->_type.kind == AST_TYPE_TEMPLATE)
+ return actual_type(type->_type.template.actual);
+
+ if (type->_type.kind == AST_TYPE_TYPEOF)
+ return actual_type(type->_type.typeo.actual);
+
+ return type;
+}
+
static int actualize_struct_init(struct act_state *state,
struct scope *scope, struct ast_node *init,
struct ast_node *struct_type)
{
- if (struct_type->_type.kind != AST_TYPE_STRUCT) {
+ struct ast_node *actual = actual_type(struct_type);
+ if (actual->_type.kind != AST_TYPE_STRUCT) {
semantic_error(scope->fctx, struct_type,
"type is not a structure");
return -1;
}
- struct ast_node *id = struct_type->_type.struc.id;
+ struct ast_node *id = actual->_type.struc.id;
struct ast_node *exists = file_scope_resolve_type(scope, id);
assert(exists);
return init_struct(state, scope, exists, init);
}
+static int proc_pointer(struct ast_node *type)
+{
+ if (type->_type.kind != AST_TYPE_POINTER)
+ return 0;
+
+ struct ast_node *next = type->_type.next;
+ if (next->_type.kind != AST_TYPE_SIGN)
+ return 0;
+
+ return 1;
+}
+
+static int proc_choice(struct ast_node *expr, struct ast_node *type)
+{
+ if (expr->node_type != AST_ID)
+ return 0;
+
+ if (!proc_pointer(type))
+ return 0;
+
+ return 1;
+}
+
+/* still slightly unsure about this, but hey ho */
+static int match_proc(struct act_state *state, struct scope *scope,
+ struct ast_node *cast)
+{
+ semantic_error(scope->fctx, cast,
+ "procedure signature casts not yet implemented");
+ return -1;
+}
+
static int actualize_cast(struct act_state *state,
struct scope *scope, struct ast_node *cast)
{
assert(cast->node_type == AST_CAST);
struct ast_node *expr = cast->_cast.expr;
+ struct ast_node *type = cast->_cast.type;
- if (actualize(state, scope, expr))
+ if (actualize(state, scope, type))
return -1;
- struct ast_node *type = cast->_cast.type;
- if (actualize(state, scope, type))
+ if (proc_choice(expr, type)) {
+ cast->type = type;
+ return match_proc(state, scope, cast);
+ }
+
+ if (actualize(state, scope, expr))
return -1;
if (expr->node_type == AST_INIT) {
@@ -2186,8 +2250,44 @@ void replace_type(struct ast_node *type, struct ast_node *from,
void replace_param_types(struct ast_node *param, struct ast_node *param_type,
struct ast_node *arg_type)
{
+ if (arg_type->_type.kind == AST_TYPE_TEMPLATE) {
+ replace_param_types(param, param_type, arg_type);
+ return;
+ }
+
while (param) {
replace_type(param->type, param_type, arg_type);
param = param->next;
}
}
+
+void init_template_type(struct ast_node *type, struct ast_node *param_type,
+ struct ast_node *arg_type)
+{
+ if (!types_match(type, param_type))
+ return;
+
+ struct ast_node *template = extract_template(type);
+ if (template) {
+ /* TODO: this shares a fair bit of similarities with
+ * actualize_template_types, could probably create a common
+ * backend? */
+ while (type != template) {
+ type = param_type->_type.next;
+ type = arg_type->_type.next;
+ assert(param_type);
+ assert(arg_type);
+ }
+
+ template->_type.template.actual = arg_type;
+ }
+}
+
+void init_template_types(struct ast_node *params, struct ast_node *param_type,
+ struct ast_node *arg_type)
+{
+ while (params) {
+ init_template_type(params->type, param_type, arg_type);
+ params = params->next;
+ }
+}
diff --git a/src/ast.c b/src/ast.c
index d258b91..056cc73 100644
--- a/src/ast.c
+++ b/src/ast.c
@@ -30,9 +30,15 @@
static struct src_loc loc_span(struct ast_node *left, struct ast_node *right)
{
struct src_loc loc = {0};
- if (!left || !right)
+ if (!left && !right)
return loc;
+ if (!left && right)
+ return right->loc;
+
+ if (left && !right)
+ return left->loc;
+
/* this might eventually be a good thing to do,
* but right now I'm still having issues with initializing all nodes
* with some kind of even slightly accurate location
diff --git a/src/compiler.c b/src/compiler.c
index a1a5c92..b124a6f 100644
--- a/src/compiler.c
+++ b/src/compiler.c
@@ -140,6 +140,7 @@ int compile(const char *file) {
int ret = -1;
struct scope *root = NULL;
if (process_file(&root, 0, file)) {
+ destroy_scope(root);
error("compilation of %s stopped due to errors", file);
return ret;
}
diff --git a/src/debug.c b/src/debug.c
index e42aedc..c9a1f86 100644
--- a/src/debug.c
+++ b/src/debug.c
@@ -206,6 +206,11 @@ static void _type_str(FILE *fp, struct ast_node *type)
fprintf(fp, ")");
}
}
+
+ case AST_TYPE_TYPEOF: {
+ _type_str(fp, type->_type.typeo.actual);
+ fprintf(fp, " (typeof)");
+ }
}
_type_str(fp, type->_type.next);
diff --git a/src/parser.y b/src/parser.y
index 4ad4d8b..50edf13 100644
--- a/src/parser.y
+++ b/src/parser.y
@@ -444,7 +444,7 @@ variadic_sign: func_sign { $$ = $1; }
type: id { $$ = gen_type(AST_TYPE_ID, $1, NULL, NULL); }
| "'" func_sign {
$$ = gen_type(AST_TYPE_POINTER, NULL, NULL, NULL);
- $$->next = $2;
+ $$->_type.next = $2;
}
| id "(" type_list ")" {
$$ = gen_type(AST_TYPE_STRUCT, $1, $3, NULL);
diff --git a/src/scope.c b/src/scope.c
index 721a31e..eca425b 100644
--- a/src/scope.c
+++ b/src/scope.c
@@ -386,7 +386,26 @@ static void remove_implementation(struct ast_node *template,
static int find_implementation(struct ast_node *template, struct ast_node *type)
{
+ if (!type)
+ return 0;
+
assert(template->node_type == AST_TEMPLATE);
+ if (type->_type.kind == AST_TYPE_TEMPLATE)
+ return find_implementation(template,
+ type->_type.template.actual);
+
+ if (type->_type.kind == AST_TYPE_ALIAS)
+ return find_implementation(template, type->_type.alias.actual);
+
+ /* I'm not 100% sold on having to handle these special cases multiple
+ * times in different places, but I'm not sure what alternatives I have.
+ * For debugging purposes, maintaining as much info about the original
+ * code is useful, but I wonder if I can somehow maybe clone this stuff
+ * and keep a reference to the original or something without too much
+ * work? TODO */
+ if (type->_type.kind == AST_TYPE_TYPEOF)
+ return find_implementation(template, type->_type.typeo.actual);
+
struct template_implemented *prev = template->_template.impl_by, *cur;
if (prev)
do {
@@ -448,8 +467,8 @@ static int implements_proc(struct scope *scope, struct ast_node *arg_type,
struct ast_node *params = sign->_type.sign.params;
struct ast_node *ret = sign->_type.sign.ret;
- replace_param_types(params, param_type, arg_type);
- replace_param_types(ret, param_type, arg_type);
+ init_template_types(params, param_type, arg_type);
+ init_template_type(ret, param_type, arg_type);
struct ast_node *impl = match_proc(1, scope, id, params);
if (!impl)
@@ -552,6 +571,40 @@ not_implemented:
return 0;
}
+static int implements_alias(struct scope *scope, struct ast_node *arg_type,
+ struct ast_node *param_type)
+{
+ struct ast_node *a_act = NULL, *p_act = NULL;
+ if (arg_type->_type.kind == AST_TYPE_ALIAS)
+ a_act = arg_type->_type.alias.actual;
+ else
+ a_act = arg_type;
+
+ if (param_type->_type.kind == AST_TYPE_ALIAS)
+ p_act = param_type->_type.alias.actual;
+ else
+ p_act = param_type;
+
+ return implements(scope, a_act, p_act);
+}
+
+static int implements_typeof(struct scope *scope, struct ast_node *arg_type,
+ struct ast_node *param_type)
+{
+ struct ast_node *a_act = NULL, *p_act = NULL;
+ if (arg_type->_type.kind == AST_TYPE_TYPEOF)
+ a_act = arg_type->_type.typeo.actual;
+ else
+ a_act = arg_type;
+
+ if (param_type->_type.kind == AST_TYPE_TYPEOF)
+ p_act = param_type->_type.typeo.actual;
+ else
+ p_act = param_type;
+
+ return implements(scope, a_act, p_act);
+}
+
int implements(struct scope *scope,
struct ast_node *arg_type, struct ast_node *param_type)
{
@@ -571,18 +624,22 @@ int implements(struct scope *scope,
/* at this point, we should always have some type for the argument */
assert(arg_type);
- if (param_type->_type.kind == AST_TYPE_ALIAS) {
+ if (param_type->_type.kind == AST_TYPE_ALIAS ||
+ arg_type->_type.kind == AST_TYPE_ALIAS) {
assert(param_type->_type.next == NULL);
- return implements(scope, arg_type,
- param_type->_type.alias.actual);
+ return implements_alias(scope, arg_type, param_type);
}
- if (arg_type->_type.kind == AST_TYPE_ALIAS) {
- assert(arg_type->_type.next == NULL);
- return implements(scope, arg_type->_type.alias.actual,
- param_type);
+ if (param_type->_type.kind == AST_TYPE_TYPEOF ||
+ arg_type->_type.kind == AST_TYPE_TYPEOF) {
+ return implements_typeof(scope, arg_type, param_type);
}
+ /* having the arg be a template is a bit of a special case */
+ if (arg_type->_type.kind == AST_TYPE_TEMPLATE)
+ return implements(scope, arg_type->_type.template.actual,
+ param_type);
+
/* TODO: do aliases and templates have to be converted to types? Are
* there any situations where a template will have to be followed by
* some other type? */
@@ -817,16 +874,18 @@ int scope_add_existing_proc(struct scope *scope, struct visible *visible)
return 0;
}
-#define FIND_FILE_VISIBLE(name, obj_type) \
- struct ast_node *name(struct scope *scope, struct ast_node *id) \
- { \
- assert(id->node_type == AST_ID); \
- struct ast_node *found = scope_find_##obj_type(scope, id); \
- if (found) \
- return found; \
- if (!scope_flags(scope, SCOPE_FILE)) \
- return file_scope_find_##obj_type(scope->parent, id); \
- return NULL; \
+#define FIND_FILE_VISIBLE(name, obj_type) \
+ struct ast_node *name(struct scope *scope, struct ast_node *id) \
+ { \
+ assert(id->node_type == AST_ID); \
+ struct ast_node *found = scope_find_##obj_type(scope, id); \
+ if (found) { \
+ return found; \
+ } \
+ if (!scope_flags(scope, SCOPE_FILE)) { \
+ return file_scope_find_##obj_type(scope->parent, id); \
+ } \
+ return NULL; \
}
struct ast_node *file_scope_find_type(struct scope *scope,
@@ -860,6 +919,10 @@ struct ast_node *file_scope_find(struct scope *scope, struct ast_node *id)
if (found)
return found;
+ found = file_scope_find_override(scope, id);
+ if (found)
+ return found;
+
found = file_scope_find_proc(scope, id);
if (found)
return found;
diff --git a/tests/callbacks.cu b/tests/callbacks.cu
new file mode 100644
index 0000000..0249065
--- /dev/null
+++ b/tests/callbacks.cu
@@ -0,0 +1,17 @@
+do_stuff(proc '(u32))
+{
+ proc();
+}
+
+other_proc(u32)
+{
+}
+
+other_proc()
+{
+}
+
+main()
+{
+ do_stuff(other_proc as '(u32));
+}
diff --git a/tests/calls.cu b/tests/calls.cu
new file mode 100644
index 0000000..4978649
--- /dev/null
+++ b/tests/calls.cu
@@ -0,0 +1,19 @@
+typedef some_type {
+ add(some_type, some_type => some_type);
+}
+
+add(a u32, b u32 => u32)
+{
+ return a + b;
+}
+
+/* ah fuck, analyze_proc gobbles up the typeof */
+some_proc(a some_type, b typeof a => typeof a)
+{
+ return add(a, b);
+}
+
+main()
+{
+ some_proc(20 as u32, 40 as u32);
+}
diff --git a/tests/loops.cu b/tests/loops.cu
new file mode 100644
index 0000000..5b6f702
--- /dev/null
+++ b/tests/loops.cu
@@ -0,0 +1,4 @@
+/* this should error out with some decent message about type loops or something
+ * */
+f(=>typeof main()){}
+main(=> typeof f()){}