Fixes to aarch64 float struct return. Missing byval and align on calls. This *breaks* try-catch.

This commit is contained in:
Christoffer Lerno
2021-08-11 12:38:19 +02:00
committed by Christoffer Lerno
parent afeb555e2f
commit da76777ee4
30 changed files with 1099 additions and 205 deletions

View File

@@ -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)

View File

@@ -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);

View File

@@ -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;

View File

@@ -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

View File

@@ -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

View File

@@ -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:

View File

@@ -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);

View File

@@ -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;

View File

@@ -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);

View File

@@ -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 },

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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:

View File

@@ -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)

View File

@@ -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;

View File

@@ -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:

View File

@@ -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

View 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
}

View 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

View 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

View 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;
}
}

View 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
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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()

View File

@@ -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()

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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()

View File

@@ -0,0 +1,12 @@
enum Foo
{
A, B
}
func void test(Foo f)
{}
func void test2()
{
test(Foo.A);
}