commit d256036430e8ad2aa3661dd53e21bacf8b32d8c4
parent c5e2fbb8695cde54150ebe59645651553d88e45d
Author: Drew DeVault <sir@cmpwn.com>
Date: Thu, 31 Dec 2020 14:07:12 -0500
Initial implementation of constants
Some work is needed to make this work better with forward references and
aggregate types.
Diffstat:
4 files changed, 63 insertions(+), 11 deletions(-)
diff --git a/include/scope.h b/include/scope.h
@@ -5,6 +5,7 @@
enum object_type {
O_BIND,
+ O_CONST,
O_DECL,
};
@@ -13,6 +14,7 @@ struct scope_object {
enum object_type otype;
struct identifier ident;
const struct type *type;
+ struct expression *value; // For O_CONST
struct scope_object *next;
};
@@ -35,7 +37,7 @@ void scope_free_all(struct scopes *scopes);
const struct scope_object *scope_insert(struct scope *scope,
enum object_type otype, const struct identifier *ident,
- const struct type *type);
+ const struct type *type, struct expression *value);
const struct scope_object *scope_lookup(struct scope *scope,
const struct identifier *ident);
diff --git a/src/check.c b/src/check.c
@@ -5,6 +5,7 @@
#include <string.h>
#include "ast.h"
#include "check.h"
+#include "eval.h"
#include "expr.h"
#include "scope.h"
#include "trace.h"
@@ -64,8 +65,13 @@ check_expr_access(struct context *ctx,
char buf[1024];
identifier_unparse_static(&aexpr->access.ident, buf, sizeof(buf));
expect(obj, "Unknown object", buf);
- expr->result = obj->type;
- expr->access.object = obj;
+ if (obj->otype == O_CONST) {
+ // Lower constants
+ *expr = *obj->value;
+ } else {
+ expr->result = obj->type;
+ expr->access.object = obj;
+ }
break;
case ACCESS_INDEX:
expr->access.array = xcalloc(1, sizeof(struct expression));
@@ -239,8 +245,8 @@ check_expr_binding(struct context *ctx,
expect(type_is_assignable(&ctx->store, type, initializer->result),
"Initializer is not assignable to binding type");
- const struct scope_object *obj = scope_insert(ctx->scope,
- O_BIND, &ident, type);
+ const struct scope_object *obj = scope_insert(
+ ctx->scope, O_BIND, &ident, type, NULL);
binding->object = obj;
binding->initializer = initializer;
@@ -733,7 +739,7 @@ check_function(struct context *ctx,
};
const struct type *type = type_store_lookup_atype(
&ctx->store, params->type);
- scope_insert(decl->func.scope, O_BIND, &ident, type);
+ scope_insert(decl->func.scope, O_BIND, &ident, type, NULL);
params = params->next;
}
@@ -768,7 +774,7 @@ check_declarations(struct context *ctx,
{
trenter(TR_CHECK, "declarations");
while (adecls) {
- struct declaration *decl;
+ struct declaration *decl = NULL;
const struct ast_decl *adecl = &adecls->decl;
switch (adecl->decl_type) {
case AST_DECL_FUNC:
@@ -779,7 +785,7 @@ check_declarations(struct context *ctx,
case AST_DECL_GLOBAL:
assert(0); // TODO
case AST_DECL_CONST:
- assert(0); // TODO
+ break; // Handled in scan
}
if (decl) {
@@ -809,11 +815,45 @@ scan_function(struct context *ctx, const struct ast_function_decl *decl)
&ctx->store, &fn_atype);
assert(fntype); // TODO: Forward references
+ struct identifier ident = {0};
+ mkident(ctx, &ident, &decl->ident);
+ scope_insert(ctx->unit, O_DECL, &ident, fntype, NULL);
+
char buf[1024];
identifier_unparse_static(&decl->ident, buf, sizeof(buf));
trleave(TR_SCAN, "func %s", buf);
+}
- scope_insert(ctx->unit, O_DECL, &decl->ident, fntype);
+static void
+scan_const(struct context *ctx, const struct ast_global_decl *decl)
+{
+ trenter(TR_SCAN, "constant");
+ assert(!decl->symbol); // Invariant
+
+ const struct type *type = type_store_lookup_atype(
+ &ctx->store, decl->type);
+ // TODO:
+ // - Free the initializer
+ // - Defer if we can't evaluate it now (for forward references)
+ struct expression *initializer =
+ xcalloc(1, sizeof(struct expression));
+ check_expression(ctx, decl->init, initializer);
+
+ // TODO: Lower implicit casts
+ expect(type_is_assignable(&ctx->store, type, initializer->result),
+ "Constnat type is not assignable from initializer type");
+
+ struct expression *value =
+ xcalloc(1, sizeof(struct expression));
+ enum eval_result r = eval_expr(ctx, initializer, value);
+ // TODO: More forward reference issues:
+ expect(r == EVAL_OK, "Unable to evaluate initializer at compile time");
+
+ struct identifier ident = {0};
+ mkident(ctx, &ident, &decl->ident);
+ scope_insert(ctx->unit, O_CONST, &ident, type, value);
+
+ trleave(TR_SCAN, NULL);
}
static void
@@ -831,7 +871,8 @@ scan_declarations(struct context *ctx, const struct ast_decls *decls)
case AST_DECL_GLOBAL:
assert(0); // TODO
case AST_DECL_CONST:
- assert(0); // TODO
+ scan_const(ctx, &decl->constant);
+ break;
}
decls = decls->next;
}
diff --git a/src/gen.c b/src/gen.c
@@ -105,6 +105,8 @@ qval_for_object(struct gen_context *ctx,
val->kind = QV_GLOBAL;
val->indirect = false;
break;
+ case O_CONST:
+ assert(0); // Invariant (lowered in check)
}
if (type_is_aggregate(obj->type)) {
diff --git a/src/scope.c b/src/scope.c
@@ -1,5 +1,6 @@
#include <assert.h>
#include <stdlib.h>
+#include "expr.h"
#include "identifier.h"
#include "scope.h"
#include "trace.h"
@@ -64,12 +65,18 @@ const struct scope_object *
scope_insert(struct scope *scope,
enum object_type otype,
const struct identifier *ident,
- const struct type *type)
+ const struct type *type,
+ struct expression *value)
{
struct scope_object *o = xcalloc(1, sizeof(struct scope_object));
identifier_dup(&o->ident, ident);
o->otype = otype;
o->type = type;
+ o->value = value;
+ if (value) {
+ assert(otype == O_CONST);
+ assert(value->type == EXPR_CONSTANT);
+ }
*scope->next = o;
scope->next = &o->next;
return o;