diff --git a/README.md b/README.md index 0e3f6645a..91f68d52e 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,6 @@ There are some small work being done on the parser here, but most of the structu - Errors not correctly handled. - Type resolution not complete for all types. - `type` not handled. -- `?:` not handled. - Identifier analysis incomplete. - Macro call not handled completely. - Function calls not handled completely. diff --git a/resources/grammar.y b/resources/grammar.y index caabf1eab..2c1b30961 100644 --- a/resources/grammar.y +++ b/resources/grammar.y @@ -197,26 +197,11 @@ macro_argument_list | macro_argument_list ',' macro_argument ; -implicit_decl - : IDENT - | IDENT '=' initializer - ; - -explicit_decl +declaration : type_expression IDENT '=' initializer | type_expression IDENT ; -declaration - : explicit_decl - | explicit_decl ',' implicit_decl - | explicit_decl ',' explicit_decl - ; - -declaration_list - : declaration - ; - param_declaration : type_expression | type_expression IDENT @@ -391,8 +376,8 @@ expression_statement control_expression - : decl_or_expr_list - | declaration_list ';' decl_or_expr_list + : decl_expr_list + | decl_expr_list ';' decl_expr_list ; selection_statement @@ -406,14 +391,16 @@ expression_list | expression_list ',' expression ; -decl_or_expr_list - : expression_list - | declaration_list +decl_expr_list + : expression + | declaration + | decl_expr_list ',' expression + | decl_expr_list ',' declaration ; for_statement - : FOR '(' decl_or_expr_list ';' expression_statement ')' statement - | FOR '(' decl_or_expr_list ';' expression_statement expression_list ')' statement + : FOR '(' decl_expr_list ';' expression_statement ')' statement + | FOR '(' decl_expr_list ';' expression_statement expression_list ')' statement ; iteration_statement diff --git a/resources/testfragments/super_simple.c3 b/resources/testfragments/super_simple.c3 index 00a9e0398..c6ecefc3b 100644 --- a/resources/testfragments/super_simple.c3 +++ b/resources/testfragments/super_simple.c3 @@ -23,6 +23,20 @@ union Test3 func int boba(int y, int j) { + for (int i = 0; i < 10; i++) + { + + } + for (int i = 0, int foo = 0; i < 10; i++) + { + + } + + for (int i = 0, j = 1; i < 10; i++, j++) + { + + } + Test2 bar; bar.b = 1; //int w = y ? y : j; @@ -66,6 +80,11 @@ func int test(int x) return y; } +func int* elvis(int *x, int *y) +{ + return x ?: y; +} + func int test3() { if (test() < 0) return -1; @@ -93,6 +112,26 @@ func void bob() func int main(int x) { + for (int ok = 0; ok < 10; ok++) + { + printf("ok"); + } + printf("\n"); + for (int ok = 0, int ko = 0, ok = 2; ok + ko < 10; ok++, ko++) + { + printf(":okko"); + } + printf("\n"); + while (int ok = 0; int j = ok++, ok < 10) + { + printf("foo"); + } + printf("\n"); + x = 3; + if (int odk = x, x > 0) + { + printf("helo\n"); + } Test2 efe; efe.t.a = 3; if (efe.t.a > 2) printf("Works!\n"); diff --git a/src/compiler/ast.c b/src/compiler/ast.c index a3d443f3e..11962e161 100644 --- a/src/compiler/ast.c +++ b/src/compiler/ast.c @@ -560,21 +560,21 @@ void fprint_expr_recursive(FILE *file, Expr *expr, int indent) } } break; - case EXPR_CONDITIONAL: - if (!expr->conditional_expr.then_expr) + case EXPR_TERNARY: + if (!expr->ternary_expr.then_expr) { fprintf_indented(file, indent, "(elvis\n"); fprint_expr_common(file, expr, indent + 1); - fprint_expr_recursive(file, expr->conditional_expr.cond, indent + 1); + fprint_expr_recursive(file, expr->ternary_expr.cond, indent + 1); } else { fprintf_indented(file, indent, "(cond\n"); fprint_expr_common(file, expr, indent + 1); - fprint_expr_recursive(file, expr->conditional_expr.cond, indent + 1); - fprint_expr_recursive(file, expr->conditional_expr.then_expr, indent + 1); + fprint_expr_recursive(file, expr->ternary_expr.cond, indent + 1); + fprint_expr_recursive(file, expr->ternary_expr.then_expr, indent + 1); } - fprint_expr_recursive(file, expr->conditional_expr.else_expr, indent + 1); + fprint_expr_recursive(file, expr->ternary_expr.else_expr, indent + 1); break; case EXPR_INITIALIZER_LIST: fprintf_indented(file, indent, "(initializerlist\n"); @@ -613,6 +613,15 @@ void fprint_expr_recursive(FILE *file, Expr *expr, int indent) fprint_type_info_recursive(file, expr->cast_expr.type_info, indent + 1); fprint_expr_recursive(file, expr->cast_expr.expr, indent + 1); break; + case EXPR_EXPRESSION_LIST: + fprintf_indented(file, indent, "(expression-list\n"); + fprint_expr_common(file, expr, indent + 1); + fprint_type_info_recursive(file, expr->struct_value_expr.type, indent + 1); + VECEACH(expr->expression_list, i) + { + fprint_expr_recursive(file, expr->expression_list[i], indent + 1); + } + break; default: fprintf_indented(file, indent, "(TODOEXPR)\n"); return; @@ -826,6 +835,12 @@ static void fprint_ast_recursive(FILE *file, Ast *ast, int indent) fprint_asts_recursive(file, ast->compound_stmt.stmts, indent + 1); } break; + case AST_DECL_EXPR_LIST: + fprintf(file, "(declexprlist\n"); + { + fprint_asts_recursive(file, ast->decl_expr_stmt, indent + 1); + } + break; case AST_DECLARE_STMT: fprintf(file, "(declare\n"); fprint_decl_recursive(file, ast->declare_stmt, indent + 1); @@ -856,10 +871,6 @@ static void fprint_ast_recursive(FILE *file, Ast *ast, int indent) fprint_ast_recursive(file, ast->do_stmt.body, indent + 1); fprint_expr_recursive(file, ast->do_stmt.expr, indent + 1); break; - case AST_STMT_LIST: - fprintf(file, "(stmtlist\n"); - fprint_asts_recursive(file, ast->stmt_list, indent + 1); - break; case AST_RETURN_STMT: if (ast->return_stmt.expr) { @@ -884,25 +895,19 @@ static void fprint_ast_recursive(FILE *file, Ast *ast, int indent) case AST_DEFAULT_STMT: fprintf(file, "(default)\n"); return; - case AST_COND_STMT: - fprintf(file, "(cond\n"); - if (ast->cond_stmt.expr) - { - fprint_expr_recursive(file, ast->cond_stmt.expr, indent + 1); - } - else - { - fprint_indent(file, indent); - fprintf(file, "(noexpr)"); - } - fprint_asts_recursive(file, ast->cond_stmt.stmts, indent + 1); - break; case AST_FOR_STMT: fprintf(file, "(for\n"); - fprint_ast_recursive(file, ast->for_stmt.cond, indent + 1); + if (ast->for_stmt.init) + { + fprint_ast_recursive(file, ast->for_stmt.init, indent + 1); + } + if (ast->for_stmt.cond) + { + fprint_expr_recursive(file, ast->for_stmt.cond, indent + 1); + } if (ast->for_stmt.incr) { - fprint_ast_recursive(file, ast->for_stmt.incr, indent + 1); + fprint_expr_recursive(file, ast->for_stmt.incr, indent + 1); } else { @@ -1012,3 +1017,4 @@ void fprint_decl(FILE *file, Decl *dec) } Module poisoned_module = { .name = "INVALID" }; Decl all_error = { .decl_kind = DECL_ERROR, .name = { .type = TOKEN_INVALID_TOKEN, .string = NULL } }; + diff --git a/src/compiler/casts.c b/src/compiler/casts.c index 845b075ce..b0b1644d5 100644 --- a/src/compiler/casts.c +++ b/src/compiler/casts.c @@ -562,6 +562,49 @@ bool cast_implicit(Expr *expr, Type *to_type) return cast(expr, to_type, CAST_TYPE_IMPLICIT); } +CastKind cast_to_bool_kind(Type *type) +{ + switch (type->type_kind) + { + case TYPE_TYPEDEF: + return cast_to_bool_kind(type->canonical); + case TYPE_POISONED: + case TYPE_VOID: + case TYPE_ERROR_UNION: + case TYPE_STRUCT: + case TYPE_UNION: + case TYPE_STRING: + case TYPE_ERROR: + case TYPE_ENUM: + case TYPE_FUNC: + case TYPE_ARRAY: + case TYPE_VARARRAY: + case TYPE_SUBARRAY: + // Improve consider vararray / subarray conversion to boolean. + return CAST_ERROR; + case TYPE_BOOL: + UNREACHABLE + case TYPE_I8: + case TYPE_I16: + case TYPE_I32: + case TYPE_I64: + case TYPE_IXX: + case TYPE_U8: + case TYPE_U16: + case TYPE_U32: + case TYPE_U64: + case TYPE_UXX: + return CAST_INTBOOL; + case TYPE_F32: + case TYPE_F64: + case TYPE_FXX: + return CAST_FPBOOL; + case TYPE_POINTER: + return CAST_PTRBOOL; + } + UNREACHABLE +} + bool cast(Expr *expr, Type *to_type, CastType cast_type) { Type *from_type = expr->type->canonical; diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 546157913..05596a968 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -444,6 +444,7 @@ typedef struct }; } ExprCast; + struct _Expr { ExprKind expr_kind : 8; @@ -458,7 +459,7 @@ struct _Expr ExprTry try_expr; ExprBinary binary_expr; ExprAssign assign_expr; - ExprTernary conditional_expr; + ExprTernary ternary_expr; ExprUnary unary_expr; ExprUnary post_expr; ExprCall call_expr; @@ -500,6 +501,7 @@ typedef struct typedef struct { + Ast *decl; Ast *cond; Ast *body; } AstWhileStmt; @@ -512,6 +514,7 @@ typedef struct typedef struct { + Ast *decl; Ast *cond; Ast *then_body; Ast *else_body; @@ -536,6 +539,7 @@ typedef struct typedef struct { + Ast *decl; Ast *cond; Ast *body; Ast **cases; @@ -543,19 +547,13 @@ typedef struct typedef struct { - Ast **init; - Ast *cond; - Ast *incr; + Ast *init; + Expr *cond; + Expr *incr; Ast *body; } AstForStmt; -typedef struct -{ - Ast **stmts; - Expr *expr; -} AstCondStmt; - typedef struct { @@ -652,14 +650,13 @@ typedef struct _Ast AstCatchStmt catch_stmt; AstGotoStmt goto_stmt; AstForStmt for_stmt; - AstCondStmt cond_stmt; AstCtIfStmt ct_if_stmt; AstCtIfStmt ct_elif_stmt; Ast *ct_else_stmt; AstCtForStmt ct_for_stmt; AstGenericCaseStmt generic_case_stmt; Ast *generic_default_stmt; - Ast** stmt_list; + Ast** decl_expr_stmt; }; } Ast; @@ -826,7 +823,7 @@ static inline ConstType sign_from_type(Type *type) bool cast_implicit(Expr *expr, Type *to_type); bool cast(Expr *expr, Type *to_type, CastType cast_type); bool cast_binary_arithmetic(Expr *left, Expr *right, const char *action); - +CastKind cast_to_bool_kind(Type *type); bool cast_to_runtime(Expr *expr); void llvm_codegen(Context *context); diff --git a/src/compiler/enums.h b/src/compiler/enums.h index 0a8822ff1..4afb6b515 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -74,7 +74,6 @@ typedef enum AST_CASE_STMT, AST_CATCH_STMT, AST_COMPOUND_STMT, - AST_COND_STMT, AST_CONTINUE_STMT, AST_CT_IF_STMT, AST_CT_ELIF_STMT, @@ -96,7 +95,7 @@ typedef enum AST_LABEL, AST_NOP_STMT, AST_RETURN_STMT, - AST_STMT_LIST, + AST_DECL_EXPR_LIST, AST_SWITCH_STMT, AST_THROW_STMT, AST_TRY_STMT, @@ -227,7 +226,7 @@ typedef enum EXPR_TRY, EXPR_CONST, EXPR_BINARY, - EXPR_CONDITIONAL, + EXPR_TERNARY, EXPR_UNARY, EXPR_POST_UNARY, EXPR_TYPE, @@ -279,7 +278,7 @@ typedef enum { PREC_NONE, PREC_ASSIGNMENT, // =, *=, /=, %=, ... - PREC_CONDITIONAL, // ?: + PREC_TERNARY, // ?: PREC_LOGICAL, // && || PREC_RELATIONAL, // < > <= >= == != PREC_ADDITIVE, // + - diff --git a/src/compiler/expr_analysis.c b/src/compiler/expr_analysis.c index 502fb8d7f..763f3ba84 100644 --- a/src/compiler/expr_analysis.c +++ b/src/compiler/expr_analysis.c @@ -43,12 +43,30 @@ static inline bool sema_type_error_on_binop(Expr *expr) } -static inline bool sema_expr_analyse_conditional(Context *context, Type *to, Expr *expr) +static inline bool sema_expr_analyse_ternary(Context *context, Type *to, Expr *expr) { - if (!sema_analyse_expr(context, type_bool, expr->conditional_expr.cond)) return expr_poison(expr); - Expr *left = expr->conditional_expr.then_expr; - Expr *right = expr->conditional_expr.else_expr; - if (!sema_analyse_expr(context, to, left)) return expr_poison(expr); + Expr *left = expr->ternary_expr.then_expr; + Expr *cond = expr->ternary_expr.cond; + // Normal + if (left) + { + if (!sema_analyse_expr(context, type_bool, cond)) return expr_poison(expr); + if (!sema_analyse_expr(context, to, left)) return expr_poison(expr); + } + else + { + // Elvis + if (!sema_analyse_expr(context, to, cond)) return expr_poison(expr); + Type *type = cond->type->canonical; + if (type->type_kind != TYPE_BOOL && cast_to_bool_kind(type) == CAST_ERROR) + { + SEMA_ERROR(cond->loc, "Cannot convert expression to boolean."); + return false; + } + left = cond; + } + + Expr *right = expr->ternary_expr.else_expr; if (!sema_analyse_expr(context, to, right)) return expr_poison(expr); Type *left_canonical = left->type->canonical; @@ -452,7 +470,7 @@ static inline bool sema_expr_analyse_expr_list(Context *context, Type *to, Expr size_t last = vec_size(expr->expression_list) - 1; VECEACH(expr->expression_list, i) { - success &= sema_analyse_expr(context, i == last ? to : NULL, expr); + success &= sema_analyse_expr(context, i == last ? to : NULL, expr->expression_list[i]); } return success; } @@ -1291,6 +1309,8 @@ static inline bool sema_expr_analyse_incdec(Context *context, Type *to, Expr *ex return true; } + + static inline bool sema_expr_analyse_binary(Context *context, Type *to, Expr *expr) { assert(expr->resolve_status == RESOLVE_RUNNING); @@ -1446,8 +1466,8 @@ static inline bool sema_analyse_expr_dispatch(Context *context, Type *to, Expr * return true; case EXPR_BINARY: return sema_expr_analyse_binary(context, to, expr); - case EXPR_CONDITIONAL: - return sema_expr_analyse_conditional(context, to, expr); + case EXPR_TERNARY: + return sema_expr_analyse_ternary(context, to, expr); case EXPR_UNARY: return sema_expr_analyse_unary(context, to, expr); case EXPR_POST_UNARY: diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index 3083f75ab..4e5edeec7 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -50,7 +50,7 @@ LLVMValueRef gencontext_emit_address(GenContext *context, Expr *expr) case EXPR_TRY: case EXPR_SIZEOF: case EXPR_BINARY: - case EXPR_CONDITIONAL: + case EXPR_TERNARY: case EXPR_POST_UNARY: case EXPR_TYPE_ACCESS: case EXPR_CALL: @@ -64,17 +64,16 @@ LLVMValueRef gencontext_emit_address(GenContext *context, Expr *expr) UNREACHABLE } -static inline LLVMValueRef gencontext_emit_cast_expr(GenContext *context, Expr *expr) +LLVMValueRef gencontext_emit_cast(GenContext *context, CastKind cast_kind, LLVMValueRef value, Type *type, Type *target_type) { - LLVMValueRef rhs = gencontext_emit_expr(context, expr->cast_expr.expr); - switch (expr->cast_expr.kind) + switch (cast_kind) { case CAST_ERROR: UNREACHABLE case CAST_PTRPTR: - return LLVMBuildPointerCast(context->builder, rhs, BACKEND_TYPE(expr->type), "ptrptr"); + return LLVMBuildPointerCast(context->builder, value, BACKEND_TYPE(type), "ptrptr"); case CAST_PTRXI: - return LLVMBuildPtrToInt(context->builder, rhs, BACKEND_TYPE(expr->type), "ptrxi"); + return LLVMBuildPtrToInt(context->builder, value, BACKEND_TYPE(type), "ptrxi"); case CAST_VARRPTR: TODO case CAST_ARRPTR: @@ -82,49 +81,54 @@ static inline LLVMValueRef gencontext_emit_cast_expr(GenContext *context, Expr * case CAST_STRPTR: TODO case CAST_PTRBOOL: - return LLVMBuildICmp(context->builder, LLVMIntNE, rhs, LLVMConstPointerNull(BACKEND_TYPE(expr->type->canonical->pointer)), "ptrbool"); + return LLVMBuildICmp(context->builder, LLVMIntNE, value, LLVMConstPointerNull(BACKEND_TYPE(type->canonical->pointer)), "ptrbool"); case CAST_BOOLINT: - return LLVMBuildTrunc(context->builder, rhs, BACKEND_TYPE(expr->type), "boolsi"); + return LLVMBuildTrunc(context->builder, value, BACKEND_TYPE(type), "boolsi"); case CAST_FPBOOL: - return LLVMBuildFCmp(context->builder, LLVMRealUNE, rhs, LLVMConstNull(LLVMTypeOf(rhs)), "fpbool"); + return LLVMBuildFCmp(context->builder, LLVMRealUNE, value, LLVMConstNull(LLVMTypeOf(value)), "fpbool"); case CAST_BOOLFP: - return LLVMBuildSIToFP(context->builder, rhs, BACKEND_TYPE(expr->type), "boolfp"); + return LLVMBuildSIToFP(context->builder, value, BACKEND_TYPE(type), "boolfp"); case CAST_INTBOOL: - return LLVMBuildICmp(context->builder, LLVMIntNE, rhs, LLVMConstNull(LLVMTypeOf(rhs)), "intbool"); + return LLVMBuildICmp(context->builder, LLVMIntNE, value, LLVMConstNull(LLVMTypeOf(value)), "intbool"); case CAST_FPFP: - return type_convert_will_trunc(expr->type, expr->cast_expr.expr->type) - ? LLVMBuildFPTrunc(context->builder, rhs, BACKEND_TYPE(expr->type), "fpfptrunc") - : LLVMBuildFPExt(context->builder, rhs, BACKEND_TYPE(expr->type), "fpfpext"); + return type_convert_will_trunc(type, target_type) + ? LLVMBuildFPTrunc(context->builder, value, BACKEND_TYPE(type), "fpfptrunc") + : LLVMBuildFPExt(context->builder, value, BACKEND_TYPE(type), "fpfpext"); case CAST_FPSI: - return LLVMBuildFPToSI(context->builder, rhs, BACKEND_TYPE(expr->type), "fpsi"); + return LLVMBuildFPToSI(context->builder, value, BACKEND_TYPE(type), "fpsi"); case CAST_FPUI: - return LLVMBuildFPToUI(context->builder, rhs, BACKEND_TYPE(expr->type), "fpui"); + return LLVMBuildFPToUI(context->builder, value, BACKEND_TYPE(type), "fpui"); case CAST_SISI: - return type_convert_will_trunc(expr->type, expr->cast_expr.expr->type) - ? LLVMBuildTrunc(context->builder, rhs, BACKEND_TYPE(expr->type), "sisitrunc") - : LLVMBuildSExt(context->builder, rhs, BACKEND_TYPE(expr->type), "sisiext"); + return type_convert_will_trunc(type, target_type) + ? LLVMBuildTrunc(context->builder, value, BACKEND_TYPE(type), "sisitrunc") + : LLVMBuildSExt(context->builder, value, BACKEND_TYPE(type), "sisiext"); case CAST_SIUI: - return type_convert_will_trunc(expr->type, expr->cast_expr.expr->type) - ? LLVMBuildTrunc(context->builder, rhs, BACKEND_TYPE(expr->type), "siuitrunc") - : LLVMBuildZExt(context->builder, rhs, BACKEND_TYPE(expr->type), "siuiext"); + return type_convert_will_trunc(type, target_type) + ? LLVMBuildTrunc(context->builder, value, BACKEND_TYPE(type), "siuitrunc") + : LLVMBuildZExt(context->builder, value, BACKEND_TYPE(type), "siuiext"); case CAST_SIFP: - return LLVMBuildSIToFP(context->builder, rhs, BACKEND_TYPE(expr->type), "sifp"); + return LLVMBuildSIToFP(context->builder, value, BACKEND_TYPE(type), "sifp"); case CAST_XIPTR: - return LLVMBuildIntToPtr(context->builder, rhs, BACKEND_TYPE(expr->type), "xiptr"); + return LLVMBuildIntToPtr(context->builder, value, BACKEND_TYPE(type), "xiptr"); case CAST_UISI: - return type_convert_will_trunc(expr->type, expr->cast_expr.expr->type) - ? LLVMBuildTrunc(context->builder, rhs, BACKEND_TYPE(expr->type), "uisitrunc") - : LLVMBuildZExt(context->builder, rhs, BACKEND_TYPE(expr->type), "uisiext"); + return type_convert_will_trunc(type, target_type) + ? LLVMBuildTrunc(context->builder, value, BACKEND_TYPE(type), "uisitrunc") + : LLVMBuildZExt(context->builder, value, BACKEND_TYPE(type), "uisiext"); case CAST_UIUI: - return type_convert_will_trunc(expr->type, expr->cast_expr.expr->type) - ? LLVMBuildTrunc(context->builder, rhs, BACKEND_TYPE(expr->type), "uiuitrunc") - : LLVMBuildZExt(context->builder, rhs, BACKEND_TYPE(expr->type), "uiuiext"); + return type_convert_will_trunc(type, target_type) + ? LLVMBuildTrunc(context->builder, value, BACKEND_TYPE(type), "uiuitrunc") + : LLVMBuildZExt(context->builder, value, BACKEND_TYPE(type), "uiuiext"); case CAST_UIFP: - return LLVMBuildUIToFP(context->builder, rhs, BACKEND_TYPE(expr->type), "uifp"); + return LLVMBuildUIToFP(context->builder, value, BACKEND_TYPE(type), "uifp"); case CAST_ENUMSI: TODO } } +static inline LLVMValueRef gencontext_emit_cast_expr(GenContext *context, Expr *expr) +{ + LLVMValueRef rhs = gencontext_emit_expr(context, expr->cast_expr.expr); + return gencontext_emit_cast(context, expr->cast_expr.kind, rhs, expr->type->canonical, expr->cast_expr.expr->type->canonical); +} LLVMValueRef gencontext_emit_unary_expr(GenContext *context, Expr *expr) { @@ -403,24 +407,59 @@ static LLVMValueRef gencontext_emit_binary_expr(GenContext *context, Expr *expr) return gencontext_emit_binary(context, expr, false, binary_op); } -LLVMValueRef gencontext_emit_conditional_expr(GenContext *context, Expr *expr) +LLVMValueRef gencontext_emit_elvis_expr(GenContext *context, Expr *expr) { + LLVMBasicBlockRef current_block = context->current_block; + LLVMBasicBlockRef phi_block = LLVMCreateBasicBlockInContext(context->context, "cond.phi"); + LLVMBasicBlockRef rhs_block = LLVMCreateBasicBlockInContext(context->context, "cond.rhs"); + + // Generate condition and conditional branch + LLVMValueRef lhs = gencontext_emit_expr(context, expr->ternary_expr.cond); + LLVMValueRef cond = lhs; + Type *cond_type = expr->ternary_expr.cond->type->canonical; + if (cond_type != type_bool) + { + CastKind cast = cast_to_bool_kind(cond_type); + cond = gencontext_emit_cast(context, cast, cond, cond_type, type_bool); + } + + gencontext_emit_cond_br(context, cond, phi_block, rhs_block); + + gencontext_emit_block(context, rhs_block); + LLVMValueRef rhs = gencontext_emit_expr(context, expr->ternary_expr.else_expr); + gencontext_emit_br(context, phi_block); + + // Generate phi + gencontext_emit_block(context, phi_block); + LLVMValueRef phi = LLVMBuildPhi(context->builder, expr->type->backend_type, "val"); + + LLVMValueRef logicValues[2] = { lhs, rhs }; + LLVMBasicBlockRef blocks[2] = { current_block, rhs_block }; + LLVMAddIncoming(phi, logicValues, blocks, 2); + + return phi; +} + +LLVMValueRef gencontext_emit_ternary_expr(GenContext *context, Expr *expr) +{ + if (expr->ternary_expr.then_expr == NULL) return gencontext_emit_elvis_expr(context, expr); + // Set up basic blocks, following Cone LLVMBasicBlockRef phi_block = LLVMCreateBasicBlockInContext(context->context, "cond.phi"); LLVMBasicBlockRef lhs_block = LLVMCreateBasicBlockInContext(context->context, "cond.lhs"); LLVMBasicBlockRef rhs_block = LLVMCreateBasicBlockInContext(context->context, "cond.rhs"); // Generate condition and conditional branch - LLVMValueRef cond = gencontext_emit_expr(context, expr->conditional_expr.cond); + LLVMValueRef cond = gencontext_emit_expr(context, expr->ternary_expr.cond); gencontext_emit_cond_br(context, cond, lhs_block, rhs_block); gencontext_emit_block(context, lhs_block); - LLVMValueRef lhs = gencontext_emit_expr(context, expr->conditional_expr.then_expr); + LLVMValueRef lhs = gencontext_emit_expr(context, expr->ternary_expr.then_expr); gencontext_emit_br(context, phi_block); gencontext_emit_block(context, rhs_block); - LLVMValueRef rhs = gencontext_emit_expr(context, expr->conditional_expr.else_expr); + LLVMValueRef rhs = gencontext_emit_expr(context, expr->ternary_expr.else_expr); gencontext_emit_br(context, phi_block); // Generate phi @@ -521,10 +560,6 @@ static inline LLVMValueRef gencontext_emit_struct_init_values_expr(GenContext *c TODO } -static inline LLVMValueRef gencontext_load_expr(GenContext *context, LLVMValueRef value) -{ - return LLVMBuildLoad(context->builder, value, ""); -} LLVMValueRef gencontext_emit_expr(GenContext *context, Expr *expr) { @@ -538,8 +573,8 @@ LLVMValueRef gencontext_emit_expr(GenContext *context, Expr *expr) return gencontext_emit_const_expr(context, expr); case EXPR_BINARY: return gencontext_emit_binary_expr(context, expr); - case EXPR_CONDITIONAL: - return gencontext_emit_conditional_expr(context, expr); + case EXPR_TERNARY: + return gencontext_emit_ternary_expr(context, expr); case EXPR_POST_UNARY: return gencontext_emit_post_unary_expr(context, expr); case EXPR_TYPE: diff --git a/src/compiler/llvm_codegen_internal.h b/src/compiler/llvm_codegen_internal.h index 6e455f559..20b23f022 100644 --- a/src/compiler/llvm_codegen_internal.h +++ b/src/compiler/llvm_codegen_internal.h @@ -89,9 +89,12 @@ static inline LLVMBasicBlockRef gencontext_create_free_block(GenContext *context void gencontext_emit_function_decl(GenContext *context, Decl *decl); LLVMValueRef gencontext_emit_address(GenContext *context, Expr *expr); #define LLVMTYPE(type) type->backend_type - +static inline LLVMValueRef gencontext_load_expr(GenContext *context, LLVMValueRef value) +{ + return LLVMBuildLoad(context->builder, value, ""); +} LLVMTypeRef gencontext_get_llvm_type(GenContext *context, Type *type); - +LLVMValueRef gencontext_emit_cast(GenContext *context, CastKind cast_kind, LLVMValueRef value, Type *type, Type *target_type); static inline bool gencontext_use_debug(GenContext *context) { return context && context->debug.builder != NULL; diff --git a/src/compiler/llvm_codegen_stmt.c b/src/compiler/llvm_codegen_stmt.c index e0b5639b4..aed58f968 100644 --- a/src/compiler/llvm_codegen_stmt.c +++ b/src/compiler/llvm_codegen_stmt.c @@ -5,8 +5,7 @@ #include "llvm_codegen_internal.h" static void gencontext_pop_break_continue(GenContext *context); -static void -gencontext_push_break_continue(GenContext *context, LLVMBasicBlockRef break_block, LLVMBasicBlockRef continue_block, +static void gencontext_push_break_continue(GenContext *context, LLVMBasicBlockRef break_block, LLVMBasicBlockRef continue_block, LLVMBasicBlockRef next_block); void gencontext_emit_compound_stmt(GenContext *context, Ast *ast) @@ -18,41 +17,6 @@ void gencontext_emit_compound_stmt(GenContext *context, Ast *ast) } } -static inline void gencontext_emit_stmt_list(GenContext *context, Ast *ast) -{ - assert(ast->ast_kind == AST_STMT_LIST); - VECEACH(ast->stmt_list, i) - { - gencontext_emit_stmt(context, ast->stmt_list[i]); - } -} - -static inline void gencontext_emit_return(GenContext *context, Ast *ast) -{ - // Ensure we are on a branch that is non empty. - if (!gencontext_check_block_branch_emit(context)) return; - if (!ast->return_stmt.expr) - { - LLVMBuildRetVoid(context->builder); - return; - } - LLVMValueRef returnValue = gencontext_emit_expr(context, ast->return_stmt.expr); - LLVMBuildRet(context->builder, returnValue); - context->current_block = NULL; - LLVMBasicBlockRef post_ret_block = gencontext_create_free_block(context, "ret"); - gencontext_emit_block(context, post_ret_block); - -} - -static inline LLVMValueRef gencontext_emit_cond(GenContext *context, Ast *ast) -{ - assert(ast->ast_kind == AST_COND_STMT); - VECEACH(ast->cond_stmt.stmts, i) - { - gencontext_emit_stmt(context, ast->cond_stmt.stmts[i]); - } - return gencontext_emit_expr(context, ast->cond_stmt.expr); -} static LLVMValueRef gencontext_emit_decl(GenContext *context, Ast *ast) { @@ -84,17 +48,75 @@ static LLVMValueRef gencontext_emit_decl(GenContext *context, Ast *ast) return decl->var.backend_ref; } +void gencontext_emit_decl_expr_list_ignore_result(GenContext *context, Ast *ast) +{ + assert(ast->ast_kind == AST_DECL_EXPR_LIST); + VECEACH(ast->decl_expr_stmt, i) + { + gencontext_emit_stmt(context, ast->decl_expr_stmt[i]); + } +} + +LLVMValueRef gencontext_emit_decl_expr_list(GenContext *context, Ast *ast, bool bool_cast) +{ + assert(ast->ast_kind == AST_DECL_EXPR_LIST); + size_t size = vec_size(ast->decl_expr_stmt); + size_t last_index = size - 1; + for (size_t i = 0; i < last_index; i++) + { + gencontext_emit_stmt(context, ast->decl_expr_stmt[i]); + } + Ast *last = ast->decl_expr_stmt[last_index]; + LLVMValueRef result; + Type *type; + switch (last->ast_kind) + { + case AST_EXPR_STMT: + type = last->expr_stmt->type; + result = gencontext_emit_expr(context, last->expr_stmt); + break; + case AST_DECLARE_STMT: + type = last->declare_stmt->var.type_info->type; + result = gencontext_load_expr(context, gencontext_emit_decl(context, last)); + break; + default: + UNREACHABLE + } + if (bool_cast) + { + type = type->canonical; + if (type->type_kind != TYPE_BOOL) + { + CastKind cast = cast_to_bool_kind(type); + result = gencontext_emit_cast(context, cast, result, type, type_bool); + } + } + return result; +} + + +static inline void gencontext_emit_return(GenContext *context, Ast *ast) +{ + // Ensure we are on a branch that is non empty. + if (!gencontext_check_block_branch_emit(context)) return; + if (!ast->return_stmt.expr) + { + LLVMBuildRetVoid(context->builder); + return; + } + LLVMValueRef returnValue = gencontext_emit_expr(context, ast->return_stmt.expr); + LLVMBuildRet(context->builder, returnValue); + context->current_block = NULL; + LLVMBasicBlockRef post_ret_block = gencontext_create_free_block(context, "ret"); + gencontext_emit_block(context, post_ret_block); + +} + + void gencontext_emit_if(GenContext *context, Ast *ast) { - // In the case of something like if (foo = 1, int bar = 2; b > 0) { ... } - // Let's first emit foo = 1, int bar = 2 - // IMPROVE Consider whether these should be lowered or not. - Ast **stmts = ast->if_stmt.cond->cond_stmt.stmts; - VECEACH(stmts, i) - { - gencontext_emit_stmt(context, stmts[i]); - } + if (ast->if_stmt.decl) gencontext_emit_decl_expr_list_ignore_result(context, ast->if_stmt.decl); // We need at least the exit block and the "then" block. LLVMBasicBlockRef exit_block = LLVMCreateBasicBlockInContext(context->context, "if.exit"); @@ -107,10 +129,8 @@ void gencontext_emit_if(GenContext *context, Ast *ast) else_block = LLVMCreateBasicBlockInContext(context->context, "if.else"); } - assert(ast->if_stmt.cond->cond_stmt.expr->type->type_kind == TYPE_BOOL); - // Output boolean value and switch. - LLVMValueRef value = gencontext_emit_expr(context, ast->if_stmt.cond->cond_stmt.expr); + LLVMValueRef value = gencontext_emit_decl_expr_list(context, ast->if_stmt.cond, true); gencontext_emit_cond_br(context, value, then_block, else_block ? else_block : exit_block); // Emit the 'then' code. @@ -169,12 +189,7 @@ void gencontext_emit_for_stmt(GenContext *context, Ast *ast) { // First, emit all inits. - Ast **init_stmts = ast->for_stmt.init; - - VECEACH(init_stmts, i) - { - gencontext_emit_stmt(context, init_stmts[i]); - } + if (ast->for_stmt.init) gencontext_emit_decl_expr_list_ignore_result(context, ast->for_stmt.init); // We have 3 optional parts, which makes this code bit complicated. LLVMBasicBlockRef exit_block = gencontext_create_free_block(context, "for.exit"); @@ -203,7 +218,7 @@ void gencontext_emit_for_stmt(GenContext *context, Ast *ast) // Emit cond gencontext_emit_br(context, cond_block); gencontext_emit_block(context, cond_block); - value = gencontext_emit_expr(context, ast->for_stmt.cond->cond_stmt.expr); + value = gencontext_emit_expr(context, ast->for_stmt.cond); // If we have a body, conditionally jump to it. if (body_block) { @@ -236,7 +251,7 @@ void gencontext_emit_for_stmt(GenContext *context, Ast *ast) { // Emit the block gencontext_emit_block(context, inc_block); - gencontext_emit_stmt(context, ast->for_stmt.incr); + gencontext_emit_expr(context, ast->for_stmt.incr); } // Loop back. @@ -247,6 +262,48 @@ void gencontext_emit_for_stmt(GenContext *context, Ast *ast) gencontext_pop_break_continue(context); } +void gencontext_emit_while_stmt(GenContext *context, Ast *ast) +{ + // First, emit all inits. + + if (ast->while_stmt.decl) gencontext_emit_decl_expr_list_ignore_result(context, ast->while_stmt.decl); + + + LLVMBasicBlockRef exit_block = gencontext_create_free_block(context, "while.exit"); + LLVMBasicBlockRef begin_block = gencontext_create_free_block(context, "while.begin"); + LLVMBasicBlockRef body_block = ast->while_stmt.body->compound_stmt.stmts ? gencontext_create_free_block(context, "while.body") : NULL; + + gencontext_push_break_continue(context, exit_block, begin_block, NULL); + + // Emit cond + gencontext_emit_br(context, begin_block); + gencontext_emit_block(context, begin_block); + LLVMValueRef value = gencontext_emit_decl_expr_list(context, ast->while_stmt.cond, true); + // If we have a body, conditionally jump to it. + if (body_block) + { + gencontext_emit_cond_br(context, value, body_block, exit_block); + } + else + { + // Otherwise jump to inc or cond depending on what's available. + gencontext_emit_cond_br(context, value, begin_block, exit_block); + } + + if (body_block) + { + gencontext_emit_block(context, body_block); + gencontext_emit_stmt(context, ast->while_stmt.body); + } + + // Loop back. + gencontext_emit_br(context, begin_block); + + // And insert exit block + gencontext_emit_block(context, exit_block); + gencontext_pop_break_continue(context); +} + void gencontext_emit_do_stmt(GenContext *context, Ast *ast) { LLVMBasicBlockRef exit_block = gencontext_create_free_block(context, "do.exit"); @@ -305,7 +362,8 @@ void gencontext_emit_label(GenContext *context, Ast *ast) void gencontext_emit_switch(GenContext *context, Ast *ast) { - LLVMValueRef switch_value = gencontext_emit_cond(context, ast->switch_stmt.cond); + if (ast->switch_stmt.decl) gencontext_emit_decl_expr_list_ignore_result(context, ast->switch_stmt.decl); + LLVMValueRef switch_value = gencontext_emit_decl_expr_list(context, ast->switch_stmt.cond, false); size_t cases = vec_size(ast->switch_stmt.cases); if (!cases) @@ -426,6 +484,9 @@ void gencontext_emit_stmt(GenContext *context, Ast *ast) case AST_FOR_STMT: gencontext_emit_for_stmt(context, ast); break; + case AST_WHILE_STMT: + gencontext_emit_while_stmt(context, ast); + break; case AST_DO_STMT: gencontext_emit_do_stmt(context, ast); break; @@ -435,7 +496,6 @@ void gencontext_emit_stmt(GenContext *context, Ast *ast) break; case AST_NOP_STMT: break; - case AST_WHILE_STMT: case AST_CATCH_STMT: case AST_DEFER_STMT: case AST_TRY_STMT: @@ -445,7 +505,6 @@ void gencontext_emit_stmt(GenContext *context, Ast *ast) case AST_ASM_STMT: TODO case AST_ATTRIBUTE: - case AST_COND_STMT: case AST_CT_IF_STMT: case AST_CT_ELIF_STMT: case AST_CT_ELSE_STMT: @@ -455,6 +514,7 @@ void gencontext_emit_stmt(GenContext *context, Ast *ast) case AST_CT_CASE_STMT: case AST_GENERIC_CASE_STMT: case AST_GENERIC_DEFAULT_STMT: + case AST_DECL_EXPR_LIST: UNREACHABLE case AST_LABEL: gencontext_emit_label(context, ast); @@ -462,9 +522,6 @@ void gencontext_emit_stmt(GenContext *context, Ast *ast) case AST_GOTO_STMT: gencontext_emit_jmp(context, ast->goto_stmt.label->label_stmt.backend_value); break; - case AST_STMT_LIST: - gencontext_emit_stmt_list(context, ast); - break; case AST_SWITCH_STMT: gencontext_emit_switch(context, ast); break; diff --git a/src/compiler/parser.c b/src/compiler/parser.c index 3c95e73d9..d38614288 100644 --- a/src/compiler/parser.c +++ b/src/compiler/parser.c @@ -436,6 +436,12 @@ static TypeInfo *parse_type_expression(void) return type_info; } +/** + * Parse ident ('=' expr)? + * @param local + * @param type + * @return + */ static inline Decl *parse_decl_after_type(bool local, TypeInfo *type) { if (tok.type == TOKEN_LPAREN) @@ -450,66 +456,19 @@ static inline Decl *parse_decl_after_type(bool local, TypeInfo *type) Visibility visibility = local ? VISIBLE_LOCAL : VISIBLE_MODULE; Decl *decl = decl_new_var(name, type, VARDECL_LOCAL, visibility); - Decl *main_decl = decl; - while (1) + if (tok.type == TOKEN_EQ) { - if (tok.type == TOKEN_RPAREN || tok.type == TOKEN_EOS) + if (!decl) { - if (!decl) - { - SEMA_ERROR(tok, "Expected an identifier before '%s'.", token_type_to_string(tok.type)); - return &poisoned_decl; - } - return main_decl; + SEMA_ERROR(tok, "Expected an identifier before '='."); + return &poisoned_decl; } - - if (tok.type == TOKEN_EQ) - { - if (!decl) - { - SEMA_ERROR(tok, "Expected an identifier before '='."); - return &poisoned_decl; - } - advance_and_verify(TOKEN_EQ); - decl->var.init_expr = TRY_EXPR_OR(parse_initializer(), &poisoned_decl); - decl = NULL; - if (try_consume(TOKEN_COMMA)) continue; - return main_decl; - } - - if (tok.type == TOKEN_COMMA) - { - if (!decl) - { - SEMA_ERROR(tok, "Expected identifier."); - return &poisoned_decl; - } - advance(); - decl = NULL; - continue; - } - - if (tok.type == TOKEN_IDENT) - { - Decl *new_decl = decl_new_var(tok, type, VARDECL_LOCAL, visibility); - advance(); - if (main_decl->decl_kind == DECL_MULTI_DECL) - { - main_decl->multi_decl = VECADD(main_decl->multi_decl, new_decl); - decl = new_decl; - continue; - } - Decl *multi = decl_new(DECL_MULTI_DECL, main_decl->name, visibility); - multi->multi_decl = VECADD(multi->multi_decl, main_decl); - multi->multi_decl = VECADD(multi->multi_decl, new_decl); - main_decl = multi; - decl = new_decl; - continue; - } - - type = TRY_TYPE_OR(parse_type_expression(), &poisoned_decl); + advance_and_verify(TOKEN_EQ); + decl->var.init_expr = TRY_EXPR_OR(parse_initializer(), &poisoned_decl); } + + return decl; } /** @@ -578,56 +537,52 @@ static Ast *parse_expr_stmt(void) * ; * @return Ast * */ -static inline Ast *parse_expression_list(void) +static inline Expr *parse_expression_list(void) { - Ast *statement_list = new_ast(AST_STMT_LIST, tok); - Ast **stmts = NULL; + Expr *expr_list = expr_new(EXPR_EXPRESSION_LIST, tok); do { - Expr *expr = TRY_EXPR_OR(parse_expr(), &poisoned_ast); - Ast *ast = new_ast(AST_EXPR_STMT, expr->loc); - ast->expr_stmt = expr; - stmts = VECADD(stmts, ast); + Expr *expr = TRY_EXPR_OR(parse_expr(), &poisoned_expr); + vec_add(expr_list->expression_list, expr); } while (try_consume(TOKEN_COMMA)); - statement_list->stmt_list = stmts; - return statement_list; + return expr_list; } /** - * decl_or_expr_list - * : expression_list - * | declaration_list - * ; + * decl_expr_list + * : expression + * | declaration + * | decl_expr_list ',' expression + * | decl_expr_list ',' declaration + * ; * * @return bool */ -static inline bool parse_decl_expr_list(Ast ***stmt_list) +static inline Ast *parse_decl_expr_list(void) { - Expr *expr = NULL; - TypeInfo *type = NULL; - - if (!parse_type_or_expr(&expr, &type)) return false; - - - if (expr) + Ast *decl_expr = new_ast(AST_DECL_EXPR_LIST, tok); + decl_expr->decl_expr_stmt = NULL; + while (1) { - while (1) + Expr *expr = NULL; + TypeInfo *type = NULL; + if (!parse_type_or_expr(&expr, &type)) return false; + if (expr) { Ast *stmt = new_ast(AST_EXPR_STMT, expr->loc); stmt->expr_stmt = expr; - *stmt_list = VECADD(*stmt_list, stmt); - if (!try_consume(TOKEN_COMMA)) break; - expr = TRY_EXPR_OR(parse_expr(), &poisoned_ast); + vec_add(decl_expr->decl_expr_stmt, stmt); } + else + { + Decl *decl = TRY_DECL_OR(parse_decl_after_type(false, type), &poisoned_ast); + Ast *stmt = new_ast(AST_DECLARE_STMT, decl->name); + stmt->declare_stmt = decl; + vec_add(decl_expr->decl_expr_stmt, stmt); + } + if (!try_consume(TOKEN_COMMA)) break; } - else - { - Decl *decl = TRY_DECL_OR(parse_decl_after_type(false, type), &poisoned_ast); - Ast *stmt = new_ast(AST_DECLARE_STMT, decl->name); - stmt->declare_stmt = decl; - *stmt_list = VECADD(*stmt_list, stmt); - } - return true; + return decl_expr; } /** @@ -636,34 +591,23 @@ static inline bool parse_decl_expr_list(Ast ***stmt_list) * | declaration_list ';' decl_or_expr_list * ; * - * @return Ast* + * @return true if it succeeds */ -static inline Ast *parse_control_expression() +static inline bool parse_control_expression(Ast **decls, Ast **exprs) { - Ast *stmt_list = AST_NEW(AST_STMT_LIST, tok); - - Ast ***stmt_ref = &stmt_list->stmt_list; - - if (!parse_decl_expr_list(stmt_ref)) return &poisoned_ast; - - assert(*stmt_ref != NULL); - if (VECLAST(*stmt_ref)->ast_kind == AST_EXPR_STMT) - { - if (tok.type == TOKEN_EOS) - { - SEMA_ERROR(tok, "Unexpected ';'."); - return &poisoned_ast; - } - return stmt_list; - } + *exprs = TRY_AST_OR(parse_decl_expr_list(), false); if (!try_consume(TOKEN_EOS)) { - return stmt_list; + *decls = NULL; + return true; } - if (!parse_decl_expr_list(stmt_ref)) return &poisoned_ast; - return stmt_list; + *decls = *exprs; + + *exprs = TRY_AST_OR(parse_decl_expr_list(), false); + + return true; } /** @@ -679,10 +623,9 @@ static inline Ast* parse_if_stmt(void) Ast *if_ast = AST_NEW(AST_IF_STMT, tok); advance_and_verify(TOKEN_IF); CONSUME_OR(TOKEN_LPAREN, &poisoned_ast); - Ast *cond = TRY_AST(parse_control_expression()); + if (!parse_control_expression(&if_ast->if_stmt.decl, &if_ast->if_stmt.cond)) return &poisoned_ast; CONSUME_OR(TOKEN_RPAREN, &poisoned_ast); Ast *stmt = TRY_AST(parse_stmt()); - if_ast->if_stmt.cond = cond; if_ast->if_stmt.then_body = stmt; if (stmt->ast_kind != AST_COMPOUND_STMT || tok.type != TOKEN_ELSE) { @@ -704,7 +647,7 @@ static inline Ast* parse_while_stmt(void) advance_and_verify(TOKEN_WHILE); CONSUME_OR(TOKEN_LPAREN, &poisoned_ast); - while_ast->while_stmt.cond = TRY_AST(parse_control_expression()); + if (!parse_control_expression(&while_ast->while_stmt.decl, &while_ast->while_stmt.cond)) return &poisoned_ast; CONSUME_OR(TOKEN_RPAREN, &poisoned_ast); while_ast->while_stmt.body = TRY_AST(parse_stmt()); return while_ast; @@ -793,7 +736,7 @@ static inline Ast* parse_switch_stmt(void) Ast *switch_ast = AST_NEW(AST_SWITCH_STMT, tok); advance_and_verify(TOKEN_SWITCH); CONSUME_OR(TOKEN_LPAREN, &poisoned_ast); - switch_ast->switch_stmt.cond = TRY_AST(parse_control_expression()); + if (!parse_control_expression(&switch_ast->switch_stmt.decl, &switch_ast->switch_stmt.cond)) return &poisoned_ast; CONSUME_OR(TOKEN_RPAREN, &poisoned_ast); switch_ast->switch_stmt.body = TRY_AST(parse_compound_stmt()); return switch_ast; @@ -801,13 +744,12 @@ static inline Ast* parse_switch_stmt(void) /** * for_statement - * : FOR '(' decl_or_expr_list ';' expression_statement ')' statement - * | FOR '(' decl_or_expr_list ';' expression_statement expression_list ')' statement - * ; - * - * decl_or_expr_list - * : expression_list - * | declaration_list + * : FOR '(' decl_expr_list ';' expression ';' ')' statement + * | FOR '(' decl_expr_list ';' ';' expression_list ')' statement + * | FOR '(' decl_expr_list ';' expression ';' expression_list ')' statement + * | FOR '(' ';' expression ';' ')' statement + * | FOR '(' ';' ';' expression_list ')' statement + * | FOR '(' ';' expression ';' expression_list ')' statement * ; * * @return Ast* @@ -818,31 +760,32 @@ static inline Ast* parse_for_stmt(void) advance_and_verify(TOKEN_FOR); CONSUME_OR(TOKEN_LPAREN, &poisoned_ast); - Ast *cond = new_ast(AST_COND_STMT, tok); - - if (!parse_decl_expr_list(&cond->cond_stmt.stmts)) return &poisoned_ast; - - CONSUME_OR(TOKEN_EOS, &poisoned_ast); if (tok.type != TOKEN_EOS) { - cond->cond_stmt.expr = TRY_EXPR_OR(parse_expr(), &poisoned_ast); + ast->for_stmt.init = TRY_AST(parse_decl_expr_list()); + } + else + { + ast->for_stmt.init = NULL; } - ast->for_stmt.cond = cond; CONSUME_OR(TOKEN_EOS, &poisoned_ast); - if (!try_consume(TOKEN_RPAREN)) - { - ast->for_stmt.incr = parse_expression_list(); - CONSUME_OR(TOKEN_RPAREN, &poisoned_ast); - } + ast->for_stmt.cond = tok.type == TOKEN_EOS ? NULL : TRY_EXPR_OR(parse_expr(), &poisoned_ast); + + CONSUME_OR(TOKEN_EOS, &poisoned_ast); + + ast->for_stmt.incr = tok.type == TOKEN_RPAREN ? NULL : TRY_EXPR_OR(parse_expression_list(), &poisoned_ast); + + CONSUME_OR(TOKEN_RPAREN, &poisoned_ast); + ast->for_stmt.body = TRY_AST(parse_stmt()); return ast; } static inline Expr* parse_constant_expr(void) { - return parse_precedence(PREC_CONDITIONAL); + return parse_precedence(PREC_TERNARY); } /** * case_stmt @@ -2677,27 +2620,27 @@ void parse_file(Context *context) #define CHECK_EXPR(_expr) do { if (!expr_ok(_expr)) return _expr; } while(0) -static Expr *parse_conditional_expr(Expr *left_side) +static Expr *parse_ternary_expr(Expr *left_side) { assert(expr_ok(left_side)); - Expr *expr_ternary = EXPR_NEW_EXPR(EXPR_CONDITIONAL, left_side); - expr_ternary->conditional_expr.cond = left_side; + Expr *expr_ternary = EXPR_NEW_EXPR(EXPR_TERNARY, left_side); + expr_ternary->ternary_expr.cond = left_side; // Check for elvis if (try_consume(TOKEN_ELVIS)) { - expr_ternary->conditional_expr.then_expr = NULL; + expr_ternary->ternary_expr.then_expr = NULL; } else { advance_and_verify(TOKEN_QUESTION); - Expr *true_expr = TRY_EXPR_OR(parse_precedence(PREC_CONDITIONAL + 1), &poisoned_expr); - expr_ternary->conditional_expr.then_expr = true_expr; + Expr *true_expr = TRY_EXPR_OR(parse_precedence(PREC_TERNARY + 1), &poisoned_expr); + expr_ternary->ternary_expr.then_expr = true_expr; CONSUME_OR(TOKEN_COLON, &poisoned_expr); } - Expr *false_expr = TRY_EXPR_OR(parse_precedence(PREC_CONDITIONAL + 1), &poisoned_expr); - expr_ternary->conditional_expr.else_expr = false_expr; + Expr *false_expr = TRY_EXPR_OR(parse_precedence(PREC_TERNARY + 1), &poisoned_expr); + expr_ternary->ternary_expr.else_expr = false_expr; return expr_ternary; } @@ -3302,8 +3245,8 @@ static Expr *parse_cast_expr(Expr *left) } ParseRule rules[TOKEN_EOF + 1] = { - [TOKEN_QUESTION] = { NULL, parse_conditional_expr, PREC_CONDITIONAL }, - [TOKEN_ELVIS] = { NULL, parse_conditional_expr, PREC_CONDITIONAL }, + [TOKEN_QUESTION] = { NULL, parse_ternary_expr, PREC_TERNARY }, + [TOKEN_ELVIS] = { NULL, parse_ternary_expr, PREC_TERNARY }, [TOKEN_PLUSPLUS] = { parse_unary_expr, parse_post_unary, PREC_CALL }, [TOKEN_MINUSMINUS] = { parse_unary_expr, parse_post_unary, PREC_CALL }, [TOKEN_LPAREN] = { parse_grouping_expr, parse_call_expr, PREC_CALL }, diff --git a/src/compiler/semantic_analyser.c b/src/compiler/semantic_analyser.c index ddae3818f..226c45a10 100644 --- a/src/compiler/semantic_analyser.c +++ b/src/compiler/semantic_analyser.c @@ -6,7 +6,20 @@ typedef bool(*AstAnalysis)(Context *, Ast*); -bool sema_analyse_stmt_list(Context *context, Ast *statement); +static inline Type *ast_cond_type(Ast *ast) +{ + assert(ast->ast_kind == AST_DECL_EXPR_LIST); + Ast *last = VECLAST(ast->decl_expr_stmt); + switch (last->ast_kind) + { + case AST_EXPR_STMT: + return last->expr_stmt->type; + case AST_DECLARE_STMT: + return last->declare_stmt->var.type_info->type; + default: + UNREACHABLE + } +} void sema_init(File *file) { @@ -440,35 +453,6 @@ static inline bool decl_or_expr_to_expr_stmt(Context *context, Ast *stmt) assert(stmt->ast_kind == AST_DECLARE_STMT); stmt->ast_kind = AST_EXPR_STMT; Decl *decl = stmt->declare_stmt; - if (decl->decl_kind == DECL_MULTI_DECL) - { - Expr *list = expr_new(EXPR_EXPRESSION_LIST, stmt->token); - Expr **exprs = NULL; - VECEACH(decl->multi_decl, i) - { - Decl *var = decl->multi_decl[i]; - assert(var->decl_kind == DECL_VAR); - assert(var->decl_kind == VARDECL_LOCAL); - if (var->var.init_expr == NULL) - { - SEMA_ERROR(var->name, "'%s' needs to be assigned.", var->name.string); - return false; - } - Expr *assign_expr = expr_new(EXPR_BINARY, stmt->token); - assign_expr->resolve_status = RESOLVE_DONE; - assign_expr->binary_expr.operator = BINARYOP_ASSIGN; - Expr *identifier = expr_new(EXPR_IDENTIFIER, var->name); - identifier->resolve_status = RESOLVE_DONE; - identifier->identifier_expr.decl = var; - identifier->type = var->var.type_info->type; - assign_expr->binary_expr.left = identifier; - assign_expr->binary_expr.right = var->var.init_expr; - exprs = VECADD(exprs, assign_expr); - } - list->expression_list = exprs; - stmt->expr_stmt = list; - return true; - } assert(decl->decl_kind == DECL_VAR); assert(decl->decl_kind == VARDECL_LOCAL); if (decl->var.init_expr == NULL) @@ -480,53 +464,75 @@ static inline bool decl_or_expr_to_expr_stmt(Context *context, Ast *stmt) return true; } -static inline bool sema_flatten_cond(Context *context, Ast *stmt, bool cast_to_bool) +static inline bool sema_analyse_decl_expr_list(Context *context, Ast *stmt) { - assert(stmt->ast_kind == AST_STMT_LIST); - assert(vec_size(stmt->stmt_list) > 0); + assert(stmt->ast_kind == AST_DECL_EXPR_LIST); - // The common case: - if (vec_size(stmt->stmt_list) == 1 && stmt->stmt_list[0]->ast_kind == AST_EXPR_STMT) + VECEACH(stmt->decl_expr_stmt, i) { - Expr *expr = stmt->stmt_list[0]->expr_stmt; - if (cast_to_bool && !cast(expr, type_bool, CAST_TYPE_IMPLICIT)) return false; - stmt->ast_kind = AST_COND_STMT; - stmt->cond_stmt.expr = expr; - stmt->cond_stmt.stmts = NULL; - return true; + if (!sema_analyse_statement(context, stmt->decl_expr_stmt[i])) return false; } - Ast **new_list = NULL; - Expr *last = NULL; - unsigned last_index = vec_size(stmt->stmt_list) - 1; - VECEACH(stmt->stmt_list, i) - { - if (!convert_stmt_for_cond(context, stmt->stmt_list[i], &new_list, &last, last_index == i)) return false; - } - if (cast_to_bool && !cast(last, type_bool, CAST_TYPE_IMPLICIT)) return false; - stmt->ast_kind = AST_COND_STMT; - stmt->cond_stmt.expr = last; - stmt->cond_stmt.stmts = new_list; return true; } +static inline bool sema_analyse_cond(Context *context, Ast *stmt, bool cast_to_bool) +{ + assert(stmt->ast_kind == AST_DECL_EXPR_LIST); + + size_t size = vec_size(stmt->decl_expr_stmt); + if (!size) + { + SEMA_ERROR(stmt->token, "Expected a boolean expression"); + return false; + } + + if (!sema_analyse_decl_expr_list(context, stmt)) return false; + + Ast *last = stmt->decl_expr_stmt[size - 1]; + switch (last->ast_kind) + { + case AST_EXPR_STMT: + if (cast_to_bool) + { + if (!cast_implicit(last->expr_stmt, type_bool)) return false; + } + return true; + case AST_DECLARE_STMT: + { + Expr *init = last->declare_stmt->var.init_expr; + if (!init) + { + SEMA_ERROR(last->token, "Expected a declaration with initializer."); + return false; + } + if (cast_to_bool && init->type->type_kind != TYPE_BOOL && + cast_to_bool_kind(last->declare_stmt->var.type_info->type) == CAST_ERROR) + { + SEMA_ERROR(last->declare_stmt->var.init_expr->loc, "The expression needs to be convertible to a boolean."); + return false; + } + return true; + } + default: + UNREACHABLE + } +} + static inline bool sema_analyse_while_stmt(Context *context, Ast *statement) { + Ast *decl = statement->while_stmt.decl; Ast *cond = statement->while_stmt.cond; Ast *body = statement->while_stmt.body; - assert(cond && cond->ast_kind == AST_STMT_LIST); context_push_scope_with_flags(context, SCOPE_CONTROL); - bool success = sema_analyse_statement(context, cond); - success = success && sema_flatten_cond(context, cond, true); + bool success = !decl || sema_analyse_statement(context, decl); + success = success && sema_analyse_cond(context, cond, true); + context_push_scope_with_flags(context, SCOPE_BREAK | SCOPE_CONTINUE); // NOLINT(hicpp-signed-bitwise) success = success && sema_analyse_statement(context, body); context_pop_scope(context); context_pop_scope(context); if (!success) return false; - statement->ast_kind = AST_FOR_STMT; - statement->for_stmt.cond = cond; - statement->for_stmt.incr = NULL; - statement->for_stmt.body = body; return success; } @@ -563,10 +569,6 @@ static inline bool sema_analyse_multi_decl(Context *context, Ast *statement) static inline bool sema_analyse_declare_stmt(Context *context, Ast *statement) { Decl *decl = statement->declare_stmt; - if (decl->decl_kind == DECL_MULTI_DECL) - { - return sema_analyse_multi_decl(context, statement); - } return sema_analyse_var_decl(context, decl); } @@ -599,21 +601,14 @@ static inline bool sema_analyse_default_stmt(Context *context, Ast *statement) return false; } -bool sema_analyse_stmt_list(Context *context, Ast *statement) -{ - VECEACH(statement->stmt_list, i) - { - if (!sema_analyse_statement(context, statement->stmt_list[i])) return false; - } - return true; -} - static inline bool sema_analyse_for_stmt(Context *context, Ast *statement) { context_push_scope_with_flags(context, SCOPE_CONTROL); - bool success = sema_analyse_statement(context, statement->for_stmt.cond); - success = success && (!statement->for_stmt.incr || sema_analyse_statement(context, statement->for_stmt.incr)); + bool success = !statement->for_stmt.init || sema_analyse_statement(context, statement->for_stmt.init); + + success = success && (!statement->for_stmt.cond || sema_analyse_expr(context, type_bool, statement->for_stmt.cond)); + success = success && (!statement->for_stmt.incr || sema_analyse_expr(context, NULL, statement->for_stmt.incr)); context_pop_scope(context); if (!success) return false; context_push_scope_with_flags(context, SCOPE_BREAK | SCOPE_CONTINUE); // NOLINT(hicpp-signed-bitwise) @@ -648,8 +643,7 @@ static inline bool sema_analyse_if_stmt(Context *context, Ast *statement) context_push_scope(context); Ast *cond = statement->if_stmt.cond; context_push_scope_with_flags(context, SCOPE_CONTROL); - bool success = sema_analyse_statement(context, cond); - success = success && sema_flatten_cond(context, cond, true); + bool success = sema_analyse_cond(context, cond, true); context_push_scope(context); success = success && sema_analyse_statement(context, statement->if_stmt.then_body); context_pop_scope(context); @@ -864,14 +858,14 @@ static bool sema_analyse_switch_case(Context *context, Ast*** prev_cases, Ast *c static bool sema_analyse_switch_stmt(Context *context, Ast *statement) { - Ast *cond = statement->switch_stmt.cond; context_push_scope_with_flags(context, SCOPE_CONTROL); - bool success = sema_analyse_statement(context, cond); - success = success && sema_flatten_cond(context, cond, false); + bool success = sema_analyse_statement(context, statement->switch_stmt.cond); + Ast *cond = statement->switch_stmt.cond; + success = success && sema_analyse_cond(context, cond, false); context_push_scope_with_flags(context, SCOPE_BREAK | SCOPE_NEXT); // NOLINT(hicpp-signed-bitwise) Ast *body = statement->switch_stmt.body; assert(body->ast_kind == AST_COMPOUND_STMT); - Type *switch_type = cond->cond_stmt.expr->type->canonical; + Type *switch_type = ast_cond_type(cond)->canonical; if (!type_is_integer(switch_type)) { SEMA_ERROR(cond->token, "Expected an integer or enum type, was '%s'.", type_to_error_string(switch_type)); @@ -945,7 +939,7 @@ static AstAnalysis AST_ANALYSIS[AST_WHILE_STMT + 1] = [AST_NEXT_STMT] = NULL, // Never reached [AST_VOLATILE_STMT] = &sema_analyse_volatile_stmt, [AST_WHILE_STMT] = &sema_analyse_while_stmt, - [AST_STMT_LIST] = &sema_analyse_stmt_list + [AST_DECL_EXPR_LIST] = &sema_analyse_decl_expr_list, }; bool sema_analyse_statement(Context *context, Ast *statement)