mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
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:
committed by
Christoffer Lerno
parent
d0ed16e60e
commit
8586bf3f8d
@@ -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:
|
||||
|
||||
@@ -272,7 +272,7 @@ typedef struct
|
||||
|
||||
typedef struct _VarDecl
|
||||
{
|
||||
VarDeclKind kind : 3;
|
||||
VarDeclKind kind : 4;
|
||||
bool constant : 1;
|
||||
bool failable : 1;
|
||||
bool unwrap : 1;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
// #skip
|
||||
|
||||
const uint AA = (~0);
|
||||
|
||||
// #expect: constant_paren.ll
|
||||
|
||||
#define test_AA (~0)
|
||||
34
test/test_suite/constants/constants.c3t
Normal file
34
test/test_suite/constants/constants.c3t
Normal 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
|
||||
@@ -1,4 +1,6 @@
|
||||
// #skip
|
||||
|
||||
|
||||
|
||||
struct Struct
|
||||
{
|
||||
int x;
|
||||
|
||||
6
test/test_suite/globals/misplaced_const.c3
Normal file
6
test/test_suite/globals/misplaced_const.c3
Normal 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
|
||||
Reference in New Issue
Block a user