mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
Fix $switch. And make top level $switch work.
This commit is contained in:
@@ -5,28 +5,30 @@ $assert(Enum.min < Enum.max, "Only strictly increasing enums may be used with en
|
||||
$assert(Enum.max < 64, "Maximum value of an enum used as enum set is 63");
|
||||
$assert(Enum.min >= 0, "Minimum value of an enum used as enum set is 0");
|
||||
|
||||
$if $$C_INT_SIZE == 64:
|
||||
$switch $$C_INT_SIZE:
|
||||
|
||||
$case 64:
|
||||
private define EnumSetType = ulong;
|
||||
|
||||
$elif $$C_INT_SIZE == 32:
|
||||
$case 32:
|
||||
|
||||
$if Enum.max < 32:
|
||||
$if Enum.max < 32:
|
||||
private define EnumSetType = uint;
|
||||
$else:
|
||||
$else:
|
||||
private define EnumSetType = ulong;
|
||||
$endif;
|
||||
$endif;
|
||||
|
||||
$else:
|
||||
$default:
|
||||
|
||||
$if Enum.max < 16:
|
||||
$if Enum.max < 16:
|
||||
private define EnumSetType = ushort;
|
||||
$elif Enum.max < 31:
|
||||
$elif Enum.max < 31:
|
||||
private define EnumSetType = uint;
|
||||
$else:
|
||||
$else:
|
||||
private define EnumSetType = ulong;
|
||||
$endif;
|
||||
$endif;
|
||||
|
||||
$endif;
|
||||
$endswitch;
|
||||
|
||||
define EnumSet = distinct EnumSetType;
|
||||
|
||||
|
||||
@@ -411,7 +411,8 @@ typedef struct
|
||||
|
||||
typedef struct
|
||||
{
|
||||
TypeInfo *type;
|
||||
Expr *expr;
|
||||
Expr *to_expr;
|
||||
Decl **body;
|
||||
} CtCaseDecl;
|
||||
|
||||
|
||||
@@ -564,7 +564,14 @@ Decl *copy_decl(Decl *decl)
|
||||
MACRO_COPY_TYPE(decl->macro_decl.rtype);
|
||||
break;
|
||||
case DECL_CT_SWITCH:
|
||||
MACRO_COPY_DECL_LIST(decl->ct_switch_decl.cases);
|
||||
MACRO_COPY_EXPR(decl->ct_switch_decl.expr);
|
||||
break;
|
||||
case DECL_CT_CASE:
|
||||
MACRO_COPY_EXPR(decl->ct_case_decl.expr);
|
||||
MACRO_COPY_EXPR(decl->ct_case_decl.to_expr);
|
||||
MACRO_COPY_DECL_LIST(decl->ct_case_decl.body);
|
||||
break;
|
||||
case DECL_ATTRIBUTE:
|
||||
TODO
|
||||
case DECL_DEFINE:
|
||||
|
||||
@@ -188,7 +188,7 @@ static inline Decl *parse_ct_case(ParseContext *context)
|
||||
case TOKEN_CT_CASE:
|
||||
decl = decl_new_ct(DECL_CT_CASE, context->tok.id);
|
||||
advance(context);
|
||||
ASSIGN_TYPE_ELSE(decl->ct_case_decl.type, parse_type(context), poisoned_decl);
|
||||
ASSIGN_EXPR_ELSE(decl->ct_case_decl.expr, parse_constant_expr(context), poisoned_decl);
|
||||
break;
|
||||
default:
|
||||
SEMA_TOKEN_ERROR(context->tok, "Expected a $case or $default statement here.");
|
||||
@@ -214,7 +214,7 @@ static inline Decl *parse_ct_switch_top_level(ParseContext *context)
|
||||
{
|
||||
advance_and_verify(context, TOKEN_CT_SWITCH);
|
||||
Decl *ct = decl_new_ct(DECL_CT_SWITCH, context->prev_tok);
|
||||
ASSIGN_EXPR_ELSE(ct->ct_switch_decl.expr, parse_const_paren_expr(context), poisoned_decl);
|
||||
ASSIGN_EXPR_ELSE(ct->ct_switch_decl.expr, parse_constant_expr(context), poisoned_decl);
|
||||
|
||||
CONSUME_OR(TOKEN_COLON, poisoned_decl);
|
||||
while (!try_consume(context, TOKEN_CT_ENDSWITCH))
|
||||
|
||||
@@ -713,7 +713,7 @@ static inline Ast* parse_ct_switch_stmt(ParseContext *context)
|
||||
{
|
||||
Ast *ast = AST_NEW_TOKEN(AST_CT_SWITCH_STMT, context->tok);
|
||||
advance_and_verify(context, TOKEN_CT_SWITCH);
|
||||
ASSIGN_EXPR_ELSE(ast->ct_switch_stmt.cond, parse_const_paren_expr(context), poisoned_ast);
|
||||
ASSIGN_EXPR_ELSE(ast->ct_switch_stmt.cond, parse_constant_expr(context), poisoned_ast);
|
||||
TRY_CONSUME(TOKEN_COLON, "Expected ':' after $switch expression, did you forget it?");
|
||||
Ast **cases = NULL;
|
||||
while (!try_consume(context, TOKEN_CT_ENDSWITCH))
|
||||
|
||||
@@ -7069,6 +7069,20 @@ static inline bool sema_cast_rvalue(SemaContext *context, Expr *expr)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool sema_analyse_ct_expr(SemaContext *context, Expr *expr)
|
||||
{
|
||||
if (!sema_analyse_expr_lvalue(context, expr)) return false;
|
||||
if (expr->expr_kind == EXPR_TYPEINFO)
|
||||
{
|
||||
Type *cond_val = expr->type_expr->type;
|
||||
expr->expr_kind = EXPR_CONST;
|
||||
expr->const_expr.const_kind = CONST_TYPEID;
|
||||
expr->const_expr.typeid = cond_val->canonical;
|
||||
expr->type = type_typeid;
|
||||
}
|
||||
return sema_cast_rvalue(context, expr);
|
||||
}
|
||||
|
||||
bool sema_analyse_expr_lvalue(SemaContext *context, Expr *expr)
|
||||
{
|
||||
switch (expr->resolve_status)
|
||||
|
||||
@@ -68,6 +68,7 @@ void sema_analysis_pass_functions(Module *module);
|
||||
void sema_analyze_stage(Module *module, AnalysisStage stage);
|
||||
Decl *sema_find_operator(SemaContext *context, Expr *expr, const char *kw);
|
||||
bool sema_analyse_expr_lvalue(SemaContext *context, Expr *expr);
|
||||
bool sema_analyse_ct_expr(SemaContext *context, Expr *expr);
|
||||
bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *struct_var, Decl *decl, bool failable);
|
||||
void expr_rewrite_to_int_const(Expr *expr_to_rewrite, Type *type, uint64_t value, bool narrowable);
|
||||
|
||||
|
||||
@@ -139,6 +139,106 @@ static inline bool sema_analyse_top_level_if(SemaContext *context, Decl *ct_if)
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool sema_analyse_top_level_switch(SemaContext *context, Decl *ct_switch)
|
||||
{
|
||||
Expr *cond = ct_switch->ct_switch_decl.expr;
|
||||
if (!sema_analyse_ct_expr(context, cond)) return false;
|
||||
Type *type = cond->type;
|
||||
bool is_type = type == type_typeid;
|
||||
ExprConst *switch_expr_const = &cond->const_expr;
|
||||
Decl **cases = ct_switch->ct_switch_decl.cases;
|
||||
|
||||
unsigned case_count = vec_size(cases);
|
||||
int matched_case = (int)case_count;
|
||||
int default_case = (int)case_count;
|
||||
for (unsigned i = 0; i < case_count; i++)
|
||||
{
|
||||
Decl *kase = cases[i];
|
||||
Expr *expr = kase->ct_case_decl.expr;
|
||||
Expr *to_expr = kase->ct_case_decl.to_expr;
|
||||
if (expr)
|
||||
{
|
||||
if (is_type)
|
||||
{
|
||||
if (!sema_analyse_ct_expr(context, expr)) return false;
|
||||
if (expr->type != type_typeid)
|
||||
{
|
||||
SEMA_ERROR(expr, "A type was expected here not %s.", type_quoted_error_string(expr->type));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!sema_analyse_expr_rhs(context, type, expr, false)) return false;
|
||||
if (to_expr && !sema_analyse_expr_rhs(context, type, to_expr, false)) return false;
|
||||
}
|
||||
if (expr->expr_kind != EXPR_CONST)
|
||||
{
|
||||
SEMA_ERROR(expr, "The $case must have a constant expression.");
|
||||
return false;
|
||||
}
|
||||
if (to_expr && to_expr->expr_kind != EXPR_CONST)
|
||||
{
|
||||
SEMA_ERROR(to_expr, "The $case must have a constant expression.");
|
||||
return false;
|
||||
}
|
||||
ExprConst *const_expr = &expr->const_expr;
|
||||
ExprConst *const_to_expr = to_expr ? &to_expr->const_expr : const_expr;
|
||||
if (to_expr && expr_const_compare(const_expr, const_to_expr, BINARYOP_GT))
|
||||
{
|
||||
SEMA_ERROR(to_expr, "The end of a range must be less or equal to the beginning.");
|
||||
return false;
|
||||
}
|
||||
// Check that it is unique.
|
||||
for (unsigned j = 0; j < i; j++)
|
||||
{
|
||||
Decl *other_case = cases[j];
|
||||
|
||||
// Default.
|
||||
if (!other_case->ct_case_decl.expr) continue;
|
||||
ExprConst *other_const = &other_case->ct_case_decl.expr->const_expr;
|
||||
ExprConst *other_const_to = other_case->ct_case_decl.to_expr
|
||||
? &other_case->ct_case_decl.to_expr->const_expr : other_const;
|
||||
if (expr_const_compare(const_expr, other_const_to, BINARYOP_LE) &&
|
||||
expr_const_compare(const_to_expr, other_const, BINARYOP_GE))
|
||||
{
|
||||
SEMA_ERROR(kase, "'%s' appears more than once.", expr_const_to_error_string(const_expr));
|
||||
SEMA_PREV(cases[j]->ct_case_decl.expr, "The previous $case was here.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (expr_const_compare(switch_expr_const, const_expr, BINARYOP_GE) &&
|
||||
expr_const_compare(switch_expr_const, const_to_expr, BINARYOP_LE))
|
||||
{
|
||||
matched_case = (int)i;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (default_case < case_count)
|
||||
{
|
||||
SEMA_ERROR(kase, "More than one $default is not allowed.");
|
||||
SEMA_PREV(cases[default_case], "The previous $default was here.");
|
||||
return false;
|
||||
}
|
||||
default_case = (int)i;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (matched_case == case_count) matched_case = default_case;
|
||||
|
||||
for (int i = matched_case; i < case_count; i++)
|
||||
{
|
||||
Decl **body = cases[i]->ct_case_decl.body;
|
||||
if (body)
|
||||
{
|
||||
sema_append_decls(context->unit, body);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void sema_analysis_pass_conditional_compilation(Module *module)
|
||||
{
|
||||
@@ -152,7 +252,18 @@ void sema_analysis_pass_conditional_compilation(Module *module)
|
||||
// Also handle switch!
|
||||
SemaContext context;
|
||||
sema_context_init(&context, unit);
|
||||
sema_analyse_top_level_if(&context, unit->ct_ifs[i]);
|
||||
Decl *decl = unit->ct_ifs[i];
|
||||
switch (decl->decl_kind)
|
||||
{
|
||||
case DECL_CT_IF:
|
||||
sema_analyse_top_level_if(&context, decl);
|
||||
break;
|
||||
case DECL_CT_SWITCH:
|
||||
sema_analyse_top_level_switch(&context, decl);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE
|
||||
}
|
||||
sema_context_destroy(&context);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2174,7 +2174,7 @@ static bool sema_analyse_ct_switch_body(SemaContext *context, Ast *statement)
|
||||
static bool sema_analyse_ct_switch_stmt(SemaContext *context, Ast *statement)
|
||||
{
|
||||
Expr *cond = statement->ct_switch_stmt.cond;
|
||||
if (!sema_analyse_expr(context, cond)) return false;
|
||||
if (!sema_analyse_ct_expr(context, cond)) return false;
|
||||
if (cond->expr_kind != EXPR_CONST)
|
||||
{
|
||||
SEMA_ERROR(cond, "A compile time $switch must be over a constant value.");
|
||||
|
||||
54
test/test_suite/compile_time/ct_switch_top_level.c3t
Normal file
54
test/test_suite/compile_time/ct_switch_top_level.c3t
Normal file
@@ -0,0 +1,54 @@
|
||||
// #target: x64-darwin
|
||||
module test;
|
||||
|
||||
extern fn void printf(char*, ...);
|
||||
|
||||
macro tester()
|
||||
{
|
||||
$Type = int;
|
||||
$switch $Type:
|
||||
$case int:
|
||||
printf("Hello\n");
|
||||
int z = 0;
|
||||
$default:
|
||||
int j = 213;
|
||||
$endswitch;
|
||||
|
||||
}
|
||||
$switch bool.typeid:
|
||||
$case int:
|
||||
int oefke = 23;
|
||||
$default:
|
||||
int oeoekgokege = 343432;
|
||||
$endswitch;
|
||||
|
||||
fn int main()
|
||||
{
|
||||
@tester();
|
||||
@tester();
|
||||
int i = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* #expect: test.ll
|
||||
|
||||
source_filename = "test"
|
||||
target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
|
||||
target triple = "x86_64-apple-darwin-1"
|
||||
|
||||
@test.oeoekgokege = local_unnamed_addr global i32 343432, align 4
|
||||
@.str = private unnamed_addr constant [7 x i8] c"Hello\0A\00", align 1
|
||||
@.str.1 = private unnamed_addr constant [7 x i8] c"Hello\0A\00", align 1
|
||||
|
||||
define i32 @main() #0 {
|
||||
entry:
|
||||
%z = alloca i32, align 4
|
||||
%z1 = alloca i32, align 4
|
||||
%i = alloca i32, align 4
|
||||
call void (i8*, ...) @printf(i8* getelementptr inbounds ([7 x i8], [7 x i8]* @.str, i32 0, i32 0))
|
||||
store i32 0, i32* %z, align 4
|
||||
call void (i8*, ...) @printf(i8* getelementptr inbounds ([7 x i8], [7 x i8]* @.str.1, i32 0, i32 0))
|
||||
store i32 0, i32* %z1, align 4
|
||||
store i32 1, i32* %i, align 4
|
||||
ret i32 1
|
||||
}
|
||||
Reference in New Issue
Block a user