commit c8555dd82d94cef6cb3ee87210a3e5bc8593d1be
parent 9db7edc36603cc8088e279514d07b5299f6ddd35
Author: Drew DeVault <sir@cmpwn.com>
Date: Sat, 19 Dec 2020 10:54:30 -0500
Type check constant expressions
Diffstat:
7 files changed, 153 insertions(+), 72 deletions(-)
diff --git a/include/ast.h b/include/ast.h
@@ -87,8 +87,8 @@ struct ast_type {
struct ast_constant_expression {
enum type_storage storage;
union {
- intmax_t _signed;
- uintmax_t _unsigned;
+ intmax_t ival;
+ uintmax_t uval;
struct {
size_t len;
char *value;
diff --git a/include/lex.h b/include/lex.h
@@ -135,15 +135,15 @@ struct token {
enum lexical_token token;
enum type_storage storage;
union {
- double _float;
char *name;
uint32_t rune;
- intmax_t _signed;
+ intmax_t ival;
+ uintmax_t uval;
+ double fval;
struct {
size_t len;
char *value;
} string;
- uintmax_t _unsigned;
};
};
diff --git a/include/type_store.h b/include/type_store.h
@@ -20,4 +20,7 @@ unsigned long type_hash(struct type_store *store, const struct type *type);
const struct type *type_store_lookup_atype(
struct type_store *store, const struct ast_type *atype);
+const struct type *builtin_type_for_storage(
+ enum type_storage storage, bool is_const);
+
#endif
diff --git a/src/check.c b/src/check.c
@@ -28,12 +28,88 @@ expect(bool constraint, char *fmt, ...)
}
static void
+check_expr_constant(struct context *ctx,
+ const struct ast_expression *aexpr,
+ struct expression *expr)
+{
+ trace(TR_CHECK, "constant");
+ expr->type = EXPR_CONSTANT;
+ expr->result = builtin_type_for_storage(aexpr->constant.storage, false);
+
+ switch (aexpr->constant.storage) {
+ case TYPE_STORAGE_I8:
+ case TYPE_STORAGE_I16:
+ case TYPE_STORAGE_I32:
+ case TYPE_STORAGE_I64:
+ case TYPE_STORAGE_INT:
+ expr->constant.ival = aexpr->constant.ival;
+ break;
+ case TYPE_STORAGE_U8:
+ case TYPE_STORAGE_U16:
+ case TYPE_STORAGE_U32:
+ case TYPE_STORAGE_U64:
+ case TYPE_STORAGE_UINT:
+ case TYPE_STORAGE_SIZE:
+ expr->constant.uval = aexpr->constant.uval;
+ break;
+ case TYPE_STORAGE_RUNE:
+ assert(0); // TODO
+ case TYPE_STORAGE_BOOL:
+ case TYPE_STORAGE_F32:
+ case TYPE_STORAGE_F64:
+ assert(0); // TODO
+ case TYPE_STORAGE_CHAR:
+ case TYPE_STORAGE_UINTPTR:
+ case TYPE_STORAGE_VOID:
+ case TYPE_STORAGE_ALIAS:
+ case TYPE_STORAGE_ARRAY:
+ case TYPE_STORAGE_FUNCTION:
+ case TYPE_STORAGE_POINTER:
+ case TYPE_STORAGE_SLICE:
+ case TYPE_STORAGE_STRING:
+ case TYPE_STORAGE_STRUCT:
+ case TYPE_STORAGE_TAGGED_UNION:
+ case TYPE_STORAGE_UNION:
+ assert(0); // Invariant
+ }
+}
+
+static void
check_expression(struct context *ctx,
const struct ast_expression *aexpr,
struct expression *expr)
{
trenter(TR_CHECK, "expression");
- assert(0); // TODO
+
+ switch (aexpr->type) {
+ case EXPR_ACCESS:
+ case EXPR_ASSERT:
+ case EXPR_ASSIGN:
+ case EXPR_BINARITHM:
+ case EXPR_BINDING_LIST:
+ case EXPR_CALL:
+ case EXPR_CAST:
+ assert(0); // TODO
+ case EXPR_CONSTANT:
+ check_expr_constant(ctx, aexpr, expr);
+ break;
+ case EXPR_CONTROL:
+ case EXPR_FOR:
+ case EXPR_FREE:
+ case EXPR_FUNC:
+ case EXPR_IF:
+ case EXPR_INDEX:
+ case EXPR_LIST:
+ case EXPR_MATCH:
+ case EXPR_MEASURE:
+ case EXPR_SLICE:
+ case EXPR_STRUCT:
+ case EXPR_SWITCH:
+ case EXPR_UNARITHM:
+ case EXPR_WHILE:
+ assert(0); // TODO
+ }
+
trleave(TR_CHECK, NULL);
}
@@ -43,9 +119,9 @@ check_function(struct context *ctx,
struct declaration *decl)
{
const struct ast_function_decl *afndecl = &adecl->function;
+ trenter(TR_CHECK, "function");
assert(!afndecl->prototype.params); // TODO
assert(!afndecl->symbol); // TODO
- trenter(TR_CHECK, "function");
const struct ast_type fn_atype = {
.storage = TYPE_STORAGE_FUNCTION,
@@ -61,6 +137,7 @@ check_function(struct context *ctx,
struct expression *body = calloc(1, sizeof(struct expression));
check_expression(ctx, &afndecl->body, body);
+
// TODO: Check assignability of expression result to function type
// TODO: Add function name to errors
@@ -76,7 +153,8 @@ check_function(struct context *ctx,
if ((fntype->func.flags & FN_NORETURN)) {
expect(!body->terminates, "@noreturn function must not terminate.");
} else {
- expect(body->terminates, "This function never terminates. Add a return statement or @noreturn.");
+ expect(body->type != EXPR_LIST || body->terminates,
+ "This function never terminates. Add a return statement or @noreturn.");
}
trleave(TR_CHECK, NULL);
}
diff --git a/src/lex.c b/src/lex.c
@@ -383,9 +383,9 @@ finalize:
case TYPE_STORAGE_UINT:
case TYPE_STORAGE_U64:
case TYPE_STORAGE_SIZE:
- out->_unsigned = strtoumax(lexer->buf, NULL, strlen(base));
+ out->uval = strtoumax(lexer->buf, NULL, strlen(base));
for (uintmax_t i = 0; i < exponent; i++) {
- out->_unsigned *= 10;
+ out->uval *= 10;
}
break;
case TYPE_STORAGE_I8:
@@ -393,14 +393,14 @@ finalize:
case TYPE_STORAGE_I32:
case TYPE_STORAGE_INT:
case TYPE_STORAGE_I64:
- out->_signed = strtoimax(lexer->buf, NULL, strlen(base));
+ out->ival = strtoimax(lexer->buf, NULL, strlen(base));
for (uintmax_t i = 0; i < exponent; i++) {
- out->_signed *= 10;
+ out->ival *= 10;
}
break;
case TYPE_STORAGE_F32:
case TYPE_STORAGE_F64:
- out->_float = strtod(lexer->buf, NULL);
+ out->fval = strtod(lexer->buf, NULL);
break;
default:
assert(0);
@@ -996,18 +996,18 @@ token_str(const struct token *tok)
case TYPE_STORAGE_UINT:
case TYPE_STORAGE_UINTPTR:
case TYPE_STORAGE_SIZE:
- snprintf(buf, sizeof(buf), "%ju", tok->_unsigned);
+ snprintf(buf, sizeof(buf), "%ju", tok->uval);
break;
case TYPE_STORAGE_I8:
case TYPE_STORAGE_I16:
case TYPE_STORAGE_I32:
case TYPE_STORAGE_I64:
case TYPE_STORAGE_INT:
- snprintf(buf, sizeof(buf), "%jd", tok->_signed);
+ snprintf(buf, sizeof(buf), "%jd", tok->ival);
break;
case TYPE_STORAGE_F32:
case TYPE_STORAGE_F64:
- snprintf(buf, sizeof(buf), "%lf", tok->_float);
+ snprintf(buf, sizeof(buf), "%lf", tok->fval);
break;
case TYPE_STORAGE_RUNE:
bytes += snprintf(&buf[bytes], sizeof(buf) - bytes, "'");
diff --git a/src/parse.c b/src/parse.c
@@ -353,14 +353,14 @@ parse_simple_expression(struct parser *par, struct ast_expression *exp)
case TYPE_STORAGE_UINT:
case TYPE_STORAGE_UINTPTR:
case TYPE_STORAGE_SIZE:
- exp->constant._unsigned = (uintmax_t)tok._unsigned;
+ exp->constant.uval = (uintmax_t)tok.uval;
break;
case TYPE_STORAGE_I8:
case TYPE_STORAGE_I16:
case TYPE_STORAGE_I32:
case TYPE_STORAGE_I64:
case TYPE_STORAGE_INT:
- exp->constant._signed = (intmax_t)tok._signed;
+ exp->constant.ival = (intmax_t)tok.ival;
break;
case TYPE_STORAGE_STRING:
exp->constant.string.len = tok.string.len;
diff --git a/src/type_store.c b/src/type_store.c
@@ -3,56 +3,63 @@
#include "type_store.h"
#include "util.h"
-unsigned long
-atype_hash(struct type_store *store, const struct ast_type *type)
+const struct type *
+builtin_type_for_storage(enum type_storage storage, bool is_const)
{
- unsigned long hash = DJB2_INIT;
- hash = djb2(hash, type->storage);
- hash = djb2(hash, type->flags);
- switch (type->storage) {
+ switch (storage) {
case TYPE_STORAGE_BOOL:
+ return is_const ? &builtin_type_bool : &builtin_type_const_bool;
case TYPE_STORAGE_CHAR:
+ return is_const ? &builtin_type_char : &builtin_type_const_char;
case TYPE_STORAGE_F32:
+ return is_const ? &builtin_type_f32 : &builtin_type_const_f32;
case TYPE_STORAGE_F64:
+ return is_const ? &builtin_type_f64 : &builtin_type_const_f64;
case TYPE_STORAGE_I8:
+ return is_const ? &builtin_type_i8 : &builtin_type_const_i8;
case TYPE_STORAGE_I16:
+ return is_const ? &builtin_type_i16 : &builtin_type_const_i16;
case TYPE_STORAGE_I32:
+ return is_const ? &builtin_type_i32 : &builtin_type_const_i32;
case TYPE_STORAGE_I64:
+ return is_const ? &builtin_type_i64 : &builtin_type_const_i64;
case TYPE_STORAGE_INT:
+ return is_const ? &builtin_type_int : &builtin_type_const_int;
case TYPE_STORAGE_RUNE:
+ return is_const ? &builtin_type_rune : &builtin_type_const_rune;
case TYPE_STORAGE_SIZE:
+ return is_const ? &builtin_type_size : &builtin_type_const_size;
case TYPE_STORAGE_U8:
+ return is_const ? &builtin_type_u8 : &builtin_type_const_u8;
case TYPE_STORAGE_U16:
+ return is_const ? &builtin_type_u16 : &builtin_type_const_u16;
case TYPE_STORAGE_U32:
+ return is_const ? &builtin_type_u32 : &builtin_type_const_u32;
case TYPE_STORAGE_U64:
+ return is_const ? &builtin_type_u64 : &builtin_type_const_u64;
case TYPE_STORAGE_UINT:
+ return is_const ? &builtin_type_uint : &builtin_type_const_uint;
case TYPE_STORAGE_UINTPTR:
+ return is_const ? &builtin_type_uintptr : &builtin_type_const_uintptr;
case TYPE_STORAGE_VOID:
- break; // built-ins
+ // const void and void are the same type
+ return is_const ? &builtin_type_void : &builtin_type_void;
+ case TYPE_STORAGE_POINTER:
case TYPE_STORAGE_ALIAS:
case TYPE_STORAGE_ARRAY:
case TYPE_STORAGE_FUNCTION:
- hash = djb2(hash, atype_hash(store, type->func.result));
- hash = djb2(hash, type->func.variadism);
- hash = djb2(hash, type->func.flags);
- for (struct ast_function_parameters *param = type->func.params;
- param; param = param->next) {
- hash = djb2(hash, atype_hash(store, param->type));
- }
- break;
- case TYPE_STORAGE_POINTER:
case TYPE_STORAGE_SLICE:
case TYPE_STORAGE_STRING:
case TYPE_STORAGE_STRUCT:
case TYPE_STORAGE_TAGGED_UNION:
case TYPE_STORAGE_UNION:
- assert(0); // TODO
+ return NULL;
}
- return hash;
+ assert(0); // Unreachable
}
unsigned long
-type_hash(struct type_store *store, const struct type *type)
+atype_hash(struct type_store *store, const struct ast_type *type)
{
unsigned long hash = DJB2_INIT;
hash = djb2(hash, type->storage);
@@ -80,12 +87,12 @@ type_hash(struct type_store *store, const struct type *type)
case TYPE_STORAGE_ALIAS:
case TYPE_STORAGE_ARRAY:
case TYPE_STORAGE_FUNCTION:
- hash = djb2(hash, type_hash(store, type->func.result));
+ hash = djb2(hash, atype_hash(store, type->func.result));
hash = djb2(hash, type->func.variadism);
hash = djb2(hash, type->func.flags);
- for (struct type_func_param *param = type->func.params;
+ for (struct ast_function_parameters *param = type->func.params;
param; param = param->next) {
- hash = djb2(hash, type_hash(store, param->type));
+ hash = djb2(hash, atype_hash(store, param->type));
}
break;
case TYPE_STORAGE_POINTER:
@@ -99,66 +106,59 @@ type_hash(struct type_store *store, const struct type *type)
return hash;
}
-static const struct type *
-builtin_for_atype(const struct ast_type *atype)
+unsigned long
+type_hash(struct type_store *store, const struct type *type)
{
- bool is_const = (atype->flags & TYPE_CONST) != 0;
- switch (atype->storage) {
+ unsigned long hash = DJB2_INIT;
+ hash = djb2(hash, type->storage);
+ hash = djb2(hash, type->flags);
+ switch (type->storage) {
case TYPE_STORAGE_BOOL:
- return is_const ? &builtin_type_bool : &builtin_type_const_bool;
case TYPE_STORAGE_CHAR:
- return is_const ? &builtin_type_char : &builtin_type_const_char;
case TYPE_STORAGE_F32:
- return is_const ? &builtin_type_f32 : &builtin_type_const_f32;
case TYPE_STORAGE_F64:
- return is_const ? &builtin_type_f64 : &builtin_type_const_f64;
case TYPE_STORAGE_I8:
- return is_const ? &builtin_type_i8 : &builtin_type_const_i8;
case TYPE_STORAGE_I16:
- return is_const ? &builtin_type_i16 : &builtin_type_const_i16;
case TYPE_STORAGE_I32:
- return is_const ? &builtin_type_i32 : &builtin_type_const_i32;
case TYPE_STORAGE_I64:
- return is_const ? &builtin_type_i64 : &builtin_type_const_i64;
case TYPE_STORAGE_INT:
- return is_const ? &builtin_type_int : &builtin_type_const_int;
case TYPE_STORAGE_RUNE:
- return is_const ? &builtin_type_rune : &builtin_type_const_rune;
case TYPE_STORAGE_SIZE:
- return is_const ? &builtin_type_size : &builtin_type_const_size;
case TYPE_STORAGE_U8:
- return is_const ? &builtin_type_u8 : &builtin_type_const_u8;
case TYPE_STORAGE_U16:
- return is_const ? &builtin_type_u16 : &builtin_type_const_u16;
case TYPE_STORAGE_U32:
- return is_const ? &builtin_type_u32 : &builtin_type_const_u32;
case TYPE_STORAGE_U64:
- return is_const ? &builtin_type_u64 : &builtin_type_const_u64;
case TYPE_STORAGE_UINT:
- return is_const ? &builtin_type_uint : &builtin_type_const_uint;
case TYPE_STORAGE_UINTPTR:
- return is_const ? &builtin_type_uintptr : &builtin_type_const_uintptr;
case TYPE_STORAGE_VOID:
- // const void and void are the same type
- return is_const ? &builtin_type_void : &builtin_type_void;
- case TYPE_STORAGE_POINTER:
- if (atype->pointer.referent->storage == TYPE_STORAGE_CHAR
- && atype->pointer.referent->flags == TYPE_CONST) {
- return &builtin_type_const_ptr_char;
- }
- return NULL;
+ break; // built-ins
case TYPE_STORAGE_ALIAS:
case TYPE_STORAGE_ARRAY:
case TYPE_STORAGE_FUNCTION:
+ hash = djb2(hash, type_hash(store, type->func.result));
+ hash = djb2(hash, type->func.variadism);
+ hash = djb2(hash, type->func.flags);
+ for (struct type_func_param *param = type->func.params;
+ param; param = param->next) {
+ hash = djb2(hash, type_hash(store, param->type));
+ }
+ break;
+ case TYPE_STORAGE_POINTER:
case TYPE_STORAGE_SLICE:
case TYPE_STORAGE_STRING:
case TYPE_STORAGE_STRUCT:
case TYPE_STORAGE_TAGGED_UNION:
case TYPE_STORAGE_UNION:
- return NULL;
-
+ assert(0); // TODO
}
- assert(0); // Unreachable
+ return hash;
+}
+
+static const struct type *
+builtin_for_atype(const struct ast_type *atype)
+{
+ bool is_const = (atype->flags & TYPE_CONST) != 0;
+ return builtin_type_for_storage(atype->storage, is_const);
}
static bool