Fix $switch. And make top level $switch work.

This commit is contained in:
Christoffer Lerno
2022-01-24 00:01:54 +01:00
parent b2be8349ed
commit ef95c1a630
10 changed files with 207 additions and 17 deletions

View File

@@ -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;

View File

@@ -411,7 +411,8 @@ typedef struct
typedef struct
{
TypeInfo *type;
Expr *expr;
Expr *to_expr;
Decl **body;
} CtCaseDecl;

View File

@@ -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:

View File

@@ -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))

View File

@@ -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))

View File

@@ -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)

View File

@@ -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);

View File

@@ -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);
}
}

View File

@@ -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.");

View 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
}