diff --git a/README.md b/README.md index 5f8ce64ef..1f8135ada 100644 --- a/README.md +++ b/README.md @@ -90,7 +90,7 @@ the r/ProgrammingLanguages Discord: https://discord.gg/cfu4wdk - [x] `next` statement - [x] Expression blocks - [x] Do-without-while -- [ ] Foreach statement +- [x] Foreach statement - [ ] All attributes - [ ] Associative array literals - [ ] CT type constants @@ -117,10 +117,10 @@ the r/ProgrammingLanguages Discord: https://discord.gg/cfu4wdk - [ ] String functions - [ ] Vararrays e.g. `int[*]` - [ ] Compile time incremental arrays -- [-] Complete C ABI conformance *in progress* +- [ ] Complete C ABI conformance *in progress* - [ ] Generic functions -- [-] Debug info *in progress* -- [ ] Simd vectors +- [ ] Debug info *in progress* +- [ ] Simd vector types - [ ] Complex types #### What can you help with? diff --git a/src/compiler/ast.c b/src/compiler/ast.c index 6b2746a5a..798769e05 100644 --- a/src/compiler/ast.c +++ b/src/compiler/ast.c @@ -46,11 +46,6 @@ static TypeInfo poison_type_info = { .kind = TYPE_INFO_POISON }; Type *poisoned_type = &poison_type; TypeInfo *poisoned_type_info = &poison_type_info; -AlignSize decl_abi_alignment(Decl *decl) -{ - return decl->alignment ?: type_abi_alignment(decl->type); -} - void decl_set_external_name(Decl *decl) { if (decl->visibility == VISIBLE_EXTERN) @@ -1108,6 +1103,13 @@ static void fprint_ast_recursive(Context *context, FILE *file, Ast *ast, int ind } DUMPAST(ast->for_stmt.body); DUMPEND(); + case AST_FOREACH_STMT: + DUMP("(foreach"); + DUMPDECL(ast->foreach_stmt.index); + DUMPDECL(ast->foreach_stmt.variable); + DUMPEXPR(ast->foreach_stmt.enumeration); + DUMPAST(ast->foreach_stmt.body); + DUMPEND(); case AST_IF_STMT: DUMP("(if"); DUMPEXPR(ast->if_stmt.cond); diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 98445eec4..6750ac4b6 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -908,6 +908,20 @@ typedef struct void *exit_block; } AstForStmt; +typedef struct +{ + FlowCommon flow; + bool index_by_ref : 1; + bool value_by_ref : 1; + CastKind cast; + Decl *index; + Decl *variable; + Expr *enumeration; + Ast *body; + void *continue_block; + void *exit_block; +} AstForeachStmt; + typedef struct { AstId prev_defer; @@ -1066,6 +1080,7 @@ typedef struct _Ast AstNextStmt next_stmt; // 16 AstCatchStmt catch_stmt; // 32 AstForStmt for_stmt; // 32 + AstForeachStmt foreach_stmt; AstCtIfStmt ct_if_stmt; // 24 AstCtIfStmt ct_elif_stmt; // 24 Ast *ct_else_stmt; // 8 @@ -1435,7 +1450,6 @@ static inline Decl *decl_raw(Decl *decl); static inline bool decl_ok(Decl *decl) { return !decl || decl->decl_kind != DECL_POISONED; } static inline bool decl_poison(Decl *decl) { decl->decl_kind = DECL_POISONED; decl->resolve_status = RESOLVE_DONE; return false; } static inline bool decl_is_struct_type(Decl *decl); -AlignSize decl_abi_alignment(Decl *decl); static inline DeclKind decl_from_token(TokenType type); #pragma mark --- Diag functions @@ -1528,6 +1542,7 @@ bool sema_unwrap_var(Context *context, Decl *decl); bool sema_rewrap_var(Context *context, Decl *decl); bool sema_analyse_expr_of_required_type(Context *context, Type *to, Expr *expr, bool may_be_failable); +ArrayIndex sema_get_initializer_const_array_size(Context *context, Expr *initializer, bool *may_be_array, bool *is_const_size); bool sema_analyse_expr(Context *context, Type *to, Expr *expr); bool sema_analyse_decl(Context *context, Decl *decl); bool sema_analyse_ct_assert_stmt(Context *context, Ast *statement); @@ -1648,7 +1663,7 @@ static inline Type *type_reduced_from_expr(Expr *expr) static inline bool type_is_integer(Type *type) { assert(type == type->canonical); - return type->type_kind >= TYPE_I8 && type->type_kind <= TYPE_U64; + return type->type_kind >= TYPE_I8 && type->type_kind <= TYPE_U128; } @@ -1661,19 +1676,19 @@ static inline bool type_is_any_integer(Type *type) static inline bool type_is_integer_signed(Type *type) { assert(type == type->canonical); - return type->type_kind >= TYPE_I8 && type->type_kind <= TYPE_I64; + return type->type_kind >= TYPE_I8 && type->type_kind <= TYPE_I128; } static inline bool type_is_integer_kind(Type *type) { assert(type == type->canonical); - return type->type_kind >= TYPE_BOOL && type->type_kind <= TYPE_U64; + return type->type_kind >= TYPE_BOOL && type->type_kind <= TYPE_U128; } static inline bool type_is_integer_unsigned(Type *type) { assert(type == type->canonical); - return type->type_kind >= TYPE_U8 && type->type_kind <= TYPE_U64; + return type->type_kind >= TYPE_U8 && type->type_kind <= TYPE_U128; } static inline bool type_info_poison(TypeInfo *type) diff --git a/src/compiler/enums.h b/src/compiler/enums.h index 888a9dea1..14c5ca8bf 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -72,6 +72,7 @@ typedef enum AST_EXPR_STMT, AST_TRY_STMT, AST_FOR_STMT, + AST_FOREACH_STMT, AST_IF_STMT, AST_NOP_STMT, AST_RETURN_STMT, @@ -410,6 +411,7 @@ typedef enum TOKEN_EXTERN, TOKEN_FALSE, TOKEN_FOR, + TOKEN_FOREACH, TOKEN_FUNC, TOKEN_GENERIC, TOKEN_IF, diff --git a/src/compiler/llvm_codegen.c b/src/compiler/llvm_codegen.c index 5aff818d0..f211a65bb 100644 --- a/src/compiler/llvm_codegen.c +++ b/src/compiler/llvm_codegen.c @@ -5,10 +5,6 @@ #include "llvm_codegen_internal.h" -static inline void llvm_set_alignment(LLVMValueRef alloca, LLVMTypeRef type, AlignSize alignment) -{ - LLVMSetAlignment(alloca, alignment ?: llvm_abi_alignment(type)); -} static int get_inlining_threshold(void); static void diagnostics_handler(LLVMDiagnosticInfoRef ref, void *context) @@ -292,7 +288,6 @@ static void gencontext_emit_global_variable_definition(GenContext *c, Decl *decl if (!decl->type) return; decl->backend_ref = LLVMAddGlobal(c->module, llvm_get_type(c, decl->type), "tempglobal"); - } static void gencontext_emit_global_variable_init(GenContext *c, Decl *decl) @@ -305,7 +300,7 @@ static void gencontext_emit_global_variable_init(GenContext *c, Decl *decl) bool modified = false; LLVMValueRef init_value; - ByteSize alignment = type_abi_alignment(decl->type); + ByteSize alignment = type_alloca_alignment(decl->type); if (decl->var.init_expr) { @@ -329,7 +324,7 @@ static void gencontext_emit_global_variable_init(GenContext *c, Decl *decl) // TODO fix name LLVMValueRef old = decl->backend_ref; decl->backend_ref = LLVMAddGlobal(c->module, LLVMTypeOf(init_value), decl->name); - LLVMSetAlignment(decl->backend_ref, alignment); + llvm_set_alignment(decl->backend_ref, alignment); if (decl->visibility != VISIBLE_EXTERN) { LLVMSetInitializer(decl->backend_ref, init_value); @@ -407,10 +402,11 @@ void gencontext_print_llvm_ir(GenContext *context) LLVMValueRef llvm_emit_alloca(GenContext *context, LLVMTypeRef type, unsigned alignment, const char *name) { + assert(alignment > 0); LLVMBasicBlockRef current_block = LLVMGetInsertBlock(context->builder); LLVMPositionBuilderBefore(context->builder, context->alloca_point); LLVMValueRef alloca = LLVMBuildAlloca(context->builder, type, name); - llvm_set_alignment(alloca, type, alignment); + llvm_set_alignment(alloca, alignment); LLVMPositionBuilderAtEnd(context->builder, current_block); return alloca; } @@ -420,13 +416,20 @@ LLVMValueRef llvm_emit_alloca_aligned(GenContext *c, Type *type, const char *nam return llvm_emit_alloca(c, llvm_get_type(c, type), type_alloca_alignment(type), name); } -LLVMValueRef llvm_emit_decl_alloca(GenContext *c, Decl *decl) +void llvm_emit_and_set_decl_alloca(GenContext *c, Decl *decl) { LLVMTypeRef type = llvm_get_type(c, decl->type); - return llvm_emit_alloca(c, - type, - decl->alignment ?: type_alloca_alignment(decl->type), - decl->name ?: "anon"); + decl->backend_ref = llvm_emit_alloca(c, type, decl->alignment, decl->name ?: "anon"); +} + +void llvm_emit_local_var_alloca(GenContext *c, Decl *decl) +{ + llvm_emit_and_set_decl_alloca(c, decl); + if (llvm_use_debug(c)) + { + llvm_emit_debug_local_var(c, decl); + } + } /** @@ -697,6 +700,18 @@ void llvm_value_set_address_align(BEValue *value, LLVMValueRef llvm_value, Type value->kind = BE_ADDRESS; value->type = type_flatten(type); } +void llvm_value_set_decl_address(BEValue *value, Decl *decl) +{ + llvm_value_set_address(value, decl_ref(decl), decl->type); + value->alignment = decl->alignment; + + if (decl->decl_kind == DECL_VAR && decl->var.failable) + { + value->kind = BE_ADDRESS_FAILABLE; + value->failable = decl->var.failable_ref; + } +} + void llvm_value_set_address(BEValue *value, LLVMValueRef llvm_value, Type *type) { llvm_value_set_address_align(value, llvm_value, type_flatten(type), type_abi_alignment(type)); @@ -1015,7 +1030,7 @@ void llvm_store_self_aligned(GenContext *context, LLVMValueRef pointer, LLVMValu void llvm_store_aligned(GenContext *context, LLVMValueRef pointer, LLVMValueRef value, AlignSize alignment) { LLVMValueRef ref = LLVMBuildStore(context->builder, value, pointer); - if (alignment) LLVMSetAlignment(ref, alignment); + if (alignment) llvm_set_alignment(ref, alignment); } void llvm_store_aligned_decl(GenContext *context, Decl *decl, LLVMValueRef value) @@ -1037,14 +1052,13 @@ void llvm_emit_memcpy(GenContext *c, LLVMValueRef dest, unsigned dest_align, LLV void llvm_emit_memcpy_to_decl(GenContext *c, Decl *decl, LLVMValueRef source, unsigned source_alignment) { if (source_alignment == 0) source_alignment = type_abi_alignment(decl->type); - llvm_emit_memcpy(c, decl->backend_ref, decl->alignment ?: type_abi_alignment(decl->type), - source, source_alignment, type_size(decl->type)); + llvm_emit_memcpy(c, decl->backend_ref, decl->alignment, source, source_alignment, type_size(decl->type)); } -LLVMValueRef llvm_emit_load_aligned(GenContext *context, LLVMTypeRef type, LLVMValueRef pointer, unsigned alignment, const char *name) +LLVMValueRef llvm_emit_load_aligned(GenContext *context, LLVMTypeRef type, LLVMValueRef pointer, AlignSize alignment, const char *name) { LLVMValueRef value = LLVMBuildLoad2(context->builder, type, pointer, name); - llvm_set_alignment(value, type, alignment); + llvm_set_alignment(value, alignment ?: llvm_abi_alignment(type)); return value; } @@ -1052,3 +1066,15 @@ unsigned llvm_store_size(LLVMTypeRef type) { return LLVMStoreSizeOfType(target_data_layout(), type); } + +void llvm_set_error_exit(GenContext *c, LLVMBasicBlockRef block) +{ + c->catch_block = block; + c->error_var = NULL; +} + +void llvm_set_error_exit_and_value(GenContext *c, LLVMBasicBlockRef block, LLVMValueRef ref) +{ + c->catch_block = block; + c->error_var = ref; +} diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index afaf62df1..6d904566f 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -6,7 +6,6 @@ #include "compiler_internal.h" #include "bigint.h" -static LLVMValueRef llvm_emit_int_comparison(GenContext *c, Type *lhs_type, Type *rhs_type, LLVMValueRef lhs_value, LLVMValueRef rhs_value, BinaryOp binary_op); static void gencontext_emit_unary_expr(GenContext *context, BEValue *value, Expr *expr); static inline void llvm_emit_post_inc_dec(GenContext *c, BEValue *value, Expr *expr, int diff, bool use_mod); static inline void llvm_emit_pre_inc_dec(GenContext *c, BEValue *value, Expr *expr, int diff, bool use_mod); @@ -354,18 +353,6 @@ static void gencontext_emit_scoped_expr(GenContext *context, BEValue *value, Exp static inline void llvm_emit_initialize_reference(GenContext *c, BEValue *value, Expr *expr); -static inline void gencontext_emit_identifier(GenContext *c, BEValue *value, Decl *decl) -{ - llvm_value_set_address(value, decl_ref(decl), decl->type); - value->alignment = decl_abi_alignment(decl); - - if (decl->decl_kind == DECL_VAR && decl->var.failable) - { - value->kind = BE_ADDRESS_FAILABLE; - value->failable = decl->var.failable_ref; - } - -} /** * Here we are converting an array to a subarray. @@ -617,8 +604,8 @@ static inline void llvm_emit_initialize_reference_temporary_const(GenContext *c, LLVMValueRef global_copy = LLVMAddGlobal(c->module, type, ""); // Set a nice alignment - unsigned alignment = LLVMPreferredAlignmentOfGlobal(target_data_layout(), global_copy); - LLVMSetAlignment(global_copy, alignment); + unsigned alignment = type_alloca_alignment(expr->type); + llvm_set_alignment(global_copy, alignment); // Set the value and make it constant LLVMSetInitializer(global_copy, value); @@ -829,57 +816,6 @@ static void llvm_emit_initialize_designated_const_range(GenContext *c, BEValue * } } -static void llvm_emit_initialize_designated_range(GenContext *c, BEValue *ref, uint64_t offset, DesignatorElement** current, DesignatorElement **last, Expr *expr, BEValue *emitted_value) -{ - DesignatorElement *curr = current[0]; - llvm_value_addr(c, ref); - assert(curr->kind == DESIGNATOR_RANGE); - - BEValue emitted_local; - if (!emitted_value) - { - llvm_emit_expr(c, &emitted_local, expr); - emitted_value = &emitted_local; - } - LLVMBasicBlockRef start_block = llvm_basic_block_new(c, "set_start"); - LLVMBasicBlockRef store_block = llvm_basic_block_new(c, "set_store"); - LLVMBasicBlockRef after_block = llvm_basic_block_new(c, "set_done"); - - BEValue start; - BEValue end; - llvm_emit_expr(c, &start, curr->index_expr); - LLVMValueRef max_index = llvm_const_int(c, start.type, ref->type->canonical->array.len); - llvm_emit_array_bounds_check(c, &start, max_index); - llvm_emit_expr(c, &end, curr->index_end_expr); - llvm_emit_array_bounds_check(c, &end, max_index); - LLVMTypeRef ref_type = llvm_get_type(c, ref->type); - LLVMValueRef end_value = llvm_value_rvalue_store(c, &end); - LLVMTypeRef index_type = llvm_get_type(c, start.type); - LLVMValueRef index_ptr = llvm_emit_alloca(c, index_type, start.alignment, "rangeidx"); - LLVMValueRef add_one = llvm_const_int(c, start.type, 1); - // Assign the index_ptr to the start value. - llvm_store_bevalue_dest_aligned(c, index_ptr, &start); - LLVMValueRef indices[2] = { - llvm_get_zero(c, start.type), - NULL - }; - llvm_emit_br(c, start_block); - llvm_emit_block(c, start_block); - LLVMValueRef index_val = llvm_emit_load_aligned(c, index_type, index_ptr, start.alignment, ""); - BEValue comp; - llvm_value_set_bool(&comp, llvm_emit_int_comparison(c, start.type, end.type, index_val, end_value, BINARYOP_LE)); - llvm_emit_cond_br(c, &comp, store_block, after_block); - llvm_emit_block(c, store_block); - indices[1] = index_val; - LLVMValueRef ptr_next = LLVMBuildInBoundsGEP2(c->builder, ref_type, ref->value, indices, 2, ""); - BEValue new_ref; - llvm_value_set_address(&new_ref, ptr_next, type_get_indexed_type(ref->type)); - llvm_emit_initialize_designated(c, &new_ref, offset, current + 1, last, expr, emitted_value); - LLVMValueRef new_index = LLVMBuildAdd(c->builder, index_val, add_one, ""); - llvm_store_aligned(c, index_ptr, new_index, start.alignment); - llvm_emit_br(c, start_block); - llvm_emit_block(c, after_block); -} static void llvm_emit_initialize_designated(GenContext *c, BEValue *ref, uint64_t offset, DesignatorElement** current, DesignatorElement **last, Expr *expr, BEValue *emitted_value) { @@ -909,7 +845,7 @@ static void llvm_emit_initialize_designated(GenContext *c, BEValue *ref, uint64_ Decl *decl = ref->type->canonical->decl->strukt.members[curr->index]; offset += decl->offset; Type *type = decl->type->canonical; - unsigned decl_alignment = decl_abi_alignment(decl); + unsigned decl_alignment = decl->alignment; if (ref->type->type_kind == TYPE_UNION) { llvm_value_set_address_align(&value, llvm_emit_bitcast(c, ref->value, type_get_ptr(type)), type, type_min_alignment(offset, decl_alignment)); @@ -925,34 +861,17 @@ static void llvm_emit_initialize_designated(GenContext *c, BEValue *ref, uint64_ { Type *type = ref->type->array.base; LLVMValueRef indices[2]; - if (curr->index_expr->expr_kind == EXPR_CONST) - { - offset += curr->index * type_size(type); - Type *index_type = curr->index > UINT32_MAX ? type_uint : type_ulong; - indices[0] = llvm_const_int(c, index_type, 0); - indices[1] = llvm_const_int(c, index_type, curr->index); - } - else - { - BEValue res; - llvm_emit_expr(c, &res, curr->index_expr); - indices[0] = llvm_const_int(c, curr->index_expr->type, 0); - indices[1] = llvm_value_rvalue_store(c, &res); - } + offset += curr->index * type_size(type); + Type *index_type = curr->index > UINT32_MAX ? type_uint : type_ulong; + indices[0] = llvm_const_int(c, index_type, 0); + indices[1] = llvm_const_int(c, index_type, curr->index); LLVMValueRef ptr = LLVMBuildInBoundsGEP2(c->builder, llvm_get_type(c, ref->type), ref->value, indices, 2, ""); llvm_value_set_address_align(&value, ptr, type, type_min_alignment(offset, type_abi_alignment(type))); llvm_emit_initialize_designated(c, &value, offset, current + 1, last, expr, emitted_value); break; } case DESIGNATOR_RANGE: - if (curr->index_expr->expr_kind == EXPR_CONST && curr->index_end_expr->expr_kind == EXPR_CONST) - { - llvm_emit_initialize_designated_const_range(c, ref, offset, current, last, expr, emitted_value); - } - else - { - llvm_emit_initialize_designated_range(c, ref, offset, current, last, expr, emitted_value); - } + llvm_emit_initialize_designated_const_range(c, ref, offset, current, last, expr, emitted_value); break; default: UNREACHABLE @@ -1206,23 +1125,20 @@ static void gencontext_emit_unary_expr(GenContext *c, BEValue *value, Expr *expr UNREACHABLE } -static void gencontext_emit_len(GenContext *c, BEValue *be_value, Expr *expr) +void llvm_emit_len_for_expr(GenContext *c, BEValue *be_value, BEValue *expr_to_len) { - Expr *inner = expr->len_expr.inner; - llvm_emit_expr(c, be_value, inner); - llvm_value_addr(c, be_value); - Type *type = inner->type; - switch (type->canonical->type_kind) + llvm_value_addr(c, expr_to_len); + switch (expr_to_len->type->type_kind) { case TYPE_SUBARRAY: { - LLVMTypeRef subarray_type = llvm_get_type(c, type); - LLVMValueRef len_addr = LLVMBuildStructGEP2(c->builder, subarray_type, be_value->value, 1, "len"); + LLVMTypeRef subarray_type = llvm_get_type(c, expr_to_len->type); + LLVMValueRef len_addr = LLVMBuildStructGEP2(c->builder, subarray_type, expr_to_len->value, 1, "len"); llvm_value_set_address(be_value, len_addr, type_usize); break; } case TYPE_ARRAY: - llvm_value_set(be_value, llvm_const_int(c, type_usize, type->array.len), type_usize); + llvm_value_set(be_value, llvm_const_int(c, type_usize, expr_to_len->type->array.len), type_usize); break; case TYPE_VARARRAY: case TYPE_STRING: @@ -1231,6 +1147,11 @@ static void gencontext_emit_len(GenContext *c, BEValue *be_value, Expr *expr) UNREACHABLE } } +static void llvm_emit_len(GenContext *c, BEValue *be_value, Expr *expr) +{ + llvm_emit_expr(c, be_value, expr->len_expr.inner); + llvm_emit_len_for_expr(c, be_value, be_value); +} static void gencontext_emit_trap_negative(GenContext *context, Expr *expr, LLVMValueRef value, const char *error) { @@ -1561,7 +1482,7 @@ static void gencontext_emit_logical_and_or(GenContext *c, BEValue *be_value, Exp -static LLVMValueRef llvm_emit_int_comparison(GenContext *c, Type *lhs_type, Type *rhs_type, LLVMValueRef lhs_value, LLVMValueRef rhs_value, BinaryOp binary_op) +LLVMValueRef llvm_emit_int_comparison(GenContext *c, Type *lhs_type, Type *rhs_type, LLVMValueRef lhs_value, LLVMValueRef rhs_value, BinaryOp binary_op) { bool lhs_signed = type_is_signed(lhs_type); bool rhs_signed = type_is_signed(rhs_type); @@ -2122,8 +2043,7 @@ static void gencontext_emit_binary_expr(GenContext *context, BEValue *be_value, { failable_ref = decl_failable_ref(expr->binary_expr.left->identifier_expr.decl); } - LLVMValueRef result = llvm_emit_assign_expr(context, be_value->value, expr->binary_expr.right, failable_ref); - llvm_value_set(be_value, result, expr->type); + *be_value = llvm_emit_assign_expr(context, be_value, expr->binary_expr.right, failable_ref); return; } @@ -2594,7 +2514,7 @@ void llvm_emit_call_expr(GenContext *context, BEValue *be_value, Expr *expr) vec_add(values, return_param); if (ret_info->indirect.realignment) { - LLVMSetAlignment(return_param, ret_info->indirect.realignment); + llvm_set_alignment(return_param, ret_info->indirect.realignment); } break; case ABI_ARG_EXPAND: @@ -2834,7 +2754,7 @@ static inline void gencontext_emit_macro_block(GenContext *context, BEValue *be_ case VARDECL_PARAM: break; } - decl->backend_ref = llvm_emit_decl_alloca(context, decl); + llvm_emit_and_set_decl_alloca(context, decl); BEValue value; llvm_emit_expr(context, &value, expr->macro_block.args[i]); printf("TODO: unoptimized use of BEValue\n"); @@ -2871,8 +2791,9 @@ LLVMValueRef llvm_emit_call_intrinsic(GenContext *context, unsigned intrinsic_id return LLVMBuildCall2(context->builder, type, decl, values, arg_count, ""); } -LLVMValueRef llvm_emit_assign_expr(GenContext *c, LLVMValueRef ref, Expr *expr, LLVMValueRef failable) +BEValue llvm_emit_assign_expr(GenContext *c, BEValue *ref, Expr *expr, LLVMValueRef failable) { + llvm_value_addr(c, ref); LLVMBasicBlockRef assign_block = NULL; PUSH_ERROR(); @@ -2895,16 +2816,13 @@ LLVMValueRef llvm_emit_assign_expr(GenContext *c, LLVMValueRef ref, Expr *expr, if (expr->expr_kind == EXPR_COMPOUND_LITERAL) expr = expr->expr_compound_literal.initializer; if (expr->expr_kind == EXPR_INITIALIZER_LIST) { - BEValue val; - printf("TODO: Fix alignment and return!\n"); - llvm_value_set_address(&val, ref, expr->type); - llvm_emit_initialize_reference(c, &val, expr); + llvm_emit_initialize_reference(c, ref, expr); + value = *ref; } else { llvm_emit_expr(c, &value, expr); - printf("TODO: // Optimize store \n"); - llvm_store_bevalue_aligned(c, ref, &value, 0); + llvm_store_bevalue(c, ref, &value); } if (failable) @@ -2919,7 +2837,7 @@ LLVMValueRef llvm_emit_assign_expr(GenContext *c, LLVMValueRef ref, Expr *expr, llvm_emit_block(c, assign_block); } - return value.value; + return value; } @@ -2978,7 +2896,7 @@ void llvm_emit_expr(GenContext *c, BEValue *value, Expr *expr) gencontext_emit_slice(c, value, expr); return; case EXPR_LEN: - gencontext_emit_len(c, value, expr); + llvm_emit_len(c, value, expr); return; case EXPR_FAILABLE: gencontext_emit_failable(c, value, expr); @@ -3030,7 +2948,7 @@ void llvm_emit_expr(GenContext *c, BEValue *value, Expr *expr) UNREACHABLE case EXPR_IDENTIFIER: case EXPR_CONST_IDENTIFIER: - gencontext_emit_identifier(c, value, expr->identifier_expr.decl); + llvm_value_set_decl_address(value, expr->identifier_expr.decl); return; case EXPR_SUBSCRIPT: gencontext_emit_subscript(c, value, expr); diff --git a/src/compiler/llvm_codegen_function.c b/src/compiler/llvm_codegen_function.c index a11b2f19d..ef1e3bc61 100644 --- a/src/compiler/llvm_codegen_function.c +++ b/src/compiler/llvm_codegen_function.c @@ -156,7 +156,7 @@ static inline void llvm_process_parameter_value(GenContext *c, Decl *decl, unsig LLVMTypeRef lo = llvm_abi_type(c, info->direct_pair.lo); LLVMTypeRef hi = llvm_abi_type(c, info->direct_pair.hi); LLVMTypeRef struct_type = llvm_get_twostruct(c, lo, hi); - AlignSize decl_alignment = decl_abi_alignment(decl); + AlignSize decl_alignment = decl->alignment; // Cast to { lo, hi } LLVMValueRef cast = LLVMBuildBitCast(c->builder, decl->backend_ref, LLVMPointerType(struct_type, 0), "pair"); // Point to the lo value. @@ -181,7 +181,6 @@ static inline void llvm_process_parameter_value(GenContext *c, Decl *decl, unsig } // Cast to the coerce type. LLVMValueRef cast = LLVMBuildBitCast(c->builder, decl->backend_ref, LLVMPointerType(coerce_type, 0), "coerce"); - AlignSize decl_alignment = decl_abi_alignment(decl); // If we're not flattening, we simply do a store. if (!abi_info_should_flatten(info)) @@ -200,7 +199,7 @@ static inline void llvm_process_parameter_value(GenContext *c, Decl *decl, unsig LLVMValueRef element_ptr = LLVMBuildStructGEP2(c->builder, coerce_type, cast, idx, ""); LLVMValueRef value = llvm_get_next_param(c, index); - llvm_store_aligned(c, element_ptr, value, MIN(llvm_abi_alignment(element_type), decl_alignment)); + llvm_store_aligned(c, element_ptr, value, MIN(llvm_abi_alignment(element_type), decl->alignment)); } return; } @@ -220,7 +219,7 @@ static inline void llvm_emit_parameter(GenContext *context, Decl *decl, unsigned assert(decl->decl_kind == DECL_VAR && decl->var.kind == VARDECL_PARAM); // Allocate room on stack, but do not copy. - decl->backend_ref = llvm_emit_decl_alloca(context, decl); + llvm_emit_and_set_decl_alloca(context, decl); llvm_process_parameter_value(context, decl, index); if (llvm_use_debug(context)) { @@ -571,7 +570,7 @@ void llvm_emit_function_decl(GenContext *c, Decl *decl) } if (decl->alignment) { - LLVMSetAlignment(function, decl->alignment); + llvm_set_alignment(function, decl->alignment); } if (decl->section) { diff --git a/src/compiler/llvm_codegen_internal.h b/src/compiler/llvm_codegen_internal.h index dbe37e10d..35f053caa 100644 --- a/src/compiler/llvm_codegen_internal.h +++ b/src/compiler/llvm_codegen_internal.h @@ -187,6 +187,7 @@ void llvm_value_set_bool(BEValue *value, LLVMValueRef llvm_value); void llvm_value_set(BEValue *value, LLVMValueRef llvm_value, Type *type); void llvm_value_set_address_align(BEValue *value, LLVMValueRef llvm_value, Type *type, unsigned alignment); void llvm_value_set_address(BEValue *value, LLVMValueRef llvm_value, Type *type); +void llvm_value_set_decl_address(BEValue *value, Decl *decl); void llvm_value_fold_failable(GenContext *c, BEValue *value); void llvm_value_struct_gep(GenContext *c, BEValue *element, BEValue *struct_pointer, unsigned index); @@ -205,13 +206,13 @@ LLVMValueRef llvm_emit_const_padding(GenContext *c, ByteSize size); LLVMTypeRef llvm_const_padding_type(GenContext *c, ByteSize size); LLVMValueRef llvm_emit_alloca(GenContext *context, LLVMTypeRef type, unsigned alignment, const char *name); LLVMValueRef llvm_emit_alloca_aligned(GenContext *c, Type *type, const char *name); -LLVMValueRef llvm_emit_assign_expr(GenContext *context, LLVMValueRef ref, Expr *expr, LLVMValueRef failable); +BEValue llvm_emit_assign_expr(GenContext *context, BEValue *ref, Expr *expr, LLVMValueRef failable); static inline LLVMValueRef llvm_emit_bitcast(GenContext *context, LLVMValueRef value, Type *type); void llvm_emit_block(GenContext *c, LLVMBasicBlockRef next_block); void llvm_emit_br(GenContext *c, LLVMBasicBlockRef next_block); void llvm_emit_compound_stmt(GenContext *context, Ast *ast); +void llvm_emit_and_set_decl_alloca(GenContext *c, Decl *decl); LLVMValueRef llvm_emit_convert_value_from_coerced(GenContext *context, LLVMTypeRef coerced, LLVMValueRef value, Type *original_type); -LLVMValueRef llvm_emit_decl_alloca(GenContext *c, Decl *decl); void llvm_emit_function_body(GenContext *context, Decl *decl); void llvm_emit_function_decl(GenContext *c, Decl *decl); LLVMValueRef llvm_emit_call_intrinsic(GenContext *c, unsigned intrinsic_id, LLVMTypeRef *types, unsigned type_count, LLVMValueRef *values, unsigned arg_count); @@ -224,9 +225,13 @@ void llvm_emit_debug_local_var(GenContext *c, Decl *var); void llvm_emit_debug_global_var(GenContext *c, Decl *global); void llvm_emit_defer(GenContext *c, AstId defer_start, AstId defer_end); void llvm_emit_extern_decl(GenContext *context, Decl *decl); + LLVMValueRef llvm_emit_const_aggregate(GenContext *c, Expr *expr, bool *modified); +LLVMValueRef llvm_emit_int_comparison(GenContext *c, Type *lhs_type, Type *rhs_type, LLVMValueRef lhs_value, LLVMValueRef rhs_value, BinaryOp binary_op); LLVMValueRef llvm_emit_is_no_error(GenContext *c, LLVMValueRef error); -LLVMValueRef llvm_emit_load_aligned(GenContext *c, LLVMTypeRef type, LLVMValueRef pointer, unsigned alignment, const char *name); +void llvm_emit_len_for_expr(GenContext *c, BEValue *be_value, BEValue *expr_to_len); +LLVMValueRef llvm_emit_load_aligned(GenContext *context, LLVMTypeRef type, LLVMValueRef pointer, AlignSize alignment, const char *name); +void llvm_emit_local_var_alloca(GenContext *c, Decl *decl); void llvm_emit_expr(GenContext *c, BEValue *value, Expr *expr); LLVMValueRef llvm_emit_memclear_size_align(GenContext *c, LLVMValueRef ref, uint64_t size, unsigned align, bool bitcast); LLVMValueRef llvm_emit_memclear(GenContext *c, BEValue *ref); @@ -392,6 +397,13 @@ static inline LLVMValueRef llvm_const_int(GenContext *c, Type *type, uint64_t va return LLVMConstInt(llvm_get_type(c, type), val, type_is_integer_signed(type)); } +static inline void llvm_set_alignment(LLVMValueRef alloca, AlignSize alignment) +{ + assert(alignment > 0); + LLVMSetAlignment(alloca, alignment); +} +void llvm_set_error_exit(GenContext *c, LLVMBasicBlockRef block); +void llvm_set_error_exit_and_value(GenContext *c, LLVMBasicBlockRef block, LLVMValueRef value); #define EMIT_LOC(c, x) do { if (c->debug.builder) llvm_emit_debug_location(c, x->span); } while (0); diff --git a/src/compiler/llvm_codegen_stmt.c b/src/compiler/llvm_codegen_stmt.c index d6fb82628..cfdcd4596 100644 --- a/src/compiler/llvm_codegen_stmt.c +++ b/src/compiler/llvm_codegen_stmt.c @@ -39,29 +39,26 @@ void gencontext_emit_ct_compound_stmt(GenContext *context, Ast *ast) } } - static LLVMValueRef llvm_emit_decl(GenContext *c, Ast *ast) { Decl *decl = ast->declare_stmt; LLVMTypeRef alloc_type = llvm_get_type(c, type_lowering(decl->type)); - decl->backend_ref = llvm_emit_decl_alloca(c, decl); + llvm_emit_local_var_alloca(c, decl); if (decl->var.failable) { decl->var.failable_ref = llvm_emit_alloca_aligned(c, type_error, decl->name); LLVMBuildStore(c->builder, LLVMConstNull(llvm_get_type(c, type_error)), decl->var.failable_ref); } - if (llvm_use_debug(c)) - { - llvm_emit_debug_local_var(c, decl); - } Expr *init = decl->var.init_expr; if (init) { // If we don't have undef, then make an assign. if (init->expr_kind != EXPR_UNDEF) { - llvm_emit_assign_expr(c, decl->backend_ref, decl->var.init_expr, decl->var.failable_ref); + BEValue value; + llvm_value_set_decl_address(&value, decl); + llvm_emit_assign_expr(c, &value, decl->var.init_expr, decl->var.failable_ref); } // TODO trap on undef in debug mode. } @@ -75,7 +72,7 @@ static LLVMValueRef llvm_emit_decl(GenContext *c, Ast *ast) } else { - llvm_emit_memclear_size_align(c, decl->backend_ref, type_size(decl->type), decl_abi_alignment(decl), true); + llvm_emit_memclear_size_align(c, decl->backend_ref, type_size(decl->type), decl->alignment, true); } } return decl->backend_ref; @@ -333,7 +330,6 @@ void gencontext_emit_for_stmt(GenContext *c, Ast *ast) ast->for_stmt.continue_block = continue_block; ast->for_stmt.exit_block = exit_block; - LLVMValueRef value = NULL; LLVMBasicBlockRef loopback_block = cond_block; if (cond_block) @@ -404,6 +400,137 @@ void gencontext_emit_for_stmt(GenContext *c, Ast *ast) llvm_emit_block(c, exit_block); } +void llvm_emit_foreach_stmt(GenContext *c, Ast *ast) +{ + // First we generate an exit. + LLVMBasicBlockRef exit_block = llvm_basic_block_new(c, "foreach.exit"); + + PUSH_ERROR(); + + llvm_set_error_exit(c, exit_block); + + // First evaluate the enumerated collection + BEValue enum_value; + llvm_emit_expr(c, &enum_value, ast->foreach_stmt.enumeration); + + // Get the length + BEValue len; + llvm_emit_len_for_expr(c, &len, &enum_value); + LLVMValueRef len_value = llvm_value_rvalue_store(c, &len); + + // We pop the error here. + POP_ERROR(); + + // Create the index and optionally the index var + LLVMTypeRef real_index_type = llvm_get_type(c, type_isize); + BEValue index_var = {}; + LLVMTypeRef index_type = ast->foreach_stmt.index ? llvm_get_type(c, ast->foreach_stmt.index->type) : NULL; + BEValue index = {}; + + bool extend_bits = false; + // In case we have an index, set it up. + if (ast->foreach_stmt.index) + { + llvm_emit_local_var_alloca(c, ast->foreach_stmt.index); + llvm_value_set_address(&index_var, ast->foreach_stmt.index->backend_ref, ast->foreach_stmt.index->type); + // And set it to zero. + llvm_store_bevalue_raw(c, &index_var, llvm_get_zero(c, index_var.type)); + index = index_var; + extend_bits = type_size(index_var.type) > type_size(type_isize); + } + + // If types don't match (either index has a different type or it doesn't exist) + // then create the address for the internal index and set it to zero. + if (index_type != real_index_type) + { + llvm_value_set_address(&index, llvm_emit_alloca(c, real_index_type, type_abi_alignment(type_isize), "idx"), type_isize); + llvm_store_bevalue_raw(c, &index, llvm_get_zero(c, index.type)); + } + + Type *actual_type = type_get_indexed_type(ast->foreach_stmt.enumeration->type); + LLVMTypeRef actual_type_llvm = llvm_get_type(c, actual_type); + + llvm_emit_local_var_alloca(c, ast->foreach_stmt.variable); + Type *var_type = type_flatten(ast->foreach_stmt.variable->type); + LLVMTypeRef var_type_llvm = llvm_get_type(c, var_type); + BEValue var; + llvm_value_set_address(&var, ast->foreach_stmt.variable->backend_ref, var_type); + + LLVMBasicBlockRef inc_block = llvm_basic_block_new(c, "foreach.inc"); + LLVMBasicBlockRef body_block = llvm_basic_block_new(c, "foreach.body"); + LLVMBasicBlockRef cond_block = llvm_basic_block_new(c, "foreach.cond"); + + ast->foreach_stmt.continue_block = inc_block; + ast->foreach_stmt.exit_block = exit_block; + + // Emit cond + llvm_emit_br(c, cond_block); + llvm_emit_block(c, cond_block); + LLVMValueRef index_value = llvm_value_rvalue_store(c, &index); + LLVMValueRef may_loop = llvm_emit_int_comparison(c, type_isize, type_isize, index_value, len_value, BINARYOP_LT); + BEValue b; + llvm_value_set_bool(&b, may_loop); + llvm_emit_cond_br(c, &b, body_block, exit_block); + + llvm_emit_block(c, body_block); + + // In the case where we have an index that is smaller, we need to do a cast. + if (index_var.value && index.value != index_var.value) + { + LLVMValueRef stored_value; + // Note that the case of index_var being usize and index being isize + // does not occur here, since they are considered the same LLVM type. + if (extend_bits) + { + // Note that we zero extend. We never deal in negative indices. + stored_value = LLVMBuildZExt(c->builder, index_value, index_type, ""); + } + else + { + stored_value = LLVMBuildTrunc(c->builder, index_value, index_type, ""); + } + llvm_store_bevalue_raw(c, &index_var, stored_value); + } + + assert(llvm_value_is_addr(&enum_value)); + + LLVMValueRef ref_to_element = LLVMBuildInBoundsGEP2(c->builder, actual_type_llvm, enum_value.value, &index_value, 1, ""); + BEValue result; + if (ast->foreach_stmt.value_by_ref) + { + llvm_value_set(&result, ref_to_element, type_get_ptr(actual_type)); + LLVMTypeRef pointer_llvm = llvm_get_ptr_type(c, actual_type); + if (pointer_llvm != var_type_llvm) + { + llvm_emit_cast(c, ast->foreach_stmt.cast, &result, var_type, result.type); + } + } + else + { + llvm_value_set_address(&result, ref_to_element, actual_type); + if (var_type_llvm != actual_type_llvm) + { + llvm_emit_cast(c, ast->foreach_stmt.cast, &result, var_type, actual_type); + } + } + llvm_store_bevalue(c, &var, &result); + + llvm_emit_stmt(c, ast->foreach_stmt.body); + + llvm_emit_br(c, inc_block); + + llvm_emit_block(c, inc_block); + index_value = llvm_value_rvalue_store(c, &index); + index_value = LLVMBuildAdd(c->builder, index_value, llvm_const_int(c, type_isize, 1), ""); + llvm_store_bevalue_raw(c, &index, index_value); + + // Loop back. + llvm_emit_br(c, cond_block); + + // And insert exit block + llvm_emit_block(c, exit_block); +} + void gencontext_emit_while_stmt(GenContext *context, Ast *ast) { // First, emit all inits. @@ -736,6 +863,9 @@ void gencontext_emit_break(GenContext *context, Ast *ast) case AST_WHILE_STMT: jump = jump_target->while_stmt.break_block; break; + case AST_FOREACH_STMT: + jump = jump_target->foreach_stmt.exit_block; + break; case AST_FOR_STMT: jump = jump_target->for_stmt.exit_block; break; @@ -764,13 +894,15 @@ void gencontext_emit_continue(GenContext *context, Ast *ast) case AST_IF_STMT: case AST_SWITCH_STMT: UNREACHABLE - break; case AST_WHILE_STMT: jump = jump_target->while_stmt.continue_block; break; case AST_DO_STMT: jump = jump_target->do_stmt.continue_block; break; + case AST_FOREACH_STMT: + jump = jump_target->foreach_stmt.continue_block; + break; case AST_FOR_STMT: jump = jump_target->for_stmt.continue_block; break; @@ -1082,6 +1214,9 @@ void llvm_emit_stmt(GenContext *c, Ast *ast) case AST_FOR_STMT: gencontext_emit_for_stmt(c, ast); break; + case AST_FOREACH_STMT: + llvm_emit_foreach_stmt(c, ast); + break; case AST_WHILE_STMT: gencontext_emit_while_stmt(c, ast); break; diff --git a/src/compiler/parse_expr.c b/src/compiler/parse_expr.c index b3823aa2b..e7f0aece4 100644 --- a/src/compiler/parse_expr.c +++ b/src/compiler/parse_expr.c @@ -354,6 +354,7 @@ Expr *parse_initializer_list(Context *context) if (!parse_param_list(context, &initializer_list->initializer_expr.initializer_expr, false, TOKEN_RBRACE)) return poisoned_expr; CONSUME_OR(TOKEN_RBRACE, poisoned_expr); } + RANGE_EXTEND_PREV(initializer_list); return initializer_list; } diff --git a/src/compiler/parse_stmt.c b/src/compiler/parse_stmt.c index 69e7dd61b..db5f0f4ec 100644 --- a/src/compiler/parse_stmt.c +++ b/src/compiler/parse_stmt.c @@ -477,6 +477,77 @@ static inline Ast* parse_for_stmt(Context *context) return ast; } +static inline bool parse_foreach_var(Context *context, Ast *foreach) +{ + TypeInfo *type = NULL; + + bool failable = false; + + // If we don't get foreach (foo ... or foreach (*foo ... then a type is expected. + if (!TOKEN_IS(TOKEN_IDENT) && !TOKEN_IS(TOKEN_AMP)) + { + type = TRY_TYPE_OR(parse_type(context), false); + failable = try_consume(context, TOKEN_BANG); + // Add the failable to the type for nicer error reporting. + RANGE_EXTEND_PREV(type); + } + if (try_consume(context, TOKEN_AMP)) + { + foreach->foreach_stmt.value_by_ref = true; + } + if (!try_consume(context, TOKEN_IDENT)) + { + if (type) + { + SEMA_TOKEN_ERROR(context->tok, "Expected an identifier after the type."); + return false; + } + SEMA_TOKEN_ERROR(context->tok, "Expected an identifier or type."); + return false; + } + Decl *var = decl_new_var(context->prev_tok, type, VARDECL_LOCAL, VISIBLE_LOCAL); + var->var.failable = failable; + foreach->foreach_stmt.variable = var; + return true; +} +/** + * foreach_statement + * : FOREACH (CONST_IDENT ':')? '(' type? '*'? IDENT (',' type? '*'? IDENT) ':' expression ')' statement + * ; + */ +static inline Ast* parse_foreach_stmt(Context *context) +{ + Ast *ast = AST_NEW_TOKEN(AST_FOREACH_STMT, context->tok); + advance_and_verify(context, TOKEN_FOREACH); + ast->foreach_stmt.flow.label = TRY_DECL_OR(parse_optional_label(context, ast), poisoned_ast); + CONSUME_OR(TOKEN_LPAREN, poisoned_ast); + + // Parse the first variable. + if (!parse_foreach_var(context, ast)) return poisoned_ast; + + // Do we have a second variable? + if (try_consume(context, TOKEN_COMMA)) + { + // Copy the first variable to "index" + ast->foreach_stmt.index = ast->foreach_stmt.variable; + ast->foreach_stmt.index_by_ref = ast->foreach_stmt.value_by_ref; + ast->foreach_stmt.value_by_ref = false; + + // Parse the second variable + if (!parse_foreach_var(context, ast)) return poisoned_ast; + } + + CONSUME_OR(TOKEN_COLON, poisoned_ast); + + ast->foreach_stmt.enumeration = TRY_EXPR_OR(parse_initializer(context), poisoned_ast); + + CONSUME_OR(TOKEN_RPAREN, poisoned_ast); + + extend_ast_with_prev_token(context, ast); + ast->foreach_stmt.body = TRY_AST(parse_stmt(context)); + return ast; +} + /** * continue_stmt * : CONTINUE @@ -948,6 +1019,8 @@ Ast *parse_stmt(Context *context) return parse_do_stmt(context); case TOKEN_FOR: return parse_for_stmt(context); + case TOKEN_FOREACH: + return parse_foreach_stmt(context); case TOKEN_CATCH: return parse_catch_stmt(context); case TOKEN_CONTINUE: diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 1d5e62994..41489d8b4 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -348,6 +348,7 @@ static inline bool sema_analyse_function_param(Context *context, Decl *param, bo } *has_default = true; } + param->alignment = type_abi_alignment(param->type); return true; } @@ -853,6 +854,7 @@ static inline bool sema_analyse_global(Context *context, Decl *decl) if (decl->type) { decl->resolve_status = RESOLVE_DONE; + if (!decl->alignment) decl->alignment = type_alloca_alignment(decl->type); } // Check the initializer. diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index e75b419c2..ca1ac1986 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -2070,22 +2070,23 @@ static int64_t sema_analyse_designator_index(Context *context, Expr *index) { return -1; } - if (index->expr_kind == EXPR_CONST) + if (index->expr_kind != EXPR_CONST) { - if (!bigint_fits_in_bits(&index->const_expr.i, 64, true)) - { - SEMA_ERROR(index, "The value of the index does not fit in a long."); - return -1; - } - int64_t index_val = bigint_as_signed(&index->const_expr.i); - if (index_val < 0) - { - SEMA_ERROR(index, "Negative index values is not allowed."); - return -1; - } - return index_val; + SEMA_ERROR(index, "The index must be a constant value."); + return -1; } - return 0; + if (!bigint_fits_in_bits(&index->const_expr.i, 64, true)) + { + SEMA_ERROR(index, "The value of the index does not fit in a long."); + return -1; + } + int64_t index_val = bigint_as_signed(&index->const_expr.i); + if (index_val < 0) + { + SEMA_ERROR(index, "Negative index values is not allowed."); + return -1; + } + return index_val; } static Type *sema_find_type_of_element(Context *context, Type *type, DesignatorElement **elements, unsigned *curr_index, bool *is_constant, bool *did_report_error) @@ -2112,7 +2113,6 @@ static Type *sema_find_type_of_element(Context *context, Type *type, DesignatorE return NULL; } element->index = index; - if (element->index_expr->expr_kind != EXPR_CONST) *is_constant = false; if (element->kind == DESIGNATOR_RANGE) { @@ -2122,9 +2122,7 @@ static Type *sema_find_type_of_element(Context *context, Type *type, DesignatorE *did_report_error = true; return NULL; } - if (element->index_end_expr->expr_kind == EXPR_CONST - && element->index_expr->expr_kind == EXPR_CONST - && index > end_index) + if (index > end_index) { SEMA_ERROR(element->index_end_expr, "End index must be greater than start index."); *did_report_error = true; @@ -2136,7 +2134,6 @@ static Type *sema_find_type_of_element(Context *context, Type *type, DesignatorE SEMA_ERROR(element->index_expr, "The index may must be less than the array length (which was %llu).", (unsigned long long)len); return NULL; } - element->index_end = end_index; } return type_lowered->array.base; @@ -4921,6 +4918,13 @@ static Ast *ast_copy_from_macro(Context *context, Ast *source) MACRO_COPY_AST(ast->for_stmt.body); MACRO_COPY_EXPR(ast->for_stmt.init); return ast; + case AST_FOREACH_STMT: + copy_flow(context, ast); + MACRO_COPY_DECL(ast->foreach_stmt.index); + MACRO_COPY_DECL(ast->foreach_stmt.variable); + MACRO_COPY_EXPR(ast->foreach_stmt.enumeration); + MACRO_COPY_AST(ast->for_stmt.body); + return ast; case AST_IF_STMT: copy_flow(context, ast); MACRO_COPY_EXPR(ast->if_stmt.cond); @@ -5208,7 +5212,7 @@ bool sema_analyse_expr_of_required_type(Context *context, Type *to, Expr *expr, if (expr->failable && !may_be_failable) { if (!to) to = expr->type; - SEMA_ERROR(expr, "'%s!' cannot be implicitly cast to '%s'.", type_to_error_string(expr->type), type_to_error_string(to)); + SEMA_ERROR(expr, "'%s!' cannot be converted into '%s'.", type_to_error_string(expr->type), type_to_error_string(to)); return false; } return cast_implicit(context, expr, to); @@ -5289,6 +5293,62 @@ bool sema_analyse_expr_value(Context *context, Type *to, Expr *expr) } } +ArrayIndex sema_get_initializer_const_array_size(Context *context, Expr *initializer, bool *may_be_array, bool *is_const_size) +{ + assert(initializer->expr_kind == EXPR_INITIALIZER_LIST); + Expr **initializers = initializer->initializer_expr.initializer_expr; + *may_be_array = true; + *is_const_size = true; + unsigned element_count = vec_size(initializers); + // If it's empty or the first element is not a designator, we assume an array list + // with that many elements. + if (!element_count || initializers[0]->expr_kind != EXPR_DESIGNATOR) return element_count; + ArrayIndex size = 0; + // Otherwise we assume everything's a designator. + VECEACH(initializers, i) + { + Expr *sub_initializer = initializers[i]; + if (sub_initializer->expr_kind != EXPR_DESIGNATOR) + { + // Simply messed up: a mix of designators and regular ones. + return -1; + } + DesignatorElement *element = sub_initializer->designator_expr.path[0]; + switch (element->kind) + { + case DESIGNATOR_FIELD: + // Struct, abandon! + *may_be_array = false; + return -1; + case DESIGNATOR_ARRAY: + { + ArrayIndex index = sema_analyse_designator_index(context, element->index_expr); + if (index < 0 || element->index_expr->expr_kind != EXPR_CONST) + { + *is_const_size = false; + return -1; + } + size = MAX(size, index + 1); + break; + } + case DESIGNATOR_RANGE: + { + ArrayIndex index = sema_analyse_designator_index(context, element->index_end_expr); + if (index < 0 || element->index_end_expr->expr_kind != EXPR_CONST) + { + *is_const_size = false; + return -1; + } + size = MAX(size, index + 1); + break; + } + default: + UNREACHABLE + } + } + return size; +} + bool sema_analyse_expr(Context *context, Type *to, Expr *expr) { return sema_analyse_expr_value(context, to, expr) && sema_cast_rvalue(context, to, expr); diff --git a/src/compiler/sema_internal.h b/src/compiler/sema_internal.h index be451b2d6..40ac7b32e 100644 --- a/src/compiler/sema_internal.h +++ b/src/compiler/sema_internal.h @@ -10,6 +10,7 @@ int sema_check_comp_time_bool(Context *context, Expr *expr); bool sema_analyse_function_body(Context *context, Decl *func); void context_pop_scope(Context *context); +void context_pop_scope_error(Context *context); void context_push_scope_with_flags(Context *context, ScopeFlags flags); AstId context_start_defer(Context *context); static inline void context_push_scope(Context *context) diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index f616bdc96..34eae4a98 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -75,6 +75,12 @@ void context_pop_scope(Context *context) context->current_scope--; } +void context_pop_scope_error(Context *context) +{ + context->current_scope->defer_last = context_start_defer(context); + context_pop_scope(context); +} + static Expr *context_pop_defers_and_wrap_expr(Context *context, Expr *expr) { DeferList defers = { 0, 0 }; @@ -326,9 +332,8 @@ static inline bool sema_analyse_do_stmt(Context *context, Ast *statement) } -static inline bool sema_analyse_declare_stmt(Context *context, Ast *statement) +static inline bool sema_analyse_local_decl(Context *context, Decl *decl) { - Decl *decl = statement->declare_stmt; assert(decl->decl_kind == DECL_VAR); if (!sema_add_local(context, decl)) return decl_poison(decl); if (decl->var.kind == VARDECL_CONST) @@ -353,6 +358,7 @@ static inline bool sema_analyse_declare_stmt(Context *context, Ast *statement) } if (!sema_resolve_type_info(context, decl->var.type_info)) return decl_poison(decl); decl->type = decl->var.type_info->type; + if (!decl->alignment) decl->alignment = type_alloca_alignment(decl->type); if (decl->var.init_expr) { Expr *init = decl->var.init_expr; @@ -374,6 +380,11 @@ static inline bool sema_analyse_declare_stmt(Context *context, Ast *statement) return true; } +static inline bool sema_analyse_declare_stmt(Context *context, Ast *statement) +{ + return sema_analyse_local_decl(context, statement->declare_stmt); +} + static inline bool sema_analyse_define_stmt(Context *context, Ast *statement) { Decl *decl = statement->declare_stmt; @@ -478,11 +489,13 @@ static inline bool sema_analyse_for_stmt(Context *context, Ast *statement) // Enter for scope context_push_scope(context); + bool is_infinite = statement->for_stmt.cond == NULL; if (statement->for_stmt.init) { success = sema_analyse_decl_expr_list(context, statement->for_stmt.init); } + if (success && statement->for_stmt.cond) { // Conditional scope start @@ -490,7 +503,7 @@ static inline bool sema_analyse_for_stmt(Context *context, Ast *statement) Expr *cond = statement->for_stmt.cond; success = sema_analyse_expr_of_required_type(context, type_bool, cond, false); statement->for_stmt.cond = context_pop_defers_and_wrap_expr(context, cond); - // If this is const true, then set this to infinte and remove the expression. + // If this is const true, then set this to infinite and remove the expression. if (statement->for_stmt.cond->expr_kind == EXPR_CONST && statement->for_stmt.cond->const_expr.b) { statement->for_stmt.cond = NULL; @@ -511,7 +524,7 @@ static inline bool sema_analyse_for_stmt(Context *context, Ast *statement) } if (!success) { - context_pop_scope(context); + context_pop_scope_error(context); return false; } @@ -535,6 +548,172 @@ static inline bool sema_analyse_for_stmt(Context *context, Ast *statement) } +static inline bool sema_analyse_foreach_stmt(Context *context, Ast *statement) +{ + // Pull out the relevant data. + Decl *index = statement->foreach_stmt.index; + Decl *var = statement->foreach_stmt.variable; + Expr *enumerator = statement->foreach_stmt.enumeration; + Ast *body = statement->foreach_stmt.body; + + // First fold the enumerator expression, removing any () around it. + while (enumerator->expr_kind == EXPR_GROUP) enumerator = enumerator->group_expr; + + + // First analyse the enumerator. + + // Conditional scope start + context_push_scope_with_flags(context, SCOPE_COND); + + // In the case of foreach (int x : { 1, 2, 3 }) we will infer the int[] type, so pick out the number of elements. + Type *inferred_type = NULL; + + // We may have an initializer list, in this case we rely on an inferred type. + if (enumerator->expr_kind == EXPR_INITIALIZER_LIST) + { + bool may_be_array; + bool is_const_size; + ArrayIndex size = sema_get_initializer_const_array_size(context, enumerator, &may_be_array, &is_const_size); + if (!may_be_array) + { + SEMA_ERROR(enumerator, "This initializer appears to be a struct initializer when, an array initializer was expected."); + goto EXIT_FAIL; + } + if (!is_const_size) + { + SEMA_ERROR(enumerator, "Only constant sized initializers may be implicitly initialized."); + goto EXIT_FAIL; + } + if (size < 0) + { + SEMA_ERROR(enumerator, "The initializer mixes designated initialization with array initialization."); + goto EXIT_FAIL; + } + assert(size >= 0); + + TypeInfo *variable_type_info = var->var.type_info; + + if (!variable_type_info) + { + SEMA_ERROR(var, "Add the type of your variable here if you want to iterate over an initializer list."); + goto EXIT_FAIL; + } + // First infer the type of the variable. + if (!sema_resolve_type_info(context, variable_type_info)) return false; + // And create the inferred type: + inferred_type = type_get_array(var->var.type_info->type, size); + } + + // because we don't want the index + variable to move into the internal scope + if (!sema_analyse_expr(context, inferred_type, enumerator)) + { + // Exit early here, because semantic checking might be messed up otherwise. + goto EXIT_FAIL; + } + + // Check that we can even index this expression. + Type *indexed_type = type_get_indexed_type(enumerator->type); + if (!indexed_type) + { + SEMA_ERROR(enumerator, "It's not possible to enumerate an expression of type '%s'.", type_to_error_string(enumerator->type)); + goto EXIT_FAIL; + } + + // Pop any possible defers. + enumerator = context_pop_defers_and_wrap_expr(context, enumerator); + + // And pop the cond scope. + context_pop_scope(context); + + // Enter foreach scope, to put index + variable into the internal scope. + // the foreach may be labelled. + context_push_scope_with_label(context, statement->foreach_stmt.flow.label); + + if (index) + { + if (statement->foreach_stmt.index_by_ref) + { + SEMA_ERROR(index, "The index cannot be held by reference, did you accidentally add a '&'?"); + goto EXIT_FAIL; + } + // Set type to isize if missing. + if (!index->var.type_info) + { + index->var.type_info = type_info_new_base(type_isize, index->span); + } + // Analyse the declaration. + if (!sema_analyse_local_decl(context, index)) goto EXIT_FAIL; + + // Make sure that the index is an integer. + if (!type_is_integer(type_flatten(index->type))) + { + SEMA_ERROR(index->var.type_info, "Index must be an integer type, '%s' is not valid.", type_to_error_string(index->type)); + goto EXIT_FAIL; + } + if (index->var.failable) + { + SEMA_ERROR(index->var.type_info, "The index may not be a failable."); + goto EXIT_FAIL; + } + } + + // Find the expected type for the value. + Type *expected_var_type = indexed_type; + + // If by ref, then the pointer. + if (statement->foreach_stmt.value_by_ref) expected_var_type = type_get_ptr(expected_var_type); + + // If we type infer, then the expected type is the same as the indexed type. + if (!var->var.type_info) + { + var->var.type_info = type_info_new_base(expected_var_type, var->span); + } + + // Analyse the value declaration. + if (!sema_analyse_local_decl(context, var)) goto EXIT_FAIL; + + if (var->var.failable) + { + SEMA_ERROR(var->var.type_info, "The variable may not be a failable."); + goto EXIT_FAIL; + } + // TODO consider failables. + + // We may have an implicit cast happening. + statement->foreach_stmt.cast = CAST_ERROR; + + // If the type is different, attempt an implicit cast. + if (var->type != expected_var_type) + { + // This is hackish, replace when cast is refactored. + Expr dummy = { .resolve_status = RESOLVE_DONE, .span = { var->var.type_info->span.loc, var->span.end_loc }, .expr_kind = EXPR_IDENTIFIER, .type = expected_var_type }; + if (!cast_implicit(context, &dummy, var->type)) goto EXIT_FAIL; + assert(dummy.expr_kind == EXPR_CAST); + statement->foreach_stmt.cast = dummy.cast_expr.kind; + } + + // Push the statement on the break/continue stack. + PUSH_BREAKCONT(statement); + + bool success = sema_analyse_statement(context, body); + statement->foreach_stmt.flow.no_exit = context->current_scope->jump_end; + + // Pop the stack. + POP_BREAKCONT(); + + // End foreach scope + context_pop_defers_and_replace_ast(context, body); + context_pop_scope(context); + + return success; + +EXIT_FAIL: + + context_pop_scope_error(context); + return false; + +} + static inline bool sema_analyse_if_stmt(Context *context, Ast *statement) @@ -1234,8 +1413,15 @@ static bool sema_analyse_switch_stmt(Context *context, Ast *statement) bool success = sema_analyse_switch_body(context, statement, cond->span, switch_type->canonical, statement->switch_stmt.cases); - if (success) context_pop_defers_and_replace_ast(context, statement); - context_pop_scope(context); + if (success) + { + context_pop_defers_and_replace_ast(context, statement); + context_pop_scope(context); + } + else + { + context_pop_scope_error(context); + } if (statement->flow.no_exit && !statement->flow.has_break) { context->current_scope->jump_end = true; @@ -1333,8 +1519,15 @@ static bool sema_analyse_catch_stmt(Context *context, Ast *statement) success = sema_analyse_statement(context, statement->catch_stmt.body); if (context->current_scope->jump_end) statement->flow.no_exit = true; } - if (success) context_pop_defers_and_replace_ast(context, statement); - context_pop_scope(context); + if (success) + { + context_pop_defers_and_replace_ast(context, statement); + context_pop_scope(context); + } + else + { + context_pop_scope_error(context); + } EXIT: if (success) { @@ -1391,7 +1584,7 @@ static bool sema_analyse_try_stmt(Context *context, Ast *stmt) return true; ERR: - context_pop_scope(context); + context_pop_scope_error(context); return false; } @@ -1530,6 +1723,8 @@ static inline bool sema_analyse_statement_inner(Context *context, Ast *statement return sema_analyse_do_stmt(context, statement); case AST_EXPR_STMT: return sema_analyse_expr_stmt(context, statement); + case AST_FOREACH_STMT: + return sema_analyse_foreach_stmt(context, statement); case AST_FOR_STMT: return sema_analyse_for_stmt(context, statement); case AST_TRY_STMT: diff --git a/src/compiler/tokens.c b/src/compiler/tokens.c index a16af23d2..65097464a 100644 --- a/src/compiler/tokens.c +++ b/src/compiler/tokens.c @@ -224,6 +224,8 @@ const char *token_type_to_string(TokenType type) return "false"; case TOKEN_FOR: return "for"; + case TOKEN_FOREACH: + return "foreach"; case TOKEN_FUNC: return "func"; case TOKEN_GENERIC: diff --git a/src/compiler/types.c b/src/compiler/types.c index 6a234b9f8..ab1b3f6f0 100644 --- a/src/compiler/types.c +++ b/src/compiler/types.c @@ -576,6 +576,11 @@ bool type_is_homogenous_aggregate(Type *type, Type **base, unsigned *elements) AlignSize type_alloca_alignment(Type *type) { + if (build_target.abi == ABI_X64) + { + type = type_flatten(type); + if (type->type_kind == TYPE_ARRAY && type_size(type) >= 16) return 16; + } return type_abi_alignment(type); } diff --git a/test/test_suite/arrays/array_literal.c3t b/test/test_suite/arrays/array_literal.c3t index f0f60b9b9..4291aadeb 100644 --- a/test/test_suite/arrays/array_literal.c3t +++ b/test/test_suite/arrays/array_literal.c3t @@ -1,3 +1,4 @@ +// #target: x64_darwin module testing; func double test(uint x) @@ -13,14 +14,14 @@ func double test(uint x) // #expect: array_literal.ll -@0 = constant [30 x double] [double 0.000000e+00, double 1.270600e+01, double 4.303000e+00, double 3.182000e+00, double 2.776000e+00, double 2.571000e+00, double 2.447000e+00, double 2.365000e+00, double 2.306000e+00, double 2.262000e+00, double 2.228000e+00, double 2.201000e+00, double 2.179000e+00, double 2.160000e+00, double 2.145000e+00, double 2.131000e+00, double 2.120000e+00, double 2.110000e+00, double 2.101000e+00, double 2.093000e+00, double 2.086000e+00, double 2.080000e+00, double 2.074000e+00, double 2.069000e+00, double 2.064000e+00, double 2.060000e+00, double 2.056000e+00, double 2.052000e+00, double 2.048000e+00, double 2.045000e+00], align 8 +@0 = constant [30 x double] [double 0.000000e+00, double 1.270600e+01, double 4.303000e+00, double 3.182000e+00, double 2.776000e+00, double 2.571000e+00, double 2.447000e+00, double 2.365000e+00, double 2.306000e+00, double 2.262000e+00, double 2.228000e+00, double 2.201000e+00, double 2.179000e+00, double 2.160000e+00, double 2.145000e+00, double 2.131000e+00, double 2.120000e+00, double 2.110000e+00, double 2.101000e+00, double 2.093000e+00, double 2.086000e+00, double 2.080000e+00, double 2.074000e+00, double 2.069000e+00, double 2.064000e+00, double 2.060000e+00, double 2.056000e+00, double 2.052000e+00, double 2.048000e+00, double 2.045000e+00], align 16 entry: %x = alloca i32, align 4 - %student_t = alloca [30 x double], align 8 + %student_t = alloca [30 x double], align 16 store i32 %0, i32* %x, align 4 %1 = bitcast [30 x double]* %student_t to i8* - call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %1, i8* align 8 bitcast ([30 x double]* @0 to i8*), i32 240, i1 false) + call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 16 %1, i8* align 16 bitcast ([30 x double]* @0 to i8*), i32 240, i1 false) %2 = load i32, i32* %x, align 4 %arridx = getelementptr inbounds [30 x double], [30 x double]* %student_t, i32 0, i32 %2 %3 = load double, double* %arridx, align 8 diff --git a/test/test_suite/arrays/array_struct.c3t b/test/test_suite/arrays/array_struct.c3t index 70e42e4b5..159ca97ca 100644 --- a/test/test_suite/arrays/array_struct.c3t +++ b/test/test_suite/arrays/array_struct.c3t @@ -1,3 +1,4 @@ +// #target: x64_darwin module test; struct Foo @@ -9,4 +10,4 @@ Foo[10] array; // #expect: array_struct.ll -@array = protected global [10 x %test.Foo] zeroinitializer, align 4 \ No newline at end of file +@array = protected global [10 x %test.Foo] zeroinitializer, align 16 \ No newline at end of file diff --git a/test/test_suite/arrays/complex_array_const.c3t b/test/test_suite/arrays/complex_array_const.c3t index 00c024bf2..6bf32a529 100644 --- a/test/test_suite/arrays/complex_array_const.c3t +++ b/test/test_suite/arrays/complex_array_const.c3t @@ -1,3 +1,4 @@ +// #target: x64_darwin module test; struct Connection @@ -17,4 +18,4 @@ Connection[3] link @0 = internal constant [6 x i8] c"link1\00" @1 = internal constant [6 x i8] c"link2\00" @2 = internal constant [6 x i8] c"link3\00" -@link = protected global [3 x %test.Connection] [%test.Connection { i64 1, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @0, i32 0, i32 0), i64 10 }, %test.Connection { i64 2, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @1, i32 0, i32 0), i64 20 }, %test.Connection { i64 3, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @2, i32 0, i32 0), i64 30 }], align 8 +@link = protected global [3 x %test.Connection] [%test.Connection { i64 1, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @0, i32 0, i32 0), i64 10 }, %test.Connection { i64 2, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @1, i32 0, i32 0), i64 20 }, %test.Connection { i64 3, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @2, i32 0, i32 0), i64 30 }], align 16 diff --git a/test/test_suite/errors/illegal_use_of_failable.c3 b/test/test_suite/errors/illegal_use_of_failable.c3 index dc5d13277..586692caa 100644 --- a/test/test_suite/errors/illegal_use_of_failable.c3 +++ b/test/test_suite/errors/illegal_use_of_failable.c3 @@ -3,9 +3,9 @@ func void syntaxErrors() int! i = 0; while (i + 1) {} // #error: 'int!' cannot be converted into 'bool' if (i + 1) {} // #error: 'int!' cannot be converted into 'bool' - for (int x = i;;) {} // #error: 'int!' cannot be implicitly cast to 'int'. - for (int x = 0; x < i + 1;) {} // #error: 'bool!' cannot be implicitly cast to 'bool' - for (int x = 0; x < 10; x += i + 1) {} // #error: 'int!' cannot be implicitly cast to 'int' + for (int x = i;;) {} // #error: 'int!' cannot be converted into 'int'. + for (int x = 0; x < i + 1;) {} // #error: 'bool!' cannot be converted into 'bool' + for (int x = 0; x < 10; x += i + 1) {} // #error: 'int!' cannot be converted into 'int' switch (i + 1) // #error: 'int!' cannot be converted into 'int' { default: diff --git a/test/test_suite/functions/assorted_tests.c3t b/test/test_suite/functions/assorted_tests.c3t index a67de38fd..256e9e5d2 100644 --- a/test/test_suite/functions/assorted_tests.c3t +++ b/test/test_suite/functions/assorted_tests.c3t @@ -106,7 +106,7 @@ void @test.setInternalFPFZero(%test.InternalFPF* %0) store %test.InternalFPF* %0, %test.InternalFPF** %dest, align 8 %1 = load %test.InternalFPF*, %test.InternalFPF** %dest, align 8 %2 = getelementptr inbounds %test.InternalFPF, %test.InternalFPF* %1, i32 0, i32 0 - store i8 0, i8* %2, align 1 + store i8 0, i8* %2, align 8 ret void void @test.denormalize(%test.InternalFPF* %0) diff --git a/test/test_suite/pointers/const_pointer.c3t b/test/test_suite/pointers/const_pointer.c3t index 83469ced5..e4b2239bd 100644 --- a/test/test_suite/pointers/const_pointer.c3t +++ b/test/test_suite/pointers/const_pointer.c3t @@ -1,3 +1,5 @@ +// #target: x64_darwin + module test; double foo = 17; @@ -11,4 +13,4 @@ void*[3] data = { &foo, &bar, &xx }; @foo = protected global double 1.700000e+01, align 8 @bar = protected global double 1.200000e+01, align 8 @xx = protected global float 1.200000e+01, align 4 -@data = protected global [3 x i8*] [i8* bitcast (double* @foo to i8*), i8* bitcast (double* @bar to i8*), i8* bitcast (float* @xx to i8*)], align 8 +@data = protected global [3 x i8*] [i8* bitcast (double* @foo to i8*), i8* bitcast (double* @bar to i8*), i8* bitcast (float* @xx to i8*)], align 16 diff --git a/test/test_suite/statements/foreach_break.c3t b/test/test_suite/statements/foreach_break.c3t new file mode 100644 index 000000000..5e8b23308 --- /dev/null +++ b/test/test_suite/statements/foreach_break.c3t @@ -0,0 +1,51 @@ +module test; + +func void test() +{ + int[3] x; + int g = 0; + foreach (z : x) + { + if (z > 0) break; + if (z == 1) continue; + g += z; + } +} + +// #expect: foreach_break.ll + + store i32 0, i32* %g, align 4 + store i64 0, i64* %idx, align 8 + br label %foreach.cond +foreach.cond: + %1 = load i64, i64* %idx, align 8 + %lt = icmp ult i64 %1, 3 + br i1 %lt, label %foreach.body, label %foreach.exit + +foreach.body: + %2 = getelementptr inbounds i32, [3 x i32]* %x, i64 %1 + %3 = load i32, i32* %2, align 4 + store i32 %3, i32* %z, align 4 + %4 = load i32, i32* %z, align 4 + %gt = icmp sgt i32 %4, 0 + br i1 %gt, label %if.then, label %if.exit +if.then: + br label %foreach.exit +if.exit: + %5 = load i32, i32* %z, align 4 + %eq = icmp eq i32 %5, 1 + br i1 %eq, label %if.then1, label %if.exit2 +if.then1: + br label %foreach.inc +if.exit2: + %6 = load i32, i32* %g, align 4 + %7 = load i32, i32* %z, align 4 + %add = add nsw i32 %6, %7 + store i32 %add, i32* %g, align 4 + br label %foreach.inc +foreach.inc: + %8 = load i64, i64* %idx, align 8 + %9 = add i64 %8, 1 + store i64 %9, i64* %idx, align 8 + br label %foreach.cond +foreach.exit: diff --git a/test/test_suite/statements/foreach_common.c3t b/test/test_suite/statements/foreach_common.c3t new file mode 100644 index 000000000..b19f66fd7 --- /dev/null +++ b/test/test_suite/statements/foreach_common.c3t @@ -0,0 +1,210 @@ +module test; + +extern func void printf(char*, ...); + +func void main() +{ + float[3] foo = { 2, 4.5, 8 }; + foreach (a : foo) + { + printf("Value: %f\n", a); + } + foreach (float* &a : foo) + { + *a *= 2; + printf("Value: %f\n", *a); + } + foreach (void* &a : foo) + { + printf("Value: %f\n", *(cast(a as float*))); + } + foreach (i, a : foo) + { + printf("Value[%d]: %f\n", i, a); + } + foreach (byte i, double a : foo) + { + printf("Value2[%d]: %f\n", i, a); + } + foreach (double a : foo) + { + printf("Value3: %f\n", a); + } +} + +// #expect: foreach_common.ll + +entry: + %foo = alloca [3 x float], align 4 + %idx = alloca i64, align 8 + %a = alloca float, align 4 + %idx1 = alloca i64, align 8 + %a2 = alloca float*, align 8 + %idx9 = alloca i64, align 8 + %a10 = alloca i8*, align 8 + %i = alloca i64, align 8 + %a18 = alloca float, align 4 + %i25 = alloca i8, align 1 + %idx26 = alloca i64, align 8 + %a27 = alloca double, align 8 + %idx34 = alloca i64, align 8 + %a35 = alloca double, align 8 + %0 = bitcast [3 x float]* %foo to i8* + call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 4 %0, i8* align 4 bitcast ([3 x float]* @0 to i8*), i32 12, i1 false) + store i64 0, i64* %idx, align 8 + br label %foreach.cond + +foreach.cond: + %1 = load i64, i64* %idx, align 8 + %lt = icmp ult i64 %1, 3 + br i1 %lt, label %foreach.body, label %foreach.exit + +foreach.body: + %2 = getelementptr inbounds float, [3 x float]* %foo, i64 %1 + %3 = load float, float* %2, align 4 + store float %3, float* %a, align 4 + %4 = load float, float* %a, align 4 + %fpfpext = fpext float %4 to double + call void (i8*, ...) @printf(i8* getelementptr inbounds ([11 x i8], [11 x i8]* @1, i32 0, i32 0), double %fpfpext) + br label %foreach.inc + +foreach.inc: + %5 = load i64, i64* %idx, align 8 + %6 = add i64 %5, 1 + store i64 %6, i64* %idx, align 8 + br label %foreach.cond + +foreach.exit: + store i64 0, i64* %idx1, align 8 + br label %foreach.cond3 + +foreach.cond3: + %7 = load i64, i64* %idx1, align 8 + %lt4 = icmp ult i64 %7, 3 + br i1 %lt4, label %foreach.body5, label %foreach.exit8 + +foreach.body5: + %8 = getelementptr inbounds float, [3 x float]* %foo, i64 %7 + store float* %8, float** %a2, align 8 + %9 = load float*, float** %a2, align 8 + %10 = load float, float* %9, align 8 + %fmul = fmul float %10, 2.000000e+00 + store float %fmul, float* %9, align 8 + %11 = load float*, float** %a2, align 8 + %12 = load float, float* %11, align 8 + %fpfpext6 = fpext float %12 to double + call void (i8*, ...) @printf(i8* getelementptr inbounds ([11 x i8], [11 x i8]* @2, i32 0, i32 0), double %fpfpext6) + br label %foreach.inc7 + +foreach.inc7: + %13 = load i64, i64* %idx1, align 8 + %14 = add i64 %13, 1 + store i64 %14, i64* %idx1, align 8 + br label %foreach.cond3 + +foreach.exit8: + store i64 0, i64* %idx9, align 8 + br label %foreach.cond11 + +foreach.cond11: + %15 = load i64, i64* %idx9, align 8 + %lt12 = icmp ult i64 %15, 3 + br i1 %lt12, label %foreach.body13, label %foreach.exit17 + +foreach.body13: + %16 = getelementptr inbounds float, [3 x float]* %foo, i64 %15 + %ptrptr = bitcast float* %16 to i8* + store i8* %ptrptr, i8** %a10, align 8 + %17 = load i8*, i8** %a10, align 8 + %ptrptr14 = bitcast i8* %17 to float* + %18 = load float, float* %ptrptr14, align 8 + %fpfpext15 = fpext float %18 to double + call void (i8*, ...) @printf(i8* getelementptr inbounds ([11 x i8], [11 x i8]* @3, i32 0, i32 0), double %fpfpext15) + br label %foreach.inc16 + +foreach.inc16: + %19 = load i64, i64* %idx9, align 8 + %20 = add i64 %19, 1 + store i64 %20, i64* %idx9, align 8 + br label %foreach.cond11 + +foreach.exit17: + store i64 0, i64* %i, align 8 + br label %foreach.cond19 + +foreach.cond19: + %21 = load i64, i64* %i, align 8 + %lt20 = icmp ult i64 %21, 3 + br i1 %lt20, label %foreach.body21, label %foreach.exit24 + +foreach.body21: + %22 = getelementptr inbounds float, [3 x float]* %foo, i64 %21 + %23 = load float, float* %22, align 4 + store float %23, float* %a18, align 4 + %24 = load i64, i64* %i, align 8 + %25 = load float, float* %a18, align 4 + %fpfpext22 = fpext float %25 to double + call void (i8*, ...) @printf(i8* getelementptr inbounds ([15 x i8], [15 x i8]* @4, i32 0, i32 0), i64 %24, double %fpfpext22) + br label %foreach.inc23 + +foreach.inc23: + %26 = load i64, i64* %i, align 8 + %27 = add i64 %26, 1 + store i64 %27, i64* %i, align 8 + br label %foreach.cond19 + +foreach.exit24: + store i8 0, i8* %i25, align 1 + store i64 0, i64* %idx26, align 8 + br label %foreach.cond28 + +foreach.cond28: + %28 = load i64, i64* %idx26, align 8 + %lt29 = icmp ult i64 %28, 3 + br i1 %lt29, label %foreach.body30, label %foreach.exit33 + +foreach.body30: + %29 = trunc i64 %28 to i8 + store i8 %29, i8* %i25, align 1 + %30 = getelementptr inbounds float, [3 x float]* %foo, i64 %28 + %31 = load float, float* %30, align 4 + %fpfpext31 = fpext float %31 to double + store double %fpfpext31, double* %a27, align 8 + %32 = load i8, i8* %i25, align 1 + %uisiext = zext i8 %32 to i32 + %33 = load double, double* %a27, align 8 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([16 x i8], [16 x i8]* @5, i32 0, i32 0), i32 %uisiext, double %33) + br label %foreach.inc32 + +foreach.inc32: + %34 = load i64, i64* %idx26, align 8 + %35 = add i64 %34, 1 + store i64 %35, i64* %idx26, align 8 + br label %foreach.cond28 + +foreach.exit33: + store i64 0, i64* %idx34, align 8 + br label %foreach.cond36 + +foreach.cond36: + %36 = load i64, i64* %idx34, align 8 + %lt37 = icmp ult i64 %36, 3 + br i1 %lt37, label %foreach.body38, label %foreach.exit41 + +foreach.body38: + %37 = getelementptr inbounds float, [3 x float]* %foo, i64 %36 + %38 = load float, float* %37, align 4 + %fpfpext39 = fpext float %38 to double + store double %fpfpext39, double* %a35, align 8 + %39 = load double, double* %a35, align 8 + call void (i8*, ...) @printf(i8* getelementptr inbounds ([12 x i8], [12 x i8]* @6, i32 0, i32 0), double %39) + br label %foreach.inc40 + +foreach.inc40: + %40 = load i64, i64* %idx34, align 8 + %41 = add i64 %40, 1 + store i64 %41, i64* %idx34, align 8 + br label %foreach.cond36 + +foreach.exit41: + ret void diff --git a/test/test_suite/statements/foreach_errors.c3 b/test/test_suite/statements/foreach_errors.c3 new file mode 100644 index 000000000..571b55786 --- /dev/null +++ b/test/test_suite/statements/foreach_errors.c3 @@ -0,0 +1,59 @@ +module test; + +extern func void foo(); +int[3] z; + +func void test1() +{ + int x; + foreach (a : x) // #error: It's not possible to enumerate an expression of type 'int'. + { + foo(); + } +} + +func void test2() +{ + foreach (a : z) foo(); + foreach (i, a : z) foo(); + foreach (double i, a : z); // #error: Index must be an integer type, 'double' is not valid. +} + +func void test3() +{ + foreach (&a : z) foo(); + foreach (&i, &a : z) foo(); // #error: The index cannot be held by reference, did you accidentally add a '&'? +} + +func void test4() +{ + foreach (&a : z) foo(); + foreach (&i, a : z) foo(); // #error: The index cannot be held by reference, did you accidentally add a '&'? +} + +func void test5() +{ + foreach (int! y : z) foo(); // #error: The variable may not be a failable. +} + +func void test6() +{ + foreach (int! i, y : z) foo(); // #error: The index may not be a failable. +} + +func void test7() +{ + foreach (int a : { 1, 2, 3 }) foo(); + foreach (a : { 1, 2, 3 }) foo(); // #error: Add the type of your variable here if you want to iterate over +} + +func void test8() +{ + foreach (int a : { z }) foo(); // #error: Cannot implicitly cast 'int[3]' to 'int' +} + +func void test9() +{ + foreach (int a : { [2] = 1 }) foo(); + foreach (int a : { [2] = 2, 1 }) foo(); // #error: The initializer mixes designated initialization with array initialization +} \ No newline at end of file diff --git a/test/test_suite/statements/foreach_with_error.c3t b/test/test_suite/statements/foreach_with_error.c3t new file mode 100644 index 000000000..7972e017f --- /dev/null +++ b/test/test_suite/statements/foreach_with_error.c3t @@ -0,0 +1,68 @@ +module test; + +func void test() +{ + int[3]! x; + int g; + foreach (z : x) + { + g += z; + x[0] = 1; + } +} + +// #expect: foreach_with_error.ll + +entry: + %x = alloca [3 x i32], align 4 + %x1 = alloca { i64, i64 }, align 8 + %g = alloca i32, align 4 + %idx = alloca i64, align 8 + %z = alloca i32, align 4 + store { i64, i64 } zeroinitializer, { i64, i64 }* %x1, align 8 + %0 = bitcast [3 x i32]* %x to i8* + call void @llvm.memset.p0i8.i64(i8* align 4 %0, i8 0, i64 12, i1 false) + store i32 0, i32* %g, align 4 + %1 = load { i64, i64 }, { i64, i64 }* %x1, align 8 + %2 = extractvalue { i64, i64 } %1, 0 + %noerr = icmp eq i64 %2, 0 + br i1 %noerr, label %after_check, label %foreach.exit + +after_check: + store i64 0, i64* %idx, align 8 + br label %foreach.cond + +foreach.cond: + %3 = load i64, i64* %idx, align 8 + %lt = icmp ult i64 %3, 3 + br i1 %lt, label %foreach.body, label %foreach.exit + +foreach.body: + %4 = getelementptr inbounds i32, [3 x i32]* %x, i64 %3 + %5 = load i32, i32* %4, align 4 + store i32 %5, i32* %z, align 4 + %6 = load i32, i32* %g, align 4 + %7 = load i32, i32* %z, align 4 + %add = add nsw i32 %6, %7 + store i32 %add, i32* %g, align 4 + %8 = load { i64, i64 }, { i64, i64 }* %x1, align 8 + %9 = extractvalue { i64, i64 } %8, 0 + %noerr2 = icmp eq i64 %9, 0 + br i1 %noerr2, label %after_check3, label %voiderr + +after_check3: + %arridx = getelementptr inbounds [3 x i32], [3 x i32]* %x, i32 0, i32 0 + store i32 1, i32* %arridx, align 4 + br label %voiderr + +voiderr: + br label %foreach.inc + +foreach.inc: + %10 = load i64, i64* %idx, align 8 + %11 = add i64 %10, 1 + store i64 %11, i64* %idx, align 8 + br label %foreach.cond + +foreach.exit: + ret void diff --git a/test/test_suite/statements/if_while_do_error.c3 b/test/test_suite/statements/if_while_do_error.c3 new file mode 100644 index 000000000..5e34c8d40 --- /dev/null +++ b/test/test_suite/statements/if_while_do_error.c3 @@ -0,0 +1,34 @@ +module test; + +func void test1() +{ + bool! x = 0; + if (x) // #error: 'bool!' cannot be converted into 'bool' + { + x = 100; + } +} + +func void test2() +{ + bool! x = 0; + while (x) // #error: 'bool!' cannot be converted into 'bool' + { + x = false; + } +} + +func void test3() +{ + bool! x = 0; + double y = 1; + do + { + y = y + 1; + } while (y); + do + { + x = !x; + } + while (x); // #error: 'bool!' cannot be converted into 'bool' +} \ No newline at end of file diff --git a/test/test_suite/struct/struct_codegen.c3t b/test/test_suite/struct/struct_codegen.c3t index f60f72abb..8251c54aa 100644 --- a/test/test_suite/struct/struct_codegen.c3t +++ b/test/test_suite/struct/struct_codegen.c3t @@ -20,5 +20,5 @@ func void test1() entry: %p = alloca %test.Point, align 4 %0 = bitcast %test.Point* %p to i8* - call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 4 %0, i8* align 8 bitcast (%test.Point* @0 to i8*), i32 8, i1 false) + call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 4 %0, i8* align 4 bitcast (%test.Point* @0 to i8*), i32 8, i1 false)