mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
More correct alignment. Foreach added. Removed dynamic designated array initialization. Use 16 byte alignment on arrays >= 16 for x64
This commit is contained in:
committed by
Christoffer Lerno
parent
26d25e3f74
commit
45c4f205bb
@@ -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?
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@array = protected global [10 x %test.Foo] zeroinitializer, align 16
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
51
test/test_suite/statements/foreach_break.c3t
Normal file
51
test/test_suite/statements/foreach_break.c3t
Normal file
@@ -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:
|
||||
210
test/test_suite/statements/foreach_common.c3t
Normal file
210
test/test_suite/statements/foreach_common.c3t
Normal file
@@ -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
|
||||
59
test/test_suite/statements/foreach_errors.c3
Normal file
59
test/test_suite/statements/foreach_errors.c3
Normal file
@@ -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
|
||||
}
|
||||
68
test/test_suite/statements/foreach_with_error.c3t
Normal file
68
test/test_suite/statements/foreach_with_error.c3t
Normal file
@@ -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
|
||||
34
test/test_suite/statements/if_while_do_error.c3
Normal file
34
test/test_suite/statements/if_while_do_error.c3
Normal file
@@ -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'
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user