Constants are now variables. Constant compile time constants are typeless and act as #define and are now also correctly parsed. Enums are also correctly lowered. And expressions are copied when inlining constants. Compile time ints can no longer be bitnegated.

This commit is contained in:
Christoffer Lerno
2020-07-29 22:06:50 +02:00
committed by Christoffer Lerno
parent d0ed16e60e
commit 8586bf3f8d
13 changed files with 97 additions and 61 deletions

View File

@@ -112,6 +112,8 @@ const char *decl_var_to_string(VarDeclKind kind)
{
case VARDECL_CONST:
return "const";
case VARDECL_CONST_CT:
return "$const";
case VARDECL_GLOBAL:
return "global";
case VARDECL_LOCAL:
@@ -738,6 +740,7 @@ void fprint_decl_recursive(Context *context, FILE *file, Decl *decl, int indent)
DUMPTI(decl->var.type_info);
switch (decl->var.kind)
{
case VARDECL_CONST_CT:
case VARDECL_CONST:
case VARDECL_GLOBAL:
case VARDECL_LOCAL:

View File

@@ -272,7 +272,7 @@ typedef struct
typedef struct _VarDecl
{
VarDeclKind kind : 3;
VarDeclKind kind : 4;
bool constant : 1;
bool failable : 1;
bool unwrap : 1;

View File

@@ -508,7 +508,8 @@ typedef enum
VARDECL_MEMBER = 4,
VARDECL_LOCAL_CT = 5,
VARDECL_LOCAL_CT_TYPE = 6,
VARDECL_ALIAS = 7,
VARDECL_CONST_CT = 7,
VARDECL_ALIAS = 8,
} VarDeclKind;
typedef enum

View File

@@ -60,7 +60,9 @@ LLVMValueRef gencontext_emit_memclear(GenContext *context, LLVMValueRef ref, Typ
static void gencontext_emit_global_variable_definition(GenContext *context, Decl *decl)
{
assert(decl->var.kind == VARDECL_GLOBAL);
if (decl->var.kind == VARDECL_CONST_CT) return;
assert(decl->var.kind == VARDECL_GLOBAL || decl->var.kind == VARDECL_CONST);
// TODO fix name
decl->ref = LLVMAddGlobal(context->module, llvm_type(decl->type), decl->name);
@@ -73,7 +75,8 @@ static void gencontext_emit_global_variable_definition(GenContext *context, Decl
{
LLVMSetInitializer(decl->ref, LLVMConstNull(llvm_type(decl->type)));
}
// If read only: LLVMSetGlobalConstant(decl->var.backend_ref, 1);
LLVMSetGlobalConstant(decl->ref, decl->var.kind == VARDECL_CONST);
switch (decl->visibility)
{

View File

@@ -909,6 +909,7 @@ ParseRule rules[TOKEN_EOF + 1] = {
[TOKEN_CT_IDENT] = { parse_identifier, NULL, PREC_NONE },
[TOKEN_AT] = { parse_macro_ident, NULL, PREC_NONE },
[TOKEN_CONST_IDENT] = { parse_identifier, NULL, PREC_NONE },
[TOKEN_CT_CONST_IDENT] = { parse_identifier, NULL, PREC_NONE },
[TOKEN_STRING] = { parse_string_literal, NULL, PREC_NONE },
[TOKEN_REAL] = { parse_double, NULL, PREC_NONE },
[TOKEN_OR] = { NULL, parse_binary, PREC_LOGICAL },

View File

@@ -576,7 +576,7 @@ Decl *parse_decl(Context *context)
/**
* const_decl
* : 'const' CT_IDENT '=' const_expr ';'
* : 'const' CT_CONST_IDENT '=' const_expr ';'
* | 'const' type IDENT '=' const_expr ';'
* ;
*/
@@ -585,9 +585,12 @@ static inline Decl *parse_const_declaration(Context *context, Visibility visibil
advance_and_verify(context, TOKEN_CONST);
Decl *decl = DECL_NEW_VAR(NULL, VARDECL_CONST, visibility);
decl->span.loc = context->prev_tok;
// Parse the compile time constant.
if (TOKEN_IS(TOKEN_CT_IDENT))
if (try_consume(context, TOKEN_CT_CONST_IDENT))
{
decl->var.kind = VARDECL_CONST_CT;
if (!is_all_upper(decl->name))
{
SEMA_TOKEN_ERROR(context->tok, "Compile time constants must be all upper characters.");
@@ -596,10 +599,9 @@ static inline Decl *parse_const_declaration(Context *context, Visibility visibil
}
else
{
if (token_is_any_type(context->tok.type))
{
decl->var.type_info = TRY_TYPE_OR(parse_type(context), poisoned_decl);
}
decl->var.type_info = TRY_TYPE_OR(parse_type(context), poisoned_decl);
decl->name = TOKKSTR(context->tok);
decl->name_token = context->tok.id;
if (!consume_const_name(context, "constant")) return poisoned_decl;
}
@@ -1681,6 +1683,17 @@ static inline Decl *parse_top_level(Context *context)
TODO
//sema_error_at(context->token->span.loc - 1, "Expected a top level declaration'.");
return poisoned_decl;
case TOKEN_CT_CONST_IDENT:
if (context->next_tok.type == TOKEN_EQ)
{
SEMA_TOKEN_ERROR(context->tok,
"Did you forget a 'const' before the name of this compile time constant?");
}
else
{
SEMA_TOKEN_ERROR(context->tok, "Compile time constant unexpectedly found.");
}
return poisoned_decl;
default:
// We could have included all fundamental types above, but do it here instead.
if (token_is_type(context->tok.type))

View File

@@ -476,34 +476,6 @@ bool ixxbo(Context *context, Expr *left, Type *type)
}
bool ussi(Context *context, Expr* left, Type *from, Type *canonical, Type *type, CastType cast_type)
{
if (type->type_kind != TYPE_ENUM) return sema_type_mismatch(context, left, type, CAST_TYPE_EXPLICIT);
if (cast_type != CAST_TYPE_EXPLICIT) EXIT_T_MISMATCH();
if (left->expr_kind == EXPR_IDENTIFIER && left->identifier_expr.decl->decl_kind == DECL_ENUM_CONSTANT)
{
// TODO
Expr *value = left->identifier_expr.decl->enum_constant.expr;
assert(value->expr_kind == EXPR_CONST);
// assert(value->const_expr.type == CONST_INT);
left->const_expr.i = value->const_expr.i;
// TODO narrowing
}
insert_cast(left, CAST_ENUMLOW, canonical);
return true;
}
bool sius(Context *context, Expr* left, Type *from, Type *canonical, Type *type, CastType cast_type)
{
TODO
}
bool uius(Context *context, Expr* left, Type *from, Type *canonical, Type *type, CastType cast_type)
{
TODO
}
/**
* Cast comptime, signed or unsigned -> pointer.
* @return true unless the constant value evaluates to zero.
@@ -592,6 +564,7 @@ bool enxi(Context *context, Expr* left, Type *from, Type *canonical, Type *type,
return false;
}
// 3. Dispatch to the right cast:
insert_cast(left, CAST_ENUMLOW, enum_type);
return xixi(context, left, enum_type_canonical, canonical, type, cast_type);
}

View File

@@ -591,6 +591,8 @@ static inline bool expr_is_constant_eval(Expr *expr)
static inline bool sema_analyse_global(Context *context, Decl *decl)
{
if (decl->var.kind == VARDECL_CONST_CT) return true;
if (!sema_resolve_type_info(context, decl->var.type_info)) return false;
decl->type = decl->var.type_info->type;
if (decl->var.init_expr)
@@ -649,7 +651,6 @@ static inline bool sema_analyse_global(Context *context, Decl *decl)
default:
eprintf("Decl %s %d\n", decl->name, decl->var.kind);
UNREACHABLE
break;
}
}

View File

@@ -332,17 +332,25 @@ static inline bool sema_expr_analyse_identifier(Context *context, Type *to, Expr
{
expr->failable = true;
}
if (decl->decl_kind == DECL_VAR && decl->var.constant)
if (decl->decl_kind == DECL_VAR)
{
assert(decl->var.init_expr && decl->var.init_expr->resolve_status == RESOLVE_DONE);
if (decl->var.failable)
switch (decl->var.kind)
{
SEMA_ERROR(expr, "Constants may never be 'failable', please remove the '!'.");
return false;
case VARDECL_CONST:
assert(decl->var.init_expr && decl->var.init_expr->resolve_status == RESOLVE_DONE);
if (decl->var.failable)
{
SEMA_ERROR(expr, "Constants may never be 'failable', please remove the '!'.");
return false;
}
expr_replace(expr, expr_copy_from_macro(context, decl->var.init_expr));
return true;
case VARDECL_CONST_CT:
expr_replace(expr, expr_copy_from_macro(context, decl->var.init_expr));
return sema_analyse_expr(context, to, expr);
default:
break;
}
// Todo, maybe make a copy?
expr_replace(expr, decl->var.init_expr);
return true;
}
assert(decl->type);
expr->identifier_expr.decl = decl;
@@ -2643,12 +2651,11 @@ static bool sema_expr_analyse_neg(Context *context, Type *to, Expr *expr, Expr *
static bool sema_expr_analyse_bit_not(Context *context, Type *to, Expr *expr, Expr *inner)
{
Type *canonical = inner->type->canonical;
if (!type_is_any_integer(canonical) && canonical != type_bool)
if (!type_is_integer(canonical) && canonical != type_bool)
{
SEMA_ERROR(expr, "Cannot bit negate '%s'.", type_to_error_string(inner->type));
return false;
}
// The simple case, non-const.
if (inner->expr_kind != EXPR_CONST)
{
@@ -2660,11 +2667,10 @@ static bool sema_expr_analyse_bit_not(Context *context, Type *to, Expr *expr, Ex
switch (expr->const_expr.kind)
{
case ALL_SIGNED_INTS:
case ALL_UNSIGNED_INTS:
bigint_negate_wrap(&expr->const_expr.i, &inner->const_expr.i, canonical->builtin.bitsize);
bigint_not(&expr->const_expr.i, &inner->const_expr.i, canonical->builtin.bitsize, true);
break;
case TYPE_IXX:
bigint_negate(&expr->const_expr.i, &inner->const_expr.i);
case ALL_UNSIGNED_INTS:
bigint_not(&expr->const_expr.i, &inner->const_expr.i, canonical->builtin.bitsize, false);
break;
case TYPE_BOOL:
expr->const_expr.b = !expr->const_expr.b;

View File

@@ -1,7 +0,0 @@
// #skip
const uint AA = (~0);
// #expect: constant_paren.ll
#define test_AA (~0)

View File

@@ -0,0 +1,34 @@
const byte AA = ~0;
const byte BB = 200 ;
const uint CC = ~0;
const uint DD = $FOO;
const $FOO = ~0;
uint x = AA;
uint z = CC;
byte w = $FOO;
ushort v = $FOO;
uint z2 = DD;
func void test()
{
int xx = $FOO;
}
// #expect: constants.ll
@AA = protected constant i8 -1
@BB = protected constant i8 -56
@CC = protected constant i32 -1
@DD = protected constant i32 -1
@x = protected global i32 255
@z = protected global i32 -1
@w = protected global i8 -1
@v = protected global i16 -1
@z2 = protected global i32 -1
entry:
%xx = alloca i32
store i32 -1, i32* %xx
ret void

View File

@@ -1,4 +1,6 @@
// #skip
struct Struct
{
int x;

View File

@@ -0,0 +1,6 @@
const $FOO = 3;
$BAR = 4; // #error: Did you forget a 'const' before the name of this compile time constant?
const $BAZ = "ofke";
$FOOBAR; // #error: Compile time constant unexpectedly found