mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
Fixes to aarch64 float struct return. Missing byval and align on calls. This *breaks* try-catch.
This commit is contained in:
committed by
Christoffer Lerno
parent
afeb555e2f
commit
da76777ee4
@@ -7,6 +7,7 @@
|
||||
static void fprint_asts_recursive(Context *context, FILE *file, Ast **asts, int indent);
|
||||
static void fprint_decl_list(Context *context, FILE *file, Decl **decls, int indent);
|
||||
static void fprint_ast_recursive(Context *context, FILE *file, Ast *ast, int indent);
|
||||
static void fprint_expr_list(Context *context, FILE *file, Expr **exprs, int indent);
|
||||
|
||||
#define DUMP(text) do { fprintf_indented(file, indent, text); fprintf(file, "\n"); } while(0)
|
||||
#define DUMPF(text, ...) do { fprintf_indented(file, indent, text, __VA_ARGS__); fprintf(file, "\n"); } while(0)
|
||||
@@ -21,6 +22,7 @@ static void fprint_ast_recursive(Context *context, FILE *file, Ast *ast, int ind
|
||||
#define DUMPTYPE(_type) fprint_type_recursive(context, file, _type, indent + 1)
|
||||
#define DUMPDECLS(_decls) fprint_decl_list(context, file, _decls, indent + 1)
|
||||
#define DUMPDECL(_decl) fprint_decl_recursive(context, file, _decl, indent + 1)
|
||||
#define DUMPEXPRS(_exprs) fprint_expr_list(context, file, _exprs, indent + 1)
|
||||
|
||||
Decl *decl_new(DeclKind decl_kind, TokenId name, Visibility visibility)
|
||||
{
|
||||
@@ -596,6 +598,11 @@ void fprint_expr_recursive(Context *context, FILE *file, Expr *expr, int indent)
|
||||
if (!expr) return;
|
||||
switch (expr->expr_kind)
|
||||
{
|
||||
case EXPR_DECL:
|
||||
DUMP("(decl");
|
||||
DUMPEXPC(expr);
|
||||
DUMPDECL(expr->decl_expr);
|
||||
break;
|
||||
case EXPR_NOP:
|
||||
DUMP("(nop)");
|
||||
return;
|
||||
@@ -639,7 +646,7 @@ void fprint_expr_recursive(Context *context, FILE *file, Expr *expr, int indent)
|
||||
DUMPEND();
|
||||
case EXPR_DECL_LIST:
|
||||
DUMP("(decllist");
|
||||
DUMPASTS(expr->dexpr_list_expr);
|
||||
DUMPEXPRS(expr->dexpr_list_expr);
|
||||
DUMPEND();
|
||||
case EXPR_FAILABLE:
|
||||
DUMP("(failable");
|
||||
@@ -703,16 +710,27 @@ void fprint_expr_recursive(Context *context, FILE *file, Expr *expr, int indent)
|
||||
DUMPEXPC(expr);
|
||||
DUMPEXPR(expr->post_expr.expr);
|
||||
DUMPEND();
|
||||
case EXPR_CATCH:
|
||||
case EXPR_CATCH_OLD:
|
||||
DUMP("(catch");
|
||||
DUMPEXPC(expr);
|
||||
DUMPEXPR(expr->trycatch_expr);
|
||||
DUMPEND();
|
||||
case EXPR_TRY:
|
||||
case EXPR_TRY_OLD:
|
||||
DUMP("(try");
|
||||
DUMPEXPC(expr);
|
||||
DUMPEXPR(expr->trycatch_expr);
|
||||
DUMPEND();
|
||||
case EXPR_TRY:
|
||||
DUMPF("(try %d", expr->try_expr.is_try);
|
||||
DUMPEXPC(expr);
|
||||
DUMPEXPR(expr->try_expr.expr);
|
||||
DUMPEND();
|
||||
case EXPR_TRY_ASSIGN:
|
||||
DUMPF("(try-assign %d", expr->try_assign_expr.is_try);
|
||||
DUMPEXPC(expr);
|
||||
DUMPEXPR(expr->try_assign_expr.expr);
|
||||
DUMPEXPR(expr->try_assign_expr.init);
|
||||
DUMPEND();
|
||||
case EXPR_ACCESS:
|
||||
DUMP("(access");
|
||||
DUMPEXPC(expr);
|
||||
@@ -1092,6 +1110,14 @@ static void fprint_decl_list(Context *context, FILE *file, Decl **decls, int ind
|
||||
}
|
||||
}
|
||||
|
||||
static void fprint_expr_list(Context *context, FILE *file, Expr **exprs, int indent)
|
||||
{
|
||||
VECEACH(exprs, i)
|
||||
{
|
||||
fprint_expr_recursive(context, file, exprs[i], indent);
|
||||
}
|
||||
}
|
||||
|
||||
static void fprint_asts_recursive(Context *context, FILE *file, Ast **asts, int indent)
|
||||
{
|
||||
VECEACH(asts, i)
|
||||
@@ -1117,8 +1143,8 @@ static void fprint_ast_recursive(Context *context, FILE *file, Ast *ast, int ind
|
||||
DUMPEND();
|
||||
case AST_TRY_STMT:
|
||||
DUMP("(try");
|
||||
DUMPEXPR(ast->try_stmt.decl_expr);
|
||||
DUMPAST(ast->try_stmt.body);
|
||||
DUMPEXPR(ast->try_old_stmt.decl_expr);
|
||||
DUMPAST(ast->try_old_stmt.body);
|
||||
DUMPEND();
|
||||
case AST_COMPOUND_STMT:
|
||||
if (!ast->compound_stmt.stmts)
|
||||
|
||||
@@ -849,6 +849,19 @@ typedef struct
|
||||
AstId defer;
|
||||
} ExprGuard;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
bool is_try;
|
||||
Expr *expr;
|
||||
} ExprTryExpr;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
bool is_try;
|
||||
Expr *expr;
|
||||
Expr *init;
|
||||
} ExprTryAssign;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
Expr *inner;
|
||||
@@ -876,6 +889,7 @@ struct Expr_
|
||||
ExprStructValue struct_value_expr;
|
||||
ExprGuard guard_expr;
|
||||
Expr *trycatch_expr;
|
||||
Decl *decl_expr;
|
||||
ExprElse else_expr;
|
||||
ExprFlatElement *flatpath_expr;
|
||||
ExprSliceAssign slice_assign_expr;
|
||||
@@ -885,6 +899,8 @@ struct Expr_
|
||||
ExprPostUnary post_expr;
|
||||
ExprCall call_expr;
|
||||
ExprSlice slice_expr;
|
||||
ExprTryExpr try_expr;
|
||||
ExprTryAssign try_assign_expr;
|
||||
ExprSubscript subscript_expr;
|
||||
ExprAccess access_expr;
|
||||
ExprDesignator designator_expr;
|
||||
@@ -907,7 +923,7 @@ struct Expr_
|
||||
ExprMacroBlock macro_block;
|
||||
|
||||
Expr* failable_expr;
|
||||
Ast** dexpr_list_expr;
|
||||
Expr** dexpr_list_expr;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1205,7 +1221,8 @@ typedef struct Ast_
|
||||
Ast** ct_compound_stmt;
|
||||
Decl *declare_stmt; // 8
|
||||
Expr *expr_stmt; // 8
|
||||
AstTryStmt try_stmt;
|
||||
AstTryStmt try_old_stmt;
|
||||
Ast *try_stmt;
|
||||
Decl *define_stmt; // 8
|
||||
Ast *volatile_stmt; // 8
|
||||
AstReturnStmt return_stmt; // 16
|
||||
@@ -1803,6 +1820,7 @@ bool sema_analyse_expr_of_required_type(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);
|
||||
bool sema_analyse_expr(Context *context, Type *to, Expr *expr);
|
||||
bool sema_analyse_decl(Context *context, Decl *decl);
|
||||
bool sema_analyse_local_decl(Context *context, Decl *decl);
|
||||
bool sema_analyse_ct_assert_stmt(Context *context, Ast *statement);
|
||||
bool sema_analyse_statement(Context *context, Ast *statement);
|
||||
bool sema_expr_analyse_assign_right_side(Context *context, Expr *expr, Type *left_type, Expr *right, ExprFailableStatus lhs_is_failable);
|
||||
|
||||
@@ -77,6 +77,9 @@ Expr *copy_expr(Expr *source_expr)
|
||||
case EXPR_UNDEF:
|
||||
case EXPR_NOP:
|
||||
return expr;
|
||||
case EXPR_DECL:
|
||||
MACRO_COPY_DECL(expr->decl_expr);
|
||||
return expr;
|
||||
case EXPR_CT_CALL:
|
||||
MACRO_COPY_EXPR_LIST(expr->ct_call_expr.arguments);
|
||||
return expr;
|
||||
@@ -108,12 +111,19 @@ Expr *copy_expr(Expr *source_expr)
|
||||
case EXPR_LEN:
|
||||
MACRO_COPY_EXPR(expr->len_expr.inner);
|
||||
return expr;
|
||||
case EXPR_CATCH:
|
||||
case EXPR_TRY:
|
||||
case EXPR_CATCH_OLD:
|
||||
case EXPR_TRY_OLD:
|
||||
MACRO_COPY_EXPR(expr->trycatch_expr);
|
||||
return expr;
|
||||
case EXPR_TRY_ASSIGN:
|
||||
MACRO_COPY_EXPR(expr->try_assign_expr.expr);
|
||||
MACRO_COPY_EXPR(expr->try_assign_expr.init);
|
||||
return expr;
|
||||
case EXPR_TRY:
|
||||
MACRO_COPY_EXPR(expr->try_expr.expr);
|
||||
return expr;
|
||||
case EXPR_DECL_LIST:
|
||||
MACRO_COPY_AST_LIST(expr->dexpr_list_expr);
|
||||
MACRO_COPY_EXPR_LIST(expr->dexpr_list_expr);
|
||||
return expr;
|
||||
case EXPR_FAILABLE:
|
||||
MACRO_COPY_EXPR(expr->failable_expr);
|
||||
@@ -361,8 +371,8 @@ Ast *copy_ast(Ast *source)
|
||||
MACRO_COPY_AST_LIST(ast->switch_stmt.cases);
|
||||
return ast;
|
||||
case AST_TRY_STMT:
|
||||
MACRO_COPY_EXPR(ast->try_stmt.decl_expr);
|
||||
MACRO_COPY_AST(ast->try_stmt.body);
|
||||
MACRO_COPY_EXPR(ast->try_old_stmt.decl_expr);
|
||||
MACRO_COPY_AST(ast->try_old_stmt.body);
|
||||
return ast;
|
||||
case AST_UNREACHABLE_STMT:
|
||||
return ast;
|
||||
|
||||
@@ -178,12 +178,13 @@ typedef enum
|
||||
EXPR_MACRO_BODY_EXPANSION,
|
||||
EXPR_CALL,
|
||||
EXPR_CAST,
|
||||
EXPR_CATCH,
|
||||
EXPR_CATCH_OLD,
|
||||
EXPR_COMPOUND_LITERAL,
|
||||
EXPR_CONST,
|
||||
EXPR_CONST_IDENTIFIER,
|
||||
EXPR_CT_IDENT,
|
||||
EXPR_DECL_LIST,
|
||||
EXPR_DECL,
|
||||
EXPR_DESIGNATOR,
|
||||
EXPR_ELSE,
|
||||
EXPR_ENUM_CONSTANT,
|
||||
@@ -207,7 +208,9 @@ typedef enum
|
||||
EXPR_SLICE_ASSIGN,
|
||||
EXPR_SUBSCRIPT,
|
||||
EXPR_TERNARY,
|
||||
EXPR_TRY_OLD,
|
||||
EXPR_TRY,
|
||||
EXPR_TRY_ASSIGN,
|
||||
EXPR_TYPEID,
|
||||
EXPR_TYPEINFO,
|
||||
EXPR_TYPEOF,
|
||||
@@ -236,7 +239,7 @@ typedef enum
|
||||
{
|
||||
PREC_NONE,
|
||||
PREC_ASSIGNMENT, // =, *=, /=, %=, +=, etc
|
||||
PREC_TRY_ELSE, // try and else
|
||||
PREC_ELSE, // else
|
||||
PREC_TERNARY, // ?:
|
||||
PREC_OR, // ||
|
||||
PREC_AND, // &&
|
||||
@@ -245,7 +248,7 @@ typedef enum
|
||||
PREC_BIT, // ^ | &
|
||||
PREC_SHIFT, // << >>
|
||||
PREC_MULTIPLICATIVE, // * / %
|
||||
PREC_UNARY, // ! - + ~ * & prefix ++/--
|
||||
PREC_UNARY, // ! - + ~ * & prefix ++/-- try catch
|
||||
PREC_CALL, // . () [] postfix ++/--
|
||||
PREC_MACRO,
|
||||
PREC_FIRST = PREC_MACRO
|
||||
@@ -408,6 +411,7 @@ typedef enum
|
||||
TOKEN_ATTRIBUTE,
|
||||
TOKEN_BREAK,
|
||||
TOKEN_CASE,
|
||||
TOKEN_CATCH_OLD,
|
||||
TOKEN_CATCH,
|
||||
TOKEN_CONST,
|
||||
TOKEN_CONTINUE,
|
||||
@@ -437,6 +441,7 @@ typedef enum
|
||||
TOKEN_STRUCT,
|
||||
TOKEN_SWITCH,
|
||||
TOKEN_TRUE,
|
||||
TOKEN_TRY_OLD,
|
||||
TOKEN_TRY,
|
||||
TOKEN_UNION,
|
||||
TOKEN_VAR, // Reserved
|
||||
|
||||
@@ -117,7 +117,7 @@ ABIArgInfo *aarch64_classify_return_type(Type *type, bool variadic)
|
||||
if (type_is_homogenous_aggregate(type, &base, &members) &&
|
||||
!(platform_target.arch == ARCH_TYPE_AARCH64_32 && variadic))
|
||||
{
|
||||
return abi_arg_ignore();
|
||||
return abi_arg_new_direct();
|
||||
}
|
||||
|
||||
// Aggregates <= in registers
|
||||
|
||||
@@ -2063,8 +2063,126 @@ void gencontext_emit_trycatch_expr(GenContext *c, BEValue *value, Expr *expr)
|
||||
llvm_emit_block(c, phi_block);
|
||||
|
||||
LLVMValueRef phi = LLVMBuildPhi(c->builder, llvm_get_type(c, expr->type), "val");
|
||||
LLVMValueRef lhs = llvm_const_int(c, type_bool, expr->expr_kind == EXPR_TRY ? 1 : 0);
|
||||
LLVMValueRef rhs = llvm_const_int(c, type_bool, expr->expr_kind == EXPR_TRY ? 0 : 1);
|
||||
LLVMValueRef lhs = llvm_const_int(c, type_bool, expr->expr_kind == EXPR_TRY_OLD ? 1 : 0);
|
||||
LLVMValueRef rhs = llvm_const_int(c, type_bool, expr->expr_kind == EXPR_TRY_OLD ? 0 : 1);
|
||||
|
||||
LLVMValueRef logic_values[2] = { lhs, rhs };
|
||||
LLVMBasicBlockRef blocks[2] = { no_err_block, error_block };
|
||||
LLVMAddIncoming(phi, logic_values, blocks, 2);
|
||||
|
||||
llvm_value_set(value, phi, expr->type);
|
||||
}
|
||||
|
||||
void llvm_emit_try_assign_try_catch(GenContext *c, bool is_try, BEValue *be_value, BEValue *var_addr, BEValue *catch_addr, Expr *rhs)
|
||||
{
|
||||
assert(!catch_addr || llvm_value_is_addr(catch_addr));
|
||||
assert(!var_addr || llvm_value_is_addr(var_addr));
|
||||
|
||||
// 1. Create after try/catch block
|
||||
LLVMBasicBlockRef catch_block = llvm_basic_block_new(c, "catch_landing");
|
||||
LLVMBasicBlockRef phi_catch = llvm_basic_block_new(c, "phi_try_catch");
|
||||
|
||||
// 2. Push the error state.
|
||||
PUSH_ERROR();
|
||||
|
||||
// 3. If we have a catch *and* we want to store it, set the catch variable
|
||||
c->error_var = catch_addr ? catch_addr->value : NULL;
|
||||
|
||||
// 4. After catch we want to end up in the landing, because otherwise we don't know the value for the phi.
|
||||
c->catch_block = catch_block;
|
||||
|
||||
// 5. Emit the init part.
|
||||
llvm_emit_expr(c, be_value, rhs);
|
||||
|
||||
// 6. If we haven't jumped yet, do it here (on error) to the catch block.
|
||||
llvm_value_fold_failable(c, be_value);
|
||||
|
||||
// 7. If we have a variable, then we make the store.
|
||||
if (var_addr)
|
||||
{
|
||||
assert(is_try && "Storing will only happen on try.");
|
||||
llvm_store_bevalue(c, var_addr, be_value);
|
||||
}
|
||||
|
||||
// 8. Restore the error stack.
|
||||
POP_ERROR();
|
||||
|
||||
// 9. Store the success block.
|
||||
LLVMBasicBlockRef success_block = c->current_block;
|
||||
|
||||
// 10. Jump to the phi
|
||||
llvm_emit_br(c, phi_catch);
|
||||
|
||||
// 11. Emit the catch and jump.
|
||||
llvm_emit_block(c, catch_block);
|
||||
llvm_emit_br(c, phi_catch);
|
||||
|
||||
// 12. Emit the phi
|
||||
llvm_emit_block(c, phi_catch);
|
||||
|
||||
// 13. Use a phi to pick true / false.
|
||||
LLVMValueRef phi = LLVMBuildPhi(c->builder, c->bool_type, "val");
|
||||
LLVMValueRef from_try = LLVMConstInt(c->bool_type, is_try ? 1 : 0, false);
|
||||
LLVMValueRef from_catch = LLVMConstInt(c->bool_type, is_try ? 0 : 1, false);
|
||||
LLVMValueRef logic_values[2] = { from_try, from_catch };
|
||||
LLVMBasicBlockRef blocks[2] = { success_block, catch_block };
|
||||
LLVMAddIncoming(phi, logic_values, blocks, 2);
|
||||
|
||||
llvm_value_set_bool(be_value, phi);
|
||||
|
||||
}
|
||||
|
||||
void llvm_emit_try_assign_expr(GenContext *c, BEValue *be_value, Expr *expr)
|
||||
{
|
||||
// Create the variable reference.
|
||||
BEValue addr;
|
||||
llvm_emit_expr(c, &addr, expr->try_assign_expr.expr);
|
||||
assert(llvm_value_is_addr(&addr));
|
||||
|
||||
if (expr->try_assign_expr.is_try)
|
||||
{
|
||||
llvm_emit_try_assign_try_catch(c, true, be_value, &addr, NULL, expr->try_assign_expr.init);
|
||||
}
|
||||
else
|
||||
{
|
||||
llvm_emit_try_assign_try_catch(c, false, be_value, NULL, &addr, expr->try_assign_expr.init);
|
||||
}
|
||||
}
|
||||
|
||||
void gencontext_emit_try_expr(GenContext *c, BEValue *value, Expr *expr)
|
||||
{
|
||||
|
||||
LLVMBasicBlockRef error_block = llvm_basic_block_new(c, "error_block");
|
||||
LLVMBasicBlockRef no_err_block = llvm_basic_block_new(c, "noerr_block");
|
||||
LLVMBasicBlockRef phi_block = llvm_basic_block_new(c, "phi_trycatch_block");
|
||||
|
||||
// Store catch/error var
|
||||
PUSH_ERROR();
|
||||
|
||||
// Set the catch/error var
|
||||
c->error_var = NULL;
|
||||
c->catch_block = error_block;
|
||||
|
||||
llvm_emit_expr(c, value, expr->try_expr.expr);
|
||||
llvm_value_fold_failable(c, value);
|
||||
|
||||
// Restore.
|
||||
POP_ERROR();
|
||||
|
||||
// Emit success and jump to phi.
|
||||
llvm_emit_br(c, no_err_block);
|
||||
llvm_emit_block(c, no_err_block);
|
||||
llvm_emit_br(c, phi_block);
|
||||
|
||||
// Emit error and jump to phi
|
||||
llvm_emit_block(c, error_block);
|
||||
llvm_emit_br(c, phi_block);
|
||||
|
||||
llvm_emit_block(c, phi_block);
|
||||
|
||||
LLVMValueRef phi = LLVMBuildPhi(c->builder, llvm_get_type(c, expr->type), "val");
|
||||
LLVMValueRef lhs = llvm_const_int(c, type_bool, expr->try_expr.is_try ? 1 : 0);
|
||||
LLVMValueRef rhs = llvm_const_int(c, type_bool, expr->try_expr.is_try ? 0 : 1);
|
||||
|
||||
LLVMValueRef logic_values[2] = { lhs, rhs };
|
||||
LLVMBasicBlockRef blocks[2] = { no_err_block, error_block };
|
||||
@@ -2572,7 +2690,8 @@ void gencontext_emit_call_intrinsic_expr(GenContext *c, BEValue *be_value, Expr
|
||||
|
||||
void llvm_emit_parameter(GenContext *c, LLVMValueRef **args, ABIArgInfo *info, BEValue *be_value, Type *type)
|
||||
{
|
||||
assert(be_value->type->canonical == type->canonical);
|
||||
type = type_lowering(type);
|
||||
assert(be_value->type->canonical == type);
|
||||
switch (info->kind)
|
||||
{
|
||||
case ABI_ARG_IGNORE:
|
||||
@@ -2921,6 +3040,25 @@ void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr)
|
||||
llvm_attribute_add_call(c, call_value, attribute_alwaysinline, -1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < non_variadic_params; i++)
|
||||
{
|
||||
Decl *param = signature->params[i];
|
||||
ABIArgInfo *info = param->var.abi_info;
|
||||
switch (info->kind)
|
||||
{
|
||||
case ABI_ARG_INDIRECT:
|
||||
if (info->indirect.by_val_type)
|
||||
{
|
||||
llvm_attribute_add_call(c, call_value, attribute_byval, i + 1, 0);
|
||||
}
|
||||
llvm_attribute_add_call(c, call_value, attribute_align, i + 1, info->indirect.alignment);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 11. Process the return value.
|
||||
switch (ret_info->kind)
|
||||
{
|
||||
@@ -3327,6 +3465,9 @@ void llvm_emit_expr(GenContext *c, BEValue *value, Expr *expr)
|
||||
case EXPR_UNDEF:
|
||||
// Should never reach this.
|
||||
UNREACHABLE
|
||||
case EXPR_DECL:
|
||||
llvm_emit_local_decl(c, expr->decl_expr);
|
||||
return;
|
||||
case EXPR_SLICE_ASSIGN:
|
||||
gencontext_emit_slice_assign(c, value, expr);
|
||||
return;
|
||||
@@ -3340,7 +3481,13 @@ void llvm_emit_expr(GenContext *c, BEValue *value, Expr *expr)
|
||||
gencontext_emit_failable(c, value, expr);
|
||||
return;
|
||||
case EXPR_TRY:
|
||||
case EXPR_CATCH:
|
||||
gencontext_emit_try_expr(c, value, expr);
|
||||
return;
|
||||
case EXPR_TRY_ASSIGN:
|
||||
llvm_emit_try_assign_expr(c, value, expr);
|
||||
return;
|
||||
case EXPR_TRY_OLD:
|
||||
case EXPR_CATCH_OLD:
|
||||
gencontext_emit_trycatch_expr(c, value, expr);
|
||||
return;
|
||||
case EXPR_NOP:
|
||||
|
||||
@@ -435,7 +435,7 @@ void llvm_emit_function_body(GenContext *context, Decl *decl)
|
||||
llvm_emit_stmt(context, decl->func_decl.body->compound_stmt.stmts[i]);
|
||||
}
|
||||
|
||||
if (context->current_block && !LLVMGetFirstInstruction(context->current_block) && !LLVMGetFirstUse(LLVMBasicBlockAsValue(context->current_block)))
|
||||
if (context->current_block && llvm_basic_block_is_unused(context->current_block))
|
||||
{
|
||||
LLVMBasicBlockRef prev_block = LLVMGetPreviousBasicBlock(context->current_block);
|
||||
LLVMDeleteBasicBlock(context->current_block);
|
||||
|
||||
@@ -243,6 +243,7 @@ LLVMValueRef llvm_emit_is_no_error_value(GenContext *c, BEValue *value);
|
||||
void llvm_emit_len_for_expr(GenContext *c, BEValue *be_value, BEValue *expr_to_len);
|
||||
LLVMValueRef llvm_emit_load_aligned(GenContext *c, LLVMTypeRef type, LLVMValueRef pointer, AlignSize alignment, const char *name);
|
||||
void llvm_emit_local_var_alloca(GenContext *c, Decl *decl);
|
||||
LLVMValueRef llvm_emit_local_decl(GenContext *c, Decl *decl);
|
||||
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);
|
||||
void llvm_emit_memcpy(GenContext *c, LLVMValueRef dest, unsigned dest_align, LLVMValueRef source, unsigned src_align, uint64_t len);
|
||||
@@ -320,10 +321,15 @@ static inline LLVMValueRef llvm_emit_bitcast(GenContext *context, LLVMValueRef v
|
||||
|
||||
static inline bool llvm_use_debug(GenContext *context) { return context->debug.builder != NULL; }
|
||||
|
||||
static inline bool llvm_basic_block_is_unused(LLVMBasicBlockRef block)
|
||||
{
|
||||
return !LLVMGetFirstInstruction(block) && !LLVMGetFirstUse(LLVMBasicBlockAsValue(block));
|
||||
}
|
||||
|
||||
static inline LLVMBasicBlockRef llvm_get_current_block_if_in_use(GenContext *context)
|
||||
{
|
||||
LLVMBasicBlockRef block = context->current_block;
|
||||
if (!LLVMGetFirstInstruction(block) && !LLVMGetFirstUse(LLVMBasicBlockAsValue(block)))
|
||||
if (llvm_basic_block_is_unused(block))
|
||||
{
|
||||
LLVMDeleteBasicBlock(block);
|
||||
context->current_block = NULL;
|
||||
|
||||
@@ -46,11 +46,9 @@ void gencontext_emit_ct_compound_stmt(GenContext *context, Ast *ast)
|
||||
/**
|
||||
* This emits a local declaration.
|
||||
*/
|
||||
static LLVMValueRef llvm_emit_local_decl(GenContext *c, Ast *ast)
|
||||
LLVMValueRef llvm_emit_local_decl(GenContext *c, Decl *decl)
|
||||
{
|
||||
|
||||
// 1. Get the declaration and the LLVM type.
|
||||
Decl *decl = ast->declare_stmt;
|
||||
LLVMTypeRef alloc_type = llvm_get_type(c, type_lowering(decl->type));
|
||||
|
||||
// 2. In the case we have a static variable,
|
||||
@@ -115,7 +113,8 @@ void llvm_emit_decl_expr_list_ignore_result(GenContext *context, Expr *expr)
|
||||
assert(expr->expr_kind == EXPR_DECL_LIST);
|
||||
VECEACH(expr->dexpr_list_expr, i)
|
||||
{
|
||||
llvm_emit_stmt(context, expr->dexpr_list_expr[i]);
|
||||
BEValue value;
|
||||
llvm_emit_expr(context, &value, expr->dexpr_list_expr[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,40 +125,22 @@ void gencontext_emit_decl_expr_list(GenContext *context, BEValue *be_value, Expr
|
||||
ByteSize last_index = size - 1;
|
||||
for (ByteSize i = 0; i < last_index; i++)
|
||||
{
|
||||
Ast *ast = expr->dexpr_list_expr[i];
|
||||
if (ast->ast_kind == AST_DECLARE_STMT)
|
||||
{
|
||||
llvm_emit_local_decl(context, ast);
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(ast->ast_kind == AST_EXPR_STMT);
|
||||
BEValue value;
|
||||
llvm_emit_expr(context, &value, ast->expr_stmt);
|
||||
}
|
||||
BEValue value;
|
||||
llvm_emit_expr(context, &value, expr->dexpr_list_expr[i]);
|
||||
}
|
||||
Ast *last = expr->dexpr_list_expr[last_index];
|
||||
Type *type;
|
||||
switch (last->ast_kind)
|
||||
Expr *last = expr->dexpr_list_expr[last_index];
|
||||
Type *type = last->type;
|
||||
llvm_emit_expr(context, be_value, last);
|
||||
if (last->expr_kind == EXPR_DECL)
|
||||
{
|
||||
case AST_EXPR_STMT:
|
||||
type = last->expr_stmt->type;
|
||||
llvm_emit_expr(context, be_value, last->expr_stmt);
|
||||
break;
|
||||
case AST_DECLARE_STMT:
|
||||
type = last->declare_stmt->var.type_info->type;
|
||||
{
|
||||
LLVMValueRef decl_value = llvm_emit_local_decl(context, last);
|
||||
if (bool_cast && last->declare_stmt->var.unwrap)
|
||||
{
|
||||
llvm_value_set_bool(be_value, LLVMConstInt(context->bool_type, 1, false));
|
||||
return;
|
||||
}
|
||||
llvm_value_set_address(be_value, decl_value, type);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE
|
||||
type = last->decl_expr->var.type_info->type;
|
||||
LLVMValueRef decl_value = llvm_emit_local_decl(context, last->decl_expr);
|
||||
if (bool_cast && last->decl_expr->var.unwrap)
|
||||
{
|
||||
llvm_value_set_bool(be_value, LLVMConstInt(context->bool_type, 1, false));
|
||||
return;
|
||||
}
|
||||
llvm_value_set_address(be_value, decl_value, type);
|
||||
}
|
||||
if (bool_cast)
|
||||
{
|
||||
@@ -988,7 +969,8 @@ void gencontext_emit_try_stmt(GenContext *c, Ast *ast)
|
||||
c->catch_block = after_try;
|
||||
|
||||
// Emit the checks, which will create jumps like we want them.
|
||||
Ast **decl_expr = ast->try_stmt.decl_expr->dexpr_list_expr;
|
||||
TODO
|
||||
Ast **decl_expr = NULL; // ast->try_old_stmt.decl_expr->dexpr_list_expr;
|
||||
BEValue be_value;
|
||||
VECEACH(decl_expr, i)
|
||||
{
|
||||
@@ -1000,7 +982,7 @@ void gencontext_emit_try_stmt(GenContext *c, Ast *ast)
|
||||
llvm_value_rvalue(c, &be_value);
|
||||
break;
|
||||
case AST_DECLARE_STMT:
|
||||
llvm_emit_local_decl(c, dexpr);
|
||||
//TODO llvm_emit_local_decl(c, dexpr);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE
|
||||
@@ -1011,7 +993,7 @@ void gencontext_emit_try_stmt(GenContext *c, Ast *ast)
|
||||
POP_ERROR();
|
||||
|
||||
// Emit the statement
|
||||
llvm_emit_stmt(c, ast->try_stmt.body);
|
||||
llvm_emit_stmt(c, ast->try_old_stmt.body);
|
||||
|
||||
// Jump to after.
|
||||
llvm_emit_br(c, after_try);
|
||||
@@ -1246,7 +1228,7 @@ void llvm_emit_stmt(GenContext *c, Ast *ast)
|
||||
gencontext_emit_expr_stmt(c, ast);
|
||||
break;
|
||||
case AST_DECLARE_STMT:
|
||||
llvm_emit_local_decl(c, ast);
|
||||
llvm_emit_local_decl(c, ast->declare_stmt);
|
||||
break;
|
||||
case AST_BREAK_STMT:
|
||||
gencontext_emit_break(c, ast);
|
||||
|
||||
@@ -654,10 +654,10 @@ static Expr *parse_identifier_starting_expression(Context *context, Expr *left)
|
||||
}
|
||||
}
|
||||
|
||||
static Expr *parse_try_expr(Context *context, Expr *left)
|
||||
static Expr *parse_try_old_expr(Context *context, Expr *left)
|
||||
{
|
||||
assert(!left && "Unexpected left hand side");
|
||||
Expr *try_expr = EXPR_NEW_TOKEN(TOKEN_IS(TOKEN_TRY) ? EXPR_TRY : EXPR_CATCH, context->tok);
|
||||
Expr *try_expr = EXPR_NEW_TOKEN(TOKEN_IS(TOKEN_TRY_OLD) ? EXPR_TRY_OLD : EXPR_CATCH_OLD, context->tok);
|
||||
advance(context);
|
||||
CONSUME_OR(TOKEN_LPAREN, poisoned_expr);
|
||||
try_expr->trycatch_expr = TRY_EXPR_OR(parse_expr(context), poisoned_expr);
|
||||
@@ -665,6 +665,30 @@ static Expr *parse_try_expr(Context *context, Expr *left)
|
||||
return try_expr;
|
||||
}
|
||||
|
||||
static Expr *parse_try_expr(Context *context, Expr *left)
|
||||
{
|
||||
assert(!left && "Unexpected left hand side");
|
||||
bool try = TOKEN_IS(TOKEN_TRY);
|
||||
Expr *try_expr = EXPR_NEW_TOKEN(EXPR_TRY, context->tok);
|
||||
advance(context);
|
||||
Expr *expr = TRY_EXPR_OR(parse_precedence(context, PREC_UNARY), poisoned_expr);
|
||||
if (try_consume(context, TOKEN_EQ))
|
||||
{
|
||||
Expr *init = TRY_EXPR_OR(parse_precedence(context, PREC_ASSIGNMENT), poisoned_expr);
|
||||
try_expr->expr_kind = EXPR_TRY_ASSIGN;
|
||||
try_expr->try_assign_expr.expr = expr;
|
||||
try_expr->try_assign_expr.is_try = try;
|
||||
try_expr->try_assign_expr.init = init;
|
||||
}
|
||||
else
|
||||
{
|
||||
try_expr->try_expr.expr = expr;
|
||||
try_expr->try_expr.is_try = try;
|
||||
}
|
||||
RANGE_EXTEND_PREV(try_expr);
|
||||
return try_expr;
|
||||
}
|
||||
|
||||
static Expr *parse_bangbang_expr(Context *context, Expr *left)
|
||||
{
|
||||
Expr *guard_expr = EXPR_NEW_EXPR(EXPR_GUARD, left);
|
||||
@@ -1075,7 +1099,7 @@ ParseRule rules[TOKEN_EOF + 1] = {
|
||||
[TOKEN_TYPEID] = { parse_type_identifier, NULL, PREC_NONE },
|
||||
[TOKEN_ANYERR] = { parse_type_identifier, NULL, PREC_NONE },
|
||||
|
||||
[TOKEN_ELSE] = { NULL, parse_else_expr, PREC_TRY_ELSE },
|
||||
[TOKEN_ELSE] = { NULL, parse_else_expr, PREC_ELSE },
|
||||
[TOKEN_QUESTION] = { NULL, parse_ternary_expr, PREC_TERNARY },
|
||||
[TOKEN_ELVIS] = { NULL, parse_ternary_expr, PREC_TERNARY },
|
||||
[TOKEN_PLUSPLUS] = { parse_unary_expr, parse_post_unary, PREC_CALL },
|
||||
@@ -1084,6 +1108,8 @@ ParseRule rules[TOKEN_EOF + 1] = {
|
||||
[TOKEN_LBRAPIPE] = { parse_expr_block, NULL, PREC_NONE },
|
||||
[TOKEN_TRY] = { parse_try_expr, NULL, PREC_NONE },
|
||||
[TOKEN_CATCH] = { parse_try_expr, NULL, PREC_NONE },
|
||||
[TOKEN_TRY_OLD] = { parse_try_old_expr, NULL, PREC_NONE },
|
||||
[TOKEN_CATCH_OLD] = { parse_try_old_expr, NULL, PREC_NONE },
|
||||
[TOKEN_BANGBANG] = { NULL, parse_bangbang_expr, PREC_CALL },
|
||||
[TOKEN_LBRACKET] = { NULL, parse_subscript_expr, PREC_CALL },
|
||||
[TOKEN_MINUS] = { parse_unary_expr, parse_binary, PREC_ADDITIVE },
|
||||
|
||||
@@ -875,16 +875,14 @@ Expr *parse_decl_expr_list(Context *context)
|
||||
if (parse_next_is_decl(context))
|
||||
{
|
||||
Decl *decl = TRY_DECL_OR(parse_decl(context), poisoned_expr);
|
||||
Ast *stmt = AST_NEW(AST_DECLARE_STMT, decl->span);
|
||||
stmt->declare_stmt = decl;
|
||||
vec_add(decl_expr->dexpr_list_expr, stmt);
|
||||
Expr *expr = expr_new(EXPR_DECL, decl->span);
|
||||
expr->decl_expr = decl;
|
||||
vec_add(decl_expr->dexpr_list_expr, expr);
|
||||
}
|
||||
else
|
||||
{
|
||||
Expr *expr = TRY_EXPR_OR(parse_expr(context), poisoned_expr);
|
||||
Ast *stmt = AST_NEW(AST_EXPR_STMT, expr->span);
|
||||
stmt->expr_stmt = expr;
|
||||
vec_add(decl_expr->dexpr_list_expr, stmt);
|
||||
vec_add(decl_expr->dexpr_list_expr, expr);
|
||||
}
|
||||
if (!try_consume(context, TOKEN_COMMA)) break;
|
||||
}
|
||||
|
||||
@@ -231,7 +231,7 @@ static inline Ast *parse_case_stmts(Context *context, TokenType case_type, Token
|
||||
static inline Ast* parse_catch_stmt(Context *context)
|
||||
{
|
||||
Ast *catch_stmt = AST_NEW_TOKEN(AST_CATCH_STMT, context->tok);
|
||||
advance_and_verify(context, TOKEN_CATCH);
|
||||
advance_and_verify(context, TOKEN_CATCH_OLD);
|
||||
|
||||
catch_stmt->catch_stmt.flow.label = TRY_DECL_OR(parse_optional_label(context, catch_stmt), false);
|
||||
|
||||
@@ -643,14 +643,15 @@ static inline Ast *parse_expr_stmt(Context *context)
|
||||
static inline Ast *parse_try_stmt(Context *context)
|
||||
{
|
||||
Ast *stmt = AST_NEW_TOKEN(AST_TRY_STMT, context->tok);
|
||||
advance_and_verify(context, TOKEN_TRY);
|
||||
advance_and_verify(context, TOKEN_TRY_OLD);
|
||||
TRY_CONSUME(TOKEN_LPAREN, "Expected a '(' after 'try'.");
|
||||
stmt->try_stmt.decl_expr = TRY_EXPR_OR(parse_decl_expr_list(context), poisoned_ast);
|
||||
stmt->try_old_stmt.decl_expr = TRY_EXPR_OR(parse_decl_expr_list(context), poisoned_ast);
|
||||
TRY_CONSUME(TOKEN_RPAREN, "Expected a ')' after 'try'.");
|
||||
stmt->try_stmt.body = TRY_AST(parse_stmt(context));
|
||||
stmt->try_old_stmt.body = TRY_AST(parse_stmt(context));
|
||||
return stmt;
|
||||
}
|
||||
|
||||
|
||||
static inline Ast *parse_decl_or_expr_stmt(Context *context)
|
||||
{
|
||||
Expr *expr = TRY_EXPR_OR(parse_expr(context), poisoned_ast);
|
||||
@@ -978,7 +979,7 @@ Ast *parse_stmt(Context *context)
|
||||
case TOKEN_IDENT:
|
||||
case TOKEN_CONST_IDENT:
|
||||
return parse_decl_or_expr_stmt(context);
|
||||
case TOKEN_TRY:
|
||||
case TOKEN_TRY_OLD:
|
||||
return parse_try_stmt(context);
|
||||
case TOKEN_DEFINE:
|
||||
return parse_define_stmt(context);
|
||||
@@ -1006,7 +1007,7 @@ Ast *parse_stmt(Context *context)
|
||||
return parse_for_stmt(context);
|
||||
case TOKEN_FOREACH:
|
||||
return parse_foreach_stmt(context);
|
||||
case TOKEN_CATCH:
|
||||
case TOKEN_CATCH_OLD:
|
||||
return parse_catch_stmt(context);
|
||||
case TOKEN_CONTINUE:
|
||||
{
|
||||
@@ -1072,6 +1073,8 @@ Ast *parse_stmt(Context *context)
|
||||
case TOKEN_CT_SIZEOF:
|
||||
case TOKEN_CT_QNAMEOF:
|
||||
case TOKEN_CT_NAMEOF:
|
||||
case TOKEN_TRY:
|
||||
case TOKEN_CATCH:
|
||||
return parse_expr_stmt(context);
|
||||
case TOKEN_ASSERT:
|
||||
return parse_assert_stmt(context);
|
||||
|
||||
@@ -3415,8 +3415,13 @@ bool sema_expr_analyse_assign_right_side(Context *context, Expr *expr, Type *lef
|
||||
|
||||
// 1. Evaluate right side to required type.
|
||||
if (!sema_analyse_expr(context, left_type, right)) return false;
|
||||
if (right->failable && lhs_is_failable == FAILABLE_NO)
|
||||
if (right->failable && lhs_is_failable != FAILABLE_YES)
|
||||
{
|
||||
if (lhs_is_failable == FAILABLE_UNWRAPPED)
|
||||
{
|
||||
SEMA_ERROR(expr->binary_expr.left, "The variable is unwrapped in this context, if you don't want to unwrap it, use () around the variable to suppress unwrapping, like 'catch err = (x)' and 'try (x)'.");
|
||||
return false;
|
||||
}
|
||||
if (!left_type) left_type = right->type;
|
||||
SEMA_ERROR(right, "'%s!' cannot be converted into '%s'.", type_to_error_string(right->type), type_to_error_string(left_type));
|
||||
return false;
|
||||
@@ -3546,6 +3551,7 @@ static bool sema_expr_analyse_assign(Context *context, Expr *expr, Expr *left, E
|
||||
expr->constant = false;
|
||||
expr->pure = false;
|
||||
|
||||
|
||||
// 1. Evaluate left side
|
||||
if (left->expr_kind == EXPR_CT_IDENT)
|
||||
{
|
||||
@@ -3556,12 +3562,24 @@ static bool sema_expr_analyse_assign(Context *context, Expr *expr, Expr *left, E
|
||||
{
|
||||
return sema_expr_analyse_ct_type_identifier_assign(context, expr, left, right);
|
||||
}
|
||||
|
||||
if (!sema_analyse_expr_value(context, NULL, left)) return false;
|
||||
|
||||
if (left->expr_kind == EXPR_TRY)
|
||||
{
|
||||
if (expr_is_ltype(left->try_expr.expr))
|
||||
{
|
||||
SEMA_ERROR(left, "This part will be evaluated to a boolean, so if you want to test an assignment, "
|
||||
"you need to use () around the assignment, like this: '%s (variable = expression)'.",
|
||||
left->expr_kind == EXPR_TRY ? "try" : "catch");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Check assignability
|
||||
if (!expr_is_ltype(left))
|
||||
{
|
||||
SEMA_ERROR(left, "Expression is not assignable.");
|
||||
SEMA_ERROR(left, "This expression is not assignable, did you make a mistake?");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -5034,7 +5052,7 @@ static inline bool sema_expr_analyse_post_unary(Context *context, Type *to, Expr
|
||||
}
|
||||
|
||||
|
||||
static inline bool sema_expr_analyse_try(Context *context, Expr *expr)
|
||||
static inline bool sema_expr_analyse_try_old(Context *context, Expr *expr)
|
||||
{
|
||||
Expr *inner = expr->trycatch_expr;
|
||||
bool success = sema_analyse_expr(context, NULL, inner);
|
||||
@@ -5050,7 +5068,99 @@ static inline bool sema_expr_analyse_try(Context *context, Expr *expr)
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool sema_expr_analyse_catch(Context *context, Expr *expr)
|
||||
static inline bool sema_expr_analyse_try_assign(Context *context, Expr *expr, bool implicitly_create_variable)
|
||||
{
|
||||
expr->constant = false;
|
||||
|
||||
Expr *init = expr->try_assign_expr.init;
|
||||
Expr *lhs = expr->try_assign_expr.expr;
|
||||
|
||||
Type *lhs_type;
|
||||
|
||||
// If we have:
|
||||
// a. In a context where implicit declaration of variables occur
|
||||
// b. The LHS is an identifier without PATH
|
||||
// c. And this variable does not exist in the scope.
|
||||
// Create it (when we know the type)
|
||||
bool create_variable;
|
||||
if (implicitly_create_variable && lhs->expr_kind == EXPR_IDENTIFIER && !lhs->identifier_expr.path
|
||||
&& !sema_resolve_normal_symbol(context, lhs->identifier_expr.identifier, NULL, false))
|
||||
{
|
||||
create_variable = true;
|
||||
lhs_type = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise we just analyse it.
|
||||
create_variable = false;
|
||||
if (expr->try_assign_expr.is_try)
|
||||
{
|
||||
if (!sema_analyse_expr_value(context, NULL, lhs)) return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!sema_analyse_expr_of_required_type(context, type_anyerr, lhs, true)) return false;
|
||||
}
|
||||
if (!expr_is_ltype(lhs))
|
||||
{
|
||||
SEMA_ERROR(lhs, "This expression is not assignable, did you make a mistake?");
|
||||
return false;
|
||||
}
|
||||
ExprFailableStatus failable_status = expr_is_failable(lhs);
|
||||
if (failable_status == FAILABLE_YES)
|
||||
{
|
||||
SEMA_ERROR(lhs, "A 'try' assignment is not possible with failable on the left hand side, did you intend 'try (variable = expr)'?");
|
||||
return false;
|
||||
}
|
||||
lhs_type = lhs->type;
|
||||
}
|
||||
|
||||
if (expr->try_assign_expr.is_try)
|
||||
{
|
||||
if (!sema_analyse_expr_of_required_type(context, lhs_type, init, true)) return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!sema_analyse_expr_of_required_type(context, NULL, init, true)) return false;
|
||||
}
|
||||
|
||||
if (!init->failable)
|
||||
{
|
||||
SEMA_ERROR(init, "Expected a failable expression to '%s'.", expr->try_assign_expr.is_try ? "try" : "catch");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (create_variable)
|
||||
{
|
||||
lhs_type = init->type;
|
||||
Decl *decl = decl_new_var(lhs->identifier_expr.identifier, type_info_new_base(lhs_type, lhs->span), VARDECL_LOCAL, VISIBLE_LOCAL);
|
||||
TODO
|
||||
// try_expr->try_expr.implicit_decl = decl;
|
||||
if (!sema_add_local(context, decl)) return false;
|
||||
if (!sema_analyse_expr_value(context, NULL, lhs)) return false;
|
||||
}
|
||||
|
||||
expr->constant = false;
|
||||
expr->pure = init->pure & lhs->pure;
|
||||
expr_set_type(expr, type_bool);
|
||||
return true;
|
||||
}
|
||||
static inline bool sema_expr_analyse_try(Context *context, Expr *expr)
|
||||
{
|
||||
Expr *inner = expr->try_expr.expr;
|
||||
if (!sema_analyse_expr(context, NULL, inner)) return false;
|
||||
expr->pure = inner->pure;
|
||||
expr->constant = false;
|
||||
if (!inner->failable)
|
||||
{
|
||||
SEMA_ERROR(expr->try_expr.expr, "Expected a failable expression to '%s'.", expr->expr_kind == EXPR_TRY ? "try" : "catch");
|
||||
return false;
|
||||
}
|
||||
expr_set_type(expr, type_bool);
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool sema_expr_analyse_catch_old(Context *context, Expr *expr)
|
||||
{
|
||||
Expr *inner = expr->trycatch_expr;
|
||||
bool success = sema_analyse_expr(context, NULL, inner);
|
||||
@@ -5938,6 +6048,14 @@ static inline bool sema_expr_analyse_ct_call(Context *context, Type *to, Expr *e
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool sema_expr_analyse_decl(Context *context, Type *to, Expr *expr)
|
||||
{
|
||||
if (!sema_analyse_local_decl(context, expr->decl_expr)) return false;
|
||||
expr_set_type(expr, expr->decl_expr->type);
|
||||
expr->pure = !expr->decl_expr->var.init_expr || expr->decl_expr->var.init_expr->pure;
|
||||
expr->constant = expr->decl_expr->var.kind == VARDECL_CONST;
|
||||
return true;
|
||||
}
|
||||
static inline bool sema_analyse_expr_dispatch(Context *context, Type *to, Expr *expr)
|
||||
{
|
||||
switch (expr->expr_kind)
|
||||
@@ -5951,6 +6069,8 @@ static inline bool sema_analyse_expr_dispatch(Context *context, Type *to, Expr *
|
||||
case EXPR_FLATPATH:
|
||||
case EXPR_NOP:
|
||||
UNREACHABLE
|
||||
case EXPR_DECL:
|
||||
return sema_expr_analyse_decl(context, to, expr);
|
||||
case EXPR_CT_CALL:
|
||||
return sema_expr_analyse_ct_call(context, to, expr);
|
||||
case EXPR_HASH_IDENT:
|
||||
@@ -5974,10 +6094,14 @@ static inline bool sema_analyse_expr_dispatch(Context *context, Type *to, Expr *
|
||||
return sema_expr_analyse_typeinfo(context, expr);
|
||||
case EXPR_SLICE:
|
||||
return sema_expr_analyse_slice(context, expr);
|
||||
case EXPR_CATCH:
|
||||
return sema_expr_analyse_catch(context, expr);
|
||||
case EXPR_TRY:
|
||||
return sema_expr_analyse_try(context, expr);
|
||||
case EXPR_TRY_ASSIGN:
|
||||
return sema_expr_analyse_try_assign(context, expr, false);
|
||||
case EXPR_CATCH_OLD:
|
||||
return sema_expr_analyse_catch_old(context, expr);
|
||||
case EXPR_TRY_OLD:
|
||||
return sema_expr_analyse_try_old(context, expr);
|
||||
case EXPR_TYPEOF:
|
||||
return sema_expr_analyse_typeof(context, expr);
|
||||
case EXPR_ELSE:
|
||||
|
||||
@@ -452,8 +452,8 @@ bool sema_unwrap_var(Context *context, Decl *decl)
|
||||
alias->var.kind = VARDECL_ALIAS;
|
||||
alias->var.alias = decl;
|
||||
alias->var.failable = false;
|
||||
decl->resolve_status = RESOLVE_DONE;
|
||||
return sema_append_local(context, decl);
|
||||
alias->resolve_status = RESOLVE_DONE;
|
||||
return sema_append_local(context, alias);
|
||||
}
|
||||
|
||||
bool sema_rewrap_var(Context *context, Decl *decl)
|
||||
|
||||
@@ -6,11 +6,81 @@
|
||||
|
||||
#pragma mark --- Helper functions
|
||||
|
||||
static void sema_add_unwraps_from_try(Context *c, Expr *last_expr)
|
||||
{
|
||||
if (last_expr->expr_kind == EXPR_DECL_LIST)
|
||||
{
|
||||
unsigned elements = vec_size(last_expr->dexpr_list_expr);
|
||||
if (!elements) return;
|
||||
last_expr = last_expr->dexpr_list_expr[elements - 1];
|
||||
}
|
||||
// Flatten group.
|
||||
while (last_expr->expr_kind == EXPR_GROUP) last_expr = last_expr->group_expr;
|
||||
if (last_expr->expr_kind == EXPR_TRY && last_expr->try_expr.is_try &&
|
||||
last_expr->try_expr.expr->expr_kind == EXPR_IDENTIFIER)
|
||||
{
|
||||
Decl *var = last_expr->try_expr.expr->identifier_expr.decl;
|
||||
if (var->decl_kind != DECL_VAR || !var->var.failable) return;
|
||||
sema_unwrap_var(c, var);
|
||||
return;
|
||||
}
|
||||
if (last_expr->expr_kind != EXPR_BINARY || last_expr->binary_expr.operator != BINARYOP_AND) return;
|
||||
sema_add_unwraps_from_try(c, last_expr->binary_expr.left);
|
||||
sema_add_unwraps_from_try(c, last_expr->binary_expr.right);
|
||||
}
|
||||
|
||||
static Decl *sema_find_unwrappable_from_catch(Context *c, Expr *last_expr)
|
||||
{
|
||||
// 1. Flatten a decl list.
|
||||
if (last_expr->expr_kind == EXPR_DECL_LIST)
|
||||
{
|
||||
unsigned elements = vec_size(last_expr->dexpr_list_expr);
|
||||
if (!elements) return NULL;
|
||||
last_expr = last_expr->dexpr_list_expr[elements - 1];
|
||||
}
|
||||
|
||||
// Flatten groups on top.
|
||||
while (last_expr->expr_kind == EXPR_GROUP) last_expr = last_expr->group_expr;
|
||||
|
||||
Expr *variable = NULL;
|
||||
|
||||
// 1. Get the variable in "if (check x)"
|
||||
if (last_expr->expr_kind == EXPR_TRY && !last_expr->try_expr.is_try)
|
||||
{
|
||||
variable = last_expr->try_expr.expr;
|
||||
}
|
||||
// 2. Get the variable in "if (check err = x)"
|
||||
if (last_expr->expr_kind == EXPR_TRY_ASSIGN && !last_expr->try_assign_expr.is_try)
|
||||
{
|
||||
variable = last_expr->try_assign_expr.init;
|
||||
}
|
||||
|
||||
// 3. Not found or not identifier -> exit
|
||||
if (!variable || variable->expr_kind != EXPR_IDENTIFIER) return NULL;
|
||||
|
||||
Decl *decl = variable->identifier_expr.decl;
|
||||
|
||||
// 4. Not a var declaration, when could this happen...? Possibly redundant check.
|
||||
if (decl->decl_kind != DECL_VAR) return NULL;
|
||||
|
||||
assert(decl->var.failable && "The variable should always be failable at this point.");
|
||||
|
||||
// 5. Locals and globals may be unwrapped
|
||||
switch (decl->var.kind)
|
||||
{
|
||||
case VARDECL_LOCAL:
|
||||
case VARDECL_GLOBAL:
|
||||
return decl;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#pragma mark --- Sema analyse stmts
|
||||
|
||||
|
||||
|
||||
static inline bool sema_analyse_block_return_stmt(Context *context, Ast *statement)
|
||||
{
|
||||
assert(context->active_scope.flags & (SCOPE_EXPR_BLOCK | SCOPE_MACRO));
|
||||
@@ -91,7 +161,7 @@ static inline bool sema_analyse_decl_expr_list(Context *context, Expr *expr)
|
||||
{
|
||||
assert(expr->expr_kind == EXPR_DECL_LIST);
|
||||
|
||||
Ast **dexprs = expr->dexpr_list_expr;
|
||||
Expr **dexprs = expr->dexpr_list_expr;
|
||||
unsigned entries = vec_size(dexprs);
|
||||
|
||||
// 1. Special case, there are no entries, so the type is void
|
||||
@@ -101,30 +171,13 @@ static inline bool sema_analyse_decl_expr_list(Context *context, Expr *expr)
|
||||
return true;
|
||||
}
|
||||
|
||||
// 2. Walk through each of our declarations / expressions as if they were regular statements.
|
||||
// 2. Walk through each of our declarations / expressions as if they were regular expressions.
|
||||
for (unsigned i = 0; i < entries; i++)
|
||||
{
|
||||
if (!sema_analyse_statement(context, dexprs[i])) return false;
|
||||
if (!sema_analyse_expr(context, NULL, dexprs[i])) return false;
|
||||
}
|
||||
|
||||
// 3. We now look at the final expression and copy its type as the type of the entire list.
|
||||
// There is a subtle difference here. The expression might have a different original type
|
||||
// than the expression's type. In most (all?) cases this is isn't something one uses,
|
||||
// but this makes sure that analysis is correct.
|
||||
Ast *last = dexprs[entries - 1];
|
||||
switch (last->ast_kind)
|
||||
{
|
||||
case AST_DECLARE_STMT:
|
||||
// In the declaration case, the value is the type of the declaration.
|
||||
expr_set_type(expr, last->declare_stmt->type);
|
||||
break;
|
||||
case AST_EXPR_STMT:
|
||||
// In the expression case, *copy* the expression's types.
|
||||
expr_copy_types(expr, last->expr_stmt);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE
|
||||
}
|
||||
expr_set_type(expr, dexprs[entries - 1]->type);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -157,53 +210,47 @@ static inline bool sema_analyse_cond(Context *context, Expr *expr, bool cast_to_
|
||||
|
||||
// 3. We look at the last element (which is guaranteed to exist because
|
||||
// the type was not void.
|
||||
Ast *last = VECLAST(expr->dexpr_list_expr);
|
||||
Expr *last = VECLAST(expr->dexpr_list_expr);
|
||||
|
||||
switch (last->ast_kind)
|
||||
if (last->expr_kind == EXPR_DECL)
|
||||
{
|
||||
case AST_EXPR_STMT:
|
||||
// 3a. Check for failables in case of an expression.
|
||||
if (last->expr_stmt->failable)
|
||||
{
|
||||
SEMA_ERROR(last, "'%s!' cannot be converted into '%s'.",
|
||||
type_to_error_string(last->expr_stmt->type),
|
||||
cast_to_bool ? "bool" : type_to_error_string(last->expr_stmt->type));
|
||||
}
|
||||
// 3b. Cast to bool if that is needed
|
||||
if (cast_to_bool)
|
||||
{
|
||||
if (!cast_implicit(last->expr_stmt, type_bool)) return false;
|
||||
}
|
||||
return true;
|
||||
case AST_DECLARE_STMT:
|
||||
// 3c. The declaration case
|
||||
Decl *decl = last->decl_expr;
|
||||
Expr *init = decl->var.init_expr;
|
||||
// 3d. We expect an initialization for the last declaration.
|
||||
if (!init)
|
||||
{
|
||||
// 3c. The declaration case
|
||||
Decl *decl = last->declare_stmt;
|
||||
Expr *init = decl->var.init_expr;
|
||||
// 3d. We expect an initialization for the last declaration.
|
||||
if (!init)
|
||||
{
|
||||
SEMA_ERROR(last, "Expected a declaration with initializer.");
|
||||
return false;
|
||||
}
|
||||
// 3e. Expect that it isn't a failable
|
||||
if (init->failable && !decl->var.unwrap)
|
||||
{
|
||||
SEMA_ERROR(last, "'%s!' cannot be converted into '%s'.",
|
||||
type_to_error_string(last->expr_stmt->type),
|
||||
cast_to_bool ? "bool" : type_to_error_string(init->type));
|
||||
}
|
||||
// TODO document
|
||||
if (!decl->var.unwrap && cast_to_bool && cast_to_bool_kind(decl->var.type_info->type) == CAST_ERROR)
|
||||
{
|
||||
SEMA_ERROR(last->declare_stmt->var.init_expr, "The expression needs to be convertible to a boolean.");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
SEMA_ERROR(last, "Expected a declaration with initializer.");
|
||||
return false;
|
||||
}
|
||||
default:
|
||||
UNREACHABLE
|
||||
// 3e. Expect that it isn't a failable
|
||||
if (init->failable && !decl->var.unwrap)
|
||||
{
|
||||
SEMA_ERROR(last, "'%s!' cannot be converted into '%s'.",
|
||||
type_to_error_string(last->type),
|
||||
cast_to_bool ? "bool" : type_to_error_string(init->type));
|
||||
}
|
||||
// TODO document
|
||||
if (!decl->var.unwrap && cast_to_bool && cast_to_bool_kind(decl->var.type_info->type) == CAST_ERROR)
|
||||
{
|
||||
SEMA_ERROR(last->decl_expr->var.init_expr, "The expression needs to be convertible to a boolean.");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// 3a. Check for failables in case of an expression.
|
||||
if (last->failable)
|
||||
{
|
||||
SEMA_ERROR(last, "'%s!' cannot be converted into '%s'.",
|
||||
type_to_error_string(last->type),
|
||||
cast_to_bool ? "bool" : type_to_error_string(last->type));
|
||||
}
|
||||
// 3b. Cast to bool if that is needed
|
||||
if (cast_to_bool)
|
||||
{
|
||||
if (!cast_implicit(last, type_bool)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool sema_analyse_stmt_placement(Expr *cond, Ast *stmt)
|
||||
@@ -238,23 +285,26 @@ static inline bool sema_analyse_while_stmt(Context *context, Ast *statement)
|
||||
// 4. Push break / continue - which is independent of the scope.
|
||||
PUSH_BREAKCONT(statement);
|
||||
|
||||
// 5. Analyse the statement
|
||||
// 5. Add any try unwraps.
|
||||
sema_add_unwraps_from_try(context, cond);
|
||||
|
||||
// 6. Analyse the statement
|
||||
success = sema_analyse_statement(context, body);
|
||||
|
||||
// 6. Pop break / continue
|
||||
// 7. Pop break / continue
|
||||
POP_BREAKCONT();
|
||||
|
||||
// 7. Check placement, in case of a single statement, it must be placed on the same line.
|
||||
// 8. Check placement, in case of a single statement, it must be placed on the same line.
|
||||
if (success && !sema_analyse_stmt_placement(cond, body))
|
||||
{
|
||||
SEMA_ERROR(body, "A single statement after 'while' must be placed on the same line, or be enclosed in {}.");
|
||||
return SCOPE_POP_ERROR();
|
||||
}
|
||||
|
||||
// 8. Pop defers attach them to the statement if needed
|
||||
// 9. Pop defers attach them to the statement if needed
|
||||
context_pop_defers_and_replace_ast(context, body);
|
||||
|
||||
// 9. Pop the while scope.
|
||||
// 10. Pop the while scope.
|
||||
SCOPE_END;
|
||||
|
||||
return success;
|
||||
@@ -325,7 +375,7 @@ static inline bool sema_analyse_do_stmt(Context *context, Ast *statement)
|
||||
/**
|
||||
* Analyse a regular local declaration, e.g. int x = 123
|
||||
*/
|
||||
static inline bool sema_analyse_local_decl(Context *context, Decl *decl)
|
||||
bool sema_analyse_local_decl(Context *context, Decl *decl)
|
||||
{
|
||||
assert(decl->decl_kind == DECL_VAR && "Unexpected declaration type");
|
||||
|
||||
@@ -759,12 +809,12 @@ static bool sema_rewrite_foreach_to_for(Context *context, Ast *statement, Expr *
|
||||
|
||||
// Generate Foo *value, FooIterator ".iterator" = foo.iterator();
|
||||
Expr *init_expr = expr_new(EXPR_DECL_LIST, enumerator->span);
|
||||
Ast *ast = new_ast(AST_DECLARE_STMT, value->span);
|
||||
ast->declare_stmt = value;
|
||||
vec_add(init_expr->dexpr_list_expr, ast);
|
||||
ast = new_ast(AST_DECLARE_STMT, enumerator->span);
|
||||
ast->declare_stmt = iterator;
|
||||
vec_add(init_expr->dexpr_list_expr, ast);
|
||||
Expr *expr = expr_new(EXPR_DECL, value->span);
|
||||
expr->decl_expr = value;
|
||||
vec_add(init_expr->dexpr_list_expr, expr);
|
||||
expr = expr_new(EXPR_DECL, enumerator->span);
|
||||
expr->decl_expr = iterator;
|
||||
vec_add(init_expr->dexpr_list_expr, expr);
|
||||
init_expr->resolve_status = RESOLVE_DONE;
|
||||
expr_set_type(init_expr, iterator->type);
|
||||
|
||||
@@ -1046,6 +1096,7 @@ static inline bool sema_analyse_if_stmt(Context *context, Ast *statement)
|
||||
|
||||
SCOPE_START_WITH_LABEL(statement->if_stmt.flow.label);
|
||||
|
||||
sema_add_unwraps_from_try(context, statement->if_stmt.cond);
|
||||
success = success && sema_analyse_statement(context, statement->if_stmt.then_body);
|
||||
then_jump = context->active_scope.jump_end;
|
||||
|
||||
@@ -1055,6 +1106,8 @@ static inline bool sema_analyse_if_stmt(Context *context, Ast *statement)
|
||||
if (statement->if_stmt.else_body)
|
||||
{
|
||||
SCOPE_START_WITH_LABEL(statement->if_stmt.flow.label);
|
||||
Decl *possible_unwrap = sema_find_unwrappable_from_catch(context, statement->if_stmt.cond);
|
||||
if (possible_unwrap) sema_unwrap_var(context, possible_unwrap);
|
||||
success = success && sema_analyse_statement(context, statement->if_stmt.else_body);
|
||||
else_jump = context->active_scope.jump_end;
|
||||
SCOPE_END;
|
||||
@@ -1063,7 +1116,11 @@ static inline bool sema_analyse_if_stmt(Context *context, Ast *statement)
|
||||
context_pop_defers_and_replace_ast(context, statement);
|
||||
|
||||
SCOPE_OUTER_END;
|
||||
|
||||
if (then_jump)
|
||||
{
|
||||
Decl *possible_unwrap = sema_find_unwrappable_from_catch(context, statement->if_stmt.cond);
|
||||
if (possible_unwrap) sema_unwrap_var(context, possible_unwrap);
|
||||
}
|
||||
if (then_jump && else_jump && !statement->flow.has_break)
|
||||
{
|
||||
context->active_scope.jump_end = true;
|
||||
@@ -1433,21 +1490,6 @@ static inline bool sema_analyse_compound_statement_no_scope(Context *context, As
|
||||
return all_ok;
|
||||
}
|
||||
|
||||
static inline Type *ast_cond_type(Expr *expr)
|
||||
{
|
||||
assert(expr->expr_kind == EXPR_DECL_LIST);
|
||||
Ast *last = VECLAST(expr->dexpr_list_expr);
|
||||
switch (last->ast_kind)
|
||||
{
|
||||
case AST_EXPR_STMT:
|
||||
return last->expr_stmt->type;
|
||||
case AST_DECLARE_STMT:
|
||||
return last->declare_stmt->var.type_info->type;
|
||||
default:
|
||||
UNREACHABLE
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool sema_check_type_case(Context *context, Type *switch_type, Ast *case_stmt, Ast **cases, unsigned index, bool use_type_id)
|
||||
{
|
||||
if (!sema_resolve_type_info(context, case_stmt->case_stmt.type_info)) return false;
|
||||
@@ -1693,7 +1735,7 @@ static bool sema_analyse_switch_stmt(Context *context, Ast *statement)
|
||||
if (!sema_analyse_cond(context, cond, false)) return false;
|
||||
|
||||
|
||||
Type *switch_type = ast_cond_type(cond)->canonical;
|
||||
Type *switch_type = VECLAST(cond->dexpr_list_expr)->type->canonical;
|
||||
statement->switch_stmt.defer = context->active_scope.defer_last;
|
||||
if (!sema_analyse_switch_body(context, statement, cond->span,
|
||||
switch_type->canonical,
|
||||
@@ -1822,9 +1864,11 @@ static bool sema_analyse_catch_stmt(Context *context, Ast *statement)
|
||||
|
||||
static bool sema_analyse_try_stmt(Context *context, Ast *stmt)
|
||||
{
|
||||
assert(stmt->try_stmt.decl_expr->expr_kind == EXPR_DECL_LIST);
|
||||
assert(stmt->try_old_stmt.decl_expr->expr_kind == EXPR_DECL_LIST);
|
||||
|
||||
Ast **dexprs = stmt->try_stmt.decl_expr->dexpr_list_expr;
|
||||
Expr **dexprs = stmt->try_old_stmt.decl_expr->dexpr_list_expr;
|
||||
TODO
|
||||
/*TODO
|
||||
SCOPE_START
|
||||
unsigned entries = vec_size(dexprs);
|
||||
for (unsigned i = 0; i < entries; i++)
|
||||
@@ -1855,11 +1899,12 @@ static bool sema_analyse_try_stmt(Context *context, Ast *stmt)
|
||||
sema_unwrap_var(context, decl);
|
||||
}
|
||||
}
|
||||
if (!sema_analyse_statement(context, stmt->try_stmt.body))
|
||||
if (!sema_analyse_statement(context, stmt->try_old_stmt.body))
|
||||
{
|
||||
return SCOPE_POP_ERROR();
|
||||
}
|
||||
SCOPE_END;
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2068,13 +2113,12 @@ static bool sema_analyse_requires(Context *context, Ast *docs, Ast ***asserts)
|
||||
|
||||
VECEACH(declexpr->dexpr_list_expr, j)
|
||||
{
|
||||
Ast *ast = declexpr->dexpr_list_expr[j];
|
||||
if (ast->ast_kind != AST_EXPR_STMT)
|
||||
Expr *expr = declexpr->dexpr_list_expr[j];
|
||||
if (expr->expr_kind == EXPR_DECL)
|
||||
{
|
||||
SEMA_ERROR(ast, "Only expressions are allowed.");
|
||||
SEMA_ERROR(expr, "Only expressions are allowed.");
|
||||
return false;
|
||||
}
|
||||
Expr *expr = ast->expr_stmt;
|
||||
if (!sema_analyse_expr_of_required_type(context, type_bool, expr, false)) return false;
|
||||
Ast *assert = new_ast(AST_ASSERT_STMT, expr->span);
|
||||
assert->assert_stmt.expr = expr;
|
||||
|
||||
@@ -190,6 +190,8 @@ const char *token_type_to_string(TokenType type)
|
||||
return "break";
|
||||
case TOKEN_CASE:
|
||||
return "case";
|
||||
case TOKEN_CATCH_OLD:
|
||||
return "catchx";
|
||||
case TOKEN_CATCH:
|
||||
return "catch";
|
||||
case TOKEN_CONST:
|
||||
@@ -248,6 +250,8 @@ const char *token_type_to_string(TokenType type)
|
||||
return "switch";
|
||||
case TOKEN_TRUE:
|
||||
return "true";
|
||||
case TOKEN_TRY_OLD:
|
||||
return "tryx";
|
||||
case TOKEN_TRY:
|
||||
return "try";
|
||||
case TOKEN_TYPEID:
|
||||
|
||||
@@ -29,9 +29,9 @@ entry:
|
||||
%1 = bitcast %Large* %indirectarg to i8*
|
||||
%2 = bitcast %Large* %l to i8*
|
||||
call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %1, i8* align 8 %2, i32 64, i1 false)
|
||||
call void @pass_large(%Large* %indirectarg)
|
||||
call void @pass_large(%Large* align 8 %indirectarg)
|
||||
%3 = bitcast %Large* %indirectarg1 to i8*
|
||||
%4 = bitcast %Large* %l to i8*
|
||||
call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %3, i8* align 8 %4, i32 64, i1 false)
|
||||
call void @pass_large(%Large* %indirectarg1)
|
||||
call void @pass_large(%Large* align 8 %indirectarg1)
|
||||
ret void
|
||||
|
||||
109
test/test_suite/abi/vec2_aarch64.c3t
Normal file
109
test/test_suite/abi/vec2_aarch64.c3t
Normal file
@@ -0,0 +1,109 @@
|
||||
// #target: aarch64_linux
|
||||
module abi;
|
||||
|
||||
struct Vector2 {
|
||||
float x;
|
||||
float y;
|
||||
}
|
||||
extern func Vector2 vector2_zero() { return Vector2({}); }
|
||||
extern func Vector2 vector2_one() { return Vector2({}); }
|
||||
extern func Vector2 vector2_add(Vector2 v1, Vector2 v2) { return Vector2({}); }
|
||||
extern func Vector2 vector2_add_value(Vector2 v, float add) { return Vector2({}); }
|
||||
extern func Vector2 vector2_subtract(Vector2 v1, Vector2 v2) { return Vector2({}); }
|
||||
extern func Vector2 vector2_subtract_value(Vector2 v, float sub) { return Vector2({}); }
|
||||
|
||||
// #expect: abi.ll
|
||||
%Vector2 = type { float, float }
|
||||
|
||||
define %Vector2 @vector2_zero()
|
||||
entry:
|
||||
%literal = alloca %Vector2, align 4
|
||||
%0 = bitcast %Vector2* %literal to i8*
|
||||
call void @llvm.memset.p0i8.i64(i8* align 4 %0, i8 0, i64 8, i1 false)
|
||||
%1 = load %Vector2, %Vector2* %literal, align 4
|
||||
ret %Vector2 %1
|
||||
}
|
||||
|
||||
define %Vector2 @vector2_one()
|
||||
entry:
|
||||
%literal = alloca %Vector2, align 4
|
||||
%0 = bitcast %Vector2* %literal to i8*
|
||||
call void @llvm.memset.p0i8.i64(i8* align 4 %0, i8 0, i64 8, i1 false)
|
||||
%1 = load %Vector2, %Vector2* %literal, align 4
|
||||
ret %Vector2 %1
|
||||
|
||||
|
||||
define %Vector2 @vector2_add(float %0, float %1, float %2, float %3)
|
||||
entry:
|
||||
%v1 = alloca %Vector2, align 4
|
||||
%v2 = alloca %Vector2, align 4
|
||||
%literal = alloca %Vector2, align 4
|
||||
%coerce = bitcast %Vector2* %v1 to { float, float }*
|
||||
%4 = getelementptr inbounds { float, float }, { float, float }* %coerce, i32 0, i32 0
|
||||
store float %0, float* %4, align 4
|
||||
%5 = getelementptr inbounds { float, float }, { float, float }* %coerce, i32 0, i32 1
|
||||
store float %1, float* %5, align 4
|
||||
%coerce1 = bitcast %Vector2* %v2 to { float, float }*
|
||||
%6 = getelementptr inbounds { float, float }, { float, float }* %coerce1, i32 0, i32 0
|
||||
store float %2, float* %6, align 4
|
||||
%7 = getelementptr inbounds { float, float }, { float, float }* %coerce1, i32 0, i32 1
|
||||
store float %3, float* %7, align 4
|
||||
%8 = bitcast %Vector2* %literal to i8*
|
||||
call void @llvm.memset.p0i8.i64(i8* align 4 %8, i8 0, i64 8, i1 false)
|
||||
%9 = load %Vector2, %Vector2* %literal, align 4
|
||||
ret %Vector2 %9
|
||||
}
|
||||
|
||||
define %Vector2 @vector2_add_value(float %0, float %1, float %2)
|
||||
entry:
|
||||
%v = alloca %Vector2, align 4
|
||||
%add = alloca float, align 4
|
||||
%literal = alloca %Vector2, align 4
|
||||
%coerce = bitcast %Vector2* %v to { float, float }*
|
||||
%3 = getelementptr inbounds { float, float }, { float, float }* %coerce, i32 0, i32 0
|
||||
store float %0, float* %3, align 4
|
||||
%4 = getelementptr inbounds { float, float }, { float, float }* %coerce, i32 0, i32 1
|
||||
store float %1, float* %4, align 4
|
||||
store float %2, float* %add, align 4
|
||||
%5 = bitcast %Vector2* %literal to i8*
|
||||
call void @llvm.memset.p0i8.i64(i8* align 4 %5, i8 0, i64 8, i1 false)
|
||||
%6 = load %Vector2, %Vector2* %literal, align 4
|
||||
ret %Vector2 %6
|
||||
}
|
||||
|
||||
define %Vector2 @vector2_subtract(float %0, float %1, float %2, float %3)
|
||||
entry:
|
||||
%v1 = alloca %Vector2, align 4
|
||||
%v2 = alloca %Vector2, align 4
|
||||
%literal = alloca %Vector2, align 4
|
||||
%coerce = bitcast %Vector2* %v1 to { float, float }*
|
||||
%4 = getelementptr inbounds { float, float }, { float, float }* %coerce, i32 0, i32 0
|
||||
store float %0, float* %4, align 4
|
||||
%5 = getelementptr inbounds { float, float }, { float, float }* %coerce, i32 0, i32 1
|
||||
store float %1, float* %5, align 4
|
||||
%coerce1 = bitcast %Vector2* %v2 to { float, float }*
|
||||
%6 = getelementptr inbounds { float, float }, { float, float }* %coerce1, i32 0, i32 0
|
||||
store float %2, float* %6, align 4
|
||||
%7 = getelementptr inbounds { float, float }, { float, float }* %coerce1, i32 0, i32 1
|
||||
store float %3, float* %7, align 4
|
||||
%8 = bitcast %Vector2* %literal to i8*
|
||||
call void @llvm.memset.p0i8.i64(i8* align 4 %8, i8 0, i64 8, i1 false)
|
||||
%9 = load %Vector2, %Vector2* %literal, align 4
|
||||
ret %Vector2 %9
|
||||
|
||||
define %Vector2 @vector2_subtract_value(float %0, float %1, float %2)
|
||||
entry:
|
||||
%v = alloca %Vector2, align 4
|
||||
%sub = alloca float, align 4
|
||||
%literal = alloca %Vector2, align 4
|
||||
%coerce = bitcast %Vector2* %v to { float, float }*
|
||||
%3 = getelementptr inbounds { float, float }, { float, float }* %coerce, i32 0, i32 0
|
||||
store float %0, float* %3, align 4
|
||||
%4 = getelementptr inbounds { float, float }, { float, float }* %coerce, i32 0, i32 1
|
||||
store float %1, float* %4, align 4
|
||||
store float %2, float* %sub, align 4
|
||||
%5 = bitcast %Vector2* %literal to i8*
|
||||
call void @llvm.memset.p0i8.i64(i8* align 4 %5, i8 0, i64 8, i1 false)
|
||||
%6 = load %Vector2, %Vector2* %literal, align 4
|
||||
ret %Vector2 %6
|
||||
}
|
||||
110
test/test_suite/abi/vec2_x64.c3t
Normal file
110
test/test_suite/abi/vec2_x64.c3t
Normal file
@@ -0,0 +1,110 @@
|
||||
// #target: x64_darwin
|
||||
module abi;
|
||||
|
||||
struct Vector2 {
|
||||
float x;
|
||||
float y;
|
||||
}
|
||||
extern func Vector2 vector2_zero() { return Vector2({}); }
|
||||
extern func Vector2 vector2_one() { return Vector2({}); }
|
||||
extern func Vector2 vector2_add(Vector2 v1, Vector2 v2) { return Vector2({}); }
|
||||
extern func Vector2 vector2_add_value(Vector2 v, float add) { return Vector2({}); }
|
||||
extern func Vector2 vector2_subtract(Vector2 v1, Vector2 v2) { return Vector2({}); }
|
||||
extern func Vector2 vector2_subtract_value(Vector2 v, float sub) { return Vector2({}); }
|
||||
|
||||
// #expect: abi.ll
|
||||
|
||||
%Vector2 = type { float, float }
|
||||
|
||||
define <2 x float> @vector2_zero()
|
||||
entry:
|
||||
%literal = alloca %Vector2, align 4
|
||||
%tempcoerce = alloca <2 x float>, align 8
|
||||
%0 = bitcast %Vector2* %literal to i8*
|
||||
call void @llvm.memset.p0i8.i64(i8* align 4 %0, i8 0, i64 8, i1 false)
|
||||
%1 = bitcast <2 x float>* %tempcoerce to i8*
|
||||
%2 = bitcast %Vector2* %literal to i8*
|
||||
call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %1, i8* align 4 %2, i32 8, i1 false)
|
||||
%3 = load <2 x float>, <2 x float>* %tempcoerce, align 8
|
||||
ret <2 x float> %3
|
||||
|
||||
define <2 x float> @vector2_one()
|
||||
%literal = alloca %Vector2, align 4
|
||||
%tempcoerce = alloca <2 x float>, align 8
|
||||
%0 = bitcast %Vector2* %literal to i8*
|
||||
call void @llvm.memset.p0i8.i64(i8* align 4 %0, i8 0, i64 8, i1 false)
|
||||
%1 = bitcast <2 x float>* %tempcoerce to i8*
|
||||
%2 = bitcast %Vector2* %literal to i8*
|
||||
call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %1, i8* align 4 %2, i32 8, i1 false)
|
||||
%3 = load <2 x float>, <2 x float>* %tempcoerce, align 8
|
||||
ret <2 x float> %3
|
||||
|
||||
define <2 x float> @vector2_add(<2 x float> %0, <2 x float> %1)
|
||||
entry:
|
||||
%v1 = alloca %Vector2, align 4
|
||||
%v2 = alloca %Vector2, align 4
|
||||
%literal = alloca %Vector2, align 4
|
||||
%tempcoerce = alloca <2 x float>, align 8
|
||||
%2 = bitcast %Vector2* %v1 to <2 x float>*
|
||||
store <2 x float> %0, <2 x float>* %2, align 4
|
||||
%3 = bitcast %Vector2* %v2 to <2 x float>*
|
||||
store <2 x float> %1, <2 x float>* %3, align 4
|
||||
%4 = bitcast %Vector2* %literal to i8*
|
||||
call void @llvm.memset.p0i8.i64(i8* align 4 %4, i8 0, i64 8, i1 false)
|
||||
%5 = bitcast <2 x float>* %tempcoerce to i8*
|
||||
%6 = bitcast %Vector2* %literal to i8*
|
||||
call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %5, i8* align 4 %6, i32 8, i1 false)
|
||||
%7 = load <2 x float>, <2 x float>* %tempcoerce, align 8
|
||||
ret <2 x float> %7
|
||||
}
|
||||
|
||||
define <2 x float> @vector2_add_value(<2 x float> %0, float %1) #0 {
|
||||
%v = alloca %Vector2, align 4
|
||||
%add = alloca float, align 4
|
||||
%literal = alloca %Vector2, align 4
|
||||
%tempcoerce = alloca <2 x float>, align 8
|
||||
%2 = bitcast %Vector2* %v to <2 x float>*
|
||||
store <2 x float> %0, <2 x float>* %2, align 4
|
||||
store float %1, float* %add, align 4
|
||||
%3 = bitcast %Vector2* %literal to i8*
|
||||
call void @llvm.memset.p0i8.i64(i8* align 4 %3, i8 0, i64 8, i1 false)
|
||||
%4 = bitcast <2 x float>* %tempcoerce to i8*
|
||||
%5 = bitcast %Vector2* %literal to i8*
|
||||
call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %4, i8* align 4 %5, i32 8, i1 false)
|
||||
%6 = load <2 x float>, <2 x float>* %tempcoerce, align 8
|
||||
ret <2 x float> %6
|
||||
|
||||
define <2 x float> @vector2_subtract(<2 x float> %0, <2 x float> %1)
|
||||
entry:
|
||||
%v1 = alloca %Vector2, align 4
|
||||
%v2 = alloca %Vector2, align 4
|
||||
%literal = alloca %Vector2, align 4
|
||||
%tempcoerce = alloca <2 x float>, align 8
|
||||
%2 = bitcast %Vector2* %v1 to <2 x float>*
|
||||
store <2 x float> %0, <2 x float>* %2, align 4
|
||||
%3 = bitcast %Vector2* %v2 to <2 x float>*
|
||||
store <2 x float> %1, <2 x float>* %3, align 4
|
||||
%4 = bitcast %Vector2* %literal to i8*
|
||||
call void @llvm.memset.p0i8.i64(i8* align 4 %4, i8 0, i64 8, i1 false)
|
||||
%5 = bitcast <2 x float>* %tempcoerce to i8*
|
||||
%6 = bitcast %Vector2* %literal to i8*
|
||||
call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %5, i8* align 4 %6, i32 8, i1 false)
|
||||
%7 = load <2 x float>, <2 x float>* %tempcoerce, align 8
|
||||
ret <2 x float> %7
|
||||
|
||||
define <2 x float> @vector2_subtract_value(<2 x float> %0, float %1)
|
||||
entry:
|
||||
%v = alloca %Vector2, align 4
|
||||
%sub = alloca float, align 4
|
||||
%literal = alloca %Vector2, align 4
|
||||
%tempcoerce = alloca <2 x float>, align 8
|
||||
%2 = bitcast %Vector2* %v to <2 x float>*
|
||||
store <2 x float> %0, <2 x float>* %2, align 4
|
||||
store float %1, float* %sub, align 4
|
||||
%3 = bitcast %Vector2* %literal to i8*
|
||||
call void @llvm.memset.p0i8.i64(i8* align 4 %3, i8 0, i64 8, i1 false)
|
||||
%4 = bitcast <2 x float>* %tempcoerce to i8*
|
||||
%5 = bitcast %Vector2* %literal to i8*
|
||||
call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %4, i8* align 4 %5, i32 8, i1 false)
|
||||
%6 = load <2 x float>, <2 x float>* %tempcoerce, align 8
|
||||
ret <2 x float> %6
|
||||
149
test/test_suite/errors/try_assign.c3t
Normal file
149
test/test_suite/errors/try_assign.c3t
Normal file
@@ -0,0 +1,149 @@
|
||||
// #target: x64_darwin
|
||||
|
||||
extern func int! err();
|
||||
errtype FooErr { int x; }
|
||||
extern func int printf(char* fmt, ...);
|
||||
|
||||
func void main()
|
||||
{
|
||||
int x = 123;
|
||||
int! z = 234;
|
||||
int! w;
|
||||
int gh = 1;
|
||||
if ((try x = z) && (try gh = w))
|
||||
{
|
||||
printf("Success %d && %d!\n", x, gh);
|
||||
}
|
||||
if (try x = z + w)
|
||||
{
|
||||
printf("Test\n");
|
||||
}
|
||||
anyerr e;
|
||||
if (catch e = z)
|
||||
{
|
||||
printf("Oh noes!\n");
|
||||
}
|
||||
}
|
||||
|
||||
// #expect: try_assign.ll
|
||||
|
||||
entry:
|
||||
%x = alloca i32, align 4
|
||||
%z = alloca i32, align 4
|
||||
%z.f = alloca %error_union, align 8
|
||||
%w = alloca i32, align 4
|
||||
%w.f = alloca %error_union, align 8
|
||||
%gh = alloca i32, align 4
|
||||
%e = alloca %error_union, align 8
|
||||
store i32 123, i32* %x, align 4
|
||||
store %error_union zeroinitializer, %error_union* %z.f, align 8
|
||||
store i32 234, i32* %z, align 4
|
||||
store %error_union zeroinitializer, %error_union* %z.f, align 8
|
||||
store %error_union zeroinitializer, %error_union* %w.f, align 8
|
||||
store i32 0, i32* %w, align 4
|
||||
store i32 1, i32* %gh, align 4
|
||||
%err_domain = getelementptr inbounds %error_union, %error_union* %z.f, i32 0, i32 0
|
||||
%0 = load i64, i64* %err_domain, align 8
|
||||
%not_err = icmp eq i64 %0, 0
|
||||
br i1 %not_err, label %after_check, label %catch_landing
|
||||
|
||||
after_check:
|
||||
%1 = load i32, i32* %z, align 4
|
||||
store i32 %1, i32* %x, align 4
|
||||
br label %phi_try_catch
|
||||
|
||||
catch_landing:
|
||||
br label %phi_try_catch
|
||||
|
||||
phi_try_catch:
|
||||
%val = phi i1 [ true, %after_check ], [ false, %catch_landing ]
|
||||
br i1 %val, label %and.rhs, label %and.phi
|
||||
|
||||
and.rhs:
|
||||
%err_domain1 = getelementptr inbounds %error_union, %error_union* %w.f, i32 0, i32 0
|
||||
%2 = load i64, i64* %err_domain1, align 8
|
||||
%not_err2 = icmp eq i64 %2, 0
|
||||
br i1 %not_err2, label %after_check3, label %catch_landing4
|
||||
|
||||
after_check3:
|
||||
%3 = load i32, i32* %w, align 4
|
||||
store i32 %3, i32* %gh, align 4
|
||||
br label %phi_try_catch5
|
||||
|
||||
catch_landing4:
|
||||
br label %phi_try_catch5
|
||||
|
||||
phi_try_catch5:
|
||||
%val6 = phi i1 [ true, %after_check3 ], [ false, %catch_landing4 ]
|
||||
br label %and.phi
|
||||
|
||||
and.phi:
|
||||
%val7 = phi i1 [ false, %phi_try_catch ], [ %val6, %phi_try_catch5 ]
|
||||
br i1 %val7, label %if.then, label %if.exit
|
||||
|
||||
if.then:
|
||||
%4 = load i32, i32* %x, align 4
|
||||
%5 = load i32, i32* %gh, align 4
|
||||
%6 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([19 x i8], [19 x i8]* @.str, i32 0, i32 0), i32 %4, i32 %5)
|
||||
br label %if.exit
|
||||
|
||||
if.exit:
|
||||
%err_domain8 = getelementptr inbounds %error_union, %error_union* %z.f, i32 0, i32 0
|
||||
%7 = load i64, i64* %err_domain8, align 8
|
||||
%not_err9 = icmp eq i64 %7, 0
|
||||
br i1 %not_err9, label %after_check10, label %catch_landing14
|
||||
|
||||
after_check10:
|
||||
%8 = load i32, i32* %z, align 4
|
||||
%err_domain11 = getelementptr inbounds %error_union, %error_union* %w.f, i32 0, i32 0
|
||||
%9 = load i64, i64* %err_domain11, align 8
|
||||
%not_err12 = icmp eq i64 %9, 0
|
||||
br i1 %not_err12, label %after_check13, label %catch_landing14
|
||||
|
||||
after_check13:
|
||||
%10 = load i32, i32* %w, align 4
|
||||
%add = add i32 %8, %10
|
||||
store i32 %add, i32* %x, align 4
|
||||
br label %phi_try_catch15
|
||||
|
||||
catch_landing14:
|
||||
br label %phi_try_catch15
|
||||
|
||||
phi_try_catch15:
|
||||
%val16 = phi i1 [ true, %after_check13 ], [ false, %catch_landing14 ]
|
||||
br i1 %val16, label %if.then17, label %if.exit18
|
||||
|
||||
if.then17:
|
||||
%11 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([6 x i8], [6 x i8]* @.str.1, i32 0, i32 0))
|
||||
br label %if.exit18
|
||||
|
||||
if.exit18:
|
||||
%12 = bitcast %error_union* %e to i8*
|
||||
call void @llvm.memset.p0i8.i64(i8* align 8 %12, i8 0, i64 16, i1 false)
|
||||
%err_domain19 = getelementptr inbounds %error_union, %error_union* %z.f, i32 0, i32 0
|
||||
%13 = load i64, i64* %err_domain19, align 8
|
||||
%not_err20 = icmp eq i64 %13, 0
|
||||
br i1 %not_err20, label %after_check21, label %error
|
||||
|
||||
error:
|
||||
%14 = bitcast %error_union* %e to i8*
|
||||
%15 = bitcast %error_union* %z.f to i8*
|
||||
call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %14, i8* align 8 %15, i32 16, i1 false)
|
||||
br label %catch_landing22
|
||||
|
||||
after_check21:
|
||||
br label %phi_try_catch23
|
||||
|
||||
catch_landing22:
|
||||
br label %phi_try_catch23
|
||||
|
||||
phi_try_catch23:
|
||||
%val24 = phi i1 [ false, %after_check21 ], [ true, %catch_landing22 ]
|
||||
br i1 %val24, label %if.then25, label %if.exit26
|
||||
|
||||
if.then25:
|
||||
%16 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([10 x i8], [10 x i8]* @.str.2, i32 0, i32 0))
|
||||
br label %if.exit26
|
||||
|
||||
if.exit26:
|
||||
ret void
|
||||
106
test/test_suite/errors/try_catch_unwrapping_while_if.c3
Normal file
106
test/test_suite/errors/try_catch_unwrapping_while_if.c3
Normal file
@@ -0,0 +1,106 @@
|
||||
|
||||
extern func int printf(char* fmt, ...);
|
||||
|
||||
extern func int! err();
|
||||
errtype FooErr { int x; }
|
||||
func void test1()
|
||||
{
|
||||
int! z = 234;
|
||||
if (try z)
|
||||
{
|
||||
int y = z;
|
||||
z = 12;
|
||||
z = FooErr({1})!; // #error: The variable is unwrapped in this context
|
||||
}
|
||||
}
|
||||
|
||||
func void test2()
|
||||
{
|
||||
int! z = 234;
|
||||
if (try z || 1)
|
||||
{
|
||||
z = 12;
|
||||
z = FooErr({1})!;
|
||||
}
|
||||
}
|
||||
func void test3()
|
||||
{
|
||||
int! z = 234;
|
||||
int! w = 123;
|
||||
if (try z || try w)
|
||||
{
|
||||
int y = z; // #error: 'int!' cannot be converted into 'int'
|
||||
y = w;
|
||||
}
|
||||
}
|
||||
|
||||
func void test4()
|
||||
{
|
||||
int! z = 234;
|
||||
int! w = 123;
|
||||
if (try z && ((try w) && 1))
|
||||
{
|
||||
int y = z;
|
||||
y = w;
|
||||
}
|
||||
}
|
||||
|
||||
func void test5()
|
||||
{
|
||||
int! z = 234;
|
||||
int! w = 123;
|
||||
if (try z && try z)
|
||||
{
|
||||
int y = z;
|
||||
}
|
||||
}
|
||||
|
||||
func void test6()
|
||||
{
|
||||
int! z = 234;
|
||||
int! w = 123;
|
||||
while (try z)
|
||||
{
|
||||
int y = z;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
func void test7()
|
||||
{
|
||||
int! z = 234;
|
||||
int! w = 123;
|
||||
if (catch z)
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
int y = z;
|
||||
}
|
||||
}
|
||||
|
||||
func void test8()
|
||||
{
|
||||
int! z = 234;
|
||||
int! w = 123;
|
||||
if (catch z && 1)
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
int y = z; // #error: 'int!' cannot be converted into 'int'
|
||||
}
|
||||
}
|
||||
func void test9()
|
||||
{
|
||||
int! z = 234;
|
||||
int! w = 123;
|
||||
anyerr e;
|
||||
if (catch e = z)
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
int y = z;
|
||||
}
|
||||
}
|
||||
14
test/test_suite/errors/try_with_assign_to_failable.c3
Normal file
14
test/test_suite/errors/try_with_assign_to_failable.c3
Normal file
@@ -0,0 +1,14 @@
|
||||
func void test()
|
||||
{
|
||||
int! z;
|
||||
int! w;
|
||||
try w = z; // #error: A 'try' assignment is not possible with failable on the left hand side, did you intend 'try (variable = expr)
|
||||
}
|
||||
|
||||
func int! err();
|
||||
func void test2()
|
||||
{
|
||||
int! z;
|
||||
int! w;
|
||||
try err() = z; // #error: This expression is not assignable, did you make a mistake
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
// #skip
|
||||
// #target: x64_darwin
|
||||
|
||||
extern func char*! readLine();
|
||||
@@ -7,7 +8,7 @@ extern func int printf(char* fmt, ...);
|
||||
|
||||
func void main()
|
||||
{
|
||||
try (int val = atoi(readLine()))
|
||||
tryx (int val = atoi(readLine()))
|
||||
{
|
||||
printf("You typed the number %d\n", val);
|
||||
return;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
|
||||
// #skip
|
||||
extern func char*! readLine();
|
||||
extern func int atoi(char*);
|
||||
|
||||
@@ -7,10 +7,10 @@ extern func int printf(char* fmt, ...);
|
||||
func void main()
|
||||
{
|
||||
char*! line = readLine();
|
||||
try (line)
|
||||
tryx (line)
|
||||
{
|
||||
int! val = atoi(line);
|
||||
try (val)
|
||||
tryx (val)
|
||||
{
|
||||
printf("You typed the number %d\n", val);
|
||||
return;
|
||||
|
||||
@@ -17,32 +17,32 @@ func void test9()
|
||||
|
||||
func void test10()
|
||||
{
|
||||
10 = 20; // #error: Expression is not assignable
|
||||
10 = 20; // #error: This expression is not assignable
|
||||
}
|
||||
|
||||
func void test11()
|
||||
{
|
||||
'10' = '20'; // #error: Expression is not assignable
|
||||
'10' = '20'; // #error: This expression is not assignable
|
||||
}
|
||||
|
||||
func void test12()
|
||||
{
|
||||
true = false; // #error: Expression is not assignable
|
||||
true = false; // #error: This expression is not assignable
|
||||
}
|
||||
|
||||
func void test13()
|
||||
{
|
||||
"a" = "b"; // #error: Expression is not assignable
|
||||
"a" = "b"; // #error: This expression is not assignable
|
||||
}
|
||||
|
||||
func void test14()
|
||||
{
|
||||
1.2 = 1.3; // #error: Expression is not assignable
|
||||
1.2 = 1.3; // #error: This expression is not assignable
|
||||
}
|
||||
|
||||
func void test15()
|
||||
{
|
||||
null = null; // #error: Expression is not assignable
|
||||
null = null; // #error: This expression is not assignable
|
||||
}
|
||||
|
||||
func void test16()
|
||||
|
||||
@@ -2,34 +2,34 @@ define Number = int;
|
||||
|
||||
func void test1()
|
||||
{
|
||||
10 = 20; // #error: Expression is not assignable.
|
||||
10 = 20; // #error: This expression is not assignable
|
||||
}
|
||||
|
||||
func void test2()
|
||||
{
|
||||
"foo" = "bar"; // #error: Expression is not assignable.
|
||||
"foo" = "bar"; // #error: This expression is not assignable
|
||||
}
|
||||
|
||||
func void test3()
|
||||
{
|
||||
true = false; // #error: Expression is not assignable.
|
||||
true = false; // #error: This expression is not assignable
|
||||
}
|
||||
|
||||
func void test4()
|
||||
{
|
||||
'c' = 'd'; // #error: Expression is not assignable.
|
||||
'c' = 'd'; // #error: This expression is not assignable
|
||||
}
|
||||
|
||||
func void test5()
|
||||
{
|
||||
3.14 = 2.14; // #error: Expression is not assignable.
|
||||
3.14 = 2.14; // #error: This expression is not assignable
|
||||
}
|
||||
|
||||
func void test21()
|
||||
{
|
||||
int a = 0;
|
||||
int b = 2;
|
||||
a++ = b++; // #error: Expression is not assignable
|
||||
a++ = b++; // #error: This expression is not assignable
|
||||
}
|
||||
|
||||
func void test22()
|
||||
|
||||
@@ -317,7 +317,7 @@ define i32 @test.helo(double %0, %Bobo* byval align 8 %1)
|
||||
%7 = bitcast %Bobo* %indirectarg to i8*
|
||||
%8 = bitcast %Bobo* %c to i8*
|
||||
call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %7, i8* align 4 %8, i32 20, i1 false)
|
||||
%9 = call i32 @test.helo(double 1.000000e+00, %Bobo* %indirectarg)
|
||||
%9 = call i32 @test.helo(double 1.000000e+00, %Bobo* byval align 8 %indirectarg)
|
||||
ret i32 1
|
||||
}
|
||||
|
||||
|
||||
@@ -317,7 +317,7 @@ define i32 @test.helo(double %0, %Bobo* align 4 %1)
|
||||
%7 = bitcast %Bobo* %indirectarg to i8*
|
||||
%8 = bitcast %Bobo* %c to i8*
|
||||
call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 4 %7, i8* align 4 %8, i32 20, i1 false)
|
||||
%9 = call i32 @test.helo(double 1.000000e+00, %Bobo* %indirectarg)
|
||||
%9 = call i32 @test.helo(double 1.000000e+00, %Bobo* align 4 %indirectarg)
|
||||
ret i32 1
|
||||
}
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ func void test9()
|
||||
{
|
||||
const char A = 1;
|
||||
char b = A;
|
||||
A = b; // #error: Expression is not assignable
|
||||
A = b; // #error: This expression is not assignable, did you make
|
||||
}
|
||||
|
||||
func void test10()
|
||||
|
||||
12
test/test_suite/types/enum_param.c3
Normal file
12
test/test_suite/types/enum_param.c3
Normal file
@@ -0,0 +1,12 @@
|
||||
enum Foo
|
||||
{
|
||||
A, B
|
||||
}
|
||||
|
||||
func void test(Foo f)
|
||||
{}
|
||||
|
||||
func void test2()
|
||||
{
|
||||
test(Foo.A);
|
||||
}
|
||||
Reference in New Issue
Block a user