diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index fc4a71288..89eb21b2b 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -43,6 +43,7 @@ typedef struct #define MAX_SCOPE_DEPTH 0x100 #define MAX_STRING_BUFFER 0x10000 #define MAX_MACRO_NESTING 1024 +#define MAX_MACRO_ITERATIONS 0xFFFFFF #define MAX_FUNCTION_SIGNATURE_SIZE 2048 #define MAX_PARAMS 512 #define MAX_MEMBERS ((MemberIndex)(((uint64_t)2) << 28)) @@ -1105,7 +1106,7 @@ typedef struct Expr *init; Expr *cond; Expr *incr; - Ast *body; + AstId body; void *continue_block; void *exit_block; } AstForStmt; @@ -1993,7 +1994,7 @@ MemberIndex sema_get_initializer_const_array_size(SemaContext *context, Expr *in bool sema_analyse_expr(SemaContext *context, Expr *expr); bool sema_analyse_inferred_expr(SemaContext *context, Type *to, Expr *expr); bool sema_analyse_decl(SemaContext *context, Decl *decl); - +bool sema_analyse_var_decl_ct(SemaContext *context, Decl *decl); bool sema_analyse_var_decl(SemaContext *context, Decl *decl, bool local); bool sema_analyse_ct_assert_stmt(SemaContext *context, Ast *statement); bool sema_analyse_statement(SemaContext *context, Ast *statement); diff --git a/src/compiler/copying.c b/src/compiler/copying.c index 3f8900e5e..f5b351b73 100644 --- a/src/compiler/copying.c +++ b/src/compiler/copying.c @@ -294,8 +294,6 @@ Ast *ast_copy_deep(Ast *source) ast->ct_foreach_stmt.body = astid_copy_deep(ast->ct_foreach_stmt.body); MACRO_COPY_EXPR(ast->ct_foreach_stmt.expr); return ast; - case AST_CT_FOR_STMT: - TODO case AST_CT_SWITCH_STMT: MACRO_COPY_EXPR(ast->ct_switch_stmt.cond); MACRO_COPY_AST_LIST(ast->ct_switch_stmt.body); @@ -306,9 +304,6 @@ Ast *ast_copy_deep(Ast *source) case AST_DEFAULT_STMT: MACRO_COPY_AST(ast->case_stmt.body); return ast; - case AST_VAR_STMT: - ast->var_stmt = copy_decl(ast->var_stmt); - return ast; case AST_DEFER_STMT: assert(!ast->defer_stmt.prev_defer); MACRO_COPY_AST(ast->defer_stmt.body); @@ -322,10 +317,11 @@ Ast *ast_copy_deep(Ast *source) MACRO_COPY_EXPR(ast->expr_stmt); return ast; case AST_FOR_STMT: + case AST_CT_FOR_STMT: copy_flow(ast); MACRO_COPY_EXPR(ast->for_stmt.cond); MACRO_COPY_EXPR(ast->for_stmt.incr); - MACRO_COPY_AST(ast->for_stmt.body); + ast->for_stmt.body = astid_copy_deep(ast->for_stmt.body); MACRO_COPY_EXPR(ast->for_stmt.init); return ast; case AST_FOREACH_STMT: diff --git a/src/compiler/enums.h b/src/compiler/enums.h index ff47fe0b7..229ac6796 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -50,7 +50,6 @@ typedef enum AST_CASE_STMT, AST_COMPOUND_STMT, AST_CONTINUE_STMT, - AST_VAR_STMT, AST_CT_ASSERT, AST_CT_COMPOUND_STMT, AST_CT_IF_STMT, diff --git a/src/compiler/llvm_codegen_stmt.c b/src/compiler/llvm_codegen_stmt.c index b3d2ae4e4..17fb9a438 100644 --- a/src/compiler/llvm_codegen_stmt.c +++ b/src/compiler/llvm_codegen_stmt.c @@ -391,7 +391,8 @@ void llvm_emit_for_stmt(GenContext *c, Ast *ast) Expr *incr = ast->for_stmt.incr; LLVMBasicBlockRef inc_block = incr ? llvm_basic_block_new(c, "loop.inc") : NULL; - LLVMBasicBlockRef body_block = ast_is_not_empty(ast->for_stmt.body) ? llvm_basic_block_new(c, "loop.body") : NULL; + Ast *body = astptr(ast->for_stmt.body); + LLVMBasicBlockRef body_block = ast_is_not_empty(body) ? llvm_basic_block_new(c, "loop.body") : NULL; LLVMBasicBlockRef cond_block = NULL; // Skipping first cond? This is do-while semantics @@ -507,7 +508,7 @@ void llvm_emit_for_stmt(GenContext *c, Ast *ast) break; } // Now emit the body - llvm_emit_stmt(c, ast->for_stmt.body); + llvm_emit_stmt(c, body); // Did we have a jump to inc yet? if (inc_block && !llvm_basic_block_is_unused(inc_block)) @@ -1241,7 +1242,6 @@ void llvm_emit_stmt(GenContext *c, Ast *ast) case AST_DOCS: case AST_DOC_DIRECTIVE: case AST_POISONED: - case AST_VAR_STMT: case AST_IF_CATCH_SWITCH_STMT: case AST_SCOPING_STMT: case AST_FOREACH_STMT: diff --git a/src/compiler/parse_expr.c b/src/compiler/parse_expr.c index eaa0f0ab6..2ded1c42f 100644 --- a/src/compiler/parse_expr.c +++ b/src/compiler/parse_expr.c @@ -397,6 +397,33 @@ Expr *parse_expression_list(ParseContext *context, bool allow_decl) return expr_list; } +Expr *parse_ct_expression_list(ParseContext *context, bool allow_decl) +{ + Expr *expr_list = EXPR_NEW_TOKEN(EXPR_EXPRESSION_LIST, context->tok); + while (1) + { + Expr *expr; + if (tok_is(context, TOKEN_VAR)) + { + ASSIGN_DECL_ELSE(Decl *decl, parse_var_decl(context), poisoned_expr); + if (!allow_decl) + { + SEMA_TOKEN_ERROR(context->tok, "This looks like a declaration, which isn't allowed here."); + return poisoned_expr; + } + expr = expr_new(EXPR_DECL, decl->span); + expr->decl_expr = decl; + } + else + { + ASSIGN_EXPR_ELSE(expr, parse_expr(context), poisoned_expr); + } + vec_add(expr_list->expression_list, expr); + if (!try_consume(context, TOKEN_COMMA)) break; + } + return expr_list; +} + /** * @param left must be null. * @return Expr* diff --git a/src/compiler/parse_global.c b/src/compiler/parse_global.c index 0b45b1209..cc0157175 100644 --- a/src/compiler/parse_global.c +++ b/src/compiler/parse_global.c @@ -828,6 +828,38 @@ static inline bool is_function_start(ParseContext *context) } +Decl *parse_var_decl(ParseContext *context) +{ + TokenId start = context->tok.id; + advance_and_verify(context, TOKEN_VAR); + Decl *decl; + switch (context->tok.type) + { + case TOKEN_CT_IDENT: + decl = decl_new_var(context->tok.id, NULL, VARDECL_LOCAL_CT, VISIBLE_LOCAL); + advance(context); + if (try_consume(context, TOKEN_EQ)) + { + ASSIGN_EXPR_ELSE(decl->var.init_expr, parse_expr(context), poisoned_decl); + } + break; + case TOKEN_CT_TYPE_IDENT: + decl = decl_new_var(context->tok.id, NULL, VARDECL_LOCAL_CT_TYPE, VISIBLE_LOCAL); + advance(context); + if (try_consume(context, TOKEN_EQ)) + { + ASSIGN_EXPR_ELSE(decl->var.init_expr, parse_expr(context), poisoned_decl); + } + break; + default: + SEMA_TOKEN_ERROR(context->tok, "Expected a compile time variable name ('$Foo' or '$foo')."); + return poisoned_decl; + } + decl->span.loc = start; + RANGE_EXTEND_PREV(decl); + return decl; +} + bool parse_next_is_decl(ParseContext *context) { TokenType next_tok = context->next_tok.type; diff --git a/src/compiler/parse_stmt.c b/src/compiler/parse_stmt.c index feac52eb8..200c8b2aa 100644 --- a/src/compiler/parse_stmt.c +++ b/src/compiler/parse_stmt.c @@ -344,7 +344,8 @@ static inline Ast* parse_for_stmt(ParseContext *context) CONSUME_OR(TOKEN_RPAREN, poisoned_ast); extend_ast_with_prev_token(context, ast); - ASSIGN_AST_ELSE(ast->for_stmt.body, parse_stmt(context), poisoned_ast); + ASSIGN_AST_ELSE(Ast *body, parse_stmt(context), poisoned_ast); + ast->for_stmt.body = astid(body); return ast; } @@ -527,35 +528,8 @@ static inline Ast *parse_decl_or_expr_stmt(ParseContext *context) */ static inline Ast *parse_var_stmt(ParseContext *context) { - Ast *ast = AST_NEW_TOKEN(AST_VAR_STMT, context->tok); - TokenId start = context->tok.id; - advance_and_verify(context, TOKEN_VAR); - Decl *decl; - switch (context->tok.type) - { - case TOKEN_CT_IDENT: - decl = decl_new_var(context->tok.id, NULL, VARDECL_LOCAL_CT, VISIBLE_LOCAL); - advance(context); - if (try_consume(context, TOKEN_EQ)) - { - ASSIGN_EXPR_ELSE(decl->var.init_expr, parse_expr(context), poisoned_ast); - } - break; - case TOKEN_CT_TYPE_IDENT: - decl = decl_new_var(context->tok.id, NULL, VARDECL_LOCAL_CT_TYPE, VISIBLE_LOCAL); - advance(context); - if (try_consume(context, TOKEN_EQ)) - { - ASSIGN_EXPR_ELSE(decl->var.init_expr, parse_expr(context), poisoned_ast); - } - break; - default: - SEMA_TOKEN_ERROR(context->tok, "Expected a compile time variable name ('$Foo' or '$foo')."); - return poisoned_ast; - } - decl->span.loc = start; - ast->var_stmt = decl; - RANGE_EXTEND_PREV(decl); + Ast *ast = AST_NEW_TOKEN(AST_DECLARE_STMT, context->tok); + ASSIGN_DECL_ELSE(ast->var_stmt, parse_var_decl(context), poisoned_ast); RANGE_EXTEND_PREV(ast); TRY_CONSUME_EOS(); return ast; @@ -667,7 +641,7 @@ static inline Ast *parse_return(ParseContext *context) /** * ct_foreach_stmt - * | CT_FOREACH '(' CT_IDENT (',' CT_IDENT)? ':' expr ')' statement + * | CT_FOREACH '(' CT_IDENT (',' CT_IDENT)? ':' expr ')' ':' statement* CT_ENDFOREACH EOS * ; * * @return @@ -701,6 +675,47 @@ static inline Ast* parse_ct_foreach_stmt(ParseContext *context) return ast; } +/** + * ct_for_stmt + * | CT_FOR '(' decl_expr_list? ';' expression_list? ';' expression_list? ')' ':' statement* CT_ENDFOR ';' + * ; + */ +static inline Ast* parse_ct_for_stmt(ParseContext *context) +{ + Ast *ast = AST_NEW_TOKEN(AST_CT_FOR_STMT, context->tok); + advance_and_verify(context, TOKEN_CT_FOR); + CONSUME_OR(TOKEN_LPAREN, poisoned_ast); + + if (!TOKEN_IS(TOKEN_EOS)) + { + ASSIGN_EXPR_ELSE(ast->for_stmt.init, parse_ct_expression_list(context, true), poisoned_ast); + } + CONSUME_OR(TOKEN_EOS, poisoned_ast); + + // Cond is required. + ASSIGN_EXPR_ELSE(ast->for_stmt.cond, parse_expr(context), poisoned_ast); + CONSUME_OR(TOKEN_EOS, poisoned_ast); + + if (!TOKEN_IS(TOKEN_RPAREN)) + { + ast->for_stmt.incr = parse_ct_expression_list(context, false); + } + + CONSUME_OR(TOKEN_RPAREN, poisoned_ast); + + CONSUME_OR(TOKEN_COLON, poisoned_ast); + Ast *body = new_ast(AST_COMPOUND_STMT, ast->span); + ast->for_stmt.body = astid(body); + AstId *current = &body->compound_stmt.first_stmt; + while (!try_consume(context, TOKEN_CT_ENDFOR)) + { + ASSIGN_AST_ELSE(Ast *stmt, parse_stmt(context), poisoned_ast); + *current = astid(stmt); + current = &stmt->next; + } + return ast; +} + /** * CTSWITCH '(' expression ')' ':' '{' ct_switch_body '}' * @@ -866,7 +881,7 @@ Ast *parse_stmt(ParseContext *context) case TOKEN_CT_FOREACH: return parse_ct_foreach_stmt(context); case TOKEN_CT_FOR: - TODO + return parse_ct_for_stmt(context); case TOKEN_CT_UNREACHABLE: return parse_unreachable_stmt(context); case TOKEN_STAR: diff --git a/src/compiler/parser_internal.h b/src/compiler/parser_internal.h index 6ef1cb484..6317313d9 100644 --- a/src/compiler/parser_internal.h +++ b/src/compiler/parser_internal.h @@ -46,8 +46,10 @@ bool parse_attributes(ParseContext *context, Attr ***attributes_ref); bool parse_switch_body(ParseContext *context, Ast ***cases, TokenType case_type, TokenType default_type, bool allow_multiple_values); +Expr *parse_ct_expression_list(ParseContext *context, bool allow_decl); Expr *parse_expression_list(ParseContext *context, bool allow_decls); Decl *parse_decl_after_type(ParseContext *context, TypeInfo *type); +Decl *parse_var_decl(ParseContext *context); bool parse_parameters(ParseContext *context, Visibility visibility, Decl ***params_ref); bool parse_arg_list(ParseContext *context, Expr ***result, TokenType param_end, bool *unsplat); diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index c405e576c..9af258426 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -1756,6 +1756,81 @@ bool sema_analyse_decl_type(SemaContext *context, Type *type, SourceSpan span) } return true; } + +bool sema_analyse_var_decl_ct(SemaContext *context, Decl *decl) +{ + Expr *init; + assert(decl->decl_kind == DECL_VAR); + switch (decl->var.kind) + { + case VARDECL_LOCAL_CT_TYPE: + // Locally declared compile time type. + if (decl->var.type_info) + { + SEMA_ERROR(decl->var.type_info, "Compile time type variables may not have a type."); + return false; + } + if ((init = decl->var.init_expr)) + { + if (!sema_analyse_expr_lvalue(context, init)) return false; + if (init->expr_kind != EXPR_TYPEINFO) + { + SEMA_ERROR(decl->var.init_expr, "Expected a type assigned to %s.", decl->name); + return false; + } + } + break; + case VARDECL_LOCAL_CT: + if (decl->var.type_info && !sema_resolve_type_info(context, decl->var.type_info)) return false; + if (decl->var.type_info) + { + decl->type = decl->var.type_info->type->canonical; + if (!type_is_builtin(decl->type->type_kind)) + { + SEMA_ERROR(decl->var.type_info, "Compile time variables may only be built-in types."); + return false; + } + if ((init = decl->var.init_expr)) + { + if (!sema_analyse_expr_rhs(context, decl->type, init, false)) return false; + if (!expr_is_constant_eval(init, CONSTANT_EVAL_ANY)) + { + SEMA_ERROR(init, "Expected a constant expression assigned to %s.", decl->name); + return false; + } + } + else + { + TODO // generate. + // decl->var.init_expr = + } + } + else + { + if ((init = decl->var.init_expr)) + { + if (!sema_analyse_expr(context, init)) return false; + if (!expr_is_constant_eval(init, CONSTANT_EVAL_ANY)) + { + SEMA_ERROR(init, "Expected a constant expression assigned to %s.", decl->name); + return false; + } + decl->type = init->type; + } + else + { + decl->type = type_void; + } + } + break; + default: + UNREACHABLE + } + + decl->var.scope_depth = context->active_scope.depth; + return sema_add_local(context, decl); + +} /** * Analyse a regular global or local declaration, e.g. int x = 123 */ diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index 596e42bea..c9327e92d 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -674,7 +674,7 @@ static inline bool sema_analyse_while_stmt(SemaContext *context, Ast *statement) .cond = cond, .flow = statement->while_stmt.flow, .incr = NULL, - .body = body, + .body = astid(body), }; statement->for_stmt = for_stmt; return success; @@ -750,99 +750,25 @@ END:; .cond = expr, .flow = flow, .incr = NULL, - .body = body, + .body = astid(body), }; statement->for_stmt = for_stmt; return true; } + + static inline bool sema_analyse_declare_stmt(SemaContext *context, Ast *statement) { - return sema_analyse_var_decl(context, statement->declare_stmt, true); -} - -/** - * Check "var $foo = ... " and "var $Foo = ..." - */ -static inline bool sema_analyse_var_stmt(SemaContext *context, Ast *statement) -{ - // 1. Pick the declaration. - Decl *decl = statement->var_stmt; - - // 2. Convert it to a NOP early - statement->ast_kind = AST_NOP_STMT; - - Expr *init; - assert(decl->decl_kind == DECL_VAR); - switch (decl->var.kind) + VarDeclKind kind = statement->declare_stmt->var.kind; + if (kind == VARDECL_LOCAL_CT_TYPE || kind == VARDECL_LOCAL_CT) { - case VARDECL_LOCAL_CT_TYPE: - // Locally declared compile time type. - if (decl->var.type_info) - { - SEMA_ERROR(decl->var.type_info, "Compile time type variables may not have a type."); - return false; - } - if ((init = decl->var.init_expr)) - { - if (!sema_analyse_expr_lvalue(context, init)) return false; - if (init->expr_kind != EXPR_TYPEINFO) - { - SEMA_ERROR(decl->var.init_expr, "Expected a type assigned to %s.", decl->name); - return false; - } - } - break; - case VARDECL_LOCAL_CT: - if (decl->var.type_info && !sema_resolve_type_info(context, decl->var.type_info)) return false; - if (decl->var.type_info) - { - decl->type = decl->var.type_info->type->canonical; - if (!type_is_builtin(decl->type->type_kind)) - { - SEMA_ERROR(decl->var.type_info, "Compile time variables may only be built-in types."); - return false; - } - if ((init = decl->var.init_expr)) - { - if (!sema_analyse_expr_rhs(context, decl->type, init, false)) return false; - if (!expr_is_constant_eval(init, CONSTANT_EVAL_ANY)) - { - SEMA_ERROR(init, "Expected a constant expression assigned to %s.", decl->name); - return false; - } - } - else - { - TODO // generate. - // decl->var.init_expr = - } - } - else - { - if ((init = decl->var.init_expr)) - { - if (!sema_analyse_expr(context, init)) return false; - if (!expr_is_constant_eval(init, CONSTANT_EVAL_ANY)) - { - SEMA_ERROR(init, "Expected a constant expression assigned to %s.", decl->name); - return false; - } - decl->type = init->type; - } - else - { - decl->type = type_void; - } - } - break; - default: - UNREACHABLE + if (!sema_analyse_var_decl_ct(context, statement->declare_stmt)) return false; + statement->ast_kind = AST_NOP_STMT; + return true; } - - decl->var.scope_depth = context->active_scope.depth; - return sema_add_local(context, decl); + return sema_analyse_var_decl(context, statement->declare_stmt, true); } static inline bool sema_analyse_expr_stmt(SemaContext *context, Ast *statement) @@ -939,7 +865,7 @@ static inline bool sema_analyse_for_stmt(SemaContext *context, Ast *statement) } assert(statement->for_stmt.body); - Ast *body = statement->for_stmt.body; + Ast *body = astptr(statement->for_stmt.body); // Create the for body scope. SCOPE_START_WITH_LABEL(statement->for_stmt.flow.label) @@ -1424,7 +1350,7 @@ static inline bool sema_analyse_foreach_stmt(SemaContext *context, Ast *statemen .cond = binary, .incr = inc, .flow = flow, - .body = compound_stmt + .body = astid(compound_stmt) }; statement->ast_kind = AST_FOR_STMT; return sema_analyse_for_stmt(context, statement); @@ -2421,6 +2347,85 @@ static bool sema_analyse_compound_stmt(SemaContext *context, Ast *statement) return success; } +static inline bool sema_analyse_ct_for_stmt(SemaContext *context, Ast *statement) +{ + bool success = false; + // Enter for scope + SCOPE_OUTER_START + + Expr *init; + if ((init = statement->for_stmt.init)) + { + assert(init->expr_kind == EXPR_EXPRESSION_LIST); + Expr **expressions = init->expression_list; + VECEACH (expressions, i) + { + Expr *expr = expressions[i]; + if (expr->expr_kind == EXPR_DECL) + { + Decl *decl = expr->decl_expr; + if (decl->decl_kind != DECL_VAR || (decl->var.kind != VARDECL_LOCAL_CT && decl->var.kind != VARDECL_LOCAL_CT_TYPE)) + { + SEMA_ERROR(expr, "Only 'var $foo' and 'var $Type' declarations are allowed in a '$for'"); + goto EXIT_ERROR; + } + if (!sema_analyse_var_decl_ct(context, decl)) goto EXIT_ERROR; + continue; + } + if (!sema_analyse_expr(context, expr)) goto EXIT_ERROR; + if (!expr_is_constant_eval(expr, CONSTANT_EVAL_FOLDABLE)) + { + SEMA_ERROR(expr, "Only constant expressions are allowed."); + goto EXIT_ERROR; + } + } + } + Expr *condition = statement->for_stmt.cond; + Expr *incr = statement->for_stmt.incr; + Ast *body = astptr(statement->for_stmt.body); + AstId start = 0; + AstId *current = &start; + assert(condition); + for (int i = 0; i < MAX_MACRO_ITERATIONS; i++) + { + Expr *copy = copy_expr(condition); + if (!sema_analyse_cond_expr(context, copy)) goto EXIT_ERROR; + if (copy->expr_kind != EXPR_CONST) + { + SEMA_ERROR(copy, "Expected a value that can be evaluated at compile time."); + goto EXIT_ERROR; + } + if (!copy->const_expr.b) break; + + Ast *compound_stmt = ast_copy_deep(body); + if (!sema_analyse_compound_stmt(context, compound_stmt)) goto EXIT_ERROR; + *current = astid(compound_stmt); + current = &compound_stmt->next; + + if (incr) + { + Expr **exprs = incr->expression_list; + VECEACH(exprs, j) + { + copy = copy_expr(exprs[j]); + if (!sema_analyse_expr(context, copy)) goto EXIT_ERROR; + if (copy->expr_kind != EXPR_CONST) + { + SEMA_ERROR(copy, "Expected a value that can be evaluated at compile time."); + goto EXIT_ERROR; + } + } + } + } + statement->ast_kind = AST_COMPOUND_STMT; + statement->compound_stmt.first_stmt = start; + statement->compound_stmt.defer_list = (DeferList) { 0, 0 }; + success = true; + EXIT_ERROR: + SCOPE_OUTER_END; + return success; +} + static bool sema_analyse_ct_compound_stmt(SemaContext *context, Ast *statement) { bool all_ok = ast_ok(statement); @@ -2491,8 +2496,6 @@ static inline bool sema_analyse_statement_inner(SemaContext *context, Ast *state return false; case AST_DEFER_STMT: return sema_analyse_defer_stmt(context, statement); - case AST_VAR_STMT: - return sema_analyse_var_stmt(context, statement); case AST_DO_STMT: return sema_analyse_do_stmt(context, statement); case AST_EXPR_STMT: @@ -2523,7 +2526,7 @@ static inline bool sema_analyse_statement_inner(SemaContext *context, Ast *state case AST_CT_FOREACH_STMT: return sema_analyse_ct_foreach_stmt(context, statement); case AST_CT_FOR_STMT: - TODO + return sema_analyse_ct_for_stmt(context, statement); } UNREACHABLE diff --git a/src/version.h b/src/version.h index aad120eb5..7be917e27 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define COMPILER_VERSION "PRE.18" \ No newline at end of file +#define COMPILER_VERSION "PRE.19" \ No newline at end of file diff --git a/test/test_suite/compile_time/ct_for.c3t b/test/test_suite/compile_time/ct_for.c3t new file mode 100644 index 000000000..75647da7b --- /dev/null +++ b/test/test_suite/compile_time/ct_for.c3t @@ -0,0 +1,32 @@ +// #target: x64-darwin + +module test; + +extern fn void printf(char*, ...); + +fn void main() +{ + + $for (var $i = 0; $i < 3; $i++): + printf("Foo %d\n", $i); + $endfor; + + $for (var $i = 0, var $j = 100; $i < 4;): + printf("Foo %d %d\n", $i++, $j--); + $endfor; + +} + +/* #expect: test.ll + +define void @test.main() #0 { +entry: + call void (i8*, ...) @printf(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str, i32 0, i32 0), i32 0) + call void (i8*, ...) @printf(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str.1, i32 0, i32 0), i32 1) + call void (i8*, ...) @printf(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str.2, i32 0, i32 0), i32 2) + call void (i8*, ...) @printf(i8* getelementptr inbounds ([11 x i8], [11 x i8]* @.str.3, i32 0, i32 0), i32 0, i32 100) + call void (i8*, ...) @printf(i8* getelementptr inbounds ([11 x i8], [11 x i8]* @.str.4, i32 0, i32 0), i32 1, i32 99) + call void (i8*, ...) @printf(i8* getelementptr inbounds ([11 x i8], [11 x i8]* @.str.5, i32 0, i32 0), i32 2, i32 98) + call void (i8*, ...) @printf(i8* getelementptr inbounds ([11 x i8], [11 x i8]* @.str.6, i32 0, i32 0), i32 3, i32 97) + ret void +}